Difference between revisions of "Module:Election box US auto"
Jump to navigation
Jump to search
blackwiki>Legoktm (fix) |
blackwiki>Legoktm (implement join primary box if primary=yes. also smarter "gain" logic) |
||
| Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
local mError = require('Module:Error') | local mError = require('Module:Error') | ||
| + | local mYesno = require('Module:Yesno') | ||
| + | -- a global per #invoke | ||
| + | local linked_write_in = false | ||
function percent( part, total ) | function percent( part, total ) | ||
| Line 19: | Line 22: | ||
function p.make( frame ) | function p.make( frame ) | ||
return frame:preprocess( make( frame:getParent() ) ) | return frame:preprocess( make( frame:getParent() ) ) | ||
| + | end | ||
| + | |||
| + | function fmt_candidate(v, winner, total_votes,frame) | ||
| + | local temp = "{{" | ||
| + | if v[2] == winner[2] then | ||
| + | temp = temp .. "Election box winning candidate" | ||
| + | else | ||
| + | temp = temp .. "Election box candidate" | ||
| + | end | ||
| + | local n_party = normalize_parties( v[3] ) | ||
| + | if n_party then | ||
| + | temp = temp .. " with party link no change|party=" .. n_party | ||
| + | else | ||
| + | temp = temp .. " no party link no change" | ||
| + | end | ||
| + | link = frame.args[v[2] .. " link"] | ||
| + | if link then | ||
| + | link = mw.title.new(link) | ||
| + | else | ||
| + | -- bypass redirects, which is mostly important for display names | ||
| + | link = mw.title.new(v[2]) | ||
| + | -- except if the redirect goes to an "elections" article (e.g. Kim Vann), then | ||
| + | -- we don't want a bypass | ||
| + | if link.isRedirect and not string.find(link.redirectTarget.prefixedText, "elections", 1, true ) then | ||
| + | link = link.redirectTarget | ||
| + | end | ||
| + | end | ||
| + | -- Strip disambiguators since we can't use the pipe trick | ||
| + | display_name, ignore = mw.ustring.gsub(link.prefixedText, "%b()", "") | ||
| + | if frame.args.redlinks or (link.exists and not link.isRedirect) then | ||
| + | full_link = "[[" .. link.prefixedText .. "|" .. display_name .. "]]" | ||
| + | else | ||
| + | full_link = display_name | ||
| + | end | ||
| + | temp = temp .. "|candidate=" .. full_link | ||
| + | if v[4] then | ||
| + | -- incumbent | ||
| + | temp = temp .. " ([[incumbent]])" | ||
| + | end | ||
| + | if v[6] then | ||
| + | -- write in | ||
| + | if linked_write_in then | ||
| + | temp = temp .. " (write-in)" | ||
| + | else | ||
| + | temp = temp .. " ([[Write-in candidate|write-in]])" | ||
| + | linked_write_in = true -- only link it once | ||
| + | end | ||
| + | end | ||
| + | temp = temp .. "|votes={{formatnum:" .. v[5] .. "}}" | ||
| + | temp = temp .. "|percentage=" .. percent(v[5], total_votes) .. "%" | ||
| + | temp = temp .. "}}" | ||
| + | return temp | ||
end | end | ||
| Line 42: | Line 97: | ||
end | end | ||
end | end | ||
| − | local tab_name = state .. " Elections/" .. year .. "/" .. type .. "/Candidates.tab" | + | function load_tabular( state, year, type ) |
| − | + | local tab_name = state .. " Elections/" .. year .. "/" .. type .. "/Candidates.tab" | |
| − | + | local tabular = mw.ext.data.get(tab_name) | |
| − | return | + | if tabular then |
| + | return tabular | ||
| + | else | ||
| + | return {error="Unable to find tabular data: " .. tab_name} | ||
| + | end | ||
end | end | ||
| − | local | + | local tabular = load_tabular(state, year, type) |
| − | + | if tabular.error then | |
| − | + | return mError.error({message=tabular.error}) | |
| − | + | end | |
| − | + | function find_candidates(data, contest) | |
| + | local candidates = {} | ||
| + | for k,v in pairs(data) do | ||
| + | if v[1] == contest then | ||
| + | table.insert(candidates, v) | ||
| + | end | ||
end | end | ||
| + | return candidates | ||
end | end | ||
| − | local total_votes = 0 | + | local candidates = find_candidates(tabular.data, contest) |
| − | + | local incumb_party = false | |
| − | + | function sum_totals(candidates) | |
| − | + | local total_votes = 0 | |
| − | + | local winner = {} | |
| − | + | winner[5] = 0 | |
| − | + | for k,v in pairs(candidates) do | |
| + | total_votes = total_votes + v[5] | ||
| + | if v[5] > winner[5] then | ||
| + | winner = v | ||
| + | end | ||
| + | if v[4] then | ||
| + | incumb_party = v[3] | ||
| + | end | ||
end | end | ||
| + | return total_votes, winner | ||
end | end | ||
| + | local total_votes, winner = sum_totals(candidates) | ||
if mw.ustring.find(contest, "United States Representative", 1, true) then | if mw.ustring.find(contest, "United States Representative", 1, true) then | ||
title = "[[United States House of Representatives elections, " .. year .. "]]" | title = "[[United States House of Representatives elections, " .. year .. "]]" | ||
| + | elseif mw.ustring.find(contest, "State Assembly Member", 1, true) then | ||
| + | title = "[[" .. state .. " State Assembly election, " .. year .. "]]" | ||
elseif contest == "President" then | elseif contest == "President" then | ||
title = "U.S. presidential election in " .. state .. ", " .. year | title = "U.S. presidential election in " .. state .. ", " .. year | ||
| Line 70: | Line 146: | ||
title = "...????" | title = "...????" | ||
end | end | ||
| − | local | + | local primary = mYesno(frame.args.primary) |
| + | ptabular = load_tabular(state, year, "Primary") | ||
| + | if ptabular.error then | ||
| + | -- todo log an error here? | ||
| + | primary = false | ||
| + | end | ||
| + | if primary then | ||
| + | open = "Election box open primary begin no change" | ||
| + | else | ||
| + | open = "Election box begin no change" | ||
| + | end | ||
| + | function make_ref(tabular) | ||
| + | return '<ref name="' .. tabular.description .. '">' .. tabular.sources .. "</ref>" | ||
| + | end | ||
| + | local ref = make_ref(tabular) | ||
| + | if primary then | ||
| + | -- primary ref goes first | ||
| + | ref = make_ref(ptabular) .. ref | ||
| + | end | ||
| + | local ret = "{{" .. open .. "| title=" .. title .. ref .. "}}" | ||
| + | function total_box(total_votes) | ||
| + | return "{{Election box total no change|votes= {{formatnum:" .. total_votes .. "}}|percentage = " .. percent(total_votes, total_votes) .. "%}}" | ||
| + | end | ||
function sort_candidates(a,b) | function sort_candidates(a,b) | ||
return a[5] > b[5] | return a[5] > b[5] | ||
end | end | ||
table.sort(candidates, sort_candidates) | table.sort(candidates, sort_candidates) | ||
| − | local | + | if primary then |
| + | local pcandidates = find_candidates(ptabular.data, contest) | ||
| + | table.sort(pcandidates, sort_candidates) | ||
| + | local ptotal_votes, pwinner = sum_totals(pcandidates) | ||
| + | local fake_winner = {} | ||
| + | -- we don't want a winner in primaries, so use a fake one that no | ||
| + | -- candidate will match | ||
| + | fake_winner[2] = "" | ||
| + | for k,v in pairs(pcandidates) do | ||
| + | ret = ret .. fmt_candidate(v, fake_winner,ptotal_votes,frame) | ||
| + | end | ||
| + | ret = ret .. total_box(ptotal_votes) .. "{{Election box open primary general election no change}}" | ||
| + | end | ||
| + | |||
for k,v in pairs(candidates) do | for k,v in pairs(candidates) do | ||
| − | + | ret = ret .. fmt_candidate(v, winner,total_votes,frame) | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
end | end | ||
| − | ret = ret .. | + | ret = ret .. total_box(total_votes) |
local hold = frame.args.hold | local hold = frame.args.hold | ||
local gain = false | local gain = false | ||
| Line 145: | Line 208: | ||
win_party = normalize_parties(frame.args.gain) | win_party = normalize_parties(frame.args.gain) | ||
lose_party = normalize_parties(frame.args.loser) | lose_party = normalize_parties(frame.args.loser) | ||
| + | gain = true | ||
| + | elseif incumb_party and incumb_party ~= winner[3] then | ||
| + | win_party = normalize_parties(winner[3]) | ||
| + | lose_party = normalize_parties(incumb_party) | ||
gain = true | gain = true | ||
end | end | ||
Revision as of 08:00, 12 November 2018
| This module is rated as beta, and is ready for widespread use. It is still new and should be used with some caution to ensure the results are as expected. |
This is used by {{Election box US auto}}.
Usage
{{#invoke:Election box US auto|function_name}}
local p = {}
local mError = require('Module:Error')
local mYesno = require('Module:Yesno')
-- a global per #invoke
local linked_write_in = false
function percent( part, total )
if total >= 1000000 then
-- if > 1 million votes, then round to 2 decimals
round_to = 2
else
round_to = 1
end
local ret = mw.ext.ParserFunctions.expr( "" .. 100 * part / total .. " round " .. round_to )
if not string.find( ret, ".", 1, true ) then
-- add the decimals that expr doesn't
ret = ret .. "." .. string.rep("0", round_to)
end
return ret
end
function p.make( frame )
return frame:preprocess( make( frame:getParent() ) )
end
function fmt_candidate(v, winner, total_votes,frame)
local temp = "{{"
if v[2] == winner[2] then
temp = temp .. "Election box winning candidate"
else
temp = temp .. "Election box candidate"
end
local n_party = normalize_parties( v[3] )
if n_party then
temp = temp .. " with party link no change|party=" .. n_party
else
temp = temp .. " no party link no change"
end
link = frame.args[v[2] .. " link"]
if link then
link = mw.title.new(link)
else
-- bypass redirects, which is mostly important for display names
link = mw.title.new(v[2])
-- except if the redirect goes to an "elections" article (e.g. Kim Vann), then
-- we don't want a bypass
if link.isRedirect and not string.find(link.redirectTarget.prefixedText, "elections", 1, true ) then
link = link.redirectTarget
end
end
-- Strip disambiguators since we can't use the pipe trick
display_name, ignore = mw.ustring.gsub(link.prefixedText, "%b()", "")
if frame.args.redlinks or (link.exists and not link.isRedirect) then
full_link = "[[" .. link.prefixedText .. "|" .. display_name .. "]]"
else
full_link = display_name
end
temp = temp .. "|candidate=" .. full_link
if v[4] then
-- incumbent
temp = temp .. " ([[incumbent]])"
end
if v[6] then
-- write in
if linked_write_in then
temp = temp .. " (write-in)"
else
temp = temp .. " ([[Write-in candidate|write-in]])"
linked_write_in = true -- only link it once
end
end
temp = temp .. "|votes={{formatnum:" .. v[5] .. "}}"
temp = temp .. "|percentage=" .. percent(v[5], total_votes) .. "%"
temp = temp .. "}}"
return temp
end
function make( frame )
local state = frame.args[1]
if not state then
return mError.error({message="State is missing"})
end
local year = frame.args[2]
if not year then
return mError.error({message="Year is missing"})
end
local contest = frame.args[3]
if not contest then
return mError.error({message="Contest is missing"})
end
local type = "General"
if frame.args.type then
if frame.args.type == "Primary" then
type = "Primary"
else
return mError.error({message="Invalid value for |type="})
end
end
function load_tabular( state, year, type )
local tab_name = state .. " Elections/" .. year .. "/" .. type .. "/Candidates.tab"
local tabular = mw.ext.data.get(tab_name)
if tabular then
return tabular
else
return {error="Unable to find tabular data: " .. tab_name}
end
end
local tabular = load_tabular(state, year, type)
if tabular.error then
return mError.error({message=tabular.error})
end
function find_candidates(data, contest)
local candidates = {}
for k,v in pairs(data) do
if v[1] == contest then
table.insert(candidates, v)
end
end
return candidates
end
local candidates = find_candidates(tabular.data, contest)
local incumb_party = false
function sum_totals(candidates)
local total_votes = 0
local winner = {}
winner[5] = 0
for k,v in pairs(candidates) do
total_votes = total_votes + v[5]
if v[5] > winner[5] then
winner = v
end
if v[4] then
incumb_party = v[3]
end
end
return total_votes, winner
end
local total_votes, winner = sum_totals(candidates)
if mw.ustring.find(contest, "United States Representative", 1, true) then
title = "[[United States House of Representatives elections, " .. year .. "]]"
elseif mw.ustring.find(contest, "State Assembly Member", 1, true) then
title = "[[" .. state .. " State Assembly election, " .. year .. "]]"
elseif contest == "President" then
title = "U.S. presidential election in " .. state .. ", " .. year
else
title = "...????"
end
local primary = mYesno(frame.args.primary)
ptabular = load_tabular(state, year, "Primary")
if ptabular.error then
-- todo log an error here?
primary = false
end
if primary then
open = "Election box open primary begin no change"
else
open = "Election box begin no change"
end
function make_ref(tabular)
return '<ref name="' .. tabular.description .. '">' .. tabular.sources .. "</ref>"
end
local ref = make_ref(tabular)
if primary then
-- primary ref goes first
ref = make_ref(ptabular) .. ref
end
local ret = "{{" .. open .. "| title=" .. title .. ref .. "}}"
function total_box(total_votes)
return "{{Election box total no change|votes= {{formatnum:" .. total_votes .. "}}|percentage = " .. percent(total_votes, total_votes) .. "%}}"
end
function sort_candidates(a,b)
return a[5] > b[5]
end
table.sort(candidates, sort_candidates)
if primary then
local pcandidates = find_candidates(ptabular.data, contest)
table.sort(pcandidates, sort_candidates)
local ptotal_votes, pwinner = sum_totals(pcandidates)
local fake_winner = {}
-- we don't want a winner in primaries, so use a fake one that no
-- candidate will match
fake_winner[2] = ""
for k,v in pairs(pcandidates) do
ret = ret .. fmt_candidate(v, fake_winner,ptotal_votes,frame)
end
ret = ret .. total_box(ptotal_votes) .. "{{Election box open primary general election no change}}"
end
for k,v in pairs(candidates) do
ret = ret .. fmt_candidate(v, winner,total_votes,frame)
end
ret = ret .. total_box(total_votes)
local hold = frame.args.hold
local gain = false
if hold == "held" or winner[4] then
ret = ret .. "{{Election box hold with party link without swing|winner=" .. normalize_parties(winner[3]) .. "}}"
elseif hold == "flip" then
-- shorthand for D->R/R->D
win_party = winner[3]
if win_party == "Democratic" then
lose_party = normalize_parties("Republican")
else
lose_party = normalize_parties("Democratic")
end
win_party = normalize_parties(win_party)
gain = true
elseif frame.args.gain then
win_party = normalize_parties(frame.args.gain)
lose_party = normalize_parties(frame.args.loser)
gain = true
elseif incumb_party and incumb_party ~= winner[3] then
win_party = normalize_parties(winner[3])
lose_party = normalize_parties(incumb_party)
gain = true
end
if gain then
ret = ret .. "{{Election box gain with party link without swing|winner=" .. win_party .. "|loser=" .. lose_party .. "}}"
end
ret = ret .. "{{Election box end}}"
return ret
end
function normalize_parties( party )
-- Drop all parties after the first one?
party = mw.text.split( party, ",", true )[1]
local parties = {
Democratic = "Democratic Party (United States)",
Republican = "Republican Party (United States)",
Blank = "Independent (politician)",
Independent = "Independent (politician)",
Libertarian = "Libertarian Party (United States)",
Green = "Green Party (United States)",
}
parties["Peace and Freedom"] = "Peace and Freedom Party"
parties["No Party Preference"] = "No party preference"
for k, v in pairs(parties) do
if k == party then
return v
end
end
-- not found, return false
return false
end
return p