Add debug output to see query results (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)
 
(11 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 = frame.args
-- Try to get brand from frame.args first (direct #invoke call)
local parent = frame:getParent()
-- then from parent.args (called through a template)
if parent then
local brand = trim(frame.args.brand) or trim(frame.args[1])
args = parent.args
if not brand then
local parent = frame:getParent()
if parent then
brand = trim(parent.args.brand) or trim(parent.args[1])
end
end
end
local brand = trim(args.brand) or trim(args[1])
if not brand then
if not brand then
return '<span class="error">Error: brand parameter required</span>'
return '<span class="error">Error: brand parameter required</span>'
end
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
-- Query all CRTs for this brand with their series using broadtable
Line 32: Line 54:
)
)
local crtResult = frame:preprocess(crtQuery)
local crtResult = frame:preprocess(crtQuery)
-- DEBUG: show raw query result
local debug = '<pre>Query: ' .. crtQuery .. '\n\nResult length: ' .. tostring(#(crtResult or '')) .. '\n\nFirst 2000 chars:\n' .. (crtResult or ''):sub(1, 2000) .. '</pre>'
-- Count occurrences of each series by parsing HTML table
-- Count occurrences of each series by parsing HTML table
local seriesCounts = {}
local seriesCounts = {}
local assignedCount = 0
if crtResult and crtResult ~= '' then
if crtResult and crtResult ~= '' then
-- Parse each table row
-- Parse each table row
Line 50: Line 70:
if #cells >= 2 and cells[2] then
if #cells >= 2 and cells[2] then
local series = cells[2]
local series = cells[2]
if series and series ~= '' then
-- Filter out invalid series values like "+"
if series and series ~= '' and series ~= '+' then
seriesCounts[series] = (seriesCounts[series] or 0) + 1
seriesCounts[series] = (seriesCounts[series] or 0) + 1
assignedCount = assignedCount + 1
end
end
end
end
end
end
end
end
-- Calculate unassigned as total minus assigned
local unassignedCount = totalCount - assignedCount
-- Convert to sorted array
-- Convert to sorted array
Line 69: Line 94:
table.sort(rows, function(a, b) return a.series < b.series end)
table.sort(rows, function(a, b) return a.series < b.series end)
-- If no series found for this brand
-- If no series found and no unassigned, return nothing
if #rows == 0 then
if #rows == 0 and unassignedCount <= 0 then
return debug .. "<i>No series-assigned models for this brand.</i>"
return ""
end
end
-- Build the table as HTML
-- Build the table as wikitext (so links render properly)
local tbl = mw.html.create('table')
local lines = {}
tbl:addClass('wikitable')
table.insert(lines, '{| class="wikitable"')
table.insert(lines, '! Series !! Count')
-- Header row
-- Series rows
local headerRow = tbl:tag('tr')
for _, row in ipairs(rows) do
headerRow:tag('th'):wikitext('Series')
local seriesLink = '[[' .. row.series .. ']]'
headerRow:tag('th'):wikitext('Count')
table.insert(lines, '|-')
headerRow:tag('th'):wikitext('Browse')
table.insert(lines, '| ' .. seriesLink .. ' || ' .. tostring(row.count))
end
-- Data rows
-- Unassigned row (if any) with link to Special:Ask
for _, row in ipairs(rows) do
if unassignedCount > 0 then
local tr = tbl:tag('tr')
-- 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)'']"
-- Series link - build as HTML anchor
table.insert(lines, '|-')
local seriesLink = mw.html.create('a')
table.insert(lines, '| ' .. unassignedLink .. ' || ' .. tostring(unassignedCount))
seriesLink:attr('href', '/wiki/' .. mw.uri.encode(row.series, 'WIKI'))
seriesLink:attr('title', row.series)
seriesLink:wikitext(row.series)
tr:tag('td'):node(seriesLink)
-- Count
tr:tag('td'):wikitext(tostring(row.count))
-- Browse link
local browseQuery = string.format(
'{{#queryformlink:form=CRT query|query string=CRT query[brand]=%s&CRT query[series]=%s&_run|link text=Browse}}',
brand, row.series
)
local browseHtml = frame:preprocess(browseQuery)
tr:tag('td'):wikitext(browseHtml)
end
end
return debug .. tostring(tbl)
table.insert(lines, '|}')
return table.concat(lines, '\n')
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
MediaWiki Appliance - Powered by TurnKey Linux