Module:vote total

From Wiktionary, the free dictionary
Jump to navigation Jump to search
This module needs documentation.
Please document this module by describing its purpose and usage on the documentation page.

local export = {}

-- This function returns the full vote page.
function getFullPage (pageName)
	local pageObject = mw.title.new (pageName)
	return pageObject:getContent()
end


-- This function returns the portion of a vote page with the actual votes, without the vote description and the decision.
-- It uses the full page contents as theonly parameter.
function pageExcerptFullPage (fullPage)
	-- This is the offset of the match first match of "Enter '# {{support}} ~~~~' on next blank line", which appears in the first support section.
	local offset = string.find (fullPage, "Enter '# {{support}} ~~~~' on next blank line", 1, 1) or 0

	-- cutDescription cuts the description at the start of the vote, based on the offset parameter.
	-- cutDecision cuts the decision at the end of the vote, based on the "==== Decision ====" header.
	-- result is the part of the page that has the actual list of votes.
	local cutDescription = string.sub (fullPage, offset, 100000000)
	local cutDecision = mw.text.split (cutDescription, "==== Decision ====", true)
	local result = cutDecision[1]

	return result
end

function normalizeNamespace(namespace)
	return namespace:lower():gsub("_", " ")
end

-- Processes all links that may have a namespace, stores each username in a link
-- to a "User" or "User talk" page, and returns the last username encountered,
-- or else nil.
-- Removes fragments (the part after #) and link text (the part after | if the
-- link is piped).
-- Consider the text:
-- # {{support}} per [[User:Example1]] --[[User:Example2|Example2]] ([[User talk:Example3#Fragment|talk]]) 01:00, 13 September 2015 (UTC)
-- It has three "User" or "User talk" links.
-- The username "Example3" from the last link, [[User talk:Example3#Fragment|talk]],
-- is returned.
-- Sometimes, a person has only the link to the user page or the talk page,
-- this makes sure their votes are counted too.
function getLastUsername(text)
	local username
	for potentialNamespace, potentialUser in text:gmatch("%[%[([%a_ ]+):([^|%[%]#|]+).-%]%]") do
		potentialNamespace = normalizeNamespace(potentialNamespace)
		if potentialNamespace == "user" or potentialNamespace == "user talk" then
			username = potentialUser
		end
	end
	return username
end

assert(
	getLastUsername([=[# {{support}} per [[User:Example1]] --[[User:Example2|Example2]] ([[User talk:Example3#Fragment|talk]]) 01:00, 13 September 2015 (UTC)]=]),
	"Example3")

-- This function returns a variable with a list of all people who voted in the page.
function countVotesFullPage (fullPage)
	local votesText = pageExcerptFullPage (fullPage)

	-- Sets not-yet-created fields to 0 when they are indexed.
	local votes = setmetatable({}, {
		__index = function(self, key)
			self[key] = 0
			return 0
		end
	})

	-- Iterates over all lines that start with a "#" but not with "##", "#*" or
	-- "#:", which excludes discussion and crossed-out votes.
	-- Example of accepted line: # {{support}} --[[User:Example|Example]] ([[User talk:Example|talk]]) 03:16, 26 November 2015 (UTC)
	local i
	local commentStart
	while true do
		local nextCommentStart
		-- Get positions of all # at the beginning of a line, not followed by # or * or :.
		nextCommentStart, i = votesText:find("%f[^\n%z]#[^#*:]", i)
		-- Work with the comment that begins with the previous #
		-- and ends before the current #.
		-- Remove the current # from the end of the comment.
		local commentEnd = nextCommentStart and nextCommentStart - 1
		-- If # was found in the previous execution of the loop,
		-- get text between last # and current #.
		if commentStart then
			local voteComment = votesText:sub(commentStart, commentEnd)
			local username
			-- Look over every line starting with #, possibly followed by # or * or :.
			-- The first line will have # alone, but the signature
			-- may be in any of the following lines.
			for line in voteComment:gmatch("%f[^\n%z]#[^\n]*") do
				-- Find the first line that ends with a possible date.
				-- If it has a username, assume that is the person who is
				-- casting a vote in this vote comment.
				if line:find "%d%d:%d%d, %d%d? %u%l+ 20%d%d %(UTC%)%s*$" then
					username = getLastUsername(line)
					
					if username then
						votes[username] = votes[username] + 1
					else
						mw.log("Didn't find username on the following line that ended with a date:\n" .. line)
					end
					break
				end
			end
			if not username then
				mw.log("Didn't find username in\n" .. voteComment)
			end
		end
		if not commentEnd then break end
		commentStart = commentEnd
	end

	return votes
end

function voteParts ()
	-- Concerning the page "Wiktionary:Votes/Active":
	-- pageObject is the page object,
	-- fullPage is the full contents of the page.
	local pageObject = mw.title.new ("Wiktionary:Votes/Active")
	local fullPage = pageObject:getContent()

	-- startList is the start of the list of votes.
	-- endList is the end of the list of votes.
	-- For startList, we locate "{{votes/layout" in the code and add 14. The addition of 14 is because otherwise the "{{votes/layout" would be present in the final result.
	-- For endList, we locate "}}" in the code and subtract 1. The subtraction of 1 is because otherwise "}" would be present in the final result.
	local startList = string.find (fullPage, "{{votes/layout", 1, 1)
	startList = startList + 14
	local endList = string.find (fullPage, "}}", 1, 1)
	endList = endList - 1

	-- list is just the list of votes, as a single string.
	local list = string.sub (fullPage, startList, endList)

	-- parts = the list of votes, as an array separated by the "|" in the original text.
	local parts = mw.text.split (list, "|", true)

	return parts
end

function export.countSOA ()
	local parts = voteParts()

	-- voteOrder -> key = # of vote in the order, value = vote name
	-- voteContents -> key = vote name, value = the contents of each vote page
	local voteOrder = {}
	local voteContents = {}

	-- k%3 == 0 → vote title, without "Wiktionary:Votes/"
	-- k+1 == the status of the vote, if k%3
	-- k-1 == the end date of the vote, if k%3
	for k, v in pairs(parts) do
		if k%3 == 0 then
			table.insert(voteOrder, v)
			voteContents[v] = getFullPage("Wiktionary:Votes/" .. v)
		end
	end

	-- SOA = total number of supports, opposes, abstains
	local SOA = 0;

	for k, v in pairs(voteOrder) do
		-- count = (key = user name; value = number of votes in the given vote page)
		local count = countVotesFullPage(voteContents[v])

		for countKey, countValue in pairs (count) do
			SOA = SOA + countValue
		end
	end

	return SOA
end

function export.countPages ()
	local parts = voteParts()
	local voteCount = 0

	for k, v in pairs(parts) do
		if k%3 == 0 then
			voteCount = voteCount + 1
		end
	end

	return voteCount
end

return export