Module:Sandbox/Dipsacus fullonum/elections

Lua
CodeDiscussionLinksLink count SubpagesDocumentationTestsResultsSandboxLive code All modules

Documentation for this module may be created at Module:Sandbox/Dipsacus fullonum/elections/doc

Code

local p = {}

require('strict')

--[[ These variables are set by the initialize function and should not be changed later ]]
local messages -- Table for localized messages
local config   -- Table for local configuration variables
local args     -- Table for template or #invoke arguments
local lang     -- Language object for the local language
local decimalSeparator -- Decimal separator in the local language

--[[ Get calling arguments and local configuration. This function sets the
     variables messages, config and args. ]]
local function initialize(frame)
	-- If the parent has arguments, use these. Else use the frame's arguments
	local parent = frame:getParent()
	if parent and #parent.args > 0 then
		args = parent.args
	else
		args = frame.args
	end
	config = mw.loadData("Module:Sandbox/Dipsacus fullonum/elections/configuration")
	messages = config.messages
	lang = config.language_code and mw.getLanguage(config.language_code) or mw.getContentLanguage()
	decimalSeparator = string.sub(lang:formatNum(1.1), 2, 2)
end

--[[ Format a percentage number to a certain number of decimals and format it for the used language ]]
local function formatPercentage(percentage, decimals)
	local formattedNumber = string.format("%." .. decimals .. "f", percentage)
	if decimalSeparator ~= '.' then
		formattedNumber = string.gsub(formattedNumber, "%.", decimalSeparator)
	end
	return string.gsub(config.percentage_format, '$1', { ['$1'] = formattedNumber } )
end

--[[ The "typefunc"s - A series of functions that extract the wanted information
     from a statement value depending on the value type. ]]
local function getAmount(value) return tonumber(value.amount) end
local function getString(value) return value end
local function getItem(value)   return value.id end
local function getYear(value)   return string.sub(value.time, 2, 5) end

--[[ Get a best ranking statement value from "item", trying the properties
	 in the table "properties" in order.
	 The value is returned through the function "typefunc". ]]
local function getStatementValueWithProptable(item, properties, typefunc)
	for _, property in ipairs(properties) do
		local statements = mw.wikibase.getBestStatements(item, property)
		if statements[1] and statements[1].mainsnak.snaktype == 'value' then
			return typefunc(statements[1].mainsnak.datavalue.value)
		end
	end
end

--[[ Get a best ranking statement value from "item" with the property "property".
	 The value is returned through the function "typefunc". ]]
local function getStatementValue(item, property, typefunc)
	local statements = mw.wikibase.getBestStatements(item, property)
	if statements[1] and statements[1].mainsnak.snaktype == 'value' then
		return typefunc(statements[1].mainsnak.datavalue.value)
	end
end

--[[ Get a qualifier for a best ranking statement.
     The qualifier value is returned thorugh the function "typefunc". ]]
local function getQualifierValue(item, property, qualifier, typefunc)
	local statements = mw.wikibase.getBestStatements(item, property)
	if statements[1] and statements[1].qualifiers and statements[1].qualifiers[qualifier]
		and statements[1].qualifiers[qualifier][1].snaktype == 'value' then
		return typefunc(statements[1]["qualifiers"][qualifier][1].datavalue.value)
	end
end

--[[ Get a qualifier for a best ranking statement. "qualifiers" is table with
	 with qualifiers which are tried in turn until a value is found.
     The qualifier value is returned thorugh the function "typefunc". ]]
local function getQualifierValueWithQualtable(item, property, qualifiers, typefunc)
	local statements = mw.wikibase.getBestStatements(item, property)
	for _, qualifier in ipairs(qualifiers) do
		if statements[1] and statements[1].qualifiers and statements[1].qualifiers[qualifier]
			and statements[1].qualifiers[qualifier][1].snaktype == 'value' then
			return typefunc(statements[1]["qualifiers"][qualifier][1].datavalue.value)
		end
	end
end

local function getItemTableWithQuantityQualifier(item, property, qualifier, ignore_zero)
	local statements = mw.wikibase.getBestStatements(item, property)
	local items = {}
	local highest = 0
	local sum = 0
	for key, statement in pairs(statements) do
		if statement.mainsnak.snaktype == 'value' then
			local item = statement.mainsnak.datavalue.value.id
			if statement.qualifiers and statement.qualifiers[qualifier]
				and statement.qualifiers[qualifier][1].snaktype == 'value' then
				local quantity = tonumber(statement.qualifiers[qualifier][1].datavalue.value.amount)
				if quantity ~= 0 or not ignore_zero then
					items[item] = quantity
					sum = sum + quantity
					if quantity > highest then
						highest = quantity
					end
				end
			end
		end
	end
	return items, highest, sum
end

local function makeGetMonolingualValue(property, wanted_lang, tooltip)
    return function(item)
		local statements = mw.wikibase.getBestStatements(item, property)
		for key, statement in pairs(statements) do
			if statement.mainsnak.snaktype == 'value' then
				if wanted_lang == statement.mainsnak.datavalue.value.language then
					local text = statement.mainsnak.datavalue.value.text
					local formatted_text
					if tooltip == 'label' then
						local label = mw.wikibase.getLabelByLang(item, wanted_lang)
						if label then
							formatted_text = '<abbr title="' .. label .. '">' .. text .. '</abbr>'
						end
					end
					return text, formatted_text
				end
			end
		end
	end
end

local function makeGetLabel(wanted_lang)
    return function(item)
    	return mw.wikibase.getLabelByLang(item, wanted_lang)
    end
end

local function makeGetLink(wiki)
    return function(item)
    	local sitelink = mw.wikibase.getSitelink(item, wiki)
    	if wiki and sitelink then
    		-- For interwikilinks add globalSiteId without 'wiki' as prefix
    		sitelink = ':' .. string.sub(wiki, 1, -5) .. ':' .. sitelink
		end
		return sitelink
    end
end

local function makePartyFunctions()
	local getName = {}
	for _, value in ipairs(config.party_text) do
		if value.method == 'P1813' then
			getName[#getName + 1] = makeGetMonolingualValue('P1813', value.lang, value.tooltip)
		elseif value.method == 'label' then
			getName[#getName + 1] = makeGetLabel(value.lang)
		elseif value.method == 'qid' then
			getName[#getName + 1] = function(item) return item end
		end
	end
	local getLink = makeGetLink(config.party_link)
	return getName, getLink
end

--[[ Create a sequence table with info the all found parties.
     The values are tables with party information. ]]
local function getAllParties(electionData)
	-- First make a table with the found party items as keys
	local partyItems = {}
	for _, election in pairs(electionData) do
		for party, amount in pairs(election.elected) do
			if partyItems[party] then
				partyItems[party].elected = partyItems[party].elected + amount
				partyItems[party].elections = partyItems[party].elections + 1
			else
				partyItems[party] = { elected = amount, elections = 1, party = party }
			end
		end
	end
	-- Create and populate the party table
	local parties = {}
	local getPartyName, getPartyLink = makePartyFunctions()
	for party, partyInfo in pairs(partyItems) do
		local name, formatted_name
		for _, func in ipairs(getPartyName) do
			name, formatted_name = func(party)
			if name then break end
		end
		partyInfo.name = name
		partyInfo.formatted_name = formatted_name
		partyInfo.sitelink = getPartyLink(party)
		parties[#parties + 1] = partyInfo
	end
	return parties	
end

local function writeTableHeader(parties)
	local table = mw.html.create('table')
	local use_color_row = config.party_color_bar and config.party_color_bar > 0
	local header_rowspan = use_color_row and "3" or "2" 
	table:attr('class', 'wikitable sortable')
		:tag('tr')
		:tag('th'):attr('rowspan', header_rowspan):wikitext(messages['Year']):done()
		:tag('th'):attr('rowspan', header_rowspan):wikitext(messages['Electorate']):done()
		:tag('th'):attr('rowspan', header_rowspan):wikitext(messages['Valid votes']):done()
		:tag('th'):attr('rowspan', header_rowspan):wikitext(messages['Participation']):done()
		:tag('th'):attr('colspan', #parties):wikitext(messages['Parties']):done()
		:tag('th'):attr('rowspan', header_rowspan):wikitext(messages['Total seats']):done()
		:tag('th'):attr('rowspan', header_rowspan)
			:wikitext('[[File:Wikidata-logo.svg|20px|link=d:|' .. messages['Wikidata logo'] .. ']]')

	local row = mw.html.create('tr')
	for _, partyInfo in ipairs(parties) do
		local name = partyInfo.formatted_name or partyInfo.name
		if partyInfo.sitelink then
			name = '[[' .. partyInfo.sitelink .. '|' .. name .. ']]'
		end
		row:tag('th'):wikitext(name)
	end
	table:node(row)
	if use_color_row then
		local color_row = mw.html.create('tr'):css('height', config.party_color_bar .. 'px')
		for _, partyInfo in ipairs(parties) do
			local color_cell = color_row:tag('th')
			local color = getStatementValue(partyInfo.party, 'P465', getString)
			if color then
				color_cell:css('background', '#' .. color)
			end
		end
		table:node(color_row)
	end
	return table
end

local function getElectionData(electionItem)
	local data = {}
	data["electorate"] = getStatementValueWithProptable(electionItem, { "P1867", "P1831" }, getAmount)
	data["valid votes"] = getStatementValue(electionItem, "P1697", getAmount)
	data["total votes"] = getStatementValue(electionItem, "P1868", getAmount)
	data["mandates"] = getQualifierValueWithQualtable(electionItem, "P541", { "P1410", "P1114" }, getAmount)
	data["elected"], data["highest elected value"], data["elected sum"] =
		getItemTableWithQuantityQualifier(electionItem, "P991", "P1410", config.ignore_zero_elected)
	data["electionItem"] = electionItem
	return data
end

local function partySortFunc(a, b)
	for _, sortkey in ipairs(config.party_column_order) do
		if sortkey == 'most_elected' then
			if a.elected > b.elected then return true end
			if a.elected < b.elected then return false end
		elseif sortkey == 'most_elections' then
			if a.elections > b.elections then return true end
			if a.elections < b.elections then return false end
		elseif sortkey == 'name' then
			if a.name < b.name then return true end
			if a.name > b.name then return false end
		else
			return false
		end
	end
end

--[[ elections: Make an overview table for each of a series of elections with the number
	 of elected for each party for each election
	 arguments:
		start = item for first election in table. From that follow the chain of next qualifiers
		end = item for last election in table.
  ]]
function p.elections(frame)
	initialize(frame)
	local electionItem = args.start
	local electionData = {}

    while electionItem do
    	local year = getStatementValue(electionItem, "P585", getYear)
		if args.last_year and year > args.last_year then break end
		if not args.first_year or year >= args.first_year then
	    	local data = getElectionData(electionItem)
    		data["year"] = year
			electionData[#electionData + 1] = data
		end
		if electionItem == args["end"] then break end
		electionItem = getQualifierValue(electionItem, "P31", "P156", getItem)
	end

	local parties = getAllParties(electionData)
	table.sort(parties, partySortFunc)
	
	local table = writeTableHeader(parties)
	local getYearLink = makeGetLink(config.year_link)
	for _, election in pairs(electionData) do
		local yearLink = getYearLink(election["electionItem"])
		if not yearLink then
			-- no article for this specific election, try for what it is part of
			local part_of = getStatementValue(election["electionItem"], 'P361', getItem)
			if part_of then
				yearLink = getYearLink(part_of)
			end
		end
		local yearText = yearLink  and ('[[' .. yearLink .. '|' .. election["year"] .. ']]')
			or election["year"]
		local row = mw.html.create('tr'):css('text-align', 'right')
			:tag('td'):wikitext(yearText):done()
			:tag('td'):wikitext(lang:formatNum(election["electorate"])):done()
			:tag('td'):wikitext(lang:formatNum(election["valid votes"])):done()
		local participation
		if election["total votes"] and election["electorate"] then
			participation = formatPercentage(election["total votes"] / election["electorate"] * 100,
				config.percentage_decimals)
		end
		row:tag('td'):wikitext(participation)
		local highest = election["highest elected value"]
		for _, partyInfo in ipairs(parties) do
			local elected = election.elected[partyInfo.party]
			if elected == highest then
				row:tag('td'):css('font-weight', 'bold'):wikitext(elected)
			else
				row:tag('td'):wikitext(elected)
			end
		end
		row:tag('td'):wikitext(election["mandates"])
		row:tag('td'):wikitext("[[File:Blue pencil.svg|8px|link=d:" .. election["electionItem"]
			.. "|" .. messages["Wikidata pencil"] .. "]]")
		table:node(row)
	end
	return table
	
end
return p