Difference between revisions of "Module:Election results"
Jump to navigation
Jump to search
blackwiki>Frietjes (per request on the talk page) |
blackwiki>Frietjes (new version with new features per discussion on talk page) |
||
| Line 1: | Line 1: | ||
| + | require('Module:No globals') | ||
local p = {} | local p = {} | ||
| + | |||
function p.main(frame) | function p.main(frame) | ||
local args = require('Module:Arguments').getArgs(frame) | local args = require('Module:Arguments').getArgs(frame) | ||
| − | local index, | + | local index, headings = {}, {} |
| − | local | + | local cols, rounds = 0, 1 |
| − | local winner, winner_votes = 0, 0 | + | local winner, winner_votes = {0, 0}, {0, 0} |
| − | local valid, invalid, electorate = 0, tonumber(args. | + | local valid = {0, 0} |
| + | local invalid = {tonumber(args.invalid) or 0, tonumber(args.invalid2) or 0} | ||
| + | local electorate = {tonumber(args.electorate) or 0, tonumber(args.electorate2) or tonumber(args.electorate) or 0} | ||
| + | local row, secondrow | ||
| + | local tracking = '' | ||
| + | |||
| + | -- helper functions | ||
| + | local lang = mw.getContentLanguage() | ||
| + | local function fmt(n) | ||
| + | return n and tonumber(n) and lang:formatNum(tonumber(n)) or nil | ||
| + | end | ||
| + | local function pct(n,d) | ||
| + | n, d = tonumber(n), tonumber(d) | ||
| + | if n and d and d > 0 then | ||
| + | return string.format('%.2f', n / d * 100) | ||
| + | end | ||
| + | return '–' | ||
| + | end | ||
| + | local function unlink(s) | ||
| + | if s then | ||
| + | s = s:match("^.-%[%[(.-)|.-%]%].*$") or s | ||
| + | s = s:match("^.-%[%[(.-)%]%].*$") or s | ||
| + | end | ||
| + | return s | ||
| + | end | ||
| + | |||
| + | -- preprocess the input | ||
for i = 1, 20 do | for i = 1, 20 do | ||
| − | + | for k, key in ipairs({'cand', 'vp', 'party', 'sc', 'seats'}) do | |
| − | + | if args[key .. i] then | |
| − | if | + | headings[key] = true |
| − | |||
| − | |||
end | end | ||
| − | if | + | end |
| − | + | -- temporary tracking | |
| − | + | for k, key in ipairs({'party', 'cand', 'vp'}) do | |
| + | if args[key .. i] then | ||
| + | if unlink(args[key .. i]) == args[key .. i] then | ||
| + | tracking = tracking .. '[[Category:Pages using election results with unlinked ' .. key .. '|' .. i .. ']]' | ||
| + | end | ||
end | end | ||
| + | end | ||
| + | if args['cand' .. i] or args['party' .. i] then | ||
| + | table.insert(index, i) | ||
if args['votes' .. i] then | if args['votes' .. i] then | ||
| − | + | local votesi = tonumber(args['votes' .. i]) or 0 | |
| − | + | args['votes' .. i] = votesi | |
| − | winner = i | + | if votesi > winner_votes[1] then |
| − | winner_votes | + | winner[1] = i |
| + | winner_votes[1] = votesi | ||
end | end | ||
| − | valid = valid + args['votes' .. i] | + | valid[1] = valid[1] + votesi |
| + | end | ||
| + | if args['votes' .. i .. '_2'] then | ||
| + | rounds = 2 | ||
| + | local votesi = tonumber(args['votes' .. i .. '_2']) or 0 | ||
| + | args['votes' .. i .. '_2'] = votesi | ||
| + | if votesi > winner_votes[2] then | ||
| + | winner[2] = i | ||
| + | winner_votes[2] = votesi | ||
| + | end | ||
| + | valid[2] = valid[2] + votesi | ||
end | end | ||
end | end | ||
end | end | ||
| + | |||
| + | -- build the table | ||
local root = mw.html.create('table') | local root = mw.html.create('table') | ||
root | root | ||
| Line 32: | Line 78: | ||
:tag('caption') | :tag('caption') | ||
:wikitext(args.caption) | :wikitext(args.caption) | ||
| − | local row = | + | |
| − | if | + | local rowspan = (rounds > 1) and 2 or nil |
| + | row = root:tag('tr') | ||
| + | if headings['cand'] then | ||
row | row | ||
:tag('th') | :tag('th') | ||
| − | :wikitext('President') | + | :wikitext(headings['vp'] and 'President' or 'Candidate') |
| + | :attr('scope', 'col') | ||
:attr('colspan', 2) | :attr('colspan', 2) | ||
| + | :attr('rowspan', rowspan) | ||
| + | cols = cols + 2 | ||
| + | if headings['vp'] then | ||
| + | row | ||
| + | :tag('th') | ||
| + | :wikitext('Vice president') | ||
| + | :attr('scope', 'col') | ||
| + | :attr('rowspan', rowspan) | ||
| + | cols = cols + 1 | ||
| + | end | ||
| + | if headings['party'] then | ||
| + | row | ||
| + | :tag('th') | ||
| + | :wikitext('Party') | ||
| + | :attr('scope', 'col') | ||
| + | :attr('rowspan', rowspan) | ||
| + | cols = cols + 1 | ||
| + | end | ||
| + | elseif headings['party'] then | ||
| + | row | ||
:tag('th') | :tag('th') | ||
| − | :wikitext(' | + | :wikitext('Party') |
| − | + | :attr('scope', 'col') | |
| + | :attr('colspan', 2) | ||
| + | :attr('rowspan', rowspan) | ||
| + | cols = cols + 2 | ||
| + | end | ||
| + | if rounds > 1 then | ||
row | row | ||
:tag('th') | :tag('th') | ||
| − | :wikitext(' | + | :wikitext('First round') |
| + | :attr('scope', 'col') | ||
| + | :attr('colspan', 2) | ||
| + | :tag('th') | ||
| + | :wikitext('Second round') | ||
| + | :attr('scope', 'col') | ||
:attr('colspan', 2) | :attr('colspan', 2) | ||
| + | secondrow = root:tag('tr') | ||
| + | else | ||
| + | secondrow = row | ||
end | end | ||
| − | if | + | for k=1,rounds do |
| + | secondrow | ||
| + | :tag('th') | ||
| + | :wikitext('Votes') | ||
| + | :attr('scope', 'col') | ||
| + | :tag('th') | ||
| + | :wikitext('%') | ||
| + | :attr('scope', 'col') | ||
| + | cols = cols + 2 | ||
| + | end | ||
| + | if headings['seats'] then | ||
row | row | ||
:tag('th') | :tag('th') | ||
| − | :wikitext(' | + | :wikitext('Seats') |
| + | :attr('scope', 'col') | ||
| + | :attr('rowspan', rowspan) | ||
| + | cols = cols + 1 | ||
end | end | ||
| − | row | + | if headings['sc'] then |
| − | + | row | |
| − | + | :tag('th') | |
| − | + | :wikitext(headings['seats'] and '+/–' or 'Seats±') | |
| − | + | :attr('scope', 'col') | |
| − | + | :attr('rowspan', rowspan) | |
| − | + | cols = cols + 1 | |
| − | |||
| − | |||
end | end | ||
| + | |||
| + | local rsuff = (rounds > 1) and {'', '_2'} or {''} | ||
for i, v in ipairs(index) do | for i, v in ipairs(index) do | ||
| − | + | row = root:tag('tr') | |
| − | local color = args['colour' .. v] or args['color' .. v] | + | |
| − | or | + | -- determine the color |
| − | + | local color = args['colour' .. v] or args['color' .. v] or nil | |
| − | + | if color == nil then | |
| + | local party = unlink(args['party' .. v]) or '' | ||
| + | if party ~= '' and mw.title.new('Template:' .. party .. '/meta/color').exists then | ||
| + | color = frame:expandTemplate{title = party .. '/meta/color'} | ||
| + | end | ||
| + | end | ||
| + | -- create the empty color cell | ||
row | row | ||
:tag('td') | :tag('td') | ||
:css('background-color', color) | :css('background-color', color) | ||
| − | if | + | |
| + | -- add the rest of the row | ||
| + | if headings['cand'] then | ||
row | row | ||
:tag('td') | :tag('td') | ||
| − | :wikitext( | + | :wikitext(args['cand' .. v]) |
| + | if headings['vp'] then | ||
| + | row | ||
| + | :tag('td') | ||
| + | :wikitext(args['vp' .. v]) | ||
| + | end | ||
| + | end | ||
| + | if headings['party'] then | ||
row | row | ||
:tag('td') | :tag('td') | ||
| − | :wikitext( | + | :wikitext(args['party' .. v]) |
| − | + | end | |
| + | for kk, suf in ipairs(rsuff) do | ||
| + | if(args['votes' .. v .. suf]) then | ||
| + | row:tag('td') | ||
| + | :css('text-align', 'right') | ||
| + | :wikitext(fmt(args['votes' .. v .. suf])) | ||
| + | row:tag('td') | ||
| + | :css('text-align', 'right') | ||
| + | :wikitext(pct(args['votes' .. v .. suf], valid[kk])) | ||
| + | else | ||
| + | row:tag('td'):attr('colspan', 2) | ||
| + | end | ||
| + | end | ||
| + | if headings['seats'] then | ||
row | row | ||
:tag('td') | :tag('td') | ||
| − | : | + | :css('text-align', 'right') |
| + | :wikitext(fmt(args['seats' .. v])) | ||
end | end | ||
| − | if | + | if headings['sc'] then |
| − | |||
| − | |||
| − | |||
| − | |||
row | row | ||
:tag('td') | :tag('td') | ||
| − | + | :css('text-align', 'right') | |
| − | + | :wikitext(args['sc' .. v]) | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
end | end | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
end | end | ||
| − | root | + | row = root |
:tag('tr') | :tag('tr') | ||
:addClass('sortbottom') | :addClass('sortbottom') | ||
:css('background', '#eaecf0') | :css('background', '#eaecf0') | ||
:css('text-align', 'right') | :css('text-align', 'right') | ||
| + | row | ||
| + | :tag('td') | ||
| + | :wikitext('Valid votes') | ||
| + | :attr('colspan', cols - 2*rounds) | ||
| + | :css('text-align', 'left') | ||
| + | for k=1,rounds do | ||
| + | row | ||
:tag('td') | :tag('td') | ||
| − | :wikitext( | + | :wikitext(fmt(valid[k])) |
| − | |||
| − | |||
:tag('td') | :tag('td') | ||
| − | :wikitext( | + | :wikitext(pct(valid[k], valid[k] + invalid[k])) |
| − | + | end | |
| − | + | row = root:tag('tr') | |
| − | |||
:addClass('sortbottom') | :addClass('sortbottom') | ||
:css('background', '#eaecf0') | :css('background', '#eaecf0') | ||
:css('text-align', 'right') | :css('text-align', 'right') | ||
| + | row | ||
| + | :tag('td') | ||
| + | :wikitext('Invalid votes') | ||
| + | :attr('colspan', cols - 2*rounds) | ||
| + | :css('text-align', 'left') | ||
| + | for k=1,rounds do | ||
| + | row | ||
:tag('td') | :tag('td') | ||
| − | :wikitext( | + | :wikitext(fmt(invalid[k])) |
| − | |||
| − | |||
:tag('td') | :tag('td') | ||
| − | :wikitext( | + | :wikitext(pct(invalid[k], valid[k] + invalid[k])) |
| − | + | end | |
| − | + | row = root:tag('tr') | |
| − | |||
:addClass('sortbottom') | :addClass('sortbottom') | ||
:css('font-weight', 'bold') | :css('font-weight', 'bold') | ||
:css('background', '#eaecf0') | :css('background', '#eaecf0') | ||
:css('text-align', 'right') | :css('text-align', 'right') | ||
| + | row | ||
| + | :tag('td') | ||
| + | :wikitext('Total votes') | ||
| + | :attr('colspan', cols - 2*rounds) | ||
| + | :css('text-align', 'left') | ||
| + | for k=1,rounds do | ||
| + | row | ||
:tag('td') | :tag('td') | ||
| − | :wikitext( | + | :wikitext(fmt(valid[k] + invalid[k])) |
| − | |||
| − | |||
:tag('td') | :tag('td') | ||
| − | :wikitext( | + | :wikitext(pct(1,1)) |
| − | + | end | |
| − | + | ||
| − | + | row = root:tag('tr') | |
:addClass('sortbottom') | :addClass('sortbottom') | ||
:css('background', '#eaecf0') | :css('background', '#eaecf0') | ||
:css('text-align', 'right') | :css('text-align', 'right') | ||
| − | + | row:tag('td') | |
| − | + | :wikitext('Registered voters/turnout') | |
| − | + | :attr('colspan', cols - 2*rounds) | |
| − | + | :css('text-align', 'left') | |
| − | + | for k=1,rounds do | |
| − | + | row:tag('td') | |
| − | + | :wikitext(fmt(electorate[k])) | |
| − | + | row:tag('td') | |
| − | + | :wikitext(pct(valid[k] + invalid[k], electorate[k])) | |
| + | end | ||
| + | row = root:tag('tr') | ||
:addClass('sortbottom') | :addClass('sortbottom') | ||
:css('background', '#eaecf0') | :css('background', '#eaecf0') | ||
:css('text-align', 'right') | :css('text-align', 'right') | ||
| − | + | row:tag('td') | |
| − | + | :wikitext('Source: ', args.source) | |
| − | + | :attr('colspan', cols) | |
| − | + | :css('text-align', 'left') | |
| − | return tostring(root) | + | |
| + | return tostring(root) .. tracking | ||
end | end | ||
| + | |||
return p | return p | ||
Revision as of 16:27, 19 August 2020
Implements {{Election results}}
require('Module:No globals')
local p = {}
function p.main(frame)
local args = require('Module:Arguments').getArgs(frame)
local index, headings = {}, {}
local cols, rounds = 0, 1
local winner, winner_votes = {0, 0}, {0, 0}
local valid = {0, 0}
local invalid = {tonumber(args.invalid) or 0, tonumber(args.invalid2) or 0}
local electorate = {tonumber(args.electorate) or 0, tonumber(args.electorate2) or tonumber(args.electorate) or 0}
local row, secondrow
local tracking = ''
-- helper functions
local lang = mw.getContentLanguage()
local function fmt(n)
return n and tonumber(n) and lang:formatNum(tonumber(n)) or nil
end
local function pct(n,d)
n, d = tonumber(n), tonumber(d)
if n and d and d > 0 then
return string.format('%.2f', n / d * 100)
end
return '–'
end
local function unlink(s)
if s then
s = s:match("^.-%[%[(.-)|.-%]%].*$") or s
s = s:match("^.-%[%[(.-)%]%].*$") or s
end
return s
end
-- preprocess the input
for i = 1, 20 do
for k, key in ipairs({'cand', 'vp', 'party', 'sc', 'seats'}) do
if args[key .. i] then
headings[key] = true
end
end
-- temporary tracking
for k, key in ipairs({'party', 'cand', 'vp'}) do
if args[key .. i] then
if unlink(args[key .. i]) == args[key .. i] then
tracking = tracking .. '[[Category:Pages using election results with unlinked ' .. key .. '|' .. i .. ']]'
end
end
end
if args['cand' .. i] or args['party' .. i] then
table.insert(index, i)
if args['votes' .. i] then
local votesi = tonumber(args['votes' .. i]) or 0
args['votes' .. i] = votesi
if votesi > winner_votes[1] then
winner[1] = i
winner_votes[1] = votesi
end
valid[1] = valid[1] + votesi
end
if args['votes' .. i .. '_2'] then
rounds = 2
local votesi = tonumber(args['votes' .. i .. '_2']) or 0
args['votes' .. i .. '_2'] = votesi
if votesi > winner_votes[2] then
winner[2] = i
winner_votes[2] = votesi
end
valid[2] = valid[2] + votesi
end
end
end
-- build the table
local root = mw.html.create('table')
root
:addClass('wikitable sortable')
:tag('caption')
:wikitext(args.caption)
local rowspan = (rounds > 1) and 2 or nil
row = root:tag('tr')
if headings['cand'] then
row
:tag('th')
:wikitext(headings['vp'] and 'President' or 'Candidate')
:attr('scope', 'col')
:attr('colspan', 2)
:attr('rowspan', rowspan)
cols = cols + 2
if headings['vp'] then
row
:tag('th')
:wikitext('Vice president')
:attr('scope', 'col')
:attr('rowspan', rowspan)
cols = cols + 1
end
if headings['party'] then
row
:tag('th')
:wikitext('Party')
:attr('scope', 'col')
:attr('rowspan', rowspan)
cols = cols + 1
end
elseif headings['party'] then
row
:tag('th')
:wikitext('Party')
:attr('scope', 'col')
:attr('colspan', 2)
:attr('rowspan', rowspan)
cols = cols + 2
end
if rounds > 1 then
row
:tag('th')
:wikitext('First round')
:attr('scope', 'col')
:attr('colspan', 2)
:tag('th')
:wikitext('Second round')
:attr('scope', 'col')
:attr('colspan', 2)
secondrow = root:tag('tr')
else
secondrow = row
end
for k=1,rounds do
secondrow
:tag('th')
:wikitext('Votes')
:attr('scope', 'col')
:tag('th')
:wikitext('%')
:attr('scope', 'col')
cols = cols + 2
end
if headings['seats'] then
row
:tag('th')
:wikitext('Seats')
:attr('scope', 'col')
:attr('rowspan', rowspan)
cols = cols + 1
end
if headings['sc'] then
row
:tag('th')
:wikitext(headings['seats'] and '+/–' or 'Seats±')
:attr('scope', 'col')
:attr('rowspan', rowspan)
cols = cols + 1
end
local rsuff = (rounds > 1) and {'', '_2'} or {''}
for i, v in ipairs(index) do
row = root:tag('tr')
-- determine the color
local color = args['colour' .. v] or args['color' .. v] or nil
if color == nil then
local party = unlink(args['party' .. v]) or ''
if party ~= '' and mw.title.new('Template:' .. party .. '/meta/color').exists then
color = frame:expandTemplate{title = party .. '/meta/color'}
end
end
-- create the empty color cell
row
:tag('td')
:css('background-color', color)
-- add the rest of the row
if headings['cand'] then
row
:tag('td')
:wikitext(args['cand' .. v])
if headings['vp'] then
row
:tag('td')
:wikitext(args['vp' .. v])
end
end
if headings['party'] then
row
:tag('td')
:wikitext(args['party' .. v])
end
for kk, suf in ipairs(rsuff) do
if(args['votes' .. v .. suf]) then
row:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['votes' .. v .. suf]))
row:tag('td')
:css('text-align', 'right')
:wikitext(pct(args['votes' .. v .. suf], valid[kk]))
else
row:tag('td'):attr('colspan', 2)
end
end
if headings['seats'] then
row
:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['seats' .. v]))
end
if headings['sc'] then
row
:tag('td')
:css('text-align', 'right')
:wikitext(args['sc' .. v])
end
end
row = root
:tag('tr')
:addClass('sortbottom')
:css('background', '#eaecf0')
:css('text-align', 'right')
row
:tag('td')
:wikitext('Valid votes')
:attr('colspan', cols - 2*rounds)
:css('text-align', 'left')
for k=1,rounds do
row
:tag('td')
:wikitext(fmt(valid[k]))
:tag('td')
:wikitext(pct(valid[k], valid[k] + invalid[k]))
end
row = root:tag('tr')
:addClass('sortbottom')
:css('background', '#eaecf0')
:css('text-align', 'right')
row
:tag('td')
:wikitext('Invalid votes')
:attr('colspan', cols - 2*rounds)
:css('text-align', 'left')
for k=1,rounds do
row
:tag('td')
:wikitext(fmt(invalid[k]))
:tag('td')
:wikitext(pct(invalid[k], valid[k] + invalid[k]))
end
row = root:tag('tr')
:addClass('sortbottom')
:css('font-weight', 'bold')
:css('background', '#eaecf0')
:css('text-align', 'right')
row
:tag('td')
:wikitext('Total votes')
:attr('colspan', cols - 2*rounds)
:css('text-align', 'left')
for k=1,rounds do
row
:tag('td')
:wikitext(fmt(valid[k] + invalid[k]))
:tag('td')
:wikitext(pct(1,1))
end
row = root:tag('tr')
:addClass('sortbottom')
:css('background', '#eaecf0')
:css('text-align', 'right')
row:tag('td')
:wikitext('Registered voters/turnout')
:attr('colspan', cols - 2*rounds)
:css('text-align', 'left')
for k=1,rounds do
row:tag('td')
:wikitext(fmt(electorate[k]))
row:tag('td')
:wikitext(pct(valid[k] + invalid[k], electorate[k]))
end
row = root:tag('tr')
:addClass('sortbottom')
:css('background', '#eaecf0')
:css('text-align', 'right')
row:tag('td')
:wikitext('Source: ', args.source)
:attr('colspan', cols)
:css('text-align', 'left')
return tostring(root) .. tracking
end
return p