Module:BrandSeries: Difference between revisions
Fix: preprocess final wikitext output to render links and table (via update-page on MediaWiki MCP Server) |
Use Category:CRTs without series for unassigned link (SMW !+ queries don't work) (via update-page on MediaWiki MCP Server) |
||
| (15 intermediate revisions by the same user not shown) | |||
| Line 10: | Line 10: | ||
s = s:gsub('^%s+', ''):gsub('%s+$', '') | s = s:gsub('^%s+', ''):gsub('%s+$', '') | ||
if s == '' then return nil end | if s == '' then return nil end | ||
return s | |||
end | |||
-- Encode a string for use in Special:Ask URL path | |||
local function encodeForAsk(s) | |||
-- Special:Ask uses a custom encoding: [ = -5B, ] = -5D, space = _, etc. | |||
s = s:gsub('%[', '-5B') | |||
s = s:gsub('%]', '-5D') | |||
s = s:gsub(' ', '_') | |||
s = s:gsub('%+', '-2B') | |||
s = s:gsub('!', '-21') | |||
return s | return s | ||
end | end | ||
function p.seriesTable(frame) | function p.seriesTable(frame) | ||
local args | -- Try to get brand from frame.args first (direct #invoke call) | ||
local parent = frame:getParent() | -- then from parent.args (called through a template) | ||
local brand = trim(frame.args.brand) or trim(frame.args[1]) | |||
if not brand then | |||
local parent = frame:getParent() | |||
if parent then | |||
brand = trim(parent.args.brand) or trim(parent.args[1]) | |||
end | |||
end | end | ||
if not brand then | if not brand then | ||
| Line 26: | Line 40: | ||
end | end | ||
-- | -- Query total count of all CRTs for this brand | ||
local | local totalQuery = string.format( | ||
local | '{{#ask:[[Category:CRT models]][[Has brand::%s]]|format=count}}', | ||
brand | |||
) | |||
local totalResult = frame:preprocess(totalQuery) | |||
local totalCount = tonumber(trim(totalResult)) or 0 | |||
-- Query all CRTs for this brand with their series using broadtable | |||
local crtQuery = string.format( | |||
'{{#ask:[[Category:CRT models]][[Has brand::%s]][[Has series::+]]|?Has series|format=broadtable|link=none|headers=hide|limit=1000|searchlabel=}}', | |||
brand | |||
) | |||
local crtResult = frame:preprocess(crtQuery) | |||
-- | -- Count occurrences of each series by parsing HTML table | ||
local | local seriesCounts = {} | ||
for | local assignedCount = 0 | ||
if crtResult and crtResult ~= '' then | |||
-- Parse each table row | |||
for rowContent in crtResult:gmatch('<tr[^>]*>(.-)</tr>') do | |||
local cells = {} | |||
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do | |||
-- Strip HTML tags | |||
local cleanContent = cellContent:gsub('<[^>]+>', '') | |||
table.insert(cells, trim(cleanContent)) | |||
end | |||
-- cells[1] = page name, cells[2] = series | |||
if #cells >= 2 and cells[2] then | |||
local series = cells[2] | |||
-- Filter out invalid series values like "+" | |||
if series and series ~= '' and series ~= '+' then | |||
seriesCounts[series] = (seriesCounts[series] or 0) + 1 | |||
assignedCount = assignedCount + 1 | |||
end | |||
end | |||
end | end | ||
end | end | ||
-- | -- Calculate unassigned as total minus assigned | ||
local unassignedCount = totalCount - assignedCount | |||
-- | -- Convert to sorted array | ||
local rows = {} | local rows = {} | ||
for | for series, count in pairs(seriesCounts) do | ||
table.insert(rows, { | |||
series = series, | |||
count = count | |||
}) | |||
end | end | ||
-- If no series found | -- Sort by series name | ||
if #rows == 0 then | table.sort(rows, function(a, b) return a.series < b.series end) | ||
return " | |||
-- If no series found and no unassigned, return nothing | |||
if #rows == 0 and unassignedCount <= 0 then | |||
return "" | |||
end | end | ||
-- Build the table as wikitext | -- Build the table as wikitext (so links render properly) | ||
local | local lines = {} | ||
table.insert( | table.insert(lines, '{| class="wikitable"') | ||
table.insert( | table.insert(lines, '! Series !! Count') | ||
-- Series rows | |||
for _, row in ipairs(rows) do | for _, row in ipairs(rows) do | ||
table.insert( | local seriesLink = '[[' .. row.series .. ']]' | ||
-- | table.insert(lines, '|-') | ||
local | table.insert(lines, '| ' .. seriesLink .. ' || ' .. tostring(row.count)) | ||
end | |||
) | -- Unassigned row (if any) with link to Special:Ask | ||
table.insert( | if unassignedCount > 0 then | ||
-- Use Category:CRTs without series instead of [[Has series::!+]] | |||
-- because SMW's negated existence queries don't work correctly | |||
local askQuery = string.format('[[Category:CRTs without series]][[Has brand::%s]]', brand) | |||
local askUrl = 'https://wiki.everycrt.com/wiki/Special:Ask/' .. encodeForAsk(askQuery) | |||
-- Use wikitext external link syntax with full URL | |||
local unassignedLink = "[" .. askUrl .. " ''(Unassigned)'']" | |||
table.insert(lines, '|-') | |||
table.insert(lines, '| ' .. unassignedLink .. ' || ' .. tostring(unassignedCount)) | |||
end | end | ||
table.insert( | table.insert(lines, '|}') | ||
return table.concat(lines, '\n') | |||
return | |||
end | end | ||
return p | return p | ||
Latest revision as of 15:17, 21 January 2026
Documentation for this module may be created at Module:BrandSeries/doc
-- Module:BrandSeries
-- Generates a series breakdown table for a brand's CRTs
-- Shows only series that have CRTs for the specified brand
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
-- Encode a string for use in Special:Ask URL path
local function encodeForAsk(s)
-- Special:Ask uses a custom encoding: [ = -5B, ] = -5D, space = _, etc.
s = s:gsub('%[', '-5B')
s = s:gsub('%]', '-5D')
s = s:gsub(' ', '_')
s = s:gsub('%+', '-2B')
s = s:gsub('!', '-21')
return s
end
function p.seriesTable(frame)
-- Try to get brand from frame.args first (direct #invoke call)
-- then from parent.args (called through a template)
local brand = trim(frame.args.brand) or trim(frame.args[1])
if not brand then
local parent = frame:getParent()
if parent then
brand = trim(parent.args.brand) or trim(parent.args[1])
end
end
if not brand then
return '<span class="error">Error: brand parameter required</span>'
end
-- Query total count of all CRTs for this brand
local totalQuery = string.format(
'{{#ask:[[Category:CRT models]][[Has brand::%s]]|format=count}}',
brand
)
local totalResult = frame:preprocess(totalQuery)
local totalCount = tonumber(trim(totalResult)) or 0
-- Query all CRTs for this brand with their series using broadtable
local crtQuery = string.format(
'{{#ask:[[Category:CRT models]][[Has brand::%s]][[Has series::+]]|?Has series|format=broadtable|link=none|headers=hide|limit=1000|searchlabel=}}',
brand
)
local crtResult = frame:preprocess(crtQuery)
-- Count occurrences of each series by parsing HTML table
local seriesCounts = {}
local assignedCount = 0
if crtResult and crtResult ~= '' then
-- Parse each table row
for rowContent in crtResult:gmatch('<tr[^>]*>(.-)</tr>') do
local cells = {}
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
-- Strip HTML tags
local cleanContent = cellContent:gsub('<[^>]+>', '')
table.insert(cells, trim(cleanContent))
end
-- cells[1] = page name, cells[2] = series
if #cells >= 2 and cells[2] then
local series = cells[2]
-- Filter out invalid series values like "+"
if series and series ~= '' and series ~= '+' then
seriesCounts[series] = (seriesCounts[series] or 0) + 1
assignedCount = assignedCount + 1
end
end
end
end
-- Calculate unassigned as total minus assigned
local unassignedCount = totalCount - assignedCount
-- Convert to sorted array
local rows = {}
for series, count in pairs(seriesCounts) do
table.insert(rows, {
series = series,
count = count
})
end
-- Sort by series name
table.sort(rows, function(a, b) return a.series < b.series end)
-- If no series found and no unassigned, return nothing
if #rows == 0 and unassignedCount <= 0 then
return ""
end
-- Build the table as wikitext (so links render properly)
local lines = {}
table.insert(lines, '{| class="wikitable"')
table.insert(lines, '! Series !! Count')
-- Series rows
for _, row in ipairs(rows) do
local seriesLink = '[[' .. row.series .. ']]'
table.insert(lines, '|-')
table.insert(lines, '| ' .. seriesLink .. ' || ' .. tostring(row.count))
end
-- Unassigned row (if any) with link to Special:Ask
if unassignedCount > 0 then
-- Use Category:CRTs without series instead of [[Has series::!+]]
-- because SMW's negated existence queries don't work correctly
local askQuery = string.format('[[Category:CRTs without series]][[Has brand::%s]]', brand)
local askUrl = 'https://wiki.everycrt.com/wiki/Special:Ask/' .. encodeForAsk(askQuery)
-- Use wikitext external link syntax with full URL
local unassignedLink = "[" .. askUrl .. " ''(Unassigned)'']"
table.insert(lines, '|-')
table.insert(lines, '| ' .. unassignedLink .. ' || ' .. tostring(unassignedCount))
end
table.insert(lines, '|}')
return table.concat(lines, '\n')
end
return p