Module:Sandbox/Julio974fr
Appearance
local p = {};
local political_party = require('Module:Political party')
local lang = 'en'
--[[ Get a best ranking statement value from "item" with the property "property".
The value is returned through the function "typefunc". ]] -- Function copied from elsewhere
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
--[[ 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
function p.getAllPartiesData(electionQid, previousQid, args)
local partiesData = p.getElectionParties(electionQid, previousQid)
for partyIndex, partyData in ipairs(partiesData) do
local fetchedPartyData = p.fetchPartyData(partyData.partyQid)
partyData.partyName = fetchedPartyData.partyName
partyData.partyColor = fetchedPartyData.partyColor
partyData.partyArticle = fetchedPartyData.partyArticle
if args['name_override_'..(partyData.partyQid)] then -- If name override specified
partyData.partyName = args['name_override_'..(fetchedPartyData.partyQid)]
end
partiesData[partyIndex] = partyData
end
return partiesData
end
local function sortParties(listParties)
local function comparePartyByVotes(partyA, partyB)
if (partyA.votes or 0) > (partyB.votes or 0) then
return true
elseif (partyA.votes or 0) < (partyB.votes or 0) then
return false
else
return ((partyA.seats or 0) > (partyB.seats))
end
end
table.sort(listParties, comparePartyByVotes)
end
local function findPartyIndex(listParties, partyQid)
local finalPartyIndex = nil
for curPartyindex, curParty in ipairs(listParties) do
if curParty .partyQid == partyQid then
finalPartyIndex = curPartyindex
break
end
end
return finalPartyIndex
end
local function initPartyIndex(listParties, partyQid)
local partyIndex = findPartyIndex(listParties, partyQid)
if not partyIndex then
table.insert(listParties, {partyQid=partyQid, votes = 0, seats = 0})
partyIndex = #listParties
end
return partyIndex
end
local function addPartyDataFromStatement(partyData, statement)
if statement.qualifiers.P1111 then
if not partyData.votes then partyData.votes = 0 end
partyData.votes = partyData.votes + getAmount(statement.qualifiers.P1111[1].datavalue.value)
end
if statement.qualifiers.P1410 then
if not partyData.seats then partyData.seats = 0 end
partyData.seats = partyData.seats + getAmount(statement.qualifiers.P1410[1].datavalue.value)
end
if statement.qualifiers.P155 then
if not partyData.predecessors then partyData.predecessors = {} end
for _, predecessorStatement in ipairs(statement.qualifiers.P155) do
table.insert(partyData.predecessors, getItem(predecessorStatement.datavalue.value))
end
end
return partyData
end
local function addPrevPartyDataFromStatement(partyData, statement)
if not partyData.prevVotes then partyData.prevVotes = 0 end
if statement.qualifiers.P1111 then
partyData.prevVotes = partyData.prevVotes + getAmount(statement.qualifiers.P1111[1].datavalue.value)
end
if not partyData.prevSeats then partyData.prevSeats = 0 end
if statement.qualifiers.P1410 then
partyData.prevSeats = partyData.prevSeats + getAmount(statement.qualifiers.P1410[1].datavalue.value)
end
return partyData
end
function findPartyPredecessorOverride(listParties, partyQid, partyIndex)
for tempIndex, tempParty in ipairs(listParties) do
if tempParty.predecessors then
for _, tempPredecessor in ipairs(tempParty.predecessors) do
if tempPredecessor == partyQid then
return tempIndex
end
end
end
end
return partyIndex
end
function addPartyPredecessorsByComponents(listParties)
for partyIndex, partyData in ipairs(listParties) do
local allParts = mw.wikibase.getAllStatements(partyData.partyQid, 'P527')
for _, predecessorStatement in ipairs(allParts) do
if not partyData.predecessors then partyData.predecessors = {} end
table.insert(partyData.predecessors, getItem(predecessorStatement.mainsnak.datavalue.value))
end
listParties[partyIndex] = partyData
end
return listParties
end
-- Retrieves the list of parties and sorts it by votes/seats
function p.getElectionParties(electionQid, previousQid)
local listParties = {}
-- For each 'candidate' in the election
local allStatementsInElection = mw.wikibase.getAllStatements(electionQid, 'P726')
for _, statement in ipairs(allStatementsInElection) do
local partyQid = statement.mainsnak.datavalue.value.id
local partyIndex = initPartyIndex(listParties, partyQid)
listParties[partyIndex] = addPartyDataFromStatement(listParties[partyIndex], statement)
end
listParties = addPartyPredecessorsByComponents(listParties)
-- For each 'candidate' in the previous election
if previousQid then
local allStatementsPrevElection = mw.wikibase.getAllStatements(previousQid, 'P726')
for _, statement in ipairs(allStatementsPrevElection) do
local partyQid = statement.mainsnak.datavalue.value.id
local partyIndex = findPartyIndex(listParties, partyQid)
partyIndex = findPartyPredecessorOverride(listParties, partyQid, partyIndex)
if not partyIndex then
partyIndex = findPartyIndex(
listParties,
getStatementValue(partyQid, 'P1366', getItem)
)
end
if partyIndex then
listParties[partyIndex] = addPrevPartyDataFromStatement(listParties[partyIndex], statement)
end
end
end
sortParties(listParties)
return listParties
end
local function getPartyColor(partyQid, partyArticleName)
local partyColor = getStatementValue(partyQid, 'P465', getString)
if partyColor then
return '#'..partyColor
end
if partyArticleName == '' then
return '#ffffff'
end
partyColor = political_party._fetch({partyArticleName, 'color'})
partyColor = mw.ustring.gsub(partyColor, '&(#)35;', '%1')
return partyColor
end
-- For a given party, gets its name, article, and color
function p.fetchPartyData(partyQid)
-- Get party article name
local partyArticleName, partyLabel
partyArticleName = mw.wikibase.getSitelink(partyQid)
if not partyArticleName then
partyLabel = mw.wikibase.getLabel(partyQid)
end
-- Get party shortname
local partyShortnameStatement = mw.wikibase.getBestStatements(partyQid, 'P2561')
local partyShortname
for i, v in ipairs(partyShortnameStatement) do
if v.mainsnak.datavalue.value.language == lang then
partyShortname = v.mainsnak.datavalue.value.text
end
end
-- Make party name/link wikitext
local partyNameWikitext
if partyArticleName then
partyNameWikitext = '[['..partyArticleName..'|'..(partyShortname or partyLabel or partyArticleName)..']]'
elseif partyLabel then
partyNameWikitext = partyLabel
else
partyNameWikitext = '[[wikidata:'..partyQid..']]'
end
-- Get party color
local partyColor = getPartyColor(partyQid, partyArticleName)
return {
partyQid=partyQid,
partyName=partyNameWikitext,
partyColor=partyColor,
partyArticle=partyArticleName
}
end
function p._ch_proportional(args)
-- Formatting functions (copied from Module:Election_results)
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 diff(n, prevN)
if not n then
return '–'
elseif not prevN then
return "''New''"
end
n, prevN = tonumber(n), tonumber(prevN)
if n > prevN then
return '+'..tostring(n-prevN)
elseif prevN > n then
return '−'..tostring(prevN-n)
else
return '±0'
end
end
local function diffPct(n, d, prevN, prevD)
if not n or not d then
return '–'
elseif not prevN then
return "''New''"
end
n = tonumber(n) / tonumber(d)
prevN = tonumber(prevN) / tonumber(prevD)
if n > prevN then
return '+'..string.format('%.2f', (n - prevN) * 100)
elseif prevN > n then
return '−'..string.format('%.2f', (prevN - n) * 100)
else
return '±0.00'
end
end
local electionQid = args.qid or args.election or args[1]
local previousElectionQid = getStatementValue(electionQid, 'P155', getItem)
local cols = 0
local year = 2023 -- TODO: Remove this and use the election's year instead
local totalVotes = getStatementValue(electionQid, 'P1697', getAmount)
local prevTotalVotes
if previousElectionQid then prevTotalVotes = getStatementValue(previousElectionQid, 'P1697', getAmount) end
-- Table cell functions (to avoid nesting too deep)
local function beginTable(classes, caption)
local root = mw.html.create('table')
root:addClass(classes)
local tableCaption = root:tag('caption')
tableCaption:wikitext(caption)
tableCaption:done()
root:done()
return root
end
local function addHeaderCell(row, wikitext, colspan)
local cell = row:tag('th')
cell:wikitext(wikitext)
cell:attr('scope', 'col')
if colspan then cell:attr('colspan', tostring(colspan)) end
cell:done()
end
local function addCell(row, wikitext, align)
local cell = row:tag('td')
if align then cell:css('text-align', align) end
cell:wikitext(wikitext)
cell:done()
end
local function addColorCell(row, color)
local cell = row:tag('td')
cell:css('width', '0px')
cell:css('background-color', color)
cell:done()
end
local function fillTopCell(topCell, wikitext, colspan)
topCell:wikitext(wikitext)
topCell:css('text-align', 'center')
topCell:css('background', '#F8F9FA')
topCell:attr('colspan', colspan)
topCell:done()
end
local root = beginTable('wikitable sortable', args.caption)
-- Header
local topCell = nil
topCell = root:tag('th')
-- Table header
local headerRow = root:tag('tr')
addHeaderCell(headerRow, (args.partytitle or 'Party'), 2)
addHeaderCell(headerRow, 'Votes')
addHeaderCell(headerRow, '%')
if previousElectionQid then addHeaderCell(headerRow, '+/−') end
addHeaderCell(headerRow, 'Seats')
if previousElectionQid then addHeaderCell(headerRow, '+/−') end
cols = cols + 5
if previousElectionQid then cols = cols + 2 end
-- Fetch parties data
local partiesData = p.getAllPartiesData(electionQid, previousElectionQid, args) -- TODO: Filter party name overrides instead of passing all args
if topCell then
fillTopCell(
topCell,
(args['image'] or require('Module:Sandbox/Julio974fr/parliament_diagram').makeParliamentDiagram(partiesData, year)),
cols
)
end
-- Get parties list and make the rows
for i, v in ipairs(partiesData) do
local row = root:tag('tr')
addColorCell(row, v.partyColor)
addCell(row, v.partyName)
mw.logObject(v)
addCell(row, fmt(v.votes), 'right')
addCell(row, pct(v.votes, totalVotes), 'right')
if previousElectionQid then addCell(row, diffPct(v.votes, totalVotes, v.prevVotes, prevTotalVotes), 'right') end
addCell(row, v.seats, 'right')
if previousElectionQid then addCell(row, diff(v.seats, v.prevSeats), 'right') end
end
return root
end
function p.ch_proportional(frame)
-- Initialise and populate variables
local getArgs = require("Module:Arguments").getArgs
local args = getArgs(frame)
return p._ch_proportional(args)
end
return p