Modul:Démographie
Belgekirina modulê[biafirîne]
--[[
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",
["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",
["marge_interlignes"] = "marge_interlignes",
["taille_police"] = "taille_police",
["hauteur_lignes"] = "hauteur_lignes",
["hyperliens_annees"] = "hyperliens_annees",
["hyperliens_années"] = "hyperliens_annees",
["années_fond"] = "annees_fond",
["annees_fond"] = "annees_fond",
["population_fond"] = "population_fond",
["notes_fond"] = "notes_fond",
["style_notes"] = "style_notes",
}
-- le nom de la catégorie d'erreur (pour simplifier les changements)
p.categorie_erreur = "Page avec une erreur d'utilisation du modèle Démographie"
-- le titre par défaut (pour simplifier les changements) → plus utilisé : il n'y a plus de titre par défaut
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
temp = mw.text.trim(temp)
if (temp == "") then
return nil
else
return temp
end
else
return mw.text.trim(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)
local id = mw.wikibase.getEntityIdForCurrentPage()
if mw.wikibase.isValidEntityId(p.pframe.args.wikidata) then
id = p.pframe.args.wikidata
elseif not id then
return
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 then
pm[an] = pop:match('%d+')
end
end
end
end
--[[
Fonction de tri de la table
--]]
function p.mysort(el1, el2)
if (el1 == nil) then
return true
end
if (el2 == nil) then
return false
end
if (el1[1] < el2[1]) then
return true
else
return false
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
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)
-- titre par défaut
--[[
if (pm.titre == "off" or pm.titre == "non") then
pm.titre = nil
elseif (pm.titre == nil) then
pm.titre = p.titre_par_defaut
end
--]]
-- modification : maintenant le titre est réellement optionnel
if (pm.titre == "") then
pm.titre = nil
end
-- 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 (mw.ustring.lower(pm.flottant or "") == "gauche") then
vflottant = 'float:left; margin: 0 1em 1em 0'
elseif (mw.ustring.lower(pm.flottant or "") == "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
-- 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
table.insert(tbl, {annee,valeur})
else
-- on profite de cette boucle pour vérifier les paramètres qui n'existent pas
local tst = p.parametres[annee]
if (tst == nil) then
-- cas particulier : les paramètres sous la forme "XXXX notes" et "XXXX unité" sont acceptés
local tst = mw.ustring.match(annee, "^[0-9]+ notes$") or mw.ustring.match(annee, "^[0-9]+ unité$") or mw.ustring.match(annee, "^[0-9]+ affichage$") or mw.ustring.match(annee, "^_")
-- autre : on ignore également les paramètres vides (pb de transmission d'autres modèles)
local temp = mw.text.trim(annee)
if (tst == nil and temp ~= "") then -- pas un paramètre connu ni XXXX notes → erreur
p.erreur("Le paramètre ''>>" .. annee .. "<<'' (" .. mw.ustring.len(annee) .. "/" .. mw.ustring.len(temp) .. ") est inconnu", "paramètre inconnu")
-- on ignore simplement ce champs
end
end
end
end
-- tri de la table
table.sort(tbl, p.mysort)
-- 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 = 1
local ligne = 1
local struct = {}
table.insert(struct, {}) -- on crée la première ligne
local total = 0 -- compte du nombre total
local pos = 1
while (tbl[pos] ~= nil) do
annee = tbl[pos][1]
valeur = tbl[pos][2]
pos = pos + 1
-- il y a aussi les paramètres nommés dans cette table, qu'on laisse
if (type(annee) == "number" and not (annee == 1 and (valeur == nil or valeur == ""))) then -- protection : un paramètre non nommé vaudra "1"
-- nettoyage de la valeur
local v = mw.text.trim(valeur or "")
-- on insert dans la ligne en cours
table.insert(struct[ligne], { annee, v })
-- suivant
total = total + 1
col = col + 1
-- fin de la ligne ?
if (col > pm.colonnes) then
col = 1
ligne = ligne + 1
table.insert(struct, {})
end
end
end
-- aucune entrée ? erreur.
if (total == 0) then
p.erreur("Aucune année fournie au modèle", "absence d'années")
-- on insert une donnée fictive
tbl[1] = { 1970, 0 }
end
-- cas particulier : si les données arrivent pile à la dernière colonne on a alors
-- une nouvelle ligne vide, pour rien. On l'enlève si c'est le cas
if (struct[ligne] ~= nil and struct[ligne][1] == nil) then
struct[ligne] = nil
ligne = ligne - 1
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
-- on force le namespace à "non encyclopédique" pour avoir systématiquement les messages d'erreur
local ttl = 1 -- 1 ou autre chose, mais pas pas 0. Il faudrait si on conserve ça virer le code qui teste cette valeur (plus propre)
-- 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;">'
ligne = 1
-- boucle sur les lignes
while (struct[ligne] ~= nil) do
-- 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 and pm.titre ~= "") 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
col = 1
ret = ret .. "<tr>\n"
while (struct[ligne][col] ~= nil) do
-- présence de AAAA affichage ?
local temp = pm[struct[ligne][col][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 .. ';">[[' .. struct[ligne][col][1] .. ']]</th>\n'
else
ret = ret .. '<th scope="col" style="background-color: ' .. coul_annees .. ';">' .. struct[ligne][col][1] .. '</th>\n'
end
end
col = col + 1
end
-- si on n'a pas terminé les colonnes on termine avec du vide
if (col <= pm.colonnes) then
while (col <= pm.colonnes) do
ret = ret .. '<th scope="col" style="background-color: ' .. coul_annees .. ';">-</th>\n'
col = col + 1
end
end
ret = ret .. "</tr>\n"
-- parcours des colonnes pour insérer les valeurs
col = 1
ret = ret .. "<tr>\n"
while (struct[ligne][col] ~= nil) do
if (struct[ligne][col][2] == "" or struct[ligne][col][2] == nil) then
ret = ret .. '<td ' .. coul_valeurs .. '>-</td>'
else
-- on récupère la partie numérique au début
local pdeb = mw.ustring.match(struct[ligne][col][2], "^[0-9]*")
local pfin = mw.ustring.match(struct[ligne][col][2], "^[0-9]*(.*)$")
local tmp = ""
-- si le début est présent il passe par formatnum
if (pdeb ~= nil and pdeb ~= "") then
tmp = tmp .. lang:formatNum(tonumber(pdeb))
end
-- on ajoute la suite (éventuelle)
if (pfin ~= nil and pfin ~= "") then
tmp = tmp .. pfin
end
-- si un paramètre "XXXX unité" existe on l'insert
local unite = pm[struct[ligne][col][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[struct[ligne][col][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
col = col + 1
end
-- si on n'a pas terminé les colonnes on termine avec du vide
if (col <= pm.colonnes) then
while (col <= pm.colonnes) do
ret = ret .. '<td ' .. coul_valeurs .. '>-</td>'
col = col + 1
end
end
ret = ret .. "</tr>\n"
-- fermeture table
ret = ret .. "</table>\n"
ligne = ligne + 1
end
-- si pas encyclo + présence d'erreur on l'ajoute aux notes
local erreurs = nil
if (ttl ~= 0 and p.liste_erreurs[1] ~= nil) then
erreurs = "<span class=\"error\" style=\"font-size: 0.9em;\">Liste des erreurs :<br/>"
local i = 1
while (p.liste_erreurs[i] ~= nil) do
erreurs = erreurs .. "• " .. p.liste_erreurs[i]
if (p.liste_erreurs[i+1] ~= nil) then
erreurs = erreurs .. "<br/>"
end
i = i + 1
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
-- si présent on retire le saut de ligne
pm.sansdoublescomptes = p.sans_nl(pm.sansdoublescomptes)
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
-- si présent on retire le saut de ligne
pm.enqueteannuelle = p.sans_nl(pm.enqueteannuelle)
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
local i = 1
while (p.liste_cats[i] ~= nil) do
ret = ret .. "[[Catégorie:" .. p.categorie_erreur .. "|" .. p.liste_cats[i] .. "]]"
i = i + 1
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
pm[v] = p.lit_parametre(k, true)
end
-- les paramètres numériques maintenant (les années)
if require('Module:Yesno')(p.pframe.args.wikidata, true, false) then
-- obtient les données de wikidata, mais les données locales auront priorité
p.valeur_wikidata(pm)
mw.logObject( pm, 'pm' )
end
for k, v in pairs(p.pframe.args) do
if (type(k) == "number") then
pm[k] = mw.text.trim(v)
end
end
for k, v in pairs(p.frame.args) do
if (type(k) == "number") then
pm[k] = mw.text.trim(v)
end
end
-- on appelle (et on retourne) la fonction principale
return p.demographie_m(pm)
end
return p -- on retourne le module