Module:Rfx Explained

------------------------------------------------------------------------ Module:Rfx ---- This is a library for retrieving information about requests ---- for adminship and requests for bureaucratship on the English ---- Wikipedia. Please see the module documentation for instructions. ------------------------------------------------------------------------

local libraryUtil = require('libraryUtil')local lang = mw.getContentLanguagelocal textSplit = mw.text.splitlocal umatch = mw.ustring.matchlocal newTitle = mw.title.new

local rfx =

---------------------------------------- Helper functions ----------------------------------------

local function getTitleObject(title) local success, titleObject = pcall(newTitle, title) if success and titleObject then return titleObject else return nil endend

local function parseVoteBoundaries(section) -- Returns an array containing the raw wikitext of RfX votes in a given section. section = section:match('^.-\n#(.*)$') -- Strip non-votes from the start. if not section then return end section = section:match('^(.-)\n[^#]') or section -- Discard subsequent numbered lists. local comments = textSplit(section, '\n#') local votes = for i, comment in ipairs(comments) do if comment:find('^[^#*;:].*%S') then votes[#votes + 1] = comment end end return votesend

local function parseVote(vote) -- parses a username from an RfX vote. local userStart, userMatch = vote:match('^(.*)%[%[[%s_]*:?[%s_]*[uU][sS][eE][rR][%s_]*:[%s_]*(.-)[%s_]*%]%].-$') local talkStart, talkMatch = vote:match('^(.*)%[%[[%s_]*:?[%s_]*[uU][sS][eE][rR][%s_]+[tT][aA][lL][kK][%s_]*:[%s_]*(.-)[%s_]*%]%].-$') local contribStart, contribMatch = vote:match('^(.*)%[%[[%s_]*:?[%s_]*[sS][pP][eE][cC][iI][aA][lL][%s_]*:[%s_]*[cC][oO][nN][tT][rR][iI][bB][uU][tT][iI][oO][nN][sS]/[%s_]*(.-)[%s_]*%]%].-$') local username if userStart and talkStart then if #userStart > #talkStart then username = userMatch else username = talkMatch end elseif userStart then username = userMatch elseif talkStart then username = talkMatch elseif contribStart then username = contribMatch else return string.format("Error parsing signature: %s", vote) end username = username:match('^[^|/#]*') return usernameend

local function parseVoters(votes) local voters = for i, vote in ipairs(votes) do voters[#voters + 1] = parseVote(vote) end return votersend

local function dupesExist(...) local exists = local tables = for i, usernames in ipairs(tables) do for j, username in ipairs(usernames) do username = lang:ucfirst(username) if exists[username] then return true else exists[username] = true end end end return falseend

local function hasCategory(category, catList) for _, c in ipairs(catList) do if c

category then return true end end return falseend

-------------------------------------------- Define the constructor function --------------------------------------------

function rfx.new(title) local obj = local data = local checkSelf = libraryUtil.makeCheckSelfFunction('Module:Rfx', 'rfx', obj, 'rfx object') -- Get the title object and check to see whether we are a subpage of WP:RFA or WP:RFB. title = getTitleObject(title) if not title then return nil end function data:getTitleObject checkSelf(self, 'getTitleObject') return title end if title.namespace

4 then local rootText = title.rootText if rootText

'Requests for adminship' then data.type = 'rfa' elseif rootText

'Requests for bureaucratship' then data.type = 'rfb' else return nil end else return nil end

-- Get the page content and divide it into sections. local pageText = title:getContent if not pageText then return nil end local introText, supportText, opposeText, neutralText = umatch(pageText, '^(.-)\n

[^=\n][^\n]-

.-' .. '\n

%s*[sS]upport%s*

(.-)' .. '\n

%s*[oO]ppose%s*

(.-)' .. '\n

%s*[nN]eutral%s*

(.-)$' ) if not introText then introText, supportText, opposeText, neutralText = umatch(pageText, "^(.-\n[^\n]-%(%d+/%d+/%d+%)[^\n]-)\n.-" .. "\nSupport(.-)\nOppose(.-)\nNeutral(.-)" ) end -- Switch to reconfirmation request for adminship if in that category local categories = title.categories if hasCategory('Reconfirmation requests for adminship', categories) then data.type = 'rrfa' end

-- Get vote counts. local supportVotes, opposeVotes, neutralVotes if supportText and opposeText and neutralText then supportVotes = parseVoteBoundaries(supportText) opposeVotes = parseVoteBoundaries(opposeText) neutralVotes = parseVoteBoundaries(neutralText) end local supports, opposes, neutrals if supportVotes and opposeVotes and neutralVotes then supports = #supportVotes data.supports = supports opposes = #opposeVotes data.opposes = opposes neutrals = #neutralVotes data.neutrals = neutrals end

-- Voter methods and dupe check.

function data:getSupportUsers checkSelf(self, 'getSupportUsers') if supportVotes then return parseVoters(supportVotes) else return nil end end

function data:getOpposeUsers checkSelf(self, 'getOpposeUsers') if opposeVotes then return parseVoters(opposeVotes) else return nil end end

function data:getNeutralUsers checkSelf(self, 'getNeutralUsers') if neutralVotes then return parseVoters(neutralVotes) else return nil end end

function data:dupesExist checkSelf(self, 'dupesExist') local supportUsers = self:getSupportUsers local opposeUsers = self:getOpposeUsers local neutralUsers = self:getNeutralUsers if not (supportUsers and opposeUsers and neutralUsers) then return nil end return dupesExist(supportUsers, opposeUsers, neutralUsers) end

if supports and opposes then local total = supports + opposes if total <= 0 then data.percent = 0 else data.percent = math.floor((supports / total * 100) + 0.5) end end if introText then data.endTime = umatch(introText, '(%d%d:%d%d, %d+ %w+ %d+) %(UTC%)') data.user = umatch(introText, '

%s*%[%[[_%s]*[wW]ikipedia[_%s]*:[_%s]*[rR]equests[_ ]for[_ ]%w+/.-|[_%s]*(.-)[_%s]*%]%][_%s]*

') if not data.user then data.user = umatch(introText, '

%s*([^\n]-)%s*

') end end -- Methods for seconds left and time left. function data:getSecondsLeft checkSelf(self, 'getSecondsLeft') local endTime = self.endTime if not endTime then return nil end local now = tonumber(lang:formatDate("U")) local success, endTimeU = pcall(lang.formatDate, lang, 'U', endTime) if not success then return nil end endTimeU = tonumber(endTimeU) if not endTimeU then return nil end local secondsLeft = endTimeU - now if secondsLeft <= 0 then return 0 else return secondsLeft end end

function data:getTimeLeft checkSelf(self, 'getTimeLeft') local secondsLeft = self:getSecondsLeft if not secondsLeft then return nil end return mw.ustring.gsub(lang:formatDuration(secondsLeft,), ' and', ',') end function data:getReport -- Gets the URI object for Vote History tool checkSelf(self, 'getReport') return mw.uri.new('https://apersonbot.toolforge.org/vote-history?page=' .. mw.uri.encode(title.prefixedText)) end function data:getStatus -- Gets the current status of the RfX. Returns either "successful", "unsuccessful", -- "open", or "pending closure". Returns nil if the status could not be found. checkSelf(self, 'getStatus') local rfxType = data.type if rfxType

'rfa' or rfxType

'rrfa' then if hasCategory('Successful requests for adminship', categories) then return 'successful' elseif hasCategory('Unsuccessful requests for adminship', categories) then return 'unsuccessful' end elseif rfxType

'rfb' then if hasCategory('Successful requests for bureaucratship', categories) then return 'successful' elseif hasCategory('Unsuccessful requests for bureaucratship', categories) then return 'unsuccessful' end end local secondsLeft = self:getSecondsLeft if secondsLeft and secondsLeft > 0 then return 'open' elseif secondsLeft and secondsLeft <= 0 then return 'pending closure' else return nil end end -- Specify which fields are read-only, and prepare the metatable. local readOnlyFields = local function pairsfunc(t, k) local v repeat k = next(readOnlyFields, k) if k

nil then return nil end v = t[k] until v ~= nil return k, v end

return setmetatable(obj,)end

return rfx