Module:CategoryAudit

Revision as of 21:56, 4 February 2026 by Arclight-MCP-Bot (talk | contribs) (Create module to find brands/types missing category pages (via create-page on MediaWiki MCP Server))
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Documentation for this module may be created at Module:CategoryAudit/doc

-- Module:CategoryAudit
-- Identifies brands and types that are used on pages but don't have
-- corresponding category pages registered for the Main Page listing.

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

local function extractValues(queryResult)
	local valuesSet = {}
	local valuesList = {}
	
	if queryResult then
		for cellContent in queryResult:gmatch('<td[^>]*>(.-)</td>') do
			local value = trim(cellContent:gsub('<[^>]+>', ''))
			if value and value ~= '' and not valuesSet[value] then
				valuesSet[value] = true
				table.insert(valuesList, value)
			end
		end
	end
	
	table.sort(valuesList)
	return valuesList, valuesSet
end

-- Find CRT brands that are used but don't have a category page
function p.missingCRTBrands(frame)
	-- Get all brands used on CRT pages (with pagination)
	local usedBrandsSet = {}
	local batchSize = 500
	
	for batch = 0, 10 do
		local offset = batch * batchSize
		local queryWikitext =
			'{{#ask:\n' ..
			' [[Category:CRT models]]\n' ..
			' [[Has brand::+]]\n' ..
			' |?Has brand\n' ..
			' |format=broadtable\n' ..
			' |headers=hide\n' ..
			' |link=none\n' ..
			' |mainlabel=-\n' ..
			' |limit=' .. batchSize .. '\n' ..
			' |offset=' .. offset .. '\n' ..
			' |searchlabel=\n' ..
			'}}'
		
		local result = frame:preprocess(queryWikitext)
		local countBefore = 0
		for _ in pairs(usedBrandsSet) do countBefore = countBefore + 1 end
		
		if result then
			for cellContent in result:gmatch('<td[^>]*>(.-)</td>') do
				local brand = trim(cellContent:gsub('<[^>]+>', ''))
				if brand and brand ~= '' then
					usedBrandsSet[brand] = true
				end
			end
		end
		
		-- Check if we got a full batch
		local cellCount = 0
		if result then
			for _ in result:gmatch('<td[^>]*>') do
				cellCount = cellCount + 1
			end
		end
		if cellCount < batchSize then break end
	end
	
	-- Get registered category pages
	local registeredQuery =
		'{{#ask:\n' ..
		' [[Is CRT brand category::Yes]]\n' ..
		' |?Brand name\n' ..
		' |format=broadtable\n' ..
		' |headers=hide\n' ..
		' |link=none\n' ..
		' |mainlabel=-\n' ..
		' |limit=500\n' ..
		' |searchlabel=\n' ..
		'}}'
	
	local registeredResult = frame:preprocess(registeredQuery)
	local registeredSet = {}
	
	if registeredResult then
		for cellContent in registeredResult:gmatch('<td[^>]*>(.-)</td>') do
			local brand = trim(cellContent:gsub('<[^>]+>', ''))
			if brand and brand ~= '' then
				registeredSet[brand] = true
			end
		end
	end
	
	-- Find missing
	local missing = {}
	for brand, _ in pairs(usedBrandsSet) do
		if not registeredSet[brand] then
			table.insert(missing, brand)
		end
	end
	
	table.sort(missing)
	
	if #missing == 0 then
		return "✓ ''All CRT brands have category pages.''"
	end
	
	local output = {}
	for _, brand in ipairs(missing) do
		local count = frame:preprocess('{{#ask:[[Category:CRT models]][[Has brand::' .. brand .. ']]|format=count}}')
		table.insert(output, '* [[Category:' .. brand .. ' CRTs|' .. brand .. ']] (' .. count .. ' CRTs) — <span class="plainlinks">[{{fullurl:Category:' .. brand .. ' CRTs|action=edit&preload=Template:CRT_brand_category/preload&preloadparams%5B%5D=' .. mw.uri.encode(brand) .. '}} create]</span>')
	end
	
	return table.concat(output, '\n')
end

-- Find CRT types that are used but don't have a category page
function p.missingCRTTypes(frame)
	local usedTypesSet = {}
	local batchSize = 500
	
	for batch = 0, 10 do
		local offset = batch * batchSize
		local queryWikitext =
			'{{#ask:\n' ..
			' [[Category:CRT models]]\n' ..
			' [[Has CRT type::+]]\n' ..
			' |?Has CRT type\n' ..
			' |format=broadtable\n' ..
			' |headers=hide\n' ..
			' |link=none\n' ..
			' |mainlabel=-\n' ..
			' |limit=' .. batchSize .. '\n' ..
			' |offset=' .. offset .. '\n' ..
			' |searchlabel=\n' ..
			'}}'
		
		local result = frame:preprocess(queryWikitext)
		
		if result then
			for cellContent in result:gmatch('<td[^>]*>(.-)</td>') do
				local crtType = trim(cellContent:gsub('<[^>]+>', ''))
				if crtType and crtType ~= '' then
					usedTypesSet[crtType] = true
				end
			end
		end
		
		local cellCount = 0
		if result then
			for _ in result:gmatch('<td[^>]*>') do
				cellCount = cellCount + 1
			end
		end
		if cellCount < batchSize then break end
	end
	
	-- Get registered
	local registeredQuery =
		'{{#ask:\n' ..
		' [[Is CRT type category::Yes]]\n' ..
		' |?CRT type name\n' ..
		' |format=broadtable\n' ..
		' |headers=hide\n' ..
		' |link=none\n' ..
		' |mainlabel=-\n' ..
		' |limit=500\n' ..
		' |searchlabel=\n' ..
		'}}'
	
	local registeredResult = frame:preprocess(registeredQuery)
	local registeredSet = {}
	
	if registeredResult then
		for cellContent in registeredResult:gmatch('<td[^>]*>(.-)</td>') do
			local crtType = trim(cellContent:gsub('<[^>]+>', ''))
			if crtType and crtType ~= '' then
				registeredSet[crtType] = true
			end
		end
	end
	
	-- Find missing
	local missing = {}
	for crtType, _ in pairs(usedTypesSet) do
		if not registeredSet[crtType] then
			table.insert(missing, crtType)
		end
	end
	
	table.sort(missing)
	
	if #missing == 0 then
		return "✓ ''All CRT types have category pages.''"
	end
	
	local output = {}
	for _, crtType in ipairs(missing) do
		local count = frame:preprocess('{{#ask:[[Category:CRT models]][[Has CRT type::' .. crtType .. ']]|format=count}}')
		table.insert(output, '* [[Category:' .. crtType .. '|' .. crtType .. ']] (' .. count .. ' CRTs)')
	end
	
	return table.concat(output, '\n')
end

-- Find AV brands that are used but don't have a category page
function p.missingAVBrands(frame)
	local usedBrandsSet = {}
	local batchSize = 500
	
	for batch = 0, 10 do
		local offset = batch * batchSize
		local queryWikitext =
			'{{#ask:\n' ..
			' [[Category:AV devices]]\n' ..
			' [[Has brand::+]]\n' ..
			' |?Has brand\n' ..
			' |format=broadtable\n' ..
			' |headers=hide\n' ..
			' |link=none\n' ..
			' |mainlabel=-\n' ..
			' |limit=' .. batchSize .. '\n' ..
			' |offset=' .. offset .. '\n' ..
			' |searchlabel=\n' ..
			'}}'
		
		local result = frame:preprocess(queryWikitext)
		
		if result then
			for cellContent in result:gmatch('<td[^>]*>(.-)</td>') do
				local brand = trim(cellContent:gsub('<[^>]+>', ''))
				if brand and brand ~= '' then
					usedBrandsSet[brand] = true
				end
			end
		end
		
		local cellCount = 0
		if result then
			for _ in result:gmatch('<td[^>]*>') do
				cellCount = cellCount + 1
			end
		end
		if cellCount < batchSize then break end
	end
	
	-- Get registered
	local registeredQuery =
		'{{#ask:\n' ..
		' [[Is AV brand category::Yes]]\n' ..
		' |?Brand name\n' ..
		' |format=broadtable\n' ..
		' |headers=hide\n' ..
		' |link=none\n' ..
		' |mainlabel=-\n' ..
		' |limit=500\n' ..
		' |searchlabel=\n' ..
		'}}'
	
	local registeredResult = frame:preprocess(registeredQuery)
	local registeredSet = {}
	
	if registeredResult then
		for cellContent in registeredResult:gmatch('<td[^>]*>(.-)</td>') do
			local brand = trim(cellContent:gsub('<[^>]+>', ''))
			if brand and brand ~= '' then
				registeredSet[brand] = true
			end
		end
	end
	
	-- Find missing
	local missing = {}
	for brand, _ in pairs(usedBrandsSet) do
		if not registeredSet[brand] then
			table.insert(missing, brand)
		end
	end
	
	table.sort(missing)
	
	if #missing == 0 then
		return "✓ ''All AV brands have category pages.''"
	end
	
	local output = {}
	for _, brand in ipairs(missing) do
		local count = frame:preprocess('{{#ask:[[Category:AV devices]][[Has brand::' .. brand .. ']]|format=count}}')
		table.insert(output, '* [[Category:' .. brand .. ' AV devices|' .. brand .. ']] (' .. count .. ' devices)')
	end
	
	return table.concat(output, '\n')
end

-- Find AV device types that are used but don't have a category page
function p.missingAVTypes(frame)
	local usedTypesSet = {}
	local batchSize = 500
	
	for batch = 0, 10 do
		local offset = batch * batchSize
		local queryWikitext =
			'{{#ask:\n' ..
			' [[Category:AV devices]]\n' ..
			' [[Has device type::+]]\n' ..
			' |?Has device type\n' ..
			' |format=broadtable\n' ..
			' |headers=hide\n' ..
			' |link=none\n' ..
			' |mainlabel=-\n' ..
			' |limit=' .. batchSize .. '\n' ..
			' |offset=' .. offset .. '\n' ..
			' |searchlabel=\n' ..
			'}}'
		
		local result = frame:preprocess(queryWikitext)
		
		if result then
			for cellContent in result:gmatch('<td[^>]*>(.-)</td>') do
				local deviceType = trim(cellContent:gsub('<[^>]+>', ''))
				if deviceType and deviceType ~= '' then
					usedTypesSet[deviceType] = true
				end
			end
		end
		
		local cellCount = 0
		if result then
			for _ in result:gmatch('<td[^>]*>') do
				cellCount = cellCount + 1
			end
		end
		if cellCount < batchSize then break end
	end
	
	-- Get registered
	local registeredQuery =
		'{{#ask:\n' ..
		' [[Is AV device type category::Yes]]\n' ..
		' |?Device type name\n' ..
		' |format=broadtable\n' ..
		' |headers=hide\n' ..
		' |link=none\n' ..
		' |mainlabel=-\n' ..
		' |limit=500\n' ..
		' |searchlabel=\n' ..
		'}}'
	
	local registeredResult = frame:preprocess(registeredQuery)
	local registeredSet = {}
	
	if registeredResult then
		for cellContent in registeredResult:gmatch('<td[^>]*>(.-)</td>') do
			local deviceType = trim(cellContent:gsub('<[^>]+>', ''))
			if deviceType and deviceType ~= '' then
				registeredSet[deviceType] = true
			end
		end
	end
	
	-- Find missing
	local missing = {}
	for deviceType, _ in pairs(usedTypesSet) do
		if not registeredSet[deviceType] then
			table.insert(missing, deviceType)
		end
	end
	
	table.sort(missing)
	
	if #missing == 0 then
		return "✓ ''All AV device types have category pages.''"
	end
	
	local output = {}
	for _, deviceType in ipairs(missing) do
		local count = frame:preprocess('{{#ask:[[Category:AV devices]][[Has device type::' .. deviceType .. ']]|format=count}}')
		table.insert(output, '* [[Category:' .. deviceType .. 's|' .. deviceType .. ']] (' .. count .. ' devices)')
	end
	
	return table.concat(output, '\n')
end

return p
MediaWiki Appliance - Powered by TurnKey Linux