Module:Démographie
La documentation pour ce module peut être créée à Module:Démographie/doc
--[[
Module reprenant les fonctionnalités du modèle Démographie.
--]]
local p = {} -- le module
-- le module chartes (centralisation des styles)
local data = require "Module:Chartes"
-- liste des paramètres reconnus (valeur = nom de la variable)
p.parametres = {
["titre"] = "titre",
["charte"] = "charte",
["colonnes"] = "colonnes",
["notes"] = "notes",
["source"] = "source",
["sources"] = "sources",
["wikidata"] = "wikidata",
["flottant"] = "flottant",
["largeur-tableau"] = "largeur_tableau",
["sansdoublescomptes"] = "sansdoublescomptes",
["enquêteannuelle"] = "enqueteannuelle",
["marge-interlignes"] = "marge_interlignes",
["taille-police"] = "taille_police",
["hauteur-lignes"] = "hauteur_lignes",
["hyperliens-années"] = "hyperliens_annees",
["années-fond"] = "annees_fond",
["population-fond"] = "population_fond",
["notes-fond"] = "notes_fond",
["style-notes"] = "style_notes",
-- pour permettre les paramètres "depuis Lua"
["largeur_tableau"] = "largeur_tableau",
["enqueteannuelle"] = "enqueteannuelle",
["marge_interlignes"] = "marge_interlignes",
["taille_police"] = "taille_police",
["hauteur_lignes"] = "hauteur_lignes",
["hyperliens_annees"] = "hyperliens_annees",
["annees_fond"] = "annees_fond",
["population_fond"] = "population_fond",
["notes_fond"] = "notes_fond",
["style_notes"] = "style_notes",
}
-- le nom de la catégorie d'erreur
p.categorie_erreur = "Page avec une erreur d'utilisation du modèle Démographie"
-- le titre par défaut, utilisé en "caption hidden" lorsqu'on ne passe pas de titre à afficher
p.titre_par_defaut = "Évolution démographique"
--[[
Fonction exportée reprenant le fonctionnement de {{m|Charte de couleur}}
Fonction devenue inutile (voir Module:Chartes), maintenue pour compatibilité éventuelle
question : est-il possible de tester un appel à une fonction précise ?
--]]
function p.charte_de_couleur(frame)
local pframe = frame:getParent()
-- les deux paramètres
local nom = mw.ustring.lower(mw.text.trim(pframe.args[1] or ""))
local code = mw.ustring.lower(mw.text.trim(pframe.args[2] or ""))
return data.charte_m("geographie", "secondaire", code, "non")
end
--[[
Insert une catégorie d'erreur
--]]
p.liste_erreurs = {}
p.liste_cats = {}
function p.erreur(message, cle)
table.insert(p.liste_erreurs, message)
table.insert(p.liste_cats, cle)
end
--[[
Fonction de récupération d'un paramètre nommé.
--]]
function p.lit_parametre(nom, pasvide)
if (type(nom) ~= "string") then
return nil -- pas un paramètre nommé
end
local temp = p.frame.args[nom] or p.pframe.args[nom] -- du modèle, puis de l'article
if (temp ~= nil) then
if (pasvide) then
if (temp == "") then
return nil
else
return temp
end
else
return temp
end
else
return nil
end
end
local function get_snack_value(t, v, ...)
if v and type(t) == 'table' then
return get_snack_value(t[v], ...)
elseif v then
return nil
else
return t
end
end
--[[
Fonction de récupération des données de Wikidata.
--]]
function p.valeur_wikidata(pm, customId)
local id
if customId and mw.wikibase.isValidEntityId(customId) then
id = customId
if not mw.wikibase.entityExists(id) then
return
end
else
id = mw.wikibase.getEntityIdForCurrentPage()
if not id then
return
end
end
local popProp = mw.wikibase.getAllStatements( id, 'P1082' )
if not popProp or #popProp == 0 then
return
end
for _, statement in ipairs( popProp ) do
local pop = get_snack_value(statement, 'mainsnak', 'datavalue', 'value', 'amount')
local date_table = get_snack_value(statement, 'qualifiers', 'P585', 1, 'datavalue', 'value' )
if pop and date_table and date_table.precision > 8 and statement.rank ~= 'deprecated' then
local an = tonumber(date_table.time:match('^%+(%d%d%d%d)'))
if an and pm[an] == nil then
pm[an] = pop:match('%d+')
end
end
end
end
--[[
Supprime le premier retour à la ligne (éventuel) de forme <br/>
--]]
function p.sans_nl(texte)
if (texte == nil or texte == "" or type(texte) ~= "string") then
return texte
end
-- parenthèses car gsub() retourne plusieurs valeurs, et on veut retourner uniquement la première valeur
return ( mw.ustring.gsub(texte, "[<][bB][rR][ ]*[/]?[>]", "", 1) )
end
--[[
Fonction principale
reçoit une table des paramètres (pm) issus de l'appel
--]]
function p.demographie_m(pm)
-- valeur marge interlignes
if (pm.marge_interlignes == nil) then
pm.marge_interlignes = "5px"
else
-- les valeurs trop petites
if (pm.marge_interlignes == "0" or pm.marge_interlignes == "0em" or pm.marge_interlignes == "0.1em" or pm.marge_interlignes == "0px" or
pm.marge_interlignes == "1px" or pm.marge_interlignes == "2px" or pm.marge_interlignes == "3px" or pm.marge_interlignes == "4px") then
pm.marge_interlignes = "5px"
end
end
-- valeur effective du flottant
local vflottant = 'margin: 0 auto' -- valeur par défaut
if (pm.flottant and pm.flottant:lower() == "gauche") then
vflottant = 'float:left; margin: 0 1em 1em 0'
elseif (pm.flottant and pm.flottant:lower() == "droite") then
vflottant = 'float:right; margin: 0 0 1em 1em'
end
if (pm.hauteur_lignes == nil) then
pm.hauteur_lignes = ""
else
pm.hauteur_lignes = "line-height:" .. pm.hauteur_lignes .. ";"
end
pm.taille_police = (pm.taille_police or "100%") -- valeur par défaut taille police
if (pm.notes_fond ~= nil) then
pm.notes_fond = "background: " .. pm.notes_fond .. ";"
else
pm.notes_fond = ""
end
local parenthese = false
if (pm.style_notes == "gauche") then
pm.style_notes = 'border: 1px solid #aaa; text-align:left;'
else
pm.style_notes = 'border: 0; border-width: 0;'
parenthese = true
end
-- valeur par défaut lien
if (pm.hyperliens_annees == nil) then
pm.hyperliens_annees = false
else
-- validation valeur
if (pm.hyperliens_annees == "on" or pm.hyperliens_annees == "oui") then
pm.hyperliens_annees = true
else
pm.hyperliens_annees = false -- toute valeur autre que "on" = "off"
end
end
-- valeurs par défaut des colonnes
local colonnes_par_defaut
if (pm.colonnes == nil) then
colonnes_par_defaut = true
pm.colonnes = 9
else
pm.colonnes = tonumber(pm.colonnes) -- pour que ce soit un nombre
end
-- on valide les colonnes
if (type(pm.colonnes) ~= "number" or pm.colonnes < 1) then
-- colonne erronée : erreur
p.erreur("La valeur du paramètre ''colonnes'' (" .. (pm.colonnes or "<pas un nombre>") .. ") n'est pas valide", "nombre de colonnes")
pm.colonnes = 9
end
-- largeur par défaut : 5.4em * colonnes
local largeur_tableau_par_defaut
if (pm.largeur_tableau == nil) then
largeur_tableau_par_defaut = true
pm.largeur_tableau = pm.colonnes*5.4 .. "em"
end
if (pm.charte == nil) then
pm.charte = "défaut"
else
-- on valide la charte
pm.charte = mw.ustring.lower(pm.charte)
end
-- on récupère les couleurs de la charte sauf si indiquées
local coul_annees = (pm.annees_fond or data.charte_m("geographie", "secondaire", pm.charte , "oui"))
local coul_valeurs = (pm.population_fond or nil) -- valeur par défaut = rien
if (coul_valeurs == nil) then
coul_valeurs = ""
else
coul_valeurs = 'style="background:' .. coul_valeurs .. ';"'
end
-- obtient les données de wikidata, mais sans écraser les données locales
-- * définir à une valeur "truly" pour activer (désactivé par défaut)
-- * définir à un id wikidata pour récupérer les informations depuis une autre entité
if pm.wikidata ~= nil then
local testParam = require('Module:Yesno')(pm.wikidata, 'custom_id')
if testParam == true then
p.valeur_wikidata(pm)
elseif testParam == 'custom_id' then
p.valeur_wikidata(pm, pm.wikidata)
end
end
-- extraction des éléments de la table, rangés dans une "vraie" table pour les trier
local tbl = {}
for annee, valeur in pairs(pm) do
-- il y a aussi les paramètres nommés dans cette table, qu'on laisse
if type(annee) == "number" then
if annee == 1 then -- protection : un paramètre non nommé sera à l'index "1"
p.erreur("Présence d'un paramètre non nommé", "paramètre inconnu")
else
-- nettoyage de la valeur
local v = mw.text.trim(valeur or "")
table.insert(tbl, {annee, v})
end
else
-- on profite de cette boucle pour vérifier les paramètres qui n'existent pas
if p.parametres[annee] == nil then
-- cas particulier : les paramètres sous la forme "XXXX notes" et "XXXX unité" sont acceptés
if not (annee:match("^[0-9]+ notes$") or annee:match("^[0-9]+ unité$") or annee:match("^[0-9]+ affichage$")) then
-- pas un paramètre connu ni XXXX notes → erreur
p.erreur("Le paramètre ''>>" .. annee .. "<<'' est inconnu", "paramètre inconnu")
-- on ignore simplement ce champs
end
end
end
end
-- tri de la table
table.sort(tbl, function (el1, el2)
return el1[1] < el2[1]
end)
-- cette fois on parcours la structure des infos
local ret = ""
local odebug = "" -- sortie de debug
-- on parcours les données (années) pour générer la table structurée
local col = 0
local ligne = 0
local struct = {}
local total = 0 -- compte du nombre total
for pos = 1, #tbl do
-- colonne d'insertion
col = col + 1
-- fin de la ligne ? on retourne à la première colonne
if (col > pm.colonnes) then
col = 1
end
-- première colonne ? on crée une nouvelle ligne
if (col == 1) then
ligne = ligne + 1
table.insert(struct, {})
end
-- on insert dans la ligne
table.insert(struct[ligne], tbl[pos])
total = total + 1
end
-- aucune entrée ? erreur.
if (total == 0) then
p.erreur("Aucune année fournie au modèle", "absence d'années")
end
-- on traite la largeur
if (colonnes_par_defaut == true and ligne == 1 and total < pm.colonnes) then
pm.colonnes = total -- restriction du nombre de colonnes au nombre réel d'éléments
-- il faut aussi recalculer la largeur totale : on fait le rapport entre 9 (ancien) et le nouveau nombre de colonnes
if (largeur_tableau_par_defaut == true) then
-- uniquement si l'utilisateur n'a pas fixé la taille
pm.largeur_tableau = pm.colonnes*5.4 .. "em"
end
end
-- on récupère le "langage" courant pour utiliser formatnum
local lang = mw.language.getContentLanguage()
-- on récupère le namespace
local ttl = mw.title.getCurrentTitle().namespace
-- création du div principal
ret = ret .. '<div style="overflow:hidden; width: ' .. pm.largeur_tableau .. '; ' .. vflottant .. '; padding:0 1px; text-align:center; font-family: Liberation Sans, Arial, sans-serif;">'
-- boucle sur les lignes
for ligne = 1, #struct do
local structLigne = struct[ligne]
-- une ligne à faire, on crée le tableau
if (ligne == 1) then
ret = ret .. '<table class="wikitable" style="table-layout:fixed;width:100%;text-align:center;margin-top: 1px; ' .. pm.hauteur_lignes .. 'margin-bottom: 0;font-size:' .. pm.taille_police .. ';">\n'
else
ret = ret .. '<table class="wikitable" style="table-layout:fixed;width:100%;text-align:center;margin-top:' .. pm.marge_interlignes .. '; ' .. pm.hauteur_lignes .. 'margin-bottom: 0;font-size:' .. pm.taille_police .. ';">\n'
end
-- si titre présent on l'ajoute : visible si 1ère ligne, caché sinon
if (pm.titre ~= nil) then
if (ligne == 1) then
ret = ret .. '<caption style="margin-bottom:' .. pm.marge_interlignes .. ';">' .. pm.titre .. '</caption>'
else
ret = ret .. '<caption class="hidden">' .. pm.titre .. ", suite (" .. ligne-1 .. ')</caption>'
end
else
-- titre par défaut, caché
ret = ret .. '<caption class="hidden">' .. p.titre_par_defaut .. ' (ligne ' .. ligne .. ')</caption>'
end
-- parcours des colonnes pour insérer les années
ret = ret .. "<tr>\n"
for col = 1, #structLigne do
local structCol = structLigne[col]
-- présence de AAAA affichage ?
local temp = pm[structCol[1] .. " affichage"]
if (temp ~= nil) then
-- on affiche l'élément indiqué à la place
ret = ret .. '<th scope="col" style="background-color: ' .. coul_annees .. ';">' .. temp .. '</th>\n'
else
if (pm.hyperliens_annees) then
ret = ret .. '<th scope="col" style="background-color: ' .. coul_annees .. ';">[[' .. structCol[1] .. ']]</th>\n'
else
ret = ret .. '<th scope="col" style="background-color: ' .. coul_annees .. ';">' .. structCol[1] .. '</th>\n'
end
end
end
-- si on n'a pas terminé les colonnes on termine avec du vide
if #structLigne < pm.colonnes then
for col = #structLigne + 1, pm.colonnes do
ret = ret .. '<th scope="col" style="background-color: ' .. coul_annees .. ';">-</th>\n'
end
end
ret = ret .. "</tr>\n"
-- parcours des colonnes pour insérer les valeurs
ret = ret .. "<tr>\n"
for col = 1, #structLigne do
local structCol = structLigne[col]
if (structCol[2] == "" or structCol[2] == nil) then
ret = ret .. '<td ' .. coul_valeurs .. '>-</td>'
else
local tmp = ""
-- on récupère la partie numérique au début
local pdeb = ""
do
local fStart, fEnd, match = structCol[2]:find("^([0-9]+)")
while fEnd do
pdeb = pdeb .. match
-- dans l'éventualité où la valeur est un nombre déjà formaté
fStart, fEnd, match = structCol[2]:find("^(\194\160[0-9]+)", fEnd + 1)
end
end
if (pdeb ~= "") then
-- si le nombre est déjà formaté on l'ajoute tel quel, sinon on lui applique un formatnum
-- (attention à ne pas modifier la variable pdeb, pour ne pas modifier sa longueur)
if (pdeb:find("\194\160")) then
tmp = tmp .. pdeb
else
tmp = tmp .. lang:formatNum(tonumber(pdeb))
end
end
-- on ajoute la suite (éventuelle)
local pfin = structCol[2]:sub(#pdeb + 1)
if (pfin ~= "") then
tmp = tmp .. pfin
end
-- si un paramètre "XXXX unité" existe on l'insert
local unite = pm[structCol[1] .. " unité"]
if (unite ~= nil) then
tmp = tmp .. " " .. unite
end
-- si un paramètre "XXXX notes" existe on l'insert en tant que note
local note = pm[structCol[1] .. " notes"]
if (note ~= nil) then
-- test : on regarde si la note est déjà une ref (pour insérer une espace ou pas avant)
local estref = mw.text.unstrip(note)
-- si 'estref' == '' la note ne contient qu'un strip marker (ref, pre, gallery). Probablement une ref.
if (estref ~= '') then
tmp = tmp .. " "
end
tmp = tmp .. note
end
ret = ret .. '<td ' .. coul_valeurs .. '>' .. tmp .. '</td>'
end
end
-- si on n'a pas terminé les colonnes on termine avec du vide
if #structLigne < pm.colonnes then
for col = #structLigne + 1, pm.colonnes do
ret = ret .. '<td ' .. coul_valeurs .. '>-</td>'
end
end
ret = ret .. "</tr>\n"
-- fermeture table
ret = ret .. "</table>\n"
end
-- si présence d'erreur on l'ajoute aux notes
local erreurs = nil
if (p.liste_erreurs[1] ~= nil) then
erreurs = "<span class=\"error\" style=\"font-size: 0.9em;\">Liste des erreurs :"
for i = 1, #p.liste_erreurs do
erreurs = erreurs .. "<br>• " .. p.liste_erreurs[i]
end
erreurs = erreurs .. "</span>"
end
-- gestion des notes et sources
if (pm.notes ~= nil or pm.source ~= nil or pm.sources ~= nil or pm.sansdoublescomptes ~= nil or pm.enqueteannuelle ~= nil or erreurs ~= nil) then
local pred = false
ret = ret .. '<div style="padding: 0.3em; margin: 6px 0; line-height: 150%; font-size: 0.9em; ' .. pm.notes_fond .. ' ' .. pm.style_notes .. '">'
-- le double-compte si présent
if (pm.sansdoublescomptes ~= nil) then
ret = ret .. "Nombre retenu à partir de [[" .. pm.sansdoublescomptes .. "]] : [[Chiffres de population de la France|population sans doubles comptes]]."
pred = true
end
if (pm.enqueteannuelle ~= nil) then
if (pred) then
ret = ret .. "<br/>"
end
ret = ret .. "[[" .. pm.enqueteannuelle .. "]] : Population provisoire (enquête annuelle)."
pred = true
end
-- on ajoute les notes si présentes
if (pm.notes ~= nil) then
if (pred) then
ret = ret .. "<br/>"
end
-- si présent on retire le saut de ligne
pm.notes = p.sans_nl(pm.notes)
ret = ret .. pm.notes
pred = true
end
-- sources si présentes
if (pm.source ~= nil or pm.sources ~= nil) then
if (pred) then
ret = ret .. "<br/>"
end
pred = true
-- si on a source et sources on met tout dans sources
if (pm.source ~= nil and pm.sources ~= nil) then
pm.sources = pm.source .. " " .. pm.sources
pm.source = nil
end
if (pm.sources ~= nil) then
-- si présent on retire le saut de ligne
pm.sources = p.sans_nl(pm.sources)
end
if (pm.source ~= nil) then
-- si présent on retire le saut de ligne
pm.source = p.sans_nl(pm.source)
end
local tmp
if (pm.sources ~= nil) then
tmp = "Sources : " .. pm.sources
else
tmp = "Source : " .. pm.source
end
if (parenthese) then
ret = ret .. "(" .. tmp .. ")"
else
ret = ret .. tmp
end
end
-- on ajoute les erreurs si présentes
if (erreurs ~= nil) then
if (pred) then
ret = ret .. "<br/>"
end
ret = ret .. erreurs
end
-- on ferme la div des notes
ret = ret .. '</div>'
end
-- on ferme la div principale
ret = ret .. "</div>"
-- si namespace encyclo (ttl = 0) on insert les catégories d'erreur
if (ttl == 0) then
for i = 1, #p.liste_cats do
ret = ret .. "[[Catégorie:" .. p.categorie_erreur .. "|" .. p.liste_cats[i] .. "]]"
end
end
-- on retourne le résultat
return ret
end
--[[
Fonction appelable depuis un modèle. Se contente d'appeler demographie_m() qui fait le
traitement et est également appelable depuis un autre module
--]]
function p.demographie(frame)
-- pour simplifier on stocke la frame et la pframe
p.frame = frame
p.pframe = frame:getParent()
-- pm est la table des parametres → on lit tous les paramètres référencés
local pm = {}
for k, v in pairs(p.parametres) do
local value = p.lit_parametre(k, true)
if value then
pm[v] = value
end
end
-- les paramètres numériques maintenant (les années)
for k, v in pairs(p.pframe.args) do
if type(k) == "number" then
pm[k] = mw.text.trim(v)
elseif p.parametres[k] == nil and k:sub(1, 1) ~= "_" then
pm[k] = v
end
end
for k, v in pairs(p.frame.args) do
if type(k) == "number" then
pm[k] = mw.text.trim(v)
elseif p.parametres[k] == nil and k:sub(1, 1) ~= "_" then
pm[k] = v
end
end
-- on appelle (et on retourne) la fonction principale
return p.demographie_m(pm)
end
return p -- on retourne le module