Module:Election results-STV
Appearance
require('strict')
local p = {}
local political_party = require('Module:Political party')
function p.main(frame)
local args = require('Module:Arguments').getArgs(frame)
local index, headings, showtotal = {}, {}, {}
local cols, rounds = 0, 1
local winner, winner_votes = {0, 0, 0}, {0, 0, 0}
local valid = {0, 0, 0}
local invalidblank = {tonumber(args.invalidblank) or 0, tonumber(args.invalidblank2) or 0, tonumber(args.invalidblank3) or 0}
local invalid = {tonumber(args.invalid) or 0, tonumber(args.invalid2) or 0, tonumber(args.invalid3) or 0}
local blank = {tonumber(args.blank) or 0, tonumber(args.blank2) or 0, tonumber(args.blank3) or 0}
local totalvotes = {tonumber(args.totalvotes), tonumber(args.totalvotes2), tonumber(args.totalvotes3)}
local electorate = {tonumber(args.electorate), tonumber(args.electorate2), tonumber(args.electorate3)}
local turnout = {tonumber(args.turnout), tonumber(args.turnout2), tonumber(args.turnout3)}
local row, secondrow
local tracking = ''
local max_rows = 0
-- 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 tonumdash(s)
if s then
s = mw.ustring.gsub(s, '&[MmNn][Dd][Aa][Ss][Hh];', '-')
s = mw.ustring.gsub(s, '&[Mm][Ii][Nn][Uu][Ss];', '-')
s = mw.ustring.gsub(s, '[—–−]', '-')
return tonumber(s) or 0
end
end
local function unlink(s)
if s then
s = s:match("^[^%[]-%[%[([^%]]-)|[^%]]-%]%].*$") or s
s = s:match("^[^%[]-%[%[([^%]]-)%]%].*$") or s
end
return s
end
local function get_color(color, party)
if color == nil then
local party = unlink(party) or ''
if party ~= '' then
color = political_party._fetch({party, 'color'})
end
end
if color ~= nil then
color = mw.ustring.gsub(color, '&(#)35;', '%1')
end
return color
end
-- preprocess the input
local stop_flag = false
local i = 0
while stop_flag == false do
stop_flag = true
for kk = 1, 20 do
i = i + 1
for k, key in ipairs({'alliance', 'cand', 'party', 'ivotes', 'ipct', 'totalvotes', 'acolor', 'rcolor'}) do
if args[key .. i] then
headings[key] = true
stop_flag = false
max_rows = i > max_rows and i or max_rows
end
end
if args['row' .. i] then
stop_flag = false
max_rows = i > max_rows and i or max_rows
end
if args['row' .. i] or args['cand' .. i] or args['header' .. i] then
table.insert(index, i)
if args['votes' .. i] then
if tonumber(args['votes' .. i]) then showtotal.votes = 1 end
local votesi = tonumber(args['votes' .. i]) or 0
args['votes' .. i] = votesi
valid[1] = valid[1] + votesi
end
if args['party' .. i] then
has_party = true
end
if args['votes' .. i .. '_2'] then
rounds = (rounds < 2) and 2 or rounds
if tonumber(args['votes' .. i .. '_2']) then showtotal.votes_2 = 1 end
local votesi = tonumber(args['votes' .. i .. '_2']) or 0
args['votes' .. i .. '_2'] = votesi
valid[2] = valid[2] + votesi
end
if args['votes' .. i .. '_3'] then
rounds = (rounds < 3) and 3 or rounds
if tonumber(args['votes' .. i .. '_3']) then showtotal.votes_3 = 1 end
local votesi = tonumber(args['votes' .. i .. '_3']) or 0
args['votes' .. i .. '_3'] = votesi
valid[3] = valid[3] + votesi
end
if args['votes' .. i .. '_4'] then
rounds = (rounds < 4) and 4 or rounds
if tonumber(args['votes' .. i .. '_4']) then showtotal.votes_4 = 1 end
local votesi = tonumber(args['votes' .. i .. '_4']) or 0
args['votes' .. i .. '_4'] = votesi
valid[4] = valid[4] + votesi
end
if args['votes' .. i .. '_5'] then
rounds = (rounds < 5) and 5 or rounds
if tonumber(args['votes' .. i .. '_5']) then showtotal.votes_5 = 1 end
local votesi = tonumber(args['votes' .. i .. '_5']) or 0
args['votes' .. i .. '_5'] = votesi
valid[5] = valid[5] + votesi
end
if args['votes' .. i .. '_6'] then
rounds = (rounds < 6) and 6 or rounds
if tonumber(args['votes' .. i .. '_3']) then showtotal.votes_6 = 1 end
local votesi = tonumber(args['votes' .. i .. '_6']) or 0
args['votes' .. i .. '_6'] = votesi
valid[6] = valid[6] + votesi
end
if args['votes' .. i .. '_7'] then
rounds = (rounds < 7) and 7 or rounds
if tonumber(args['votes' .. i .. '_7']) then showtotal.votes_7 = 1 end
local votesi = tonumber(args['votes' .. i .. '_7']) or 0
args['votes' .. i .. '_7'] = votesi
valid[7] = valid[7] + votesi
end
if args['votes' .. i .. '_8'] then
rounds = (rounds < 8) and 8 or rounds
if tonumber(args['votes' .. i .. '_8']) then showtotal.votes_8 = 1 end
local votesi = tonumber(args['votes' .. i .. '_8']) or 0
args['votes' .. i .. '_8'] = votesi
valid[8] = valid[8] + votesi
end
if args['votes' .. i .. '_9'] then
rounds = (rounds < 9) and 3 or rounds
if tonumber(args['votes' .. i .. '_9']) then showtotal.votes_9 = 1 end
local votesi = tonumber(args['votes' .. i .. '_9']) or 0
args['votes' .. i .. '_9'] = votesi
valid[9] = valid[9] + votesi
end
if args['votes' .. i .. '_10'] then
rounds = (rounds < 10) and 10 or rounds
if tonumber(args['votes' .. i .. '_10']) then showtotal.votes_10 = 1 end
local votesi = tonumber(args['votes' .. i .. '_10']) or 0
args['votes' .. i .. '_10'] = votesi
valid[10] = valid[10] + votesi
end
if args['votes' .. i .. '_11'] then
rounds = (rounds < 11) and 11 or rounds
if tonumber(args['votes' .. i .. '_11']) then showtotal.votes_11 = 1 end
local votesi = tonumber(args['votes' .. i .. '_11']) or 0
args['votes' .. i .. '_11'] = votesi
valid[11] = valid[11] + votesi
end
if args['votes' .. i .. '_12'] then
rounds = (rounds < 12) and 12 or rounds
if tonumber(args['votes' .. i .. '_12']) then showtotal.votes_12 = 1 end
local votesi = tonumber(args['votes' .. i .. '_12']) or 0
args['votes' .. i .. '_12'] = votesi
valid[12] = valid[12] + votesi
end
if args['votes' .. i .. '_13'] then
rounds = (rounds < 13) and 13 or rounds
if tonumber(args['votes' .. i .. '_13']) then showtotal.votes_13 = 1 end
local votesi = tonumber(args['votes' .. i .. '_13']) or 0
args['votes' .. i .. '_13'] = votesi
valid[13] = valid[13] + votesi
end
end
end
end
local ovalid = {valid[1], valid[2], valid[3], valid[4], valid[5], valid[6], valid[7], valid[8], valid[9], valid[10], valid[11], valid[12], valid[13]}
if args['valid'] or ((rounds > 1) and args['valid2']) or ((rounds > 1) and args['valid3']) then
max_rows = max_rows + 1
local i = max_rows
table.insert(index, i)
args['votes' .. i] = showtotal.votes and valid[1] or nil
args['votes' .. i .. '_2'] = showtotal.votes_2 and valid[2] or nil
args['votes' .. i .. '_3'] = showtotal.votes_3 and valid[3] or nil
args['colour' .. i] = 'inherit'
args['color' .. i] = 'inherit'
args['row' .. i] = 'Total'
args['font-weight' .. i] = 'bold'
args['class' .. i] = 'sortbottom'
ovalid[1] = tonumber(args['valid']) or valid[1]
ovalid[2] = tonumber(args['valid2']) or valid[2]
ovalid[3] = tonumber(args['valid3']) or valid[3]
ovalid[4] = tonumber(args['valid4']) or valid[4]
ovalid[5] = tonumber(args['valid5']) or valid[5]
ovalid[6] = tonumber(args['valid6']) or valid[6]
ovalid[7] = tonumber(args['valid7']) or valid[7]
ovalid[8] = tonumber(args['valid8']) or valid[8]
ovalid[9] = tonumber(args['valid9']) or valid[9]
ovalid[10] = tonumber(args['valid10']) or valid[10]
ovalid[11] = tonumber(args['valid11']) or valid[11]
ovalid[12] = tonumber(args['valid12']) or valid[12]
ovalid[13] = tonumber(args['valid3']) or valid[13]
end
-- build the table
local root = mw.html.create(args['embed'] and '' or 'table')
if args['embed'] == nil then
root
:addClass('wikitable sortable')
:tag('caption')
:wikitext(args.caption)
:done()
end
local topcell = nil
if args['image'] then
topcell = root
:tag('th')
:css('text-align', 'center')
:css('background', '#F8F9FA')
:wikitext(args['image'])
end
if args['reporting'] then
if (topcell == nil) then
topcell = root
:tag('td')
:css('text-align', 'center')
:css('background', '#F8F9FA')
end
topcell:tag('div')
:addClass('center')
:wikitext(frame:expandTemplate{
title = 'percentage bar',
args = {args.reporting,
args.reporting .. '% reporting',
['width'] = '200px',
['hex'] = '1BCE0E'
}})
end
row = args['embed'] and mw.html.create('') or root:tag('tr')
if not args['party' .. i] then
row
:tag('th')
:wikitext('Candidate' or args.candtitle)
:attr('scope', 'col')
:attr('rowspan', 2)
:done()
cols = cols + 1
else
row
:tag('th')
:wikitext('Candidate' or args.candtitle)
:attr('scope', 'col')
:attr('rowspan', 2)
:attr('colspan', 2)
:done()
cols = cols + 2
end
if args['party' .. i] and not args['alliance' .. i] then
row
:tag('th')
:wikitext('Party' or args.partytitle)
:attr('scope', 'col')
:done()
cols = cols + 1
elseif args['alliance' .. i] then
row
:tag('th')
:wikitext('Party or alliance' or args.partytitle)
:attr('scope', 'col')
:attr('colspan', 2)
:done()
cols = cols + 2
end
row
:tag('th')
:wikitext('Votes')
:attr('rowspan', rowspan)
:attr('colspan', 40)
end
secondrow = args['embed'] and mw.html.create('') or root:tag('tr')
for k=1, rounds do
secondrow
:tag('th')
:wikitext('Votes')
:attr('scope', 'col')
:attr('rowspan', rowspan)
:done()
:tag('th')
:wikitext('%')
:attr('scope', 'col')
:attr('rowspan', rowspan)
:done()
cols = cols + 2
end
if topcell then
topcell:attr('colspan', cols)
end
local cs = cols - 2*rounds
local rsuff = (rounds > 1) and {'', '_2'} or (rounds > 2) and {'', '_3'} or {''}
row = root:tag('tr')
:addClass(args['class' .. v])
:css('font-weight', args['font-weight' .. v])
-- determine the colors
local color = get_color(args['colour' .. v] or args['color' .. v] or nil, args['party' .. v])
local acolor = get_color(args['acolour' .. v] or args['acolor' .. v] or nil, args['alliance' .. v])
local rcolor = get_color(args['rcolour' .. v] or args['rcolor' .. v] or nil)
if args['alliance' .. v] then
row
:tag('td')
:attr('rowspan', args['aspan' .. v])
:css('width', '0px')
:css('background-color', acolor)
elseif args['party' .. v] then
row
:tag('td')
:css('width', '0px')
:css('background-color', color)
end
if args['cand' .. v] then
row
:css('background-color', rcolor)
:tag('td')
:attr('rowspan', args['candspan' .. v] or args['aspan' .. v])
:wikitext(args['cand' .. v])
end
if args['alliance' .. v] then
row
:tag('td')
:attr('rowspan', args['aspan' .. v])
:wikitext(args['alliance' .. v])
:tag('td')
:css('width', '0px')
:css('background-color', color)
end
if args['party' .. v] then
row
:tag('td')
:wikitext(args['party' .. v])
end
if args['row' .. v] then
row
:css('background-color', rcolor)
:tag('td')
:attr('colspan', cs)
:wikitext(args['row' .. v])
end
row:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['votes' .. v]))
row:tag('td')
:css('text-align', 'right')
:wikitext(pct(args['votes' .. v], valid[1]))
if args['votes' .. v .. '_2'] then
row:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['votes' .. v .. '_2']))
row:tag('td')
:css('text-align', 'right')
:wikitext(pct(args['votes' .. v .. '_2'], valid[2]))
end
if args['votes' .. v .. '_3'] then
row:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['votes' .. v .. '_3']))
row:tag('td')
:css('text-align', 'right')
:wikitext(pct(args['votes' .. v .. '_3'], valid[3]))
end
if args['votes' .. v .. '_4'] then
row:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['votes' .. v .. '_4']))
row:tag('td')
:css('text-align', 'right')
:wikitext(pct(args['votes' .. v .. '_4'], valid[4]))
end
if args['votes' .. v .. '_5'] then
row:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['votes' .. v .. '_5']))
row:tag('td')
:css('text-align', 'right')
:wikitext(pct(args['votes' .. v .. '_5'], valid[5]))
end
if args['votes' .. v .. '_6'] then
row:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['votes' .. v .. '_6']))
row:tag('td')
:css('text-align', 'right')
:wikitext(pct(args['votes' .. v .. '_6'], valid[6]))
end
if args['votes' .. v .. '_7'] then
row:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['votes' .. v .. '_7']))
row:tag('td')
:css('text-align', 'right')
:wikitext(pct(args['votes' .. v .. '_7'], valid[7]))
end
if args['votes' .. v .. '_8'] then
row:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['votes' .. v .. '_8']))
row:tag('td')
:css('text-align', 'right')
:wikitext(pct(args['votes' .. v .. '_8'], valid[8]))
end
if args['votes' .. v .. '_9'] then
row:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['votes' .. v .. '_9']))
row:tag('td')
:css('text-align', 'right')
:wikitext(pct(args['votes' .. v .. '_9'], valid[9]))
end
if args['votes' .. v .. '_10'] then
row:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['votes' .. v .. '_10']))
row:tag('td')
:css('text-align', 'right')
:wikitext(pct(args['votes' .. v .. '_10'], valid[10]))
end
if args['votes' .. v .. '_11'] then
row:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['votes' .. v .. '_11']))
row:tag('td')
:css('text-align', 'right')
:wikitext(pct(args['votes' .. v .. '_11'], valid[11]))
end
if args['votes' .. v .. '_12'] then
row:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['votes' .. v .. '_12']))
row:tag('td')
:css('text-align', 'right')
:wikitext(pct(args['votes' .. v .. '_12'], valid[12]))
end
if args['votes' .. v .. '_13'] then
row:tag('td')
:css('text-align', 'right')
:wikitext(fmt(args['votes' .. v .. '_13']))
row:tag('td')
:css('text-align', 'right')
:wikitext(pct(args['votes' .. v .. '_13'], valid[13]))
end
-- separating line
if args['valid'] or args['invalidblank'] or args['invalid'] or args['totalvotes'] or args['electorate'] or args['turnout'] or args['source'] then
row = root
:tag('tr')
:addClass('sortbottom')
row
:tag('td')
:css('background', '#eaecf0')
:attr('colspan', cols)
end
-- valid votes
if args['invalidblank'] or args['invalid'] then
row = root
:tag('tr')
:addClass('sortbottom')
:css('text-align', 'right')
row
:tag('th')
:wikitext('Valid votes')
:attr('scope', 'row')
:attr('colspan', cs)
:css('text-align', 'left')
:css('font-weight', 'normal')
:css('background', 'inherit')
:tag('td')
:wikitext(fmt(ovalid[1]))
:tag('td')
:wikitext(pct(ovalid[1], ovalid[1] + invalidblank[1] + invalid[1] + blank[1]))
end
-- invalid votes
if args['invalidblank'] then
row = root:tag('tr')
:addClass('sortbottom')
:css('text-align', 'right')
row
:tag('th')
:wikitext('Invalid/blank votes')
:wikitext(args.invalidnote)
:attr('scope', 'row')
:attr('colspan', cs)
:css('text-align', 'left')
:css('font-weight', 'normal')
:css('background', 'inherit')
:tag('td')
:wikitext(fmt(invalidblank[1]))
:tag('td')
:wikitext(pct(invalidblank[1], ovalid[1] + invalidblank[1]))
end
if args['invalid'] then
row = root:tag('tr')
:addClass('sortbottom')
:css('text-align', 'right')
row
:tag('th')
:wikitext('Invalid votes')
:wikitext(args.invalidnote)
:attr('scope', 'row')
:attr('colspan', cs)
:css('text-align', 'left')
:css('font-weight', 'normal')
:css('background', 'inherit')
:tag('td')
:wikitext(fmt(invalid[1]))
:tag('td')
:wikitext(pct(invalid[1], ovalid[1] + invalid[1] + blank[1]))
end
-- blank votes
if args['blank'] then
row = root:tag('tr')
:addClass('sortbottom')
:css('text-align', 'right')
row
:tag('th')
:wikitext('Blank votes')
:attr('scope', 'row')
:attr('colspan', cs)
:css('text-align', 'left')
:css('font-weight', 'normal')
:css('background', 'inherit')
row
:tag('td')
:wikitext(fmt(blank[1]))
:tag('td')
:wikitext(pct(blank[1], ovalid[1] + invalid[1] + blank[1]))
end
-- total
if args['invalidblank'] or args['invalid'] or args['totalvotes'] then
row = root:tag('tr')
:addClass('sortbottom')
:css('font-weight', 'bold')
:css('text-align', 'right')
row
:tag('th')
:wikitext(args.tvtitle or 'Total votes')
:attr('scope', 'row')
:attr('colspan', cs)
:css('text-align', 'left')
:css('background', 'inherit')
:css('color', 'inherit')
if not args['totalvotes'] and args['invalid'] or not args['totalvotes'] and args['invalidblank'] then
row
:tag('td')
:wikitext(fmt(ovalid[1] + invalidblank[1] + invalid[1] + blank[1]))
:tag('td')
:wikitext(pct(1, 1))
elseif args['totalvotes'] then
row
:tag('td')
:wikitext(fmt(args.totalvotes))
:tag('td')
:wikitext('–')
end
end
-- registered
if args['electorate'] or args['turnout'] then
row = root:tag('tr')
:addClass('sortbottom')
:css('text-align', 'right')
row
:tag('th')
:wikitext('Registered voters/turnout')
:attr('scope', 'row')
:attr('colspan', cs)
:css('text-align', 'left')
:css('font-weight', 'normal')
:css('color', 'inherit')
:css('background', 'inherit')
row
:tag('td')
:wikitext(fmt(electorate[1]))
if args['invalidblank'] and args['electorate'] and not args['totalvotes'] or args['invalid'] and args['electorate'] and not args['totalvotes'] then
row
:tag('td')
:wikitext(args.turnout or pct(ovalid[1] + invalidblank[1] + invalid[1] + blank[1], electorate[1]))
elseif args['totalvotes'] and args['electorate'] then
row
:tag('td')
:wikitext(args.turnout or pct(totalvotes[1], electorate[1]))
elseif args['electorate'] then
row
:tag('td')
:wikitext(args.turnout or '–')
else
row
:tag('td')
:wikitext(args.turnout)
end
if args['majority'] then
if args['invalid'] or args['electorate'] then
row = root
:tag('tr')
:addClass('sortbottom')
row
:tag('td')
:css('background', '#eaecf0')
:attr('colspan', cols)
end
row = root
:tag('tr')
:addClass('sortbottom')
:css('text-align', 'right')
row
:tag('th')
:wikitext('Majority')
:attr('scope', 'row')
:attr('colspan', cs)
:css('text-align', 'left')
:css('font-weight', 'normal')
:css('background', 'inherit')
for k=1, rounds do
row
:tag('td')
:wikitext(fmt(majority[k]))
:tag('td')
:wikitext(fmt(majoritypct[k]))
end
if args['majoritysw'] then
row
:tag('td')
:wikitext(args['majoritysw'])
end
end
if args['result'] then
row = root:tag('tr')
:addClass('sortbottom')
-- determine the color
local color = get_color(args['resultcolour'] or nil, args['result'])
if args['resultsw'] then
row
:tag('td')
:css('background-color', color)
row
:tag('td')
:attr('colspan', 2)
:wikitext(args['result'])
row
:tag('td')
:attr('colspan', 2)
:css('text-align', 'right')
:wikitext('Swing')
row
:tag('td')
:css('text-align', 'right')
:wikitext(args['resultsw'])
else
row
:tag('td')
:css('background-color', color)
row
:tag('td')
:attr('colspan', cols - 1)
:wikitext(args['result'])
end
end
if args['source'] then
row = root:tag('tr')
:addClass('sortbottom')
:css('text-align', 'right')
row:tag('td')
:wikitext('Source: ', args.source)
:attr('colspan', cols)
:css('text-align', 'left')
end
if args['embedded'] then
root:wikitext(args['embedded'])
end
return tostring(root) .. tracking
end
return p