Module:Taxobox2

Une page de Wikipédia, l'encyclopédie libre.

 Documentation[voir] [modifier] [historique] [purger]

Ce module fournit une fonction permettant de générer une taxobox.

Utilisation[modifier le code]

Fonctions exportables :

  • taxobox(frame) – la fonction servant à créer une taxobox. Voir documentation détaillée pour son utilisation

Autres fonctions :

  • genere_sortie(texte, debug) – formate le retour de taxobox(frame) et insert si besoin la sortie debug

Modules externes et autres éléments dont ce module a besoin pour fonctionner :

  • Module:Taxobox données − module contenant toutes les données constantes relatives à la construction d'une taxobox
  • Module:Taxobox fonctions − module contenant toutes les fonctions de traitement des paramètres et de création du contenu
  • mw.title – utilisé pour connaître le titre de la page où on est utilisé
  • .mw.text, mw.ustring…

Déroulement de l'exécution[modifier le code]

Les étapes pour créer une taxobox sont toujours les mêmes. Les actions de la fonction taxobox(frame) sont :

  • enregistrement frame et pframe dans la table donnees.defauts.{frame|pframe}} (pour utilisation par d'autres fonctions
  • récupération du titre et du namespace de l'article (donnees.defauts.{article|titre})
  • analyse des paramètres nommés. Contenu : "charte" et "nom vernaculaire" ; comportement : c_titre, c_sous_titre, c_categories, c_categories_err, c_erreurs, c_force_titre ; debug : raw, debug. Vérifications sur les paramètres (présence obligatoire de "charte", et qu'elle corresponde à une charte connue).
  • analyse des paramètres non nommés pour créer la liste des commandes (suite des lignes de taxobox à créer : donnees.defauts.commandes) avec la commande tri_parametres(). Validation que cette phase c'est bien passée.
  • initialisation de l'état courant de la taxobox (ouverte, fermée, nombre de tables ouvertes…)
  • recherche des entrées "taxon" (commande tri_taxons). Cette fonction regroupe les lignes "taxon" dans une table, note le dernier "taxon" donné Note également si l'un des taxons indiqué peut correspondre au titre de l'article.
  • détermination du titre de la taxobox (genere_titre_taxobox()).
  • boucle de parcours des commandes de la taxobox. Parcours sur les lignes de donnees.defauts.commandes :
    • analyse de la ligne (lecture_parametres()) en utilisant la syntaxe de la commande. Retourne resu contenant les paramètres de la ligne
    • exécution de la fonction traitant la commande correspondante, sur les paramètres de la ligne
    • gestion de la structuration (ouverture/fermeture de tables typiquement)
    • ajout du code de la ligne à la taxobox en cours de création
    • note : à chaque étape diverses vérifications de la présence d'erreurs
  • détermination des modifications éventuelles du titre de l'article (mise en forme)
  • détermination de l'ajout éventuel d'un sous-titre à l'article
  • détermination des catégories éventuelles à ajouter (et selon le mode choisi et le namespace)
  • détermination des catégories d'erreurs éventuelles à ajouter (et selon le mode choisi et le namespace)
  • détermination des erreurs éventuelles à ajouter (et selon le mode choisi et le namespace)
  • retour du résultat
--[[
  Module créant une taxobox.
  
  Utilise Module:Taxobox données et Module:Taxobox fonctions.

  Note : le nom est temporaire. Module:Taxobox existe et contient une vieille version destinée à être remplacée.
--]]


-- la table du module
local p = {}

local donnees = require "Module:Taxobox données"
local fnc = require "Module:Taxobox fonctions"


--[[
  Fonction générant la sortie finale. Permet d'inclure le debug éventuel
--]]
function p.prepare_sortie(contenu, db)
    if (donnees.defauts.c_debug) then
        return db .. donnees.defauts.frame:preprocess(contenu)
    else
        return donnees.defauts.frame:preprocess(contenu)
    end
end


--[[
  Fonction principale : traite les paramètres et génère la taxobox.
  Les paramètres sont :
    règne : obligatoire. Sélectionne le thème (couleur, convention typographique, lignes de classification)
    nom vernaculaire : facultatif. Informe la taxobox du ou des noms vernaculaires existants

--]]
function p.taxobox(frame)
    local dg = ""
    
    donnees.defauts.frame = frame -- pour que les fonctions aient accès à la frame courante simplement
    donnees.defauts.pframe = frame:getParent() -- pour que les fonctions aient accès à la frame courante simplement
    
    dg = dg .. "frames préparées.<br/>"
    
    -- on récupère les infos sur l'article où on se trouve
    local curarticle = mw.title.getCurrentTitle()
    if (curarticle.namespace == 0) then -- est-ce un article ou pas
        donnees.defauts.article = true
    else
        donnees.defauts.article = false
    end
    -- le titre courant
    donnees.defauts.titre = curarticle.text
    
    dg = dg .. "namespaces+titre extraits.<br/>"
    
    -- récupération des paramètres
    donnees.defauts.regne = fnc.lit_parametre("charte", donnees.regnes, false, nil)  -- la charte
    if (donnees.defauts.regne == nil) then -- on teste charte, puis règne si marche pas
        donnees.defauts.regne = fnc.lit_parametre("règne", donnees.regnes, false, true)  -- le règne
    end
    donnees.defauts.nom_vernaculaire = fnc.lit_parametre("nom vernaculaire", nil, false, nil)  -- le non vernaculaire
    -- les options permettant de modifier le comportement
    donnees.defauts.c_titre = fnc.lit_parametre("titre", {"oui","non","boîte","auto"}, false, "auto")
    donnees.defauts.c_sous_titre = fnc.lit_parametre("sous-titre", {"oui","non","auto"}, false, "auto")
    donnees.defauts.c_erreurs = fnc.lit_parametre("erreurs", {"oui","non","auto"}, false, "auto")
    donnees.defauts.c_categories = fnc.lit_parametre("catégories", {"en ligne","non","boîte","auto"}, false, "auto")
    donnees.defauts.c_err_categories = fnc.lit_parametre("catégorie erreurs", {"en ligne","non","boîte","auto"}, false, "auto")
    donnees.defauts.c_force_titre = fnc.lit_parametre("force titre", nil, false, nil)
    -- si force titre vide on utilise le titre réel
    if (donnees.defauts.c_force_titre == nil) then
        donnees.defauts.c_force_titre = donnees.defauts.titre
    end

    -- debug
    donnees.defauts.c_raw = fnc.lit_parametre("raw", nil, true, nil)
    donnees.defauts.c_debug =  fnc.lit_parametre("debug", nil, true, nil)

    -- on regarde si une erreur (ou plusieurs) s'est produite durant la récupération des paramètres
    if (donnees.defauts.fatal) then
        return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg)
    end
    
    dg = dg .. "paramètres récupérés.<br/>"

    -- on récupère la structure règne
    donnees.defauts.regne_data = donnees.regnes[donnees.defauts.regne]
    if (donnees.defauts.regne_data == nil) then
        fnc.erreur_normale("Le règne indiqué ''" .. donnees.defauts.regne .. "'' n'est pas reconnu.", "syntaxe")
        return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg)
    end

    -- on analyse les paramètres pour générer la liste des commandes présentes
    local xxx, yyy = fnc.tri_parametres(donnees)
    dg = dg .. yyy
    if (donnees.defauts.fatal) then -- erreur → fatal
        return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg)
    end
    
    dg = dg .. "commandes analysées.<br/>"
    local w = 1
    while (donnees.defauts.commandes[w] ~= nil) do
        local ww = 1
        while (donnees.defauts.commandes[w][ww] ~= nil) do
            dg = dg .. donnees.defauts.commandes[w][ww] .. " "
            ww = ww + 1
        end
        dg = dg .. "<br/>"
        w = w + 1
    end
    dg = dg .. "EOC<br/>"

    -- initialisation de l'état de la taxobox en construction
    donnees.etat = {}
    donnees.etat.ouvert = false -- la taxobox est commencée
    donnees.etat.ferme = false -- la taxobox est terminée
    donnees.etat.ligne = "non commencé" -- le nom de la ligne en cours
    donnees.etat.tbl = 0 -- nombre de tables ouvertes
    
    dg = dg .. "état taxobox initialisé.<br/>"
    
    -- on parcours les commandes pour trouver les "taxon", afin de valider leur présence
    -- et de trouver le titre de la taxobox
    fnc.tri_taxons()
    if (donnees.defauts.fatal) then -- erreur → fatal
        return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg)
    end
    
    dg = dg .. "taxons analyés (dernier taxon et titre = " .. donnees.defauts.titre_taxobox .. ").<br/>"
    
    -- on traite le titre/sous-titre : est-ce le NS ? Est-ce le NV ? Autre ?
    fnc.genere_titre_taxobox()
    dg = dg .. "titre taxobox géré (titre = " .. donnees.defauts.titre_taxobox .. ").<br/>"

    -- parcours des commandes et appel des fonctions de traitement de chaque ligne
    local tb = ""
    local i = 1
    local courant = ""
    local precedent = ""
    while (donnees.defauts.commandes[i] ~= nil) do
        -- le mot-clé
        local cle = donnees.defauts.commandes[i][1]
        if (cle == nil) then  -- fatal
            fnc.erreur_fatale("La " .. i .. "ème commande n'existe pas.", "interne")
            return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg)
        end
        courant = cle
        dg = dg .. "  - commande : " .. cle .. "<br/>"
        -- on récupère le descriptif de cette commande
        local cmd = fnc.syntaxe[cle]
        if (cmd == nil) then
            dg = dg .. "    -> commande non référencée<br/>"
            fnc.erreur_fatale("La " .. i .. "ème commande (''" .. cle .. "'') n'est pas référencée dans les actions.", "interne")
            return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg)
        end
        dg = dg .. "   -> cmd : " .. type(cmd[1]) .. ", " .. type(cmd[2]) .. ", " .. type(cmd[3]) .. "<br/>"
        -- si pas de fonction associée erreur non fatale et on ignore
        if (cmd[3] == nil) then
        dg = dg .. "    -> commande non implémentée<br/>"
            fnc.erreur_normale("La commande ''" .. cle .. "'' n'est pas encore implémentée.", "interne")
        else
            -- on analyse la ligne
            local etat, resu = pcall(fnc.lecture_parametres, donnees.defauts.commandes[i], 2, cmd[2])
            if (not etat) then
                fnc.erreur_fatale("Échec d'appel à lecture_parametres (" .. cle .. ") (" .. resu .. ").", "interne")
                return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg) -- on quitte
            end
            -- on ajoute le nom de la commande réelle (pour les commandes différentes qui utilisent la même fonction)
            resu["commande"] = cle

            dg = dg .. "    - syntaxe analysée<br/>"
            -- gestion des erreurs
            if (resu == nil or resu.erreurs ~= nil) then
                -- une erreur sur cette ligne est-elle fatale ?
                dg = dg .. "    -> commande retourne une erreur<br/>"
                if (cmd[1] == 2) then
                    fnc.erreur_fatale((resu.erreurs or "non précisé"), "syntaxe")
                    return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg) -- on quitte
                else
                    fnc.erreur_normale((resu.erreurs or "non précisé"), "syntaxe")
                end
            else 
                dg = dg .. "    - syntaxe validée<br/>"
                -- on exécute la ligne
                local tmpr, tmpd
                tmpr, tmpd = pcall(cmd[3], resu)
                if (not tmpr) then
                    fnc.erreur_fatale("Échec d'appel à la fonction de gestion de ''" .. cle .. "'' (" .. tmpd .. ").", "interne")
                    return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg) -- on quitte
                end
                dg = dg .. "données : precedent=" .. precedent .. ", courant=" .. courant .. ", tbl=" .. donnees.etat.tbl .. "<br/>"
                -- gestion des fermetures de tables
                if (courant ~= "rang" and courant ~= "conflit" and (precedent == "rang" or precedent == "conflit")) then
                    tb = tb .. "</table>"
                    donnees.etat.tbl = donnees.etat.tbl - 1
                end
                -- cas particulier : on sort de début sans ligne de classification ensuite
                if (donnees.etat.tbl > 0 and courant ~= "rang" and courant ~= "conflit" and precedent == "début") then
                    dg = dg .. "tbl>0 (" .. donnees.etat.tbl .. "), courant ~= rang,conflit (" .. courant .. ")<br/>"
                    -- on quitte un rang → on ferme la table
                    tb = tb .. "</table>"
                    donnees.etat.tbl = donnees.etat.tbl - 1
                end
                -- traitement de "dépannage" : si "rang" sans table ouverte on ouvre la table mais c'est une erreur
                if ((courant == "rang" or courant == "conflit") and donnees.etat.tbl <= 0) then
                    fnc.erreur_normale("Ligne " .. i .. " : commande ''rang'' en dehors d'une zone adéquate.", "syntaxe")
                    tb = tb .. '<table  class="taxobox_classification"><caption>Erreur</caption>\n'
                    donnees.etat.tbl = donnees.etat.tbl + 1
                end

                -- on insert l'élément
                tb = tb .. tmpd
                dg = dg .. "    - commande traitée<br/>"
            end
        end
        -- on bascule courant/precedent
        precedent = courant
        -- ligne suivante
        i = i + 1
    end
    
    -- ici il faut valider que la taxobox est terminée proprement
    if (donnees.etat.tbl >= 1) then
        -- on ferme les tables
        for i = 1, donnees.etat.tbl do
            tb = tb .. "</table>"
        end
        donnees.etat.tbl = 0
    end
    -- fermeture
    if (donnees.etat.ferme == false) then
        tb = tb .. "</div>"
        donnees.etat.ferme = true
    end
    
    -- traitement titre article (mise en forme si nécessaire)
    local mtitre = ""
    if (donnees.defauts.taxon_titre ~= nil) then -- il y a un titre NS
        if (donnees.defauts.c_titre ~= "non") then
            dg =dg .. "titre a modifier a priori : titre = " .. donnees.defauts.c_force_titre .. ", ciblé = " .. donnees.defauts.commandes[donnees.defauts.taxon_titre][3] .. "<br/>"
            -- le titre modifié
            local tmp = fnc.italiques_titre(donnees.defauts.c_force_titre, donnees.defauts.commandes[donnees.defauts.taxon_titre][3], donnees.defauts.commandes[donnees.defauts.taxon_titre][2])
            dg = dg .. " -> " .. tmp .. "<br/>"
            -- selon le mode
            if (donnees.defauts.c_titre == "boîte" or (donnees.defauts.c_titre == "auto" and not donnees.defauts.article)) then
                mtitre = mtitre .. fnc.box_ouverture("Titre")
                mtitre = mtitre .. fnc.box_texte(frame:preprocess("<nowiki>" .. tmp .. "</nowiki>"))
                mtitre = mtitre .. fnc.box_fermeture()
            elseif (donnees.defauts.c_titre == "oui" or (donnees.defauts.c_titre == "auto" and donnees.defauts.article)) then
                mtitre = mtitre .. frame:preprocess(tmp)
            end
        end
    end

    -- traitement sous-titre éventuel
    local mstitre = ""
    if (donnees.defauts.c_sous_titre ~= "non") then
        dg = dg .. "sous-titre potentiel<br/>"
        local sst = nil
        -- si taxon_titre → titre en NS, on ajoute le NV si présent
        if (donnees.defauts.taxon_titre ~= nil) then
            dg = dg .. " -> vernaculaire : " .. (donnees.defauts.nom_vernaculaire or "non défini") .. "<br/>"
            sst = donnees.defauts.nom_vernaculaire
        else
            -- titre NV, on ajoute le NS en sous-titre
            dg = dg .. " -> scientifique : " .. donnees.defauts.titre_taxobox .. "<br/>"
            sst = donnees.defauts.titre_taxobox
        end
        if (donnees.defauts.c_sous_titre == "boîte" or (donnees.defauts.c_sous_titre == "auto" and not donnees.defauts.article)) then
            if (sst ~= nil) then
                mstitre = mstitre .. fnc.box_ouverture("Sous-titre")
                mstitre = mstitre .. fnc.box_texte(sst)
                mstitre = mstitre .. fnc.box_fermeture()
            end
        elseif (donnees.defauts.c_sous_titre == "oui" or (donnees.defauts.c_sous_titre == "auto" and donnees.defauts.article)) then
            -- on l'insert en tant que sous-titre
            if (sst ~= nil) then
                mstitre = mstitre .. '<span id="sous_titre_h1">' .. sst .. '</span>'
            end
        end
    end
    
    -- si besoin et selon les modalités on insert les catégories
    local mcat = ""
    if (donnees.defauts.r_categories[1] ~= nil and donnees.defauts.c_categories ~= "non") then
        -- selon le mode
        if (donnees.defauts.c_categories == "boîte" or (donnees.defauts.c_categories == "auto" and donnees.defauts.article == false)) then
            mcat = mcat .. fnc.box_ouverture("Catégorie(s)")
            mcat = mcat .. fnc.box_texte(donnees.defauts.r_categories, true)
            mcat = mcat .. fnc.box_fermeture()
        elseif (donnees.defauts.c_categories == "en ligne" or (donnees.defauts.c_categories == "auto" and donnees.defauts.article == true)) then
            -- insertion "en vrai"
            local i = 1
            while (donnees.defauts.r_categories[i] ~= nil) do
                mcat = mcat .. "[[Catégorie:" .. donnees.defauts.r_categories[i] .. "]]"
                i = i + 1
            end
        end
    end
    
    -- si besoin et selon les modalités on insert les catégories d'erreur
    local mcaterr = ""
    if (donnees.defauts.r_err_categories[1] ~= nil and donnees.defauts.c_err_categories ~= "non") then
        dg = dg .. "Il y a des catégories d'erreur<br/>"
        -- selon le mode
        if (donnees.defauts.c_err_categories == "boîte" or (donnees.defauts.c_err_categories == "auto" and donnees.defauts.article == false)) then
            mcaterr = mcaterr .. fnc.box_ouverture("Catégorie d'erreur(s)")
            mcaterr = mcaterr .. fnc.box_texte(donnees.defauts.r_err_categories, true)
            mcaterr = mcaterr .. fnc.box_fermeture()
        elseif (donnees.defauts.c_err_categories == "en ligne" or (donnees.defauts.c_err_categories == "auto" and donnees.defauts.article == true)) then
            -- insertion "en vrai"
            local i = 1
            while (donnees.defauts.r_err_categories[i] ~= nil) do
                mcat = mcat .. "[[Catégorie:" .. donnees.categorie_erreur .. "|" .. donnees.defauts.r_categories[i] .. "]]"
                i = i + 1
            end
        end
    end
    
    -- si besoin et selon les modalités on insert les erreurs
    local merr = ""
    if (donnees.defauts.r_erreurs[1] ~= nil and donnees.defauts.c_erreurs ~= "non") then
        -- selon le mode
        if (donnees.defauts.c_erreurs == "boîte" or (donnees.defauts.c_erreurs == "auto" and donnees.defauts.article == false)) then
            merr = merr .. fnc.box_ouverture("Erreur(s)")
            merr = merr .. fnc.box_texte(donnees.defauts.r_erreurs)
            merr = merr .. fnc.box_fermeture()
        end
    end

    -- terminé, on retourne le tout
    return p.prepare_sortie(tb .. mtitre .. mstitre .. mcat .. mcaterr .. merr, dg)
end


-- cherche le | suivant, en ignorant ceux dans des modèles ou wikiliens
-- retourne une position (utf8)
function p.pipe_suivant(ligne, posr)
    local pos = posr
    local cnt = 0
    while (true) do
        local c = mw.ustring.sub(ligne, pos, pos)
        if (c == nil or c == "") then
            if (pos <= posr) then
                return nil
            else
                return pos
            end
        end
        -- on comptabilise les entrées/sorties de modèle/lien
        if (c == "[" or c == "{") then
            cnt = cnt + 1
        elseif (c == "]" or c == "}") then
            cnt = cnt - 1
        elseif (c == "|") then
            -- si | on retourne la position que si cnt = 0
            if (cnt == 0) then
                return pos
            end
        end
        pos = pos + 1
    end
    -- on ne vient pas là
    return nil
end

-- découpe selon les | et range dans une table
function p.decoupe_pipe(ligne)
    local tbl = {}
    local pos
    local courant = 1

    pos = z.pipe_suivant(ligne, 1)
    if (pos == nil) then
        table.insert(tbl, ligne) -- un seul élément
        return tbl
    end

    while (pos ~= nil) do
        -- on recupere de "courant" à pos
        local tmp = mw.ustring.sub(ligne, courant, pos-1)
        table.insert(tbl, tmp)
        courant = pos + 1

        -- on recupere la partie suivante
        pos = p.pipe_suivant(ligne, courant)
    end

    return tbl
end


--[[
  Découpe un modèle en ses paramètres
--]]
function p.decoupe_modele(modele)
    
    
end


--[[
  Prend en paramètre un modèle modulaire, et retourne sa version monolytique
--]]
function p.mono_vers_modu(modele)
    
    return ""
end






-- on retourne le module
return p