Module:RFX report
Appearance
![]() | This module depends on the following other modules: |
![]() | This module uses TemplateStyles: |
Replaces User:Cyberpower678/RfX Report. Used in {{RFX report}}.
Output of the print function:
Lua error: bad argument #1 to 'match' (string expected, got nil).
-- This module is a replacement for the RfX report bot.
--[==[
TODO:
* Add a purge link (but where?)
* Show a message if there are no active RfXs (again, where?)
* Get the last active RfX date if there are none open?
This could be screen-scraped from
[[Wikipedia:Successful requests for adminship/2013]] etc.
* Comment the code properly
--]==]
local colours = mw.loadData('Module:RFX report/colour')
local p = {}
local r = {
rfa = {},
rfb = {}
}
local function getLongRfxName(rfxType)
if rfxType == 'rfa' then
return 'adminship'
elseif rfxType == 'rfb' then
return 'bureaucratship'
else
error('invalid rfx code detected - must be either "rfa" or "rfb"', 2)
end
end
local function matchRfx(text, rfxType)
return mw.ustring.gmatch( text, '{{[wW]ikipedia:[rR]equests for ' .. getLongRfxName(rfxType) .. '/([^{}]-)}}' )
end
local function addRfxToTable(text, rfxType, exceptions)
for rfxPage in matchRfx(text, rfxType) do
local isException = false
if exceptions then
for _, v in ipairs(exceptions) do
if rfxPage == v then
isException = true
end
end
end
if not isException then
table.insert( r[rfxType], { page = 'Wikipedia:Requests for ' .. getLongRfxName(rfxType) .. '/' .. rfxPage } )
end
end
end
local function getRfxPages()
local title = mw.title.new( 'Wikipedia:Requests for adminship' )
local text = title:getContent()
addRfxToTable( text, 'rfa', {'Front matter', 'bureaucratship'} )
addRfxToTable( text, 'rfb' )
end
local function getRfxSections(page)
local pageObject = mw.title.new(page)
if not pageObject then
error(tostring(page) .. ' is an invalid title', 2)
end
local pageText = pageObject:getContent()
if not pageText then
error('could not get page content from page ' .. pageObject.title, 2)
end
local intro, s, o, n = mw.ustring.match(
pageText,
'^(.-)\n====[^=\n][^\n]-====.-'
.. '\n=====%s*[sS]upport%s*=====(.-)'
.. '\n=====%s*[oO]ppose%s*=====(.-)'
.. '\n=====%s*[nN]eutral%s*=====(.-)$'
)
return intro, s, o, n
end
-- Returns a table of usernames that voted in a particular section.
local function getVoteTable(section)
section = mw.ustring.match(section, '^(.-\n#.-)\n[^#]') or section -- Discard subsequent numbered lists.
local t = {}
for vote in mw.ustring.gmatch(section, '\n#[^#*;:][^\n]*') do
local username = false
for link in mw.ustring.gmatch(vote, '%[%[([^%[%]]-)%]%]') do
-- If there is a pipe, get the text before it.
if mw.ustring.match(link, '|') then
link = mw.ustring.match(link, '^(.-)|')
end
-- If there is a slash, get the text before that.
if mw.ustring.match(link, '/') then
link = mw.ustring.match(link, '^(.-)/')
end
-- Decode html entities and percent encodings.
link = mw.text.decode(link, true)
link = mw.uri.decode(link, 'WIKI')
-- If there is a hash, get the text before that.
if mw.ustring.match(link, '#') then
link = mw.ustring.match(link, '^(.-)#')
end
local userLink = mw.ustring.match(link, '^[%s_]*[uU][sS][eE][rR][%s_]*:[%s_]*(.-)[%s_]*$')
local userTalkLink = mw.ustring.match(link, '^[%s_]*[uU][sS][eE][rR][%s_]*[tT][aA][lL][kK][%s_]*:[%s_]*(.-)[%s_]*$')
if userLink then
username = userLink
elseif userTalkLink then
username = userTalkLink
end
end
if not username then
username = mw.ustring.format( 'Error processing vote no. %d. (Random number: %.8f.)', #t, math.random() )
end
table.insert(t, username)
end
return t
end
local function checkDups(...)
local t = {}
local tables = {...}
for i, v in ipairs(tables) do
for j, name in ipairs(v) do
if t[name] then
return true
else
t[name] = true
end
end
end
return false
end
local function getEndTime(intro)
local ret = mw.ustring.match(intro, '(%d%d:%d%d, %d+ %w+ %d+) %(UTC%)')
return ret
end
local function getUser(intro)
local ret = mw.ustring.match(intro, '===%s*%[%[%s*[wW]ikipedia%s*:%s*[rR]equests for %w+/.-|%s*(.-)%s*%]%]%s*===')
return ret
end
local function getTimeLeft(endTime)
local lang = mw.getContentLanguage()
local now = tonumber( lang:formatDate("U") )
endTime = tonumber( lang:formatDate("U", endTime) )
local secondsLeft = endTime - now
local daysLeft
if secondsLeft <= 0 then
daysLeft = 'Pending closure...'
else
daysLeft = mw.ustring.gsub( lang:formatDuration(secondsLeft, {'days', 'hours'}), ' and', ',')
end
return daysLeft
end
local function makeRfxTables(rfxType)
for i, rfx in ipairs(r[rfxType]) do
local intro, s, o, n = getRfxSections(rfx.page)
s = getVoteTable(s)
o = getVoteTable(o)
n = getVoteTable(n)
r[rfxType][i].user = getUser(intro)
r[rfxType][i].support = #s
r[rfxType][i].oppose = #o
r[rfxType][i].neutral = #n
r[rfxType][i].percent = math.floor( (r[rfxType][i].support / (r[rfxType][i].support + r[rfxType][i].oppose) * 100) + 0.5 )
r[rfxType][i].dups = checkDups(s, o, n)
r[rfxType][i].endTime = getEndTime(intro)
r[rfxType][i].timeLeft = getTimeLeft( r[rfxType][i].endTime )
r[rfxType][i].report = '[//tools.wmflabs.org/xtools/rfa/?p=' .. mw.uri.encode( r[rfxType][i].page ) .. ' report]'
end
end
local function makeReportRows(rfxType)
local ret = ''
for i, rfx in ipairs(r[rfxType]) do
local dups
if r[rfxType][i].dups then
dups = "'''yes'''"
else
dups = 'no'
end
local style = ''
local styleInline = ''
if r[rfxType][i].timeLeft == 'Pending closure...' then
style = ' style="background: #f8cdc6;" |'
styleInline = ' background: #f8cdc6;'
end
local page = r[rfxType][i].page
local colour = colours[rfxType][ r[rfxType][i].percent ]
ret = ret .. mw.ustring.format( [==[
|-
|%s [[%s|%s]]
| style="text-align: right;%s" | [[%s#Support|%d]]
| style="text-align: right;%s" | [[%s#Oppose|%d]]
| style="text-align: right;%s" | [[%s#Neutral|%d]]
| style="text-align: right; background: #%s;" | %d
|%s %s
|%s %s
| style="text-align: center;%s" | %s
|%s %s]==],
style, page, r[rfxType][i].user,
styleInline, page, r[rfxType][i].support,
styleInline, page, r[rfxType][i].oppose,
styleInline, page, r[rfxType][i].neutral,
colour, r[rfxType][i].percent,
style, r[rfxType][i].endTime,
style, r[rfxType][i].timeLeft,
styleInline, dups,
style, r[rfxType][i].report
)
end
return ret
end
local function makeReport(args)
getRfxPages()
makeRfxTables('rfa')
makeRfxTables('rfb')
local float = args.float or args.align or 'right'
local clear = args.clear or 'left'
local ret = '{| class="wikitable" '
if args.style then
ret = ret .. args.style
else
ret = ret .. 'style="white-space:wrap; clear: ' .. clear .. '; '
.. 'margin-top: 0em; margin-bottom: .5em; float: ' .. float .. '; '
.. 'padding: .5em 0em 0em 1.4em; background: #ffffff; '
.. 'border-collapse: collapse; border-spacing: 0;"'
end
if #r.rfa > 0 then
ret = ret .. '\n|-\n! RfA candidate !! S !! O !! N !! S% !! Ending (UTC) !! Time left !! Dups? !! Report'
ret = ret .. makeReportRows('rfa')
end
if #r.rfb > 0 then
ret = ret .. '\n|-\n! RfB candidate !! S !! O !! N !! S% !! Ending (UTC) !! Time left !! Dups? !! Report'
ret = ret .. makeReportRows('rfb')
end
ret = ret .. '\n|-\n|}'
return ret
end
function p.main(frame)
-- Process the arguments.
local args
if frame == mw.getCurrentFrame() then
args = frame:getParent().args
for k, v in pairs(frame.args) do
args = frame.args
break
end
else
args = frame
end
return makeReport(args)
end
return p