Module:Résultats d'élections

 Documentation[voir] [modifier] [historique] [purger]

Fonctions frame modifier

results() modifier

paramètres :

  • 1 = identifiant Wikidata de l'élection
  • method = "votes" ou "sièges". Peut prendre plusieurs valeurs séparées par des virgules (defaut: votes).
  • type = "camembert" ou "table". (defaut: tableau)
{{#invoke:Résultats d'élections|results|Q19958381|method=votes,sièges}}
{{#invoke:Résultats d'élections|results|Q19958381|type=camembert}}
Nombre de votes invalide (7486 trouvés, attendait 7494)
candidatvotes%sièges%
Bjärepartiet (d)247733.051331.71
Parti modéré de rassemblement (Suède)138318.45819.51
Parti social-démocrate suédois des travailleurs93212.44512.2
Démocrates de Suède90212.04512.2
Parti du centre87111.62512.2
Les Libéraux5016.6937.32
Parti de l'environnement Les Verts3094.1224.88
Les chrétiens-démocrates961.28--
Direktdemokraterna (en)110.15--
Parti neutre réformiste (d)20.03--
parti de gauche20.03--
Feministiskt initiativ----
Parti des intérêts des citoyens suédois séniors (en)----
Nombre de votes invalide (7486 trouvés, attendait 7494)

Erreur : aucun lien valide n’a été trouvé à la fin de la ligne 11.

participation() modifier

paramètres :

  • 1 = identifiant Wikidata de l'élection
  • method = "votes" ou "sièges". Peut prendre plusieurs valeurs séparées par des virgules (defaut: votes).
  • type = "camembert" ou "table". (defaut: tableau)
{{#invoke:Résultats d'élections|participation|Q19958381}}
{{#invoke:Résultats d'élections|participation|Q19958381|type=camembert}}
votes%
Exprimés749463.11
Blancs ou nuls580.49
Abstention432236.4
Exprimés: 7 494Abstention: 4 322Blancs ou nuls: 58
  •   Exprimés: 7 494
  •   Abstention: 4 322
  •   Blancs ou nuls: 58
local p =   {}
local wikidata = require( 'Module:Wikidata' )
local linguistic = require 'Module:Linguistique'
local pieChart = require "Module:Fabricant de camemberts"
local TableChart = require "Module:Fabricant de tables"

local maintenancestr = '' -- stores error messages

-- I18N
local labels = {
	validvotes = 'Exprimés',
	invalidvotes = 'Blancs ou nuls',
	novotes = 'Abstention',
	candidate = 'candidat',
	percent = '%',
	votes = 'votes',
	seats = 'sièges',
	maintenancecat = 'Page avec des données électorales à vérifier',
	invalidtotal = 'Nombre de $typedata invalide ($real trouvés, attendait $expected)',
	missingdata = 'Données insuffisantes',

	-- reverse translations for inputs
	['camembert'] = 'pie',
	['sièges'] = 'seats',
	['voix'] = 'votes',
	['tableau'] = 'table',
}

local function translate(str)
	return labels[str] or str or nil 
end

-- Error handling
local function invaliddata(msg)
	local cat = ''
	if mw.getCurrentFrame():preprocess("{{NAMESPACE}}") == '' then
		cat = '[[Category:' .. translate('maintenancecat') .. ']]'
	end
	maintenancestr = maintenancestr ..  (msg or '') .. cat .. '<br />'
end

local function invalidtotal(expected, real, typedata)
	local msg = translate('invalidtotal')
	msg = msg:gsub('$typedata', typedata)
	msg = msg:gsub('$expected', tostring(expected))
	msg = msg:gsub('$real', tostring(real))
	return invaliddata(msg)
end

local methodparams = {
	-- method = {label, property for total number, property for qualifiers}
	votes = {'votes', 'P1697', 'P1111'},
	seats = {'seats', 'P1410', 'P1410'}	,	
}

--TABLE function (need dedicated module) ?
local function addRow(obj, vals, typecell)
	local row = mw.html.create('tr')
	typecell = typecell or 'td'
	for i, j in pairs(vals) do
		row:tag(typecell):wikitext(j):done()
	end
	return obj:node(row):done()
end
local function addHeader(obj, vals)
	return addRow(obj, vals, 'th')
end


local function showpercents(val)
	if val then
		-- arrondi à 0.01 %
		val = math.floor( val * 10000 + 0.5 ) / 100
		return val
	end
end

local function validtotal(expected, real, typedata, margin)
	if not margin then
		margin = .001
	end
	local difference = expected-real
	if math.abs(difference) / expected > margin then
		invalidtotal(expected, real, typedata)
	end
	return nil
end
	
local function color(item)
	local RGB = wikidata.formatStatements{entity = item, property = 'P465', numval = 1}
	if RGB then
		return '#' .. RGB
	end
	return '' -- for random color
end


local function getAmount(item, prop)
	local val = wikidata.formatStatements{entity = item, property = prop, numval = 1, displayformat =  'raw'}
	return tonumber(val or 0) -- what for missing data ?
end

local function getQualifAmount(claim, qualif)
	local val = wikidata.getFormattedQualifiers(claim, {qualif}, {numval =1, displayformat = 'raw'})
	if val and not tonumber(val) then -- could be because there are several value for the qualif, or speical value, display what happens
		invaliddata(translate('invaliddata') .. ': ' .. wikidata.formatStatement(claim) .. val )
		return 0
	end
	return tonumber(val or 0) -- what for missing data ?
end

local function valFromStatement(statement, qualif, total)
	local val = getQualifAmount(statement, qualif)
	local pct
	if val and total then
		pct = val / total
	end
	return {val, pct}
end

local function getData(typedata, method, obj)
	local d = {}
	method = translate(method)
	local params = methodparams[method]
	if not params then
		return error('invalid counting method: ' .. '"' .. (method or 'none provided') .. '"')
	end
	if typedata == 'name' then
		return translate(params[1])
	elseif typedata == 'amount' then
		return getQualifAmount(obj, params[3])
	elseif typedata == 'totalamount' then
		return getAmount(obj, params[2])
	end
	return	error('invalid request: ' .. (typedata or 'none provided'))
end

-- Participation results functions
local function participationchart(valid, invalid, novotes, displayformat)
	local chart = pieChart:new()
	chart:addSlice{valid, translate('validvotes'), 'green'}
	chart:addSlice{novotes, translate('novotes'), 'blue'}
	chart:addSlice{invalid, translate('invalidvotes'), 'red'}
	displayformat = displayformat or {	radius = '100', percent = 'true'}	
	chart:format(displayformat)
	return chart:show()
end

local function participationtable(valid, invalid, novotes)
	local total = valid + invalid + novotes
	local pctvalid, pctinvalid, pctnovotes = showpercents(valid/total), showpercents(invalid/total), showpercents(novotes/total)
	local tab = TableChart:new()
	tab:addHeaders{'', translate('votes'), translate('percent')}
	tab:addRow{translate('validvotes'), valid, pctvalid}
	tab:addRow{translate('invalidvotes'), invalid, pctinvalid}
	tab:addRow{translate('novotes'), novotes, pctnovotes}
	return tab:show()
end

function p._participation(election, typedisplay, title)
	election = mw.wikibase.getEntityObject(election)
	local eligible = getAmount(election, 'P1867')
	local voting = getAmount(election, 'P1868')
	local valid = getAmount(election, 'P1697')
	if not (eligible and valid and voting) then
		return invaliddata(translate('missingdata'))
	end
	
	local novotes = eligible - voting
	local invalid = voting - valid
	
	typedisplay = translate(typedisplay)
	if (not typedisplay) or (typedisplay == '') then
		typedisplay = 'table'
	end
	local mainstr = ''
	if type(typedisplay) ~= 'string' then
		return error('bad datatype: ' ..  type(typedisplay))
	end
	if typedisplay == 'table' then
		mainstr = mainstr .. participationtable(valid, invalid, novotes)
	else
		typedisplay = translate(typedisplay)
		if typedisplay == 'pie' then
			mainstr = mainstr .. participationchart(valid, invalid, novotes)
		else
			return error('I want a pie or a table, what is a ' .. typedisplay .. '?')
		end
	end
	if title then
		mainstr = '<table><tr><th>' .. title .. '</th></tr><tr><td>' ..  mainstr .. '</td></tr></table>'
	end
	return maintenancestr .. mainstr
end


-- Score result functions
local function makePie(data, request, displayformat)
	displayformat = displayformat or {	radius = '100', percent = 'true'}
	local pie = pieChart:new()
	for _,candidate in pairs(data) do
		local val = candidate.vals[request]
		pie:addSlice{val, candidate.name, candidate.color, candidate.name}
	end
	pie:format(displayformat)
	return pie:show()
end

local function makePies(data, requests, displayformat)
	local out = ''
	for i, request in pairs(requests) do
		out = out ..makePie(data, i, displayformat)
	end
	return out
end

local function resultsTable(data, methods, totals)
	local headers = {}
	table.insert(headers, translate('candidate'))
	local pctheader = translate('percent')
	for i, method in pairs(methods) do
		table.insert(headers, method)
		table.insert(headers, pctheader)
	end
	
	local tab = TableChart:new()
	tab:addHeaders(headers)

	-- CONTENT
	for _, candidate in pairs(data) do
		local row = {}
		local sortkey
		table.insert(row, candidate.name)
		for i, val in pairs(candidate.vals) do
			sortkey = sortkey or -val -- la première colonne de valeur est utilisée comme clé de tri, en négatif pour avoir les gagnants en haut
			local pct = val / totals[methods[i]]
			pct = showpercents(pct)
			if val == 0 then
				val, pct = '-', '-'
			end
			table.insert(row, val)
			table.insert(row, pct)
		end
		tab:addRow(row, sortkey)
	end
	return tab:show()
end

function p._results(election, typedisplay, methods, title, displayformat, errormargin)
	local entity =  mw.wikibase.getEntityObject(election)

	if type(methods) == 'nil' then
		methods = {'votes'}
	elseif type(methods) == 'string' then
		methods = mw.text.split(methods, '%,%s*')
	end
	local candidates = wikidata.getClaims{entity = entity, property = 'P726', excludespecial = true}

	
	-- keeps variable to check totals
	local targettotals = {}
	local gottotals = {}
	for i, method in pairs(methods) do
		targettotals[method] = getData('totalamount', method, entity)
		gottotals[method] = 0
	end

	local data = {}
	-- structure of an entry: { d.datas{valforcolumnA, valforcolumnB}, d.name, d.color, d.qid}
	for _, candidate in pairs(candidates) do
		local d = {}
		d.qid = wikidata.getMainId(candidate)
		d.name = wikidata.formatEntity(d.qid)
		d.color = color(d.qid)
		d.party = wikidata.getFormattedQualifiers{entity = candidate, property = 'P102'}
		if name and party then
			name = name .. linguistic.inparentheses(party)
		end

		d.vals = {}
		for i, method in pairs(methods) do
			local val = getData('amount', method, candidate)
			gottotals[method] = gottotals[method] + val
			table.insert(d.vals, val)
		end
		table.insert(data, d)
	end
	
	-- CHECK data consistency
	for method, data in pairs(targettotals) do
		validtotal(data, gottotals[method], method, errormargin)
	end
	if type(typedisplay) ~= 'string' and (type(typedisplay) ~= 'nil') then
		return error('bad datatype: ' ..  type(typedisplay))
	end
	
	typedisplay = translate(typedisplay)
	if (not typedisplay) or (typedisplay == '') then
		typedisplay = 'table'
	end
	
	local mainstr = ''
	if typedisplay == 'table' then
		mainstr = resultsTable(data, methods, targettotals or gottotals, displayformat)
	elseif typedisplay == 'pie' then
			mainstr = makePies(data, methods, displayformat)
	else
			return error('I want a pie or a table, what is a ' .. typedisplay .. '?')
	end
	if title then
		mainstr = '<table><tr><th>' .. title .. '</th></tr><tr><td>' ..  mainstr .. '</td></tr></table>'
	end
	return  maintenancestr .. mainstr
end

-- FRAME FUNCTIONS
local function trimargs(args) -- remove annoying white spaces
	local newargs = {}
	for i, j in pairs(args) do
		if j ~= '' then newargs[i] = j end
	end
	return args
end

function p.participation(frame)
	local args = trimargs(frame.args)
	return p._participation(args[1], args['type'], args['title'])
end

function p.results(frame)
	local args = trimargs(frame.args)
	return p._results(args[1], args['type'], args['method'] or 'votes', args['title'])
end

return p