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 partyB.partyQid == 'Q86630688' then -- Q86630688 is 'Others'
return true
elseif partyA.partyQid == 'Q86630688' then
return false
elseif (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, colspan)
local cell = row:tag('td')
if align then cell:css('text-align', align) end
if colspan then cell:attr('colspan', tostring(colspan)) 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')
if v.partyQid == 'Q86630688' then --Others
addCell(row, 'Others', 'left', 2)
else
addColorCell(row, v.partyColor)
addCell(row, v.partyName)
end
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