Module:Œuvre/Bac à sable

La bibliothèque libre.

La documentation pour ce module peut être créée à Module:Œuvre/Bac à sable/Documentation

-- Créé à partir :
-- * du module Opera sur it.wikisource, récupéré le 12 mai 2022
--   (https://it.wikisource.org/w/index.php?title=Modulo:Opera&oldid=2764861),
-- * de [[Module:Auteur2]]

-- Pour faire des tests : en prévisualisation, utiliser la console de débogage 
-- (tout en bas de cette page) avec des codes tels que ceux qui suivent.
--   =p.main("Q170583")  -- renvoit le code wiki/HTML généré
--   data = p.getDonneesOeuvre("Q170583") -- renvoit une structure contenant les données
--   mw.logObject(data)
-- Pour obtenir un item Wikidata :
--   item = mw.wikibase.getEntity("Q170583")

local p = {}

-- Codes des langues considérées comme relevant du français (complémentaire de 
-- Module:Œuvre/Langue française qui donne les identifiants Wikidata de ces langues)
local langues_fr_codes = {
  'fr', -- français
  'fro', -- Q35222, ancien français
  'frm', -- Q1473289, moyen français
  -- pas de code Wikimédia pour le français classique (Q3100376)
}

function p.errorMessage(text)
    -- Return a html formated version of text stylized as an error.
    local html = mw.html.create('div')
    html:addClass('error')
        :wikitext(text)
        :wikitext('[[Catégorie:Pages faisant un appel erroné au modèle Œuvre]]')
    return tostring(html)
end

local function has_value (tab, val)
    for index, value in ipairs(tab) do
        if value == val then
            return true
        end
    end
    return false
end

local function tablelength(T)
  local count = 0
  for _ in pairs(T) do count = count + 1 end
  return count
end

-- Améliore la présentation typographique d'un texte pour l'affichage
function p.printable(s)
    return string.gsub(s or "", "'", "’")
end

toromain = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X",
    "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX",
    "XXI", "XXII"}

-- Améliore la présentation des dates
function p.printableDate(s)
    res = s
    res = string.gsub(res, "(%d+)e siècle", function(d) 
            if toromain[tonumber(d)] then
                d = toromain[tonumber(d)]
            end
            return mw.getCurrentFrame():expandTemplate{title="s", args = {d}}
        end)
    return res
end

-- Met la première lettre en capitales
function p.capitaliser(s)
    if s and type(s) == "string" then 
       if string.len(s) > 1 then
          return string.upper(string.sub(s, 1, 1)) .. string.sub(s, 2)
       else
          return string.upper(s)
       end
    else
       return s
    end
end

-- Transcrit en lettres les nombres inférieurs ou égaux à 10
nb2lettres = {"zéro", "un", "deux", "trois", "quatre", "cinq", 
    "six", "sept", "huit", "neuf", "dix"}
function p.nombre2lettres(nb)
    if((type(nb) == "number") == false) then
        return p.errorMessage("nombre2lettres doit recevoir un nombre et non " .. nb)
    end
    if(nb2lettres[nb+1]) then
        return nb2lettres[nb+1]
    else
        return nb
    end
end

function p.isInteger(str)
  return not (str == "" or str:find("%D"))
end


-- Renvoit la valeur d'une propriété d'un item (lorsque la valeur n'est pas elle-même une structure),
-- (première valeur du statement uniquement)
function p.get_property_value(item, property_id)
    statements = item:getBestStatements(property_id)
    if #statements and statements[1] and statements[1].mainsnak.datavalue then
         return statements[1].mainsnak.datavalue.value
    end
end

-- Renvoit toutes les valeurs d'une propriété d'un item
function p.get_property_values(item, property_id)
    statements = item:getBestStatements(property_id)
    res = {}
    for _,statement in pairs(statements) do
        if statement.mainsnak and statement.mainsnak.datavalue then
            table.insert(res, statement.mainsnak.datavalue.value)
        end
    end
    return res
end

-- Renvoit le texte d'une propriété d'un item, sans vérifier la langue et 
-- (première valeur du statement uniquement)
function p.get_property_value_text(item, property_id)
    statements = item:getBestStatements(property_id)
    if #statements and statements[1] and statements[1].mainsnak.datavalue then
         return statements[1].mainsnak.datavalue.value.text
    end
end

-- Renvoit la valeur en français d'une propriété d'un item
-- (première valeur en français du statement uniquement)
function p.get_property_value_text_fr(item, property_id)
    statements = item:getBestStatements(property_id)
    for _,statement in pairs(statements) do
        if statement.mainsnak and statement.mainsnak.datavalue then
            value = statement.mainsnak.datavalue.value
            if value.language and has_value(langues_fr_codes, value.language) then
                return value.text
            end
         end
    end
    return nil
end

-- Renvoit le label de l'item désigné par une propriété d'un autre item,
-- (première valeur du statement uniquement)
function p.get_property_value_label(item, property_id)
    statements = item:getBestStatements(property_id)
    if #statements and statements[1] and statements[1].mainsnak.datavalue then
         return mw.wikibase.getLabel(statements[1].mainsnak.datavalue.value.id)
    end
end

-- Renvoit les labels des statements, séparés par un séparateur, avec des liens
-- le cas échéant
function p.get_property_values_labels_join(item, property_id, separateur)
    if separateur == nil then
        separateur = ", "
    end
    statements = item:getBestStatements(property_id)
    res = ""
    for _,statement in pairs(statements) do
        if statement.mainsnak and statement.mainsnak.datavalue then
            value_id = statement.mainsnak.datavalue.value.id
            sitelink = mw.wikibase.getSitelink(value_id)
            label = mw.wikibase.getLabel(value_id)
            ajout = nil
            if label then
                if sitelink then
                    ajout = "[[" .. sitelink .. "|" .. label .. "]]"
                else
                    ajout = label
                end
            else
                if sitelink then
                    ajout = "[[" .. sitelink .. "|]]" 
                end
            end
            if ajout then
                if string.len(res) > 0 then 
                    res = res .. separateur 
                end
                res = res .. ajout
            end
        end
    end
    return res
end
 
-- Rappel : précisions
-- 0 — milliard d’années, 1 — centaine de million d’années, …, 6 — millénaire, 
-- 7 — siècle, 8 — décennie, 9 — année, 10 — mois, 11 — jour, 12 — heure, 13 — minute, 14 — seconde

-- Renvoit le siècle à partir de l'année
function p.computeCenturyFromYear(year)
    if year >= 0 then
        return math.ceil(year / 100)
    else
        return -math.ceil(-year / 100)
    end
end

-- Renvoit le siècle en chiffres romains
function p.getTextForCentury(century, withHtml)
    local romanNumbers1 = {'', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X'}
    local romanNumbers2 = {'', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'}

    local text = romanNumbers2[math.floor(math.abs(century) / 10) + 1] .. romanNumbers1[math.floor(math.abs(century) % 10) + 1]
    if century == 1 or century == -1 then
         term = 'er'
    else
         term = 'e'
    end
    if withHtml then
        text = text .. '<sup>' .. term .. '</sup>'
    else
        text = text .. term
    end
    if century > 0 then
        return text .. ' siècle'
    else
        return text .. ' siècle av. J.-C.'
    end
end

function p.getTextForYear(year)
    local text = math.abs(year)
    if year < 0 then
        text = text .. ' av. J.-C.'
    end
    return text
end

function p.getDateFromTimeStatements(statements, field)
    if #statements == 0 then
        return {
            text = "",
            precision = 0
        }
    end

    local time = nil
    for _, statement in pairs(statements) do
        local newTime = p.getDateFromTimeStatement(statement, field)
        if time == nil then
            time = newTime
        elseif time.year ~= newTime.year then --années contradictoires
            mw.addWarning('Plusieurs années'  .. (field and ' de ' .. field or '') .. ' possibles sur Wikidata. Une manière simple de résoudre se problème est de mettre la date à afficher au rang "préféré".')
        end
    end

    if time == nil then
        return {
            text = "",
            precision = 0
        }
    end

    return time
end

function p.parseWbTime(value)
    local _,_, year = string.find(value.time, '([%+%-]%d%d%d+)%-')
    year = tonumber(year)
    
    return {
        year = year,
        century = p.computeCenturyFromYear(year),
        text = nil,
        precision = value.precision
    }
end

function p.getDateFromDataValue(datavalue, field)
    struct = p.parseWbTime(datavalue.value)
    local prefix = struct.prefix or ''

    --Create text
    if struct.precision >= 9 then
        struct.text = prefix .. p.getTextForYear(struct.year)
    elseif struct.precision == 8 then
        struct.text = prefix .. p.getTextForYear(struct.year)
    elseif struct.precision == 7 then
        struct.text = prefix .. p.getTextForCentury(struct.century, true)
    else
        struct.text = p.errorMessage('La date de ' .. field .. ' a une précision trop faible sur Wikidata')
    end
    
    return struct
end

function p.getDateFromTimeStatement(statement, field)
    local struct = {
        year = nil,
        century = nil,
        text = nil,
        precision = 0
    }
    local snak = statement.mainsnak
    if snak.snaktype == 'novalue' then
        return struct
    end
    if snak.snaktype == 'somevalue' then
        struct.text = '??'
        return struct
    end

    struct = p.getDateFromDataValue(snak.datavalue)

    if statement.qualifiers ~= nil then
        --Extract circa
        if statement.qualifiers.P1480 ~= nil then
            for _,qualifier in pairs(statement.qualifiers.P1480) do
                if qualifier.datavalue.value.id == 'Q5727902' then
                    prefix = 'vers '
                    struct.precision = 8 --TODO: hacky
                end
            end
        end

        -- Date de début et de fin
        if struct.precision <= 8 then
            if statement.qualifiers.P580 ~= nil then
                startdate = p.getDateFromDataValue(statement.qualifiers.P580[1].datavalue)
            end
            if statement.qualifiers.P582 ~= nil then
                enddate = p.getDateFromDataValue(statement.qualifiers.P582[1].datavalue)
            end
            if startdate and enddate then
                struct.text = startdate.year .. "-" .. enddate.year
                -- struct.text = mw.wikibase.renderSnaks(statement.qualifiers)
            end
        end

        --Use before and after if precision <= century
        if struct.precision <= 7 then
            if statement.qualifiers.P1319 ~= nil then
                for _,qualifier in pairs(statement.qualifiers.P1319) do
                    struct = p.parseWbTime(qualifier.datavalue.value)
                    struct.prefix = 'après '
                    struct.precision = 8 --TODO: hacky
               end
            elseif statement.qualifiers.P1326 ~= nil then
                for _,qualifier in pairs(statement.qualifiers.P1326) do
                    struct = p.parseWbTime(qualifier.datavalue.value)
                    struct.prefix = 'avant '
                    struct.precision = 8 --TODO: hacky
                end
            elseif statement.qualifiers.P1317 ~= nil then
                for _,qualifier in pairs(statement.qualifiers.P1317) do
                    struct = p.parseWbTime(qualifier.datavalue.value)
                    struct.prefix = 'floruit '
                    struct.precision = 8 --TODO: hacky
                end
           end
        end
    end

    return struct
end

function p.addCat(donnees, name)
    -- TODO si on crée un namespace Œuvre : vérifier le namespace (mw.title.getCurrentTitle().nsText) 
    if name then
        cattxt = '[[' .. mw.site.namespaces[14].name .. ':' .. name 
        cattxt = cattxt .. ']]'
        donnees.categories = donnees.categories .. cattxt 
    end
end

-- renvoit le nom du ou des traducteurs et éditeurs d'une édition.
function p.getTraducteursEditeursFromStatement(statement)
    res = ''
    premier = 0
    
    if statement.mainsnak and statement.mainsnak.datavalue then
        itemid = statement.mainsnak.datavalue.value.id

        -- traducteur
        traducteurs = mw.wikibase.getBestStatements(itemid, 'P655')
        for k, personne in pairs(traducteurs) do
            if personne.mainsnak and personne.mainsnak.datavalue then
                personneid = personne.mainsnak.datavalue.value.id
                name = mw.wikibase.getLabel(personneid)
                if name then
                    sitelink = mw.wikibase.getSitelink(personneid)
                    if premier == 0 then 
                        premier = 1
                        if string.len(res) > 0 then res = res .. ', ' end
                        res = res .. "trad. "
                    else 
                        if k == 1 then res = res .. " trad. " end 
                        res = res .. ", " 
                    end
                    if sitelink then 
                        res = res .. '[[' .. sitelink .. '|' .. name .. ']]'
                    else
                        res = res .. name
                    end
                end
            end
        end

        -- éditeur scientifique
        editeurs = mw.wikibase.getBestStatements(itemid, 'P98')
        for _, editeur in pairs(editeurs) do
            if editeur.mainsnak and editeur.mainsnak.datavalue then
                editeurid = editeur.mainsnak.datavalue.value.id
                name = mw.wikibase.getLabel(editeurid)
                if string.len(res) > 0 then res = res .. ', ' end
                res = res .. "éd. " .. name
            end
        end
    end
    return res
end

-- Trie les éditions par ordre chronologique
function p.sort_editions(a, b)
    if a.pubdat and a.pubdat.precision > 0 and b.pubdat and b.pubdat.precision > 0 then
        return (a.pubdat.year < b.pubdat.year)
    else
        return a.id < b.id
    end
end

-- Fonction utilitaire pour trier une table selon l'ordre des clés (http://lua-users.org/wiki/SortedIteration)
function __genOrderedIndex( t )
    local orderedIndex = {}
    for key in pairs(t) do
        table.insert( orderedIndex, key )
    end
    table.sort( orderedIndex )
    return orderedIndex
end

function orderedNext(t, state)
    -- Equivalent of the next function, but returns the keys in the alphabetic
    -- order. We use a temporary ordered key table that is stored in the
    -- table being iterated.

    local key = nil
    --print("orderedNext: state = "..tostring(state) )
    if state == nil then
        -- the first time, generate the index
        t.__orderedIndex = __genOrderedIndex( t )
        key = t.__orderedIndex[1]
    else
        -- fetch the next value
        for i = 1,table.getn(t.__orderedIndex) do
            if t.__orderedIndex[i] == state then
                key = t.__orderedIndex[i+1]
            end
        end
    end

    if key then
        return key, t[key]
    end

    -- no more value to return, cleanup
    t.__orderedIndex = nil
    return
end

function orderedPairs(t)
    -- Equivalent of the pairs() function on tables. Allows to iterate
    -- in order
    return orderedNext, t, nil
end

-- Renvoit un lien d'édition de propriété sur Wikidata (icône stylo)
function p.dataEdit(itemId, propertyId)
    local edit_message = mw.message.new('vector-view-edit'):plain()
    res = '&nbsp;[[File:OOjs UI icon edit-ltr.svg|' .. edit_message .. '|12px|baseline|class=noviewer|link=https://www.wikidata.org/wiki/' .. itemId
    if propertyId then
        res = res .. '#' .. propertyId 
    end
    res = res .. ']]'
    return res
end

-- Traitement intelligent des prépositions
function p.auteurAblatif(nom, html)
    if string.match(nom:sub(1,1):lower(), '[aâeéêèiîoôuuy]') then
        return "d’"
    else
        if html then
            return "de&nbsp;"
        else
            return "de "
        end
    end
end

-- Renvoit le code wiki pour une édition
function p.print_edition(edition_info)
    if edition_info.label and string.len(edition_info.label) > 0 then
		intitule = p.printable(edition_info.label)
    elseif edition_info.titre and string.len(edition_info.titre) > 0 then
		intitule = p.printable(edition_info.titre)
		if edition_info.sousTitre and string.len(edition_info.sousTitre) > 0 then
			postIntitule = ", " .. edition_info.sousTitre
		end
	else
		intitule = ""
	end

	-- Icône pour l'avancement
	q_badges = {
		["Q28064618"] = nil,  -- libellé Wikidata : document numérique
		["Q20748094"] = nil,  -- libellé Wikidata : texte incomplet 
		["Q20748091"] = "3/4",  -- libellé Wikidata : texte non corrigé
		["Q20748092"] = "4/4",  -- libellé Wikidata : texte relu et corrigé
		["Q20748093"] = "validé" -- libellé Wikidata : texte validé
	}
	avancement = nil
	if edition_info.sitelinks and edition_info.sitelinks["frwikisource"] and edition_info.sitelinks["frwikisource"]["badges"] then
		badges = edition_info.sitelinks["frwikisource"]["badges"]
		for _, badge in pairs(badges) do
			if q_badges[badge] then
				avancement = mw.getCurrentFrame():expandTemplate{
					title = q_badges[badge], 
					args = {}
				}
			end
		end
	end

    -- Intitulé avec lien vers le fac-similé
	lien = edition_info.lien or label
	wspagename = edition_info.wikisourcePageName
	facsimiles = edition_info.facsimiles
	fs_main = nil
	if #facsimiles == 1 then
		fs_main = facsimiles[1]
	end
	if edition_info.wikisourceLang == "fr" and fs_main and fs_main.mainsnak and fs_main.mainsnak.datavalue and wspagename then
		-- Utilisation du modèle L2S si un fac-similé a priorité sur les autres
		indexfile = 'Livre:' ..  fs_main.mainsnak.datavalue.value
		desc = mw.getCurrentFrame():expandTemplate{
			title = "Livre2Scanné", 
			args = {wspagename, indexfile, intitule}
		}
	else
		desc = ""
		if edition_info.wikisourceLang == "fr" then
			-- Liens vers les fac-similés, aucun n'étant privilégié
			for k, facsimile in pairs(facsimiles) do 
				if facsimile.mainsnak and facsimile.mainsnak.datavalue then
					if k > 1 then desc = desc .. ", " end
                    if #facsimiles > 1 then 
                        idx = "" .. k 
                    else 
                        idx = ""
                    end                   
					desc = desc .. "<span class='imagelink'>[[Image:Open book nae 02.svg|20px|link=Livre:"
                    desc = desc .. facsimile.mainsnak.datavalue.value .. "|alt=index" .. idx  .. "]]</span>"
                    desc = desc .. "<span style='font-size:small'>" .. idx .. "</span>"
				  end
			end
		end
		if string.len(desc) > 0 then desc = desc .. " " end
		-- Sinon, simple liste des fac-similes
		if edition_info.wikisourcePageName then
			if intitule and #intitule > 0 then
				s_intitule = intitule
			else
				s_intitule = edition_info.wikisourcePageName 
			end
			desc = desc .. '[[:' .. edition_info.wikisourceLang .. ":" .. edition_info.wikisourcePageName .. "|" .. s_intitule .. ']]'
		else
			if intitule and #intitule > 0 then
				s_intitule = intitule
			else
				s_intitule = "(sans intitulé)"
			end
			desc = desc .. s_intitule
		end
	end
	if postIntitule then desc = desc .. " " .. postIntitule end
	
	-- Informations sur l'édition
	elem_desc = ""
	if edition_info.numeroEdition and p.isInteger(edition_info.numeroEdition) then
		if string.len(elem_desc) > 0 then elem_desc = elem_desc .. ", " end
		elem_desc = elem_desc .. edition_info.numeroEdition .. "<sup>e</sup> édition"
	end
	if string.len(edition_info.maisonEdition) > 0 then
		if string.len(elem_desc) > 0 then elem_desc = elem_desc .. ", " end
		elem_desc = elem_desc .. edition_info.maisonEdition
	end
	if edition_info.pubdat and string.len(edition_info.pubdat.text) > 0 and edition_info.pubdat.precision > 0 then
		if string.len(elem_desc) > 0 then elem_desc = elem_desc .. ", " end
		elem_desc = elem_desc .. edition_info.pubdat.text
	end
	if string.len(edition_info.traducteursEditeurs) > 0 then
		if string.len(elem_desc) > 0 then elem_desc = elem_desc .. ", " end
		elem_desc = elem_desc .. edition_info.traducteursEditeurs
	end
	if string.len(edition_info.publieDans) > 0 then
		if string.len(elem_desc) > 0 then elem_desc = elem_desc .. ", " end
		elem_desc = elem_desc .. "publié dans " .. edition_info.publieDans
	end
	if string.len(elem_desc) > 0 then desc = desc .. " (" .. elem_desc .. ")" end
	if fs_main == nil then
		if #facsimiles == 0 then
			-- On ne met le lien vers Gallica que si on n'a pas encore de fac-similé
			if edition_info.gallicaids and #edition_info.gallicaids > 0 then
				desc = desc .. " — "
				if #edition_info.gallicaids == 1 then
					desc = desc .. "[https://gallica.bnf.fr/ark:/12148/" .. edition_info.gallicaids[1] .. " Gallica]"
				else
					idx_gallica = 1
					desc = desc .. "Gallica "
					for _, gallicaid in pairs(edition_info.gallicaids) do
						-- amélioration possible : récupérer les qualificatifs, genre "nom de volume"
						if idx_gallica > 1 then desc = desc .. ", " end
						desc = desc .. "[https://gallica.bnf.fr/ark:/12148/" .. gallicaid .. " " .. idx_gallica .. "]"
						idx_gallica = idx_gallica + 1
					end
				end
			end
		end
	end
	if avancement then
		desc = desc .. " " .. avancement
	end
	desc = desc .. " " .. p.dataEdit(edition_info.id)
    return desc
end


-- Récupère toutes les données de l'oeuvre
function p.getDonneesOeuvre(item)

    -- Pour débugguer
    if type(item) == "string" then
        -- ex. "Q512807"
        item = mw.wikibase.getEntity(item) 
    end

    donnees = {}
    item_id = item:getId()

    local langueFrancaiseModule = mw.getCurrentFrame():getTitle() .. '/Langue française'
    local itemsLangueFrancaise = require(langueFrancaiseModule)

    -- On vérifie d'abord que le modèle n'a pas été appliqué par 
    -- erreur sur une édition
    natures = item:getBestStatements('P31')
    for _,nature in pairs(natures) do
        if nature.mainsnak and nature.mainsnak.datavalue and nature.mainsnak.datavalue.value.id == 'Q3331189' then
            donnees.erreur = "[[d:" .. item:getId() .. "|" .. item:getId() .. "]] (" .. item:getLabel() .. ") est une édition, pas une œuvre."
            return donnees
        end       
    end

    donnees.wikidata = item_id
    donnees.categories = ""

    -- interwikis de l'œuvre
    donnees.interwikis = {} 
    if item.sitelinks then
        for k,v in pairs(item["sitelinks"]) do
            if string.sub(k, 3) == "wikisource" then 
                local langue = string.sub(k, 0, 2)
                local titre = item["sitelinks"][k]["title"]
                donnees.interwikis[langue] = titre
            end
        end
    end

    -- le label de l'item sur Wikidata    
    donnees.label = p.printable(item:getLabel())

    -- le titre de la page, en retirant le nom de l'auteur et d'autres choses
    donnees.titre = mw.title.getCurrentTitle().text
    donnees.titre = p.printable(string.gsub(donnees.titre, ' %(.*%)', ''))
    
    -- la langue de l'œuvre
    langues = item:getBestStatements('P407')
    lst_langues = {}
    if langues and #langues >= 1 then
        for _, langue in pairs(langues) do
            if langue.mainsnak and langue.mainsnak.datavalue then
                langue_value = langue.mainsnak.datavalue.value
                table.insert(lst_langues, mw.wikibase.getLabel(langue_value.id))
            end
        end
    end
    donnees.langues = lst_langues
    
    -- Titre original de l'œuvre
    tous_titres = item:getBestStatements('P1476')
    for _, titre in pairs(tous_titres) do
        if titre.mainsnak and titre.mainsnak.datavalue then
            -- On considère que la première valeur "préférée" suffit (cf. doc de P1476)
            -- Le problème est toutefois que cette propriété n'a pas un sens clair. 
            -- On pourrait éventuellement la prendre seulement lorsqu'elle est dans la langue
            -- originale de l'ouvrage, mais je ne suis pas sûr que ça vaille le coup.
            titre_value = titre.mainsnak.datavalue.value
            donnees.titreOriginal = p.printable(titre_value.text)
            donnees.titreOriginalLangue = titre_value.language
            donnees.titreOriginalEditer = p.dataEdit(item_id, 'P1476')
            break
        end
    end
    
    -- Sous-titre
    donnees.sousTitre = p.printable(p.get_property_value_text(item, 'P1680'))
    -- donnees.sousTitre = c.getClaimValue(c.getSingleClaimByProperty(item, 'P1680'))
    
    -- Forme (roman, etc.) de l'oeuvre
    donnees.forme = p.printable(p.get_property_value_label(item, 'P7937'))

    -- Auteurs
    auteurs = item:getBestStatements('P50')
    listeAuteurs = {}
    listeLiensAuteurs = {}
    ablatif = nil
    for _, auteur in pairs(auteurs) do
        if auteur.mainsnak and auteur.mainsnak.datavalue then
            auteur_id = auteur.mainsnak.datavalue.value.id

            labelAuteur = mw.wikibase.getLabel(auteur_id) or "Anonyme"
            table.insert(listeAuteurs, labelAuteur)

            lien = mw.wikibase.sitelink(auteur_id)
            if lien then
                lienAuteur = '[[' .. mw.wikibase.sitelink(auteur_id) .. '|' .. labelAuteur .. ']]'
            else
                lienAuteur = labelAuteur
            end
            table.insert(listeLiensAuteurs, lienAuteur)
        end
    end
    auteursHorsWikidata = item:getBestStatements('P2093')
    for _, auteur in pairs(auteursHorsWikidata) do
        if auteur.mainsnak and auteur.mainsnak.datavalue then
            table.insert(listeAuteurs, auteur.mainsnak.datavalue.value)
            table.insert(listeLiensAuteurs, auteur.mainsnak.datavalue.value)
        end
    end
    donnees.auteur = p.printable(mw.text.listToText(listeLiensAuteurs, ', ', ' et '))
    if #listeAuteurs > 0 then 
        donnees.auteurAblatif = p.auteurAblatif(listeAuteurs[1], true)
    else
        donnees.auteurAblatif = "de "
    end

    -- Genre : pas très intéressant me semble-t-il
    -- donnees.genre = string.lower(c.concat(c.getLabelsFromPropertyValues(item, 'P136'), ', '))
    
    -- Date de publication
    datePublication = item:getBestStatements('P577')
    if datePublication and #datePublication > 0 and datePublication[1].mainsnak then        
        -- donnees.datePublication = p.getDateFromTimeStatement(datePublication[1]).text
        donnees.datePublication = p.printableDate(mw.wikibase.renderSnak(datePublication[1].mainsnak))
        donnees.anneePublication = p.getDateFromTimeStatement(datePublication[1]).year
    end 
    
    -- Composition
    dateDebut = item:getBestStatements('P580')
    if dateDebut and #dateDebut > 0 and dateDebut[1].mainsnak then
        donnees.dateDebut = p.printableDate(p.getDateFromTimeStatements(dateDebut, nil).text)
    end 

    dateFin = item:getBestStatements('P582')
    if dateFin and #dateFin > 0 and dateFin[1].mainsnak then
        donnees.dateFin = p.printableDate(p.getDateFromTimeStatements(dateFin, nil).text)
    end 

    -- date de fondation ou de création 
    dateCreation = item:getBestStatements('P571')
    if dateCreation and #dateCreation > 0 and dateCreation[1].mainsnak then
        donnees.anneeCreation = p.getDateFromTimeStatement(dateCreation[1]).year
        donnees.dateCreation = p.printableDate(p.getDateFromTimeStatements(dateCreation, nil).text)
    end 
    
    -- date de dissolution ou de démolition (opposé de dateCreation)
    -- Voir https://www.wikidata.org/wiki/Property:P571#P8882 pour les propriétés opposées à dateCreation
    dat = item:getBestStatements('P576')
    if dat and #dat > 0 and dat[1].mainsnak then
        donnees.dateDissolution = p.printableDate(p.getDateFromTimeStatements(dat, nil).text)
    end

    -- date de fin de disponibilité (opposé de dateCreation)
    dat = item:getBestStatements('P2669')
    if dat and #dat > 0 and dat[1].mainsnak then
        donnees.dateFinDisponibilite = p.printableDate(p.getDateFromTimeStatements(dat, nil).text)
    end

    -- Première représentation (pièce de théâtre)
    datePremiereRepr = item:getBestStatements('P1191')
    if datePremiereRepr and #datePremiereRepr > 0 and datePremiereRepr[1].mainsnak then
        donnees.anneePremiereRepr = p.getDateFromTimeStatement(datePremiereRepr[1]).year
        donnees.datePremiereRepr = p.printableDate(p.getDateFromTimeStatements(datePremiereRepr, nil).text)
    end 
    
    -- Identifiant dans le catalogue de la BNF
    bnfid = item:getBestStatements('P268')
    if bnfid and #bnfid > 0 and bnfid[1].mainsnak and bnfid[1].mainsnak.datavalue then
        donnees.bnfid = bnfid[1].mainsnak.datavalue.value
    end

 
    -- Sujet principal
    donnees.arguments = {}
    arguments = item:getBestStatements('P921')
    if #arguments and arguments[1] and arguments[1].mainsnak.datavalue then
         table.insert(donnees.arguments,
                      mw.wikibase.getLabel(arguments[1].mainsnak.datavalue.value.id))
    end
        
    -- Mouvement
    donnees.mouvements = {}
    mouvements = item:getBestStatements('P135')
    if #mouvements and mouvements[1] and mouvements [1].mainsnak.datavalue then
         table.insert(donnees.mouvements,
                      mw.wikibase.getLabel(mouvements [1].mainsnak.datavalue.value.id))
    end

    -- Série
    serie = item:getBestStatements("P179")
    if #serie and serie[1] and serie[1].mainsnak.datavalue then
        serie_item = mw.wikibase.getEntity(serie[1].mainsnak.datavalue.value.id)
        donnees.serie = serie_item:getLabel()
        donnees.lienSerie = serie_item:getSitelink()
    end
    
    -- Fait partie de
    -- donnees.partieOeuvre = c.getClaimValuesByProperty('P361')
    -- donnees.partieOeuvre = c.getLinksParteOpere(donnees.partieOeuvre )
    
    -- Incipit de l'œuvre
    donnees.incipit = p.get_property_value_text_fr(item, 'P1922')
    
    -- Liens vers autres projets
    donnees.wikipedia = item:getSitelink('frwiki')
    donnees.wikiquote = item:getSitelink('frwikiquote')
    donnees.wikibooks = item:getSitelink('frwikibooks')
    donnees.commons = item:getSitelink('commonswiki')
    donnees.commonscat = p.get_property_value(item, 'P373')
    
    -- Image
    donnees.image = p.get_property_value(item, 'P18')
    
    -- Éditions
    donnees.editions = item:getBestStatements('P747')
    donnees.editions_editer = p.dataEdit(item_id, 'P747')
    editions_info = {}
	editions_info_autreslangues = {}
    
    donnees.listeEditionsEnFrancais = ''
    donnees.listeEditionsAutresLangues = ''
    donnees.nbEditionsEnFrancais = 0
    donnees.nbEditionsAutresLangues = 0
    
    -- identifiants Wikidata des éléments assimilables à la langue française
    -- pour ce qui concerne Wikisource-fr
    for _, edition in pairs(donnees.editions) do
        if edition.mainsnak and edition.mainsnak.datavalue then
            edition_id = edition.mainsnak.datavalue.value.id
            edition_item = mw.wikibase.getEntity(edition_id)
            langues = edition_item:getBestStatements('P407')
            -- On se limite aux éditions en français
            est_francais = false
            afficher_edition = false
            wikisourcePageName = nil
            wikisourceLang = nil
            if langues and #langues > 0 then
                for _, langue in pairs(langues) do
                    if langue.mainsnak and langue.mainsnak.datavalue and has_value(itemsLangueFrancaise, langue.mainsnak.datavalue.value.id) then
                         est_francais = true
                         afficher_edition = true 
                         wikisourcePageName = edition_item:getSitelink('frwikisource')
                         wikisourceLang = "fr"   
                         break
                    end
                end
            end
            if est_francais == false then
                sitelinks = mw.wikibase.getEntity(edition_id)["sitelinks"]
                if sitelinks and tablelength(sitelinks) > 0 then
                    for index, value in pairs(sitelinks) do
                        fin_lang = string.find(value["site"], "wikisource")
                        if fin_lang then
                            wikisourceLang = string.sub(value["site"], 1, fin_lang-1)
                            wikisourcePageName = value["title"]
                            afficher_edition = true
                            break
                        end
                    end
                end
            end
            if afficher_edition then
                edition_info = {
                    ["id"] = edition_id,                 
                    ["lien"] = edition_item:getSitelink(),
                    ["label"] = edition_item:getLabel(),
                    ["titre"] = p.get_property_value_text_fr(edition_item, 'P1476'),
                    ["sousTitre"] = p.get_property_value_text_fr(edition_item, 'P1680') ,
                    ["wikisourcePageName"] = wikisourcePageName,
                    ["wikisourceLang"] = wikisourceLang,
                    ["pubdat"] = p.getDateFromTimeStatements(edition_item:getBestStatements('P577')),
					["numeroEdition"] = p.get_property_value(edition_item, 'P393'),
                    ["maisonEdition"] = p.get_property_values_labels_join(edition_item, "P123", ", "),
                    ["traducteursEditeurs"] = 
                        p.printable(p.getTraducteursEditeursFromStatement(edition)),
                    ["publieDans"] = 
                        p.printable(p.get_property_values_labels_join(edition_item, 'P1433')),
                    ["facsimiles"] = edition_item:getBestStatements('P996'),
                    ["gallicaids"] = p.get_property_values(edition_item, 'P4258'),
                    ["sitelinks"] = edition_item["sitelinks"],
                }
				if wikisourceLang == "fr" then
                    table.insert(editions_info, edition_info)
				else
                    if not editions_info_autreslangues[wikisourceLang] then
                        editions_info_autreslangues[wikisourceLang] = {} 
                    end
                    table.insert(editions_info_autreslangues[wikisourceLang], edition_info)
				end
            end
        end
    end
    for k, v in pairs(donnees.interwikis) do 
        if not editions_info_autreslangues[k] then
            editions_info_autreslangues[k] = {}
        end
    end
    table.sort(editions_info, p.sort_editions)
    for _, edition_info in pairs(editions_info) do
        desc = p.print_edition(edition_info)       
        donnees.listeEditionsEnFrancais = donnees.listeEditionsEnFrancais .. '* ' .. desc .. "\n"
    end
	donnees.listeEditionsAutresLangues = ""
    for wslangue, editions_infos in orderedPairs(editions_info_autreslangues) do
        local langue = mw.language.fetchLanguageName(wslangue, "fr")
        table.sort(editions_infos, p.sort_editions)
        local trouve = false
        local txt = ""
        txt = txt .. "En " .. langue .. " : "
        local interwiki = donnees.interwikis[wslangue]
        if interwiki and not(wslangue == "fr") then
            txt = txt .. " [[:" .. wslangue .. ":" .. interwiki .. "|" .. interwiki .. "]]" 
            trouve = true
        end
        txt = txt .. "\n"
        for _, edition_info in pairs(editions_infos) do
            desc = p.print_edition(edition_info)       
            txt = txt .. '* ' .. desc .. "\n"
            donnees.nbEditionsAutresLangues = donnees.nbEditionsAutresLangues + 1
            trouve = true
        end
        txt = txt .. "\n"
        if trouve then
            donnees.listeEditionsAutresLangues = donnees.listeEditionsAutresLangues .. txt
        end
    end
    donnees.nbEditionsEnFrancais = #editions_info
    donnees.nbEditionsEnFrancaisLettres = p.capitaliser(p.nombre2lettres(#editions_info))
    
    return donnees
end

function p.main(frame)
    -- Table de paramètres sans les paramètres vides
    local args = {}
    if type(frame) == "string" then
        args["item"] = frame -- ex. "Q512807"
    else
        for k,v in pairs(frame:getParent().args) do
            if v ~= '' then
                args[k] = v
           end
        end
    end

    -- Contenu de la page
    local contenu = args["contenu"]

    -- Argument à passer pour éviter l'affichage des catégories (ex. dans une page de test)
    local nocat = args["nocat"]

    -- Description à rajouter
	local description = args["description"]

    -- Pour forcer l'image (si l'image de Wikidata est trop large, par exemple)
    local image = args["image"]

    -- On peut passer l'argument "item" (ex. pour faire des tests)
    if args["item"] == nil then
        item = mw.wikibase.getEntityObject()
    else
        item = mw.wikibase.getEntity(args["item"])
    end

    if item == nil then
        -- Pas d'élément Wikidata
        output = "<div class=error>'''Erreur''' : le modèle Œuvre doit être utilisé sur une page reliée depuis un élément Wikidata.</div>"
        output = output .. '[[' .. mw.site.namespaces[14].name .. ':Pages appliquant le modèle Œuvre à une page sans élément Wikidata]]'
        return output
    end

    donneesOeuvre = p.getDonneesOeuvre(item)

    if donneesOeuvre.erreur then
        output = "<div class=error>'''Erreur''' : " .. donneesOeuvre.erreur .. "</div>"
        output = output .. '[[' .. mw.site.namespaces[14].name .. ':Pages appliquant le modèle Œuvre à une édition]]'
        return output
    end

    -- On peut désactiver l'affichage de la liste des éditions
    if args["éditions"] == "non" then
        donneesOeuvre["afficherEditions"] = nil
    else
        donneesOeuvre["afficherEditions"] = "oui"
    end 

    donnees = donneesOeuvre

    if nocat == "oui" then
        donnees.nocat = "oui"
    end

    if image then
        donnees.image = image
    end

    -- Catégorie générique
    p.addCat(donnees, "Œuvres") 

    -- Date de création
    if donnees.anneeCreation == nil then
        p.addCat(donnees, 'Œuvres sans date de création')
    else
        -- Siècle
        siecle = p.getTextForCentury(p.computeCenturyFromYear(donnees.anneeCreation))
        p.addCat(donnees, siecle) 
    end
    
    -- Langue originale de l'œuvre
    if #donnees.langues > 0 then 
        for _, langue in pairs(donnees.langues) do
            p.addCat(donnees, 'Œuvres de langue originale en '..langue) 
        end
    else
        p.addCat(donnees, 'Œuvres sans langue originale sur Wikidata')
    end
    
    -- Description à rajouter
    if description then
        donnees.description = description
	end

    -- Catégorie par argument ou mouvement artistique
    for _, arg in pairs(donnees.arguments) do p.addCat(donnees, arg) end
    for _, mov in pairs(donnees.mouvements) do p.addCat(donnees, mov) end

    -- Et le contenu de la page créé par l'utilisateur du modèle
    if contenu then 
        donnees.contenu = contenu 
    end

    -- On appelle le modèle « boîte » pour afficher toutes ces données
    cleanArgs = {}
    for i, v in pairs(donnees) do
        if type(v) ~= "table" then cleanArgs[i] = v end
    end
    local titre_modele = string.gsub(mw.getCurrentFrame():getTitle(), "Module", "Template")
    
    output = mw.getCurrentFrame():expandTemplate{title = titre_modele .. '/boîte', args = cleanArgs}

    return output 
end

function p.oeuvre( frame )
    return p.main( frame )
end

return p