Module:SMWUtils: Difference between revisions

Add debug function to diagnose parsing issues (via update-page on MediaWiki MCP Server)
Fix deduplication: key on printout columns instead of subject (subobject subjects are unique per-input) (via update-page on MediaWiki MCP Server)
 
(3 intermediate revisions by the same user not shown)
Line 14: Line 14:


-- Parse an HTML table from #ask broadtable format into rows of cells
-- Parse an HTML table from #ask broadtable format into rows of cells
-- Filters out the "further results" row
local function parseHtmlTable(html)
local function parseHtmlTable(html)
     local rows = {}
     local rows = {}
Line 19: Line 20:
      
      
     for rowContent in html:gmatch('<tr[^>]*>(.-)</tr>') do
     for rowContent in html:gmatch('<tr[^>]*>(.-)</tr>') do
         local cells = {}
         -- Skip "further results" rows
        for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
        if not rowContent:match('smw%-broadtable%-furtherresults') then
            -- Keep HTML intact for links
            local cells = {}
            table.insert(cells, trim(cellContent) or '')
            for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
        end
                -- Keep HTML intact for links
        if #cells > 0 then
                table.insert(cells, trim(cellContent) or '')
            table.insert(rows, cells)
            end
            if #cells > 0 then
                table.insert(rows, cells)
            end
         end
         end
     end
     end
Line 36: Line 40:
     if not s then return nil end
     if not s then return nil end
     return trim(s:gsub('<[^>]+>', ''):gsub('&[^;]+;', ''))
     return trim(s:gsub('<[^>]+>', ''):gsub('&[^;]+;', ''))
end
-- Deduplicate rows by printout columns, keeping the first occurrence
-- Uses stripped text of printout columns (2..n+1) as the dedup key,
-- since subobject queries produce unique subjects (e.g. Page#sub1, Page#sub2)
-- but identical printout values for the same parent page
local function deduplicateRows(rows, numPrintouts)
    local seen = {}
    local deduped = {}
    for _, row in ipairs(rows) do
        local keyParts = {}
        for j = 2, numPrintouts + 1 do
            table.insert(keyParts, stripHtml(row[j]) or '')
        end
        local key = table.concat(keyParts, '|')
        if not seen[key] then
            seen[key] = true
            table.insert(deduped, row)
        end
    end
    return deduped
end
end


Line 85: Line 110:
     end
     end
      
      
     -- Show what we'd calculate as min
     -- Show what we'd calculate as min (corrected: +2 for subject column)
     if #rows > 0 then
     if #rows > 0 then
         local minValueCol = #printoutProps + 1
         local minValueCol = #printoutProps + 2  -- +1 for subject column, +1 for minProperty column
         table.insert(output, "'''Min value column index:''' " .. minValueCol .. "\n")
        table.insert(output, "'''Printout count:''' " .. #printoutProps .. "\n")
         table.insert(output, "'''Min value column index:''' " .. minValueCol .. " (printouts + 2 for subject col)\n")
         if rows[1][minValueCol] then
         if rows[1][minValueCol] then
             local minText = stripHtml(rows[1][minValueCol])
             local minText = stripHtml(rows[1][minValueCol])
Line 107: Line 133:
--  printouts: Comma-separated properties to display
--  printouts: Comma-separated properties to display
--  headers: Comma-separated header names
--  headers: Comma-separated header names
--  mainlabel: Optional header for the subject/page name column
--  format: table, ul, ol (default: table)
--  format: table, ul, ol (default: table)
--  intro: Custom intro text
--  intro: Custom intro text
Line 117: Line 144:
     local headers = trim(args.headers) or ''
     local headers = trim(args.headers) or ''
     local intro = trim(args.intro)
     local intro = trim(args.intro)
    local mainlabel = trim(args.mainlabel)
      
      
     if not minProperty then
     if not minProperty then
Line 151: Line 179:
     end
     end
      
      
     -- The minProperty value is in the last column (we added it with =#)
     -- broadtable format: Column 1 = subject, Columns 2..n+1 = printouts, Column n+2 = minProperty
     -- Find the minimum value from the first row (already sorted asc)
     -- So minValueCol = #printoutProps + 2
     local minValueCol = #printoutProps + 1
     local minValueCol = #printoutProps + 2
     local minValueText = stripHtml(rows[1][minValueCol])
     local minValueText = stripHtml(rows[1][minValueCol])
     local minValue = tonumber(minValueText)
     local minValue = tonumber(minValueText)
Line 169: Line 197:
         end
         end
     end
     end
   
    -- Deduplicate (multiple subobjects on same page produce duplicate rows)
    filtered = deduplicateRows(filtered, #printoutProps)
      
      
     -- Build headers list
     -- Build headers list
Line 179: Line 210:
      
      
     if format == 'ul' then
     if format == 'ul' then
         return p._formatList(filtered, printoutProps, headerList, 'ul', introText)
         return p._formatList(filtered, printoutProps, headerList, 'ul', introText, mainlabel)
     elseif format == 'ol' then
     elseif format == 'ol' then
         return p._formatList(filtered, printoutProps, headerList, 'ol', introText)
         return p._formatList(filtered, printoutProps, headerList, 'ol', introText, mainlabel)
     else
     else
         return p._formatTable(filtered, printoutProps, headerList, introText)
         return p._formatTable(filtered, printoutProps, headerList, introText, mainlabel)
     end
     end
end
end
Line 196: Line 227:
     local headers = trim(args.headers) or ''
     local headers = trim(args.headers) or ''
     local intro = trim(args.intro)
     local intro = trim(args.intro)
    local mainlabel = trim(args.mainlabel)
      
      
     if not maxProperty then
     if not maxProperty then
Line 229: Line 261:
     end
     end
      
      
     local maxValueCol = #printoutProps + 1
    -- broadtable format: Column 1 = subject, Columns 2..n+1 = printouts, Column n+2 = maxProperty
     local maxValueCol = #printoutProps + 2
     local maxValueText = stripHtml(rows[1][maxValueCol])
     local maxValueText = stripHtml(rows[1][maxValueCol])
     local maxValue = tonumber(maxValueText)
     local maxValue = tonumber(maxValueText)
Line 245: Line 278:
         end
         end
     end
     end
   
    -- Deduplicate (multiple subobjects on same page produce duplicate rows)
    filtered = deduplicateRows(filtered, #printoutProps)
      
      
     -- Build headers list
     -- Build headers list
Line 255: Line 291:
      
      
     if format == 'ul' then
     if format == 'ul' then
         return p._formatList(filtered, printoutProps, headerList, 'ul', introText)
         return p._formatList(filtered, printoutProps, headerList, 'ul', introText, mainlabel)
     elseif format == 'ol' then
     elseif format == 'ol' then
         return p._formatList(filtered, printoutProps, headerList, 'ol', introText)
         return p._formatList(filtered, printoutProps, headerList, 'ol', introText, mainlabel)
     else
     else
         return p._formatTable(filtered, printoutProps, headerList, introText)
         return p._formatTable(filtered, printoutProps, headerList, introText, mainlabel)
     end
     end
end
end


-- Format results as a wikitable
-- Format results as a wikitable
function p._formatTable(rows, printouts, headers, introText)
-- Note: row[1] is subject, row[2..n+1] are printouts, row[n+2] is min/max value
function p._formatTable(rows, printouts, headers, introText, mainlabel)
     local output = {}
     local output = {}
      
      
Line 272: Line 309:
     -- Header row
     -- Header row
     table.insert(output, '|-\n! #')
     table.insert(output, '|-\n! #')
    if mainlabel then
        table.insert(output, ' !! ' .. mainlabel)
    end
     for i, prop in ipairs(printouts) do
     for i, prop in ipairs(printouts) do
         local headerText = headers[i] or prop:gsub('.*%.', ''):gsub('Has ', '')
         local headerText = headers[i] or prop:gsub('.*%.', ''):gsub('Has ', '')
Line 278: Line 318:
     table.insert(output, '\n')
     table.insert(output, '\n')
      
      
     -- Data rows (only show printout columns, not the min/max value column)
     -- Data rows: printout columns start at index 2 (after subject column)
     for i, row in ipairs(rows) do
     for i, row in ipairs(rows) do
         local tr = '|-\n| ' .. tostring(i)
         local tr = '|-\n| ' .. tostring(i)
        if mainlabel then
            tr = tr .. ' || ' .. (row[1] or '')
        end
         for j = 1, #printouts do
         for j = 1, #printouts do
             tr = tr .. ' || ' .. (row[j] or '')
            -- Printout j is in column j+1 (offset by subject column)
             tr = tr .. ' || ' .. (row[j + 1] or '')
         end
         end
         table.insert(output, tr .. '\n')
         table.insert(output, tr .. '\n')
Line 293: Line 337:


-- Format results as a list
-- Format results as a list
function p._formatList(rows, printouts, headers, listType, introText)
function p._formatList(rows, printouts, headers, listType, introText, mainlabel)
     local output = {}
     local output = {}
     local bullet = listType == 'ol' and '#' or '*'
     local bullet = listType == 'ol' and '#' or '*'
Line 301: Line 345:
     for _, row in ipairs(rows) do
     for _, row in ipairs(rows) do
         local parts = {}
         local parts = {}
        if mainlabel then
            local subject = row[1]
            if subject and subject ~= '' then
                table.insert(parts, subject)
            end
        end
         for j = 1, #printouts do
         for j = 1, #printouts do
             if row[j] and row[j] ~= '' then
             -- Printout j is in column j+1 (offset by subject column)
                 table.insert(parts, row[j])
            local cell = row[j + 1]
            if cell and cell ~= '' then
                 table.insert(parts, cell)
             end
             end
         end
         end
MediaWiki Appliance - Powered by TurnKey Linux