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

-- Helper to build a "create category" URL
local function createCategoryUrl(categoryTitle, preloadTemplate, brandOrType)
	return tostring(mw.uri.fullUrl(categoryTitle, {
		action = 'edit',
		preload = preloadTemplate,
		['preloadparams[]'] = brandOrType
	}))
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)
		
		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}}')
		local categoryTitle = 'Category:' .. brand .. ' CRTs'
		local createUrl = createCategoryUrl(categoryTitle, 'Template:CRT brand category/preload', brand)
		-- Use [[:Category:...]] with colon to make it a LINK not a category assignment
		table.insert(output, '* <strong>' .. brand .. '</strong> — ' .. count .. ' CRTs need [[:' .. categoryTitle .. ']] — [' .. createUrl .. ' create it]')
	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}}')
		local categoryTitle = 'Category:' .. crtType
		local createUrl = createCategoryUrl(categoryTitle, 'Template:CRT type category/preload', crtType)
		table.insert(output, '* <strong>' .. crtType .. '</strong> — ' .. count .. ' CRTs need [[:' .. categoryTitle .. ']] — [' .. createUrl .. ' create it]')
	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}}')
		local categoryTitle = 'Category:' .. brand .. ' AV devices'
		local createUrl = createCategoryUrl(categoryTitle, 'Template:AV brand category/preload', brand)
		table.insert(output, '* <strong>' .. brand .. '</strong> — ' .. count .. ' devices need [[:' .. categoryTitle .. ']] — [' .. createUrl .. ' create it]')
	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}}')
		local categoryTitle = 'Category:' .. deviceType .. 's'
		local createUrl = createCategoryUrl(categoryTitle, 'Template:AV device type category/preload', deviceType)
		table.insert(output, '* <strong>' .. deviceType .. '</strong> — ' .. count .. ' devices need [[:' .. categoryTitle .. ']] — [' .. createUrl .. ' create it]')
	end
	
	return table.concat(output, '\n')
end

return p
MediaWiki Appliance - Powered by TurnKey Linux