Module:CategoryAudit
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