Module:Démographie Québec

 Documentationcréerpurger
local p = {}

--------------------------------------------------------------------
-- Normalisation des valeurs P1082
--------------------------------------------------------------------
local function normalizePop(raw)
    if type(raw) == "table" and raw.amount then
        return tonumber(raw.amount)
    end
    return tonumber(raw)
end

--------------------------------------------------------------------
-- Extraction des données P1082 + P585 (toutes les déclarations)
--------------------------------------------------------------------
local function getPopData(entityId)
    local entity = mw.wikibase.getEntity(entityId)
    local statements = entity and entity.claims and entity.claims.P1082 or {}
    local data = {}

    for _, s in ipairs(statements) do
        if s.rank == "normal" or s.rank == "preferred" then
            local rawPop = s.mainsnak.datavalue and s.mainsnak.datavalue.value
            local pop = normalizePop(rawPop)

            local dateQualifier = s.qualifiers and s.qualifiers.P585

            if pop and dateQualifier then
                for _, q in ipairs(dateQualifier) do
                    local time = q.datavalue.value.time
                    local year = tonumber(time:match("(%d%d%d%d)"))
                    if year then
                        table.insert(data, { year = year, pop = pop })
                    end
                end
            end
        end
    end

    table.sort(data, function(a, b) return a.year < b.year end)
    return data
end

--------------------------------------------------------------------
-- Construction du modèle Graphique démographique
--------------------------------------------------------------------
local function buildGraph(data, couleur, anneeMin, largeur, note)

    -- Filtrage année min
    if anneeMin then
        local filtered = {}
        for _, v in ipairs(data) do
            if v.year >= anneeMin then
                table.insert(filtered, v)
            end
        end
        data = filtered
    end

    if #data == 0 then
        return "''Aucune donnée démographique disponible dans Wikidata.''"
    end

    if #data < 2 then
        return "''Données insuffisantes pour générer un graphique.''"
    end

    local minYear = data[1].year
    local maxYear = data[#data].year

    local maxPop = 0
    for _, v in ipairs(data) do
        if v.pop and v.pop > maxPop then
            maxPop = v.pop
        end
    end

    local out = {}

    table.insert(out, "{{Graphique démographique")
    table.insert(out, "| mode = image")
    table.insert(out, "| type = tout")
    table.insert(out, "| largeur image = " .. largeur)
    table.insert(out, "| hauteur image = 300")
    table.insert(out, "| épaisseur histogramme = 10")
    table.insert(out, "| max pop = " .. maxPop)
    table.insert(out, "| année min = " .. minYear)
    table.insert(out, "| année max = " .. maxYear)
    table.insert(out, "| couleur = " .. couleur)

    -- NOTE reçue en paramètre (singulier) → envoyée en "notes" (pluriel)
    if note and note ~= "" then
        table.insert(out, "| notes = " .. note)
    else
        table.insert(out, "| notes = Estimation et recensement de la population")
    end

    for _, v in ipairs(data) do
        table.insert(out, "| " .. v.year .. "|" .. v.pop)
    end

    table.insert(out, "}}")

    return table.concat(out, "\n")
end

--------------------------------------------------------------------
-- Fonction publique : Graphique démographique WD
--------------------------------------------------------------------
function p.graphiqueWD(frame)
    local args = frame.args

    -- Paramètres utilisateur
    local titre    = args["titre"]
    local couleur  = args["couleur"] or "#3366cc"
    local anneeMin = tonumber(args["année min"])
    local largeur  = tonumber(args["largeur"]) or 600
    local note     = args["note"]   -- ← réception du paramètre "note"

    -- Identification de l’élément Wikidata
    local entityId = args.id
    if not entityId or entityId == "" then
        entityId = mw.wikibase.getEntityIdForCurrentPage()
    end

    if not entityId then
        return "''Impossible d’identifier l’élément Wikidata.''"
    end

    -- Récupération des données WD
    local data = getPopData(entityId)

    -- Construction du rendu final
    local out = ""

    -- Titre centré
    if titre and titre ~= "" then
        out = out .. '<div style="text-align:center;">\'\'\'' .. titre .. '\'\'\'</div>\n'
    end

    -- Génération du graphique AVEC note
    out = out .. frame:preprocess(buildGraph(data, couleur, anneeMin, largeur, note))

    return out
end

--------------------------------------------------------------------
-- TABLEAU HORIZONTAL MULTI‑COLONNES  
--------------------------------------------------------------------
function p.tableauHorizontalMulti(frame)

local parent = frame:getParent()
local args = {}

-- On copie d'abord les paramètres du modèle
	for k, v in pairs(parent.args) do
	    if v ~= "" then
	        args[k] = v
	    end
	end
	
	-- Puis on écrase avec les paramètres passés directement à #invoke
	for k, v in pairs(frame.args) do
	    if v ~= "" then
	        args[k] = v
	    end
	end

    ----------------------------------------------------------------
	-- 2) Normalisation des notes
	local newArgs = {}
	
	for k, v in pairs(args) do
	    local key = tostring(k)
	
	    -- Trim des espaces invisibles
	    key = mw.text.trim(key)
	
	    -- Normalisation des espaces spéciaux
	    key = key:gsub("\194\160", " ")   -- U+00A0
	    key = key:gsub("\226\128\175", " ") -- U+202F
	    key = key:gsub("\226\128\137", " ") -- U+2009
	    key = key:gsub("\226\128\131", " ") -- U+2003
	    key = key:gsub("\226\128\130", " ") -- U+2002
	
	    -- Détection d'année
	    local year = key:match("(%d%d%d%d)$")
	
	    if year then
	        -- Toutes les formes possibles
	        if key:match("^note" .. year .. "$")          -- note2001
	        or key:match("^notes" .. year .. "$")         -- notes2001
	        or key:match("^note[%s%-%_]+" .. year .. "$") -- note 2001, note-2001, note_2001
	        or key:match("^notes[%s%-%_]+" .. year .. "$") then
	            newArgs["note" .. year] = v
	        else
	            newArgs[key] = v
	        end
	    else
	        newArgs[key] = v
	    end
	end
	
	args = newArgs

    ----------------------------------------------------------------
    -- 3) Paramètres d'affichage
    ----------------------------------------------------------------
    local showEstimations = (args.estimations == "oui")

    local source = args.source
    if not source or source == "" then
        source = "[[Statistique Canada]]"
    end

    local MAX_COLS = tonumber(args.colonnes) or 12
    if MAX_COLS < 1 then MAX_COLS = 1 end
    if MAX_COLS > 12 then MAX_COLS = 12 end

    local title = args.titre
    if not title or title == "" then
        title = "Évolution démographique"
    end

    local year_color = args.couleur
    if not year_color or year_color == "" then
        year_color = "#ddffdd"
    end

    ----------------------------------------------------------------
    -- 4) Récupération Wikidata
    ----------------------------------------------------------------
    local entityId = mw.wikibase.getEntityIdForCurrentPage()
    if not entityId then
        return "Données Wikidata introuvables."
    end

    local entity = mw.wikibase.getEntity(entityId)
    if not entity or not entity.claims or not entity.claims.P1082 then
        return "Aucune donnée de population disponible."
    end

----------------------------------------------------------------
-- 5) Construction des lignes
----------------------------------------------------------------
	local rows = {}
	
	for _, statement in ipairs(entity.claims.P1082 or {}) do
	    if statement.rank == "normal" or statement.rank == "preferred" then
	        local mainsnak = statement.mainsnak
	        if mainsnak and mainsnak.datavalue then
	
	            local raw = mainsnak.datavalue.value
	            local popNum =
	                (type(raw) == "table" and tonumber(raw.amount))
	                or tonumber(raw)
	
	            if popNum then
	                local qualifiers = statement.qualifiers or {}
	
	                -- Date
	                local date = qualifiers.P585
	                    and qualifiers.P585[1]
	                    and qualifiers.P585[1].datavalue
	                    and qualifiers.P585[1].datavalue.value.time
	
	                local year = date and mw.ustring.match(date, "%d%d%d%d")
	                year = tonumber(year)
	
	                if year and (year % 5 ~= 1) then
	                    year = nil
	                end
	
	                -- Méthode
	                local method = nil
	                if qualifiers.P459
	                   and qualifiers.P459[1]
	                   and qualifiers.P459[1].datavalue
	                   and qualifiers.P459[1].datavalue.value
	                   and qualifiers.P459[1].datavalue.value.id then
	                    method = qualifiers.P459[1].datavalue.value.id
	                end
	
	                local keep = true
	                if method == "Q29051383" and not showEstimations then
	                    keep = false
	                end
	
	                -- Références
	                local references = statement.references or {}
	                local url = nil
	
	                for _, ref in ipairs(references) do
	                    if ref.snaks and ref.snaks.P854 and ref.snaks.P854[1]
	                       and ref.snaks.P854[1].datavalue then
	                        url = ref.snaks.P854[1].datavalue.value
	                        break
	                    end
	                end
	
	                if year and keep then
	
	                    ----------------------------------------------------------------
	                    -- Gestion populationYYYY + noteYYYY
	                    ----------------------------------------------------------------
	                    local manualPopKey = "population" .. tostring(year)
	                    local manualNoteKey = "note" .. tostring(year)
	
	                    local manualPop = args[manualPopKey]
	                    local manualNote = args[manualNoteKey]
	
	                    -- Détection d’un <ref> dans la population manuelle
	                    local extractedPop, extractedNote = nil, nil
	                    if manualPop then
	                        extractedPop, extractedNote = manualPop:match("^(.-)(<ref.*)$")
	                    end
	
	                    -- Note finale
	                    local finalNote = nil
	                    if manualNote and manualNote ~= "" then
	                        finalNote = manualNote
	                    elseif extractedNote and extractedNote ~= "" then
	                        finalNote = extractedNote
	                    end
	
	                    -- Population finale (WD uniquement)
	                    local popValue = popNum
	
	                    ----------------------------------------------------------------
	                    -- Séparation URL brute / URL pour <ref group="Source">
	                    ----------------------------------------------------------------
	                    local rawUrl = url  -- affichée dans la cellule
	
	                    local refUrl = nil  -- utilisée dans les références
	                    if url and year then
	                        refUrl = string.format("[%s Recensement %d]", url, year)
	                    end
	
	                    ----------------------------------------------------------------
	                    -- Ajout final
	                    ----------------------------------------------------------------
	                    table.insert(rows, {
	                        year   = year,
	                        pop    = popValue,
	                        note   = finalNote,
	                        url    = rawUrl,   -- cellule
	                        ref    = refUrl,   -- <ref group="Source">
	                        manual = (manualPop ~= nil and manualPop ~= "")
	                    })
	
	                end
	            end
	        end
	    end
	end

	----------------------------------------------------------------
	-- 6) Filtrage, tri, affichage 
	----------------------------------------------------------------
	
	if #rows == 0 then
	    return "Aucune donnée de recensement valide disponible."
	end
	
	table.sort(rows, function(a, b) return a.year < b.year end)
	
	local filtered = {}
	for _, r in ipairs(rows) do
	    if r.year >= 1956 then
	        table.insert(filtered, r)
	    end
	end
	rows = filtered
	
	if #rows == 0 then
	    return ""
	end
	
	local langObj = mw.language.new("fr")
	
	local out = '{| class="wikitable centre" style="text-align:center; white-space:nowrap;"\n'
	local invisibleSources = ""
	out = out .. '|+ style="background:#ffffff; font-weight:bold;" | ' .. title .. '\n'
	
	local N = #rows
	
	----------------------------------------------------------------
	-- CAS 1 : tableau large (plus de 12 colonnes)
	----------------------------------------------------------------
	if N > 12 then
	    local mid = math.ceil(N / 2)
	
	    local function printBlock(startIdx, endIdx)
	        -- Ligne des années
	        out = out .. "|-\n"
	        for i = startIdx, endIdx do
	            local r = rows[i]
	            local yearText
	            if r.url then
	                yearText = '<span class="plainlinks">[' .. r.url .. ' \'\'\'' .. r.year .. '\'\'\' ]</span>'
	            else
	                yearText = "'''" .. r.year .. "'''"
	            end
				if r.url then
				    invisibleSources = invisibleSources .. frame:extensionTag("ref", "Source : " .. r.url, { group = "sources" })
				end	
	            if i == startIdx then
	                out = out .. '| style="background:' .. year_color .. ';" | ' .. yearText
	            else
	                out = out .. ' || style="background:' .. year_color .. ';" | ' .. yearText
	            end
	        end
	        out = out .. "\n"
	
	        -- Ligne des populations + notes
	        out = out .. "|-\n"
	        for i = startIdx, endIdx do
	            local r = rows[i]
	
	            -- Sécurisation : pop ne doit jamais être nil
	            local pop
	
	            if r.manual then
	                pop = r.pop or ""
	            else
	                if r.pop then
	                    pop = langObj:formatNum(r.pop)
	                else
	                    pop = ""
	                end
	            end
	
	            -- Ajout de la note sous forme de <ref>
	            if r.note and r.note ~= "" then
	                pop = pop .. " " .. frame:extensionTag("ref", r.note)
	            end
	
	            if i == startIdx then
	                out = out .. '| style="background:#f3fff3;" | ' .. pop
	            else
	                out = out .. ' || style="background:#f3fff3;" | ' .. pop
	            end
	        end
	        out = out .. "\n"
	    end
	
	    printBlock(1, mid)
	    printBlock(mid + 1, N)
	
	    out = out .. '|-\n| colspan="' .. mid .. '" style="text-align:center; font-size:90%; background:#f3fff3;" | Source : ' .. source .. '\n'
	    out = out .. "|}"
	
	    return out
	end
	
	----------------------------------------------------------------
	-- CAS 2 : tableau normal (MAX_COLS)
	----------------------------------------------------------------
	for i = 1, N, MAX_COLS do
	    local block = {}
	    for j = i, math.min(i + MAX_COLS - 1, N) do
	        table.insert(block, rows[j])
	    end
	
	    -- Ligne des années
	    out = out .. "|-\n"
	    for k, r in ipairs(block) do
	        local yearText
	        if r.url then
	            yearText = '<span class="plainlinks">[' .. r.url .. ' \'\'\'' .. r.year .. '\'\'\' ]</span>'
	        else
	            yearText = "'''" .. r.year .. "'''"
	        end
			if r.url then
				invisibleSources = invisibleSources .. frame:extensionTag("ref", r.ref, { group = "sources" })
			end
	
	        if k == 1 then
	            out = out .. '| style="background:' .. year_color .. ';" | ' .. yearText
	        else
	            out = out .. ' || style="background:' .. year_color .. ';" | ' .. yearText
	        end
	    end
	    out = out .. "\n"
	
	    -- Ligne des populations + notes
	    out = out .. "|-\n"
	    for k, r in ipairs(block) do
	        local pop
	
	        if r.manual then
	            pop = r.pop or ""
	        else
	            if r.pop then
	                pop = langObj:formatNum(r.pop)
	            else
	                pop = ""
	            end
	        end
	
	        if r.note and r.note ~= "" then
	            pop = pop .. " " .. frame:extensionTag("ref", r.note)
	        end
	
	        if k == 1 then
	            out = out .. '| style="background:#f3fff3;" | ' .. pop
	        else
	            out = out .. ' || style="background:#f3fff3;" | ' .. pop
	        end
	    end
	    out = out .. "\n"
	end
	
	out = out .. '|-\n| colspan="' .. MAX_COLS .. '" style="text-align:center; font-size:90%; background:#f3fff3;" | Source : ' .. source .. '\n'
	out = out .. "|}"
	
	return frame:preprocess(out)

end
return p