« Module:Démographie » : différence entre les versions
De Aknotl
Infobox>Zebulon84 ajout font-family avoir une largeur de police à peu près constante, ce qui n'était pas le cas sur Ubuntu et dérivé avec Firefox, qui utilise par défaut DejaVu Sans, bien plus large que Arial utilisée sur Windows et Liberation Sans utilisée par Chromium |
m 1 version importée |
(Aucune différence)
|
Dernière version du 24 juillet 2021 à 11:39
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",
["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, p.pframe.args.wikidata) 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