Documentation for this module may be created at Module:SMWUtils/doc
-- Module:SMWUtils
-- Utility functions for Semantic MediaWiki queries
-- Includes helpers for finding all results matching min/max values
local p = {}
local function trim(s)
if s == nil then return nil end
s = tostring(s)
s = s:gsub('^%s+', ''):gsub('%s+$', '')
if s == '' then return nil end
return s
end
--- Find all results where a property equals its minimum value across the result set
-- @param frame Frame object with args:
-- conditions: SMW query conditions (e.g., "[[Has video signal format::Component (YPbPr)]]")
-- minProperty: Property to find minimum of (e.g., "Has CRT model.Has screen size inches")
-- printouts: Comma-separated list of properties to display
-- format: Output format (table, ul, ol) - default "table"
-- headers: Comma-separated header names for table format
-- @return Wikitext output
function p.findAllWithMin(frame)
local args = frame.args
local conditions = trim(args.conditions) or ''
local minProperty = trim(args.minProperty)
local printouts = trim(args.printouts) or ''
local format = trim(args.format) or 'table'
local headers = trim(args.headers) or ''
if not minProperty then
return '<span class="error">Error: minProperty is required</span>'
end
-- Build the printout list
local printoutList = {}
for prop in printouts:gmatch('[^,]+') do
table.insert(printoutList, '?' .. trim(prop))
end
-- Always include the minProperty for comparison
table.insert(printoutList, '?' .. minProperty)
-- Build and execute the query
local queryString = conditions .. '\n' .. table.concat(printoutList, '\n') .. '\n|limit=500'
local results = mw.smw.ask(queryString)
if not results or #results == 0 then
return "No results found."
end
-- Find the minimum value
local minValue = nil
for _, row in ipairs(results) do
local val = row[minProperty]
if val then
local numVal = tonumber(val)
if numVal then
if minValue == nil or numVal < minValue then
minValue = numVal
end
end
end
end
if minValue == nil then
return "Could not determine minimum value."
end
-- Filter results to only those matching the minimum
local filtered = {}
for _, row in ipairs(results) do
local val = row[minProperty]
if val and tonumber(val) == minValue then
table.insert(filtered, row)
end
end
-- Build output based on format
local headerList = {}
for h in headers:gmatch('[^,]+') do
table.insert(headerList, trim(h))
end
local printoutProps = {}
for prop in printouts:gmatch('[^,]+') do
table.insert(printoutProps, trim(prop))
end
if format == 'table' then
return p._formatTable(filtered, printoutProps, headerList, minProperty, minValue)
elseif format == 'ul' or format == 'ol' then
return p._formatList(filtered, printoutProps, format, minProperty, minValue)
else
return p._formatTable(filtered, printoutProps, headerList, minProperty, minValue)
end
end
--- Find all results where a property equals its maximum value
function p.findAllWithMax(frame)
local args = frame.args
local conditions = trim(args.conditions) or ''
local maxProperty = trim(args.maxProperty)
local printouts = trim(args.printouts) or ''
local format = trim(args.format) or 'table'
local headers = trim(args.headers) or ''
if not maxProperty then
return '<span class="error">Error: maxProperty is required</span>'
end
-- Build the printout list
local printoutList = {}
for prop in printouts:gmatch('[^,]+') do
table.insert(printoutList, '?' .. trim(prop))
end
table.insert(printoutList, '?' .. maxProperty)
-- Build and execute the query
local queryString = conditions .. '\n' .. table.concat(printoutList, '\n') .. '\n|limit=500'
local results = mw.smw.ask(queryString)
if not results or #results == 0 then
return "No results found."
end
-- Find the maximum value
local maxValue = nil
for _, row in ipairs(results) do
local val = row[maxProperty]
if val then
local numVal = tonumber(val)
if numVal then
if maxValue == nil or numVal > maxValue then
maxValue = numVal
end
end
end
end
if maxValue == nil then
return "Could not determine maximum value."
end
-- Filter results to only those matching the maximum
local filtered = {}
for _, row in ipairs(results) do
local val = row[maxProperty]
if val and tonumber(val) == maxValue then
table.insert(filtered, row)
end
end
-- Build output
local headerList = {}
for h in headers:gmatch('[^,]+') do
table.insert(headerList, trim(h))
end
local printoutProps = {}
for prop in printouts:gmatch('[^,]+') do
table.insert(printoutProps, trim(prop))
end
if format == 'table' then
return p._formatTable(filtered, printoutProps, headerList, maxProperty, maxValue)
elseif format == 'ul' or format == 'ol' then
return p._formatList(filtered, printoutProps, format, maxProperty, maxValue)
else
return p._formatTable(filtered, printoutProps, headerList, maxProperty, maxValue)
end
end
-- Internal: Format results as a wikitable
function p._formatTable(results, printouts, headers, valueProperty, value)
local html = mw.html.create('div')
html:wikitext("''Showing all results where " .. valueProperty .. " = " .. tostring(value) .. ":''\n\n")
local tbl = html:tag('table')
tbl:addClass('wikitable'):addClass('sortable')
-- Header row
local headerRow = tbl:tag('tr')
headerRow:tag('th'):wikitext('#')
for i, prop in ipairs(printouts) do
local headerText = headers[i] or prop:gsub('.*%.', ''):gsub('Has ', '')
headerRow:tag('th'):wikitext(headerText)
end
-- Data rows
for i, row in ipairs(results) do
local tr = tbl:tag('tr')
tr:tag('td'):wikitext(tostring(i))
for _, prop in ipairs(printouts) do
local val = row[prop]
local displayVal = ''
if val then
if type(val) == 'table' then
-- Handle linked values (pages)
if val.fulltext then
displayVal = '[[' .. val.fulltext .. ']]'
else
displayVal = table.concat(val, ', ')
end
else
displayVal = tostring(val)
end
end
tr:tag('td'):wikitext(displayVal)
end
end
return tostring(html)
end
-- Internal: Format results as a list
function p._formatList(results, printouts, listType, valueProperty, value)
local tag = listType == 'ol' and 'ol' or 'ul'
local html = mw.html.create('div')
html:wikitext("''Showing all results where " .. valueProperty .. " = " .. tostring(value) .. ":''\n\n")
local list = html:tag(tag)
for _, row in ipairs(results) do
local li = list:tag('li')
local parts = {}
for _, prop in ipairs(printouts) do
local val = row[prop]
if val then
if type(val) == 'table' and val.fulltext then
table.insert(parts, '[[' .. val.fulltext .. ']]')
else
table.insert(parts, tostring(val))
end
end
end
li:wikitext(table.concat(parts, ' - '))
end
return tostring(html)
end
return p