Module:MaintenanceDashboard
Documentation for this module may be created at Module:MaintenanceDashboard/doc
-- Module:MaintenanceDashboard
-- Generates status cards for various maintenance checks across the wiki
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
-- Get count from an SMW query
local function getCount(frame, query)
local result = frame:preprocess('{{#ask:' .. query .. '|format=count}}')
return tonumber(trim(result)) or 0
end
-- Generate a single status card
local function statusCard(label, count, link, threshold_warning, threshold_error)
threshold_warning = threshold_warning or 1
threshold_error = threshold_error or 10
local status = 'ok'
if count >= threshold_error then
status = 'error'
elseif count >= threshold_warning then
status = 'warning'
end
local linkAttr = ''
if link then
linkAttr = '|link=' .. link
end
return '{{Maintenance status card|label=' .. label .. '|count=' .. count .. '|status=' .. status .. linkAttr .. '}}'
end
-- Category maintenance status grid
function p.categoryStatus(frame)
-- Count missing category pages using the audit logic
local missingCRTBrands = 0
local missingCRTTypes = 0
local missingAVBrands = 0
local missingAVTypes = 0
-- Get used CRT brands
local usedCRTBrands = {}
local brandQuery = frame:preprocess('{{#ask:[[Category:CRT models]][[Has brand::+]]|?Has brand|format=broadtable|headers=hide|link=none|mainlabel=-|limit=500|searchlabel=}}')
if brandQuery then
for cell in brandQuery:gmatch('<td[^>]*>(.-)</td>') do
local brand = trim(cell:gsub('<[^>]+>', ''))
if brand and brand ~= '' then
usedCRTBrands[brand] = true
end
end
end
-- Get registered CRT brand categories
local registeredCRTBrands = {}
local regQuery = frame:preprocess('{{#ask:[[Is CRT brand category::Yes]]|?Brand name|format=broadtable|headers=hide|link=none|mainlabel=-|limit=500|searchlabel=}}')
if regQuery then
for cell in regQuery:gmatch('<td[^>]*>(.-)</td>') do
local brand = trim(cell:gsub('<[^>]+>', ''))
if brand and brand ~= '' then
registeredCRTBrands[brand] = true
end
end
end
for brand, _ in pairs(usedCRTBrands) do
if not registeredCRTBrands[brand] then
missingCRTBrands = missingCRTBrands + 1
end
end
-- Get used CRT types
local usedCRTTypes = {}
local typeQuery = frame:preprocess('{{#ask:[[Category:CRT models]][[Has CRT type::+]]|?Has CRT type|format=broadtable|headers=hide|link=none|mainlabel=-|limit=500|searchlabel=}}')
if typeQuery then
for cell in typeQuery:gmatch('<td[^>]*>(.-)</td>') do
local crtType = trim(cell:gsub('<[^>]+>', ''))
if crtType and crtType ~= '' then
usedCRTTypes[crtType] = true
end
end
end
-- Get registered CRT type categories
local registeredCRTTypes = {}
local regTypeQuery = frame:preprocess('{{#ask:[[Is CRT type category::Yes]]|?CRT type name|format=broadtable|headers=hide|link=none|mainlabel=-|limit=500|searchlabel=}}')
if regTypeQuery then
for cell in regTypeQuery:gmatch('<td[^>]*>(.-)</td>') do
local crtType = trim(cell:gsub('<[^>]+>', ''))
if crtType and crtType ~= '' then
registeredCRTTypes[crtType] = true
end
end
end
for crtType, _ in pairs(usedCRTTypes) do
if not registeredCRTTypes[crtType] then
missingCRTTypes = missingCRTTypes + 1
end
end
-- Get used AV brands
local usedAVBrands = {}
local avBrandQuery = frame:preprocess('{{#ask:[[Category:AV devices]][[Has brand::+]]|?Has brand|format=broadtable|headers=hide|link=none|mainlabel=-|limit=500|searchlabel=}}')
if avBrandQuery then
for cell in avBrandQuery:gmatch('<td[^>]*>(.-)</td>') do
local brand = trim(cell:gsub('<[^>]+>', ''))
if brand and brand ~= '' then
usedAVBrands[brand] = true
end
end
end
-- Get registered AV brand categories
local registeredAVBrands = {}
local regAVQuery = frame:preprocess('{{#ask:[[Is AV brand category::Yes]]|?Brand name|format=broadtable|headers=hide|link=none|mainlabel=-|limit=500|searchlabel=}}')
if regAVQuery then
for cell in regAVQuery:gmatch('<td[^>]*>(.-)</td>') do
local brand = trim(cell:gsub('<[^>]+>', ''))
if brand and brand ~= '' then
registeredAVBrands[brand] = true
end
end
end
for brand, _ in pairs(usedAVBrands) do
if not registeredAVBrands[brand] then
missingAVBrands = missingAVBrands + 1
end
end
-- Get used AV types
local usedAVTypes = {}
local avTypeQuery = frame:preprocess('{{#ask:[[Category:AV devices]][[Has device type::+]]|?Has device type|format=broadtable|headers=hide|link=none|mainlabel=-|limit=500|searchlabel=}}')
if avTypeQuery then
for cell in avTypeQuery:gmatch('<td[^>]*>(.-)</td>') do
local deviceType = trim(cell:gsub('<[^>]+>', ''))
if deviceType and deviceType ~= '' then
usedAVTypes[deviceType] = true
end
end
end
-- Get registered AV type categories
local registeredAVTypes = {}
local regAVTypeQuery = frame:preprocess('{{#ask:[[Is AV device type category::Yes]]|?Device type name|format=broadtable|headers=hide|link=none|mainlabel=-|limit=500|searchlabel=}}')
if regAVTypeQuery then
for cell in regAVTypeQuery:gmatch('<td[^>]*>(.-)</td>') do
local deviceType = trim(cell:gsub('<[^>]+>', ''))
if deviceType and deviceType ~= '' then
registeredAVTypes[deviceType] = true
end
end
end
for deviceType, _ in pairs(usedAVTypes) do
if not registeredAVTypes[deviceType] then
missingAVTypes = missingAVTypes + 1
end
end
-- Build output
local cards = {}
table.insert(cards, statusCard('CRT Brand Categories', missingCRTBrands, 'EveryCRT:Category maintenance#CRT Brands Missing Category Pages', 1, 5))
table.insert(cards, statusCard('CRT Type Categories', missingCRTTypes, 'EveryCRT:Category maintenance#CRT Types Missing Category Pages', 1, 3))
table.insert(cards, statusCard('AV Brand Categories', missingAVBrands, 'EveryCRT:Category maintenance#AV Brands Missing Category Pages', 1, 5))
table.insert(cards, statusCard('AV Type Categories', missingAVTypes, 'EveryCRT:Category maintenance#AV Device Types Missing Category Pages', 1, 3))
local output = '<div class="maintenance-status-grid">\n'
for _, card in ipairs(cards) do
output = output .. frame:preprocess(card) .. '\n'
end
output = output .. '</div>'
return output
end
-- Data quality status grid
function p.dataQualityStatus(frame)
local crtStubs = getCount(frame, '[[Category:CRT stubs]]')
local crtNoImage = getCount(frame, '[[Category:CRT models needing images]]')
local crtNoType = getCount(frame, '[[Category:CRTs missing type]]')
local crtNoSeries = getCount(frame, '[[Category:CRTs without series]]')
local avStubs = getCount(frame, '[[Category:AV device stubs]]')
local cards = {}
table.insert(cards, statusCard('CRT Stubs', crtStubs, 'Category:CRT stubs', 50, 200))
table.insert(cards, statusCard('CRTs Need Images', crtNoImage, 'Category:CRT models needing images', 50, 200))
table.insert(cards, statusCard('CRTs Missing Type', crtNoType, 'Category:CRTs missing type', 10, 50))
table.insert(cards, statusCard('CRTs No Series', crtNoSeries, 'Category:CRTs without series', 100, 500))
table.insert(cards, statusCard('AV Device Stubs', avStubs, 'Category:AV device stubs', 10, 50))
local output = '<div class="maintenance-status-grid">\n'
for _, card in ipairs(cards) do
output = output .. frame:preprocess(card) .. '\n'
end
output = output .. '</div>'
return output
end
-- Full dashboard combining all checks
function p.fullDashboard(frame)
local output = ''
output = output .. '=== Category System ===\n'
output = output .. p.categoryStatus(frame) .. '\n\n'
output = output .. '=== Data Quality ===\n'
output = output .. p.dataQualityStatus(frame) .. '\n'
return output
end
-- Summary counts for the main dashboard
function p.summaryCounts(frame)
-- Category issues
local categoryIssues = 0
-- Quick check for missing categories (simplified)
local usedBrands = {}
local brandQuery = frame:preprocess('{{#ask:[[Category:CRT models]][[Has brand::+]]|?Has brand|format=broadtable|headers=hide|link=none|mainlabel=-|limit=500|searchlabel=}}')
if brandQuery then
for cell in brandQuery:gmatch('<td[^>]*>(.-)</td>') do
local brand = trim(cell:gsub('<[^>]+>', ''))
if brand and brand ~= '' then usedBrands[brand] = true end
end
end
local registeredBrands = {}
local regQuery = frame:preprocess('{{#ask:[[Is CRT brand category::Yes]]|?Brand name|format=broadtable|headers=hide|link=none|mainlabel=-|limit=500|searchlabel=}}')
if regQuery then
for cell in regQuery:gmatch('<td[^>]*>(.-)</td>') do
local brand = trim(cell:gsub('<[^>]+>', ''))
if brand and brand ~= '' then registeredBrands[brand] = true end
end
end
for brand, _ in pairs(usedBrands) do
if not registeredBrands[brand] then categoryIssues = categoryIssues + 1 end
end
-- Data quality
local crtStubs = getCount(frame, '[[Category:CRT stubs]]')
local crtNoImage = getCount(frame, '[[Category:CRT models needing images]]')
-- Totals
local totalIssues = categoryIssues + crtStubs + crtNoImage
return tostring(totalIssues)
end
return p