Module:Votings-global
Appearance
| This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
Usage
-- For attribution: [[:vi:Module:CurrentCandidateList]]
require('strict')
local p = {}
local srgp_page_name = 'Steward requests/Global permissions'
local srgp_page_content = mw.title.new(srgp_page_name):getContent() --[[@as string]]
---@class PermissionInfo
---@field acronym string
---@field section_name string
---@type PermissionInfo[]
local permissions = {
{
acronym = 'GRN',
section_name = 'Requests for global rename permissions'
},
{
acronym = 'GR',
section_name = 'Requests for global rollback permissions'
},
{
acronym = 'GS',
section_name = 'Requests for global sysop permissions'
},
{
acronym = 'GP',
section_name = 'Requests for other global permissions'
}
}
---@type string[]
local not_active_statuses = {
'done', '+',
'cannot', 'notdone', '-',
'alreadydone', 'withdrawn', 'redundant'
}
---@class Section
---@field name string
---@field lines string[]
---Whether `table` contains `value`.
---
---@param table any[]
---@param value any
---@return boolean
local function _table_includes(table, value)
for _, element in ipairs(table) do
if element == value then
return true
end
end
return false
end
---Extract the `status` argument from the given line.
---
---@param line string
---@return string?
function p._parse_status(line)
local without_comments = mw.ustring.gsub(line, '<!--.--->', '')
local argument = mw.ustring.match(without_comments, '%s*%|%s*status%s*=%s*(.*)')
if not argument then
return nil
end
local without_whitespace = mw.ustring.gsub(argument, '%s*', '')
return mw.ustring.lower(without_whitespace)
end
---Whether the given section's status is not inactive.
---
---@param section Section
---@return boolean
function p._section_is_active(section)
for _, line in ipairs(section.lines) do
local status = p._parse_status(line)
if status ~= nil then
return not _table_includes(not_active_statuses, status)
end
end
return true
end
---Given a line, parse it as a heading and return the level and the name.
---
---@param line string
---@return integer?, string?
local function _parse_heading(line)
local equals, name = mw.ustring.match(line, '(==+)%s*(..-)%s*(==+)')
if not equals then
return nil, nil
end
return #equals, name
end
---Given a heading, return the corresponding permission acronym.
---
---@param heading string
---@return string?
local function _get_permission_acronym(heading)
for _, permission in ipairs(permissions) do
if permission.section_name == heading then
return permission.acronym
end
end
return nil
end
---Parse the page and return a map of acronyms and corresponding sections.
---
---@return table<string, Section[]>
function p._parse_sections()
---@type table<string, Section[]>
local acronym_to_child_sections = {}
for _, permission in pairs(permissions) do
acronym_to_child_sections[permission.acronym] = {}
end
---@type Section[]?
local current_parent_section = nil
---@type Section?
local current_child_section = nil
for line in mw.text.gsplit(srgp_page_content, '\n') do
local heading_level, heading_name = _parse_heading(line)
if (
current_parent_section and current_child_section ~= nil and
(not heading_level or heading_level > 3)
) then
table.insert(current_child_section.lines, line)
elseif heading_level == 2 and heading_name then
local acronym = _get_permission_acronym(heading_name)
if acronym then
current_parent_section = acronym_to_child_sections[acronym]
else
current_parent_section = nil
end
current_child_section = nil
elseif current_parent_section and heading_level == 3 and heading_name then
---@type Section
local new_section = { name = heading_name, lines = {} }
table.insert(current_parent_section, new_section)
current_child_section = new_section
end
end
return acronym_to_child_sections
end
---Put request headings into corresponding subtables of a table.
---
---@return table<string, string[]>
function p._collect_requests()
---@type table<string, string[]>
local acronym_to_headings = {}
local sections = p._parse_sections()
for acronym, subsections in pairs(sections) do
local headings = {}
for _, subsection in ipairs(subsections) do
if p._section_is_active(subsection) then
table.insert(headings, subsection.name)
end
end
acronym_to_headings[acronym] = headings
end
return acronym_to_headings
end
---Convert the table returned by `p._count_requests`
---to a human-readable horizontal list.
---
---@param counts table<string, string[]>
---@return string
function p._join_to_human_readable_list(counts)
local links = {}
for _, permission in ipairs(permissions) do
local acronym = permission.acronym
local section_name = permission.section_name
local count = #counts[acronym]
if count > 0 then
local link_to_section = srgp_page_name .. '#' .. section_name
local link_text = count .. ' Rf' .. acronym
local link = '[[' .. link_to_section .. '|' .. link_text .. ']]'
table.insert(links, ' • <b>' .. link .. '</b>')
end
end
return table.concat(links, '')
end
function p.main()
local request_counts = p._collect_requests()
return p._join_to_human_readable_list(request_counts)
end
return p