Jump to content

Module:Election box US auto

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Legoktm (talk | contribs) at 00:58, 13 November 2018 (use lua formatnum implementation). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

local p = {}
local mError = require('Module:Error')
local mYesno = require('Module:Yesno')
-- a global per #invoke 
local linked_write_in = false

function formatnum( num )
	-- simple wrapper
	local lang = mw.getContentLanguage()
	return lang:formatNum( num )
end

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