Jump to content

Module:RFX report

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Mr. Stradivarius (talk | contribs) at 13:58, 22 June 2013 (remove debugging stuff and update the submodule name). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

-- This module is a replacement for the RfX report bot.

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====%s*[nN]omination%s*====.-'
        .. '\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.)', #d, 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, "Scheduled to end (%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 = 'expired'
    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)
    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 = ''
        if r[rfxType][i].timeLeft == 'expired' then
            style = ' style="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]]
| align="right"%s | [[%s#Support|%d]]
| align="right"%s | [[%s#Oppose|%d]]
| align="right"%s | [[%s#Neutral|%d]]
| align="right" style="background:#%s;" | %d
|%s | %s
|%s | %s
|%s | <center>%s</center>
|%s | %s]==],
            style, page, r[rfxType][i].user,
            style, page, r[rfxType][i].support,
            style, page, r[rfxType][i].oppose,
            style, page, r[rfxType][i].neutral,
            colour, r[rfxType][i].percent,
            style, r[rfxType][i].endTime,
            style, r[rfxType][i].timeLeft,
            style, dups,
            style, r[rfxType][i].report
        )
    end
    return ret
end

local function makeReport(args)
    getRfxPages()
    makeRfxTables('rfa')
    makeRfxTables('rfb')
    
    local align = 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 .. 'align="' .. align .. '" cellspacing="0" cellpadding="0" style="white-space:wrap; clear: ' .. clear .. '; '
            .. 'margin-top: 0em; margin-bottom: .5em; float: ' .. align .. ';padding: .5em 0em 0em 1.4em; background: none;"'
    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