Jump to content

Module:Signpost poll

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Mr. Stradivarius (talk | contribs) at 07:22, 9 March 2015 (start adding some button code). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

-- This module implments polls used in articles of the Signpost.

local CONFIG_MODULE = 'Module:Signpost poll/config'

local mStringCount = require('Module:String count')
local mClickableButton2 = require('Module:Clickable button 2')
local lang = mw.language.getContentLanguage()

local config = {

colors = {
	'#006699', -- Foundation blue
	'#339966', -- Foundation red
	'#990000', -- Foundation green

	-- From http://stackoverflow.com/questions/470690/how-to-automatically-generate-n-distinct-colors
	-- with some colors similar to Foundation colors removed.
	'#FFB300',
	'#803E75',
	'#FF6800',
	'#A6BDD7',
	'#CEA262',
	'#817066',
	'#F6768E',
	'#FF7A5C',
	'#53377A',
	'#FF8E00',
	'#B32851',
	'#F4C800',
	'#93AA00',
	'#593315',
	'#F13A13',
	'#232C16',
},

msg = {

-- The default vote preload text.
-- $1 is the option number.
['vote-default'] = 'Voting for option $1.',

-- The text that appears beside each option in the legend.
-- $1 is the number of votes, and
-- $2 is the percentage of the total votes.
['legend-votes'] = '$2%; $1 votes',

['not-enough-votes'] = "Need '''$1''' more votes to display results—" ..
	"if you haven't already, consider voting!",

['preload-default'] = 'Wikipedia:Wikipedia Signpost/Templates/Voter/Vote preload',

['icon-default'] = 'WikipediaSignpostIcon.svg',

['overlay-default'] = 'Foundation Logo Transparent.svg',

['minimum-default'] = 10,

},

}

--[[
We need:

preload - use a standard preload and pass the option text in by parameter.
icon
question
votepage

option1
option1vote - the vote text to use for option one - defaults to option1
option1color
option2
option2vote
option2color
...

minimum - the minimum number of votes before results are displayed
break - if 'all', breaks on all options; if a number, breaks after that number option
overlay
expiry
]]

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

local function getUnixDate(date)
	date = lang:formatDate('U', date)
	return tonumber(date)
end

-------------------------------------------------------------------------------
-- Poll class
-------------------------------------------------------------------------------

local Poll = {}
Poll.__index = Poll

function Poll.new(args, cfg)
	local self = setmetatable({}, Poll)
	self.cfg = cfg or config

	-- Set required fields
	self.question = assert(args.question, 'please specify a question')
	self.votepage = assert(args.votepage, 'please specify a vote page')

	-- Set optional fields
	self.preload = args.preload or self:message('preload-default')
	self.icon = args.icon or self:message('icon-default')
	self.overlay = args.overlay or self:message('overlay-default')
	self.minimum = tonumber(args.minimum) or self:message('minimum-default')
	self.expiry = args.expiry
	self.lineBreak = tonumber(args['break']) or args['break']

	-- Set options
	self.options = {}
	do
		local i = 1
		while true do
			local key = 'option' .. tostring(i)
			local option = args[key]
			if not option then
				break
			end
			table.insert(self.options, {
				option = option,
				vote = args[key .. 'vote'],
				color = args[key .. 'color'],
			})
			i = i + 1
		end
		if #self.options < 2 then
			error('polls must have at least two options')
		end
	end

	-- Set vote counts. Doing it here means we can avoid checking whether it
	-- has been done every time.
	self:setAllVoteCounts()

	return self
end

function Poll:message(key, ...)
	local msg = self.cfg.msg[key]
	if select('#', ...) > 0 then
		return mw.message.newRawMessage(msg, ...):plain()
	else
		return msg
	end
end

function Poll:getVoteText(n)
	return self.options[n].vote or self:message('vote-default', n)
end

function Poll:countVote(n)
	return mStringCount._count{
		page = self.votepage,
		search = self:getVoteText(n)
	}
end

function Poll:setAllVoteCounts()
	for i, option in ipairs(self.options) do
		option.count = self:countVote(i)
	end
end

function Poll:getColor(n)
	-- Get the color for option n
	local color = self.options[n].color
	if color then
		return color
	else
		local colors = self.cfg.colors
		-- colors[#colors] is necessary as Lua arrays are indexed starting at 1,
		-- and n % #colors might sometimes equal 0.
		return colors[n] or colors[n % #colors] or colors[#colors]
	end
end

function Poll:getVoteTotal()
	local total = 0
	for i, option in ipairs(self.options) do
		total = total + option.count
	end
	return total
end

function Poll:getPercentage(n)
	local total = self:getVoteTotal()
	return self.options[n].count / total * 100
end

function Poll:hasMinimumVoteCount()
	return self:getVoteTotal() >= self.minimum
end

function Poll:isOpen()
	if self.expiry then
		return getUnixDate() < getUnixDate(self.expiry)
	else
		return true
	end
end

function Poll:makeVisualizationHTML()
	local visroot = mw.html.create('table')

	-- Overlay row
	visroot
		:css('height', '250px')
		:css('border-spacing', '0px')
		:tag('tr')
			:tag('td')
				:css('position', 'absolute')
				:css('z-index', '2')
				:css('padding', '0px')
				:css('margin', '0px')
				:wikitext(string.format(
					'[[File:%s|253px|link=]] &nbsp;',
					self.overlay
				))

	-- Option color rows
	local total = self:getVoteTotal()
	for i, option in ipairs(self.options) do
		visroot:tag('tr'):tag('td')
			:css('background', self:getColor(i))
			:css('padding', '0px')
			:css('margin', '0px')
			:css('width', '250px')
			:css('height', string.format(
				'%.3f%%', -- Round to 3 decimal places and add a percent sign
				self:getPercentage(i)
			))
			:wikitext('&nbsp;')
	end
	
	return root
end

function Poll:makeLegendHTML()
	local legendRoot = mw.html.create('table')

	for i, option in ipairs(self.options) do
		legendRoot:tag('tr'):tag('td')
			:tag('span')
				:css('display', 'inline-block')
				:css('width', '1.5em')
				:css('height', '1.5em')
				:css('margin', '1px 0')
				:css('border', '1px solid black')
				:css('background-color', self:getColor(i))
				:css('text-align', 'center')
				:done()
			:wikitext('&nbsp;')
			:wikitext(option.option)
			:wikitext(' ')
			:tag('small')
				:wikitext(self:message(
					'legend-votes',
					option.count,
					string.format('%.0f', self:getPercentage(i))
				))
	end
end

function Poll:makeVoteURL(n)
	local url = mw.uri.fullUrl(
		self.votepage,
		{
			action = 'edit',
			section = 'new',
			nosummary = 'true',
			preload = self.preload,
			['preloadparams[]'] = self:getVoteText(n)
		}
	)
	return tostring(url)
end

function Poll:makeButtonHTML()
	mClickableButton2.luaMain{}
end

-------------------------------------------------------------------------------
-- Exports
-------------------------------------------------------------------------------

local p = {}

function p._main(args, cfg)
	cfg = cfg or mw.loadData(CONFIG_MODULE)
	return tostring(Poll.new(args, cfg))
end

function p.main(frame)
	local args = require('Module:Arguments').getArgs(frame, {
		wrappers = 'Wikipedia:Wikipedia Signpost/Templates/Voter'
	})
	return p._main(args)
end

-- return p

return Poll