Nuvola apps important.png Attention, suite à une faille de sécurité, les liens vers les serveurs d'exploration ont été désactivés. Des actions de régénération sont en cours et quelques serveurs sont à nouveau accessibles.

-

Module:Wikidata : Différence entre versions

De Wicri Informatique
imported>Jacques Ducloy
 
imported>Jacques Ducloy
m (1 révision importée)
 
Ligne 6 : Ligne 6 :
 
local modulesNames = {
 
local modulesNames = {
 
reference = 'Module:Wikidata/Références',
 
reference = 'Module:Wikidata/Références',
i18n = 'Module:Wikidata/I18n',
 
globes = 'Module:Wikidata/Globes',
 
langcodes = 'Module:Dictionnaire Wikidata/Codes langue', -- big, infrequently useda
 
invertedlangcodes = 'Module:Dictionnaire Wikidata/Codes langue/inversé',
 
 
 
linguistic = 'Module:Linguistique',
 
linguistic = 'Module:Linguistique',
 
formatDate = 'Module:Date complexe',
 
formatDate = 'Module:Date complexe',
Ligne 16 : Ligne 11 :
 
langmodule = 'Module:Langue',
 
langmodule = 'Module:Langue',
 
cite = 'Module:Biblio',
 
cite = 'Module:Biblio',
 +
getData = 'Module:Wikidata/Récup',
 +
entities = 'Module:Wikidata/Formatage entité',
 +
tools = 'Module:Wikidata/Outils',
 +
globes = 'Module:Wikidata/Globes',
 +
langcodes = 'Module:Dictionnaire Wikidata/Codes langue', -- gros et rarement utilisé
 
weblink = 'Module:Weblink'
 
weblink = 'Module:Weblink'
 
}
 
}
Ligne 28 : Ligne 28 :
 
setmetatable( modules, { __index = loadModule } )
 
setmetatable( modules, { __index = loadModule } )
  
local datequalifiers = {'P585', 'P571', 'P580', 'P582', 'P1319', 'P1326'}
+
local tools = require 'Module:Wikidata/Outils'
 
+
local translate = tools.translate
-- === I18n ===
 
 
local defaultlang = mw.getContentLanguage():getCode()
 
local defaultlang = mw.getContentLanguage():getCode()
  
function wd.translate(str, rep1, rep2)
+
function wd.getLabel(entity, args)
str = modules.i18n[str] or str
+
modules.entities.getLabel(entity)
if rep1 and (type (rep1) == 'string') then
 
str = str:gsub('$1', rep1)
 
end
 
if rep2 and (type (rep2) == 'string')then
 
str = str:gsub('$2', rep2)
 
end
 
return str
 
end
 
 
 
local function addCat(cat, sortkey)
 
if sortkey then
 
return  '[[Category:' .. cat .. '|' .. sortkey .. ']]'
 
end
 
return '[[Category:' .. cat  .. ']]'
 
end
 
 
 
local function formatError( key , category, debug)
 
    if debug then
 
        return error(modules.i18n[key] or key)
 
    end
 
    if category then
 
        return addCat(category, key)
 
    else
 
        return addCat('cat-unsorted-issue', key)
 
    end
 
end
 
 
 
--
 
 
 
function wd.isSpecial(snak)
 
return (snak.snaktype ~= 'value')
 
end
 
 
 
function wd.getId(snak)
 
if (snak.snaktype == 'value') then
 
return 'Q' .. snak.datavalue.value['numeric-id']
 
end
 
end
 
 
 
function wd.getNumericId(snak)
 
if (snak.snaktype == 'value') then
 
return snak.datavalue.value['numeric-id']
 
end
 
end
 
 
 
function wd.getMainId(claim)
 
return wd.getId(claim.mainsnak)
 
end
 
 
 
function wd.entityId(entity)
 
if type(entity) == 'string' then
 
return entity
 
end
 
return entity.id
 
end
 
 
 
function wd.getEntityIdForCurrentPage()
 
return mw.wikibase.getEntityIdForCurrentPage()
 
end
 
 
 
-- function that returns true if the "qid" parameter is the qid
 
-- of the item that is linked to the calling page
 
function wd.isPageOfQId(qid)
 
local entity_on_its_page = false
 
local self_entity = mw.wikibase.getEntity()
 
if self_entity ~= nil and qid == wd.entityId(self_entity) then
 
entity_on_its_page = true
 
end
 
return entity_on_its_page
 
end
 
 
 
function wd.getEntity( val )
 
if type(val) == 'table' then
 
return val
 
end
 
if val == '-' then
 
return nil
 
end
 
if val == '' then
 
val = nil
 
end
 
return mw.wikibase.getEntity(val)
 
end
 
 
 
function wd.splitStr(val) -- transforme en table les chaînes venant du Wikitexte qui utilisent des virgules de séparatin
 
if type(val) == 'string' then
 
val = mw.text.split(val, ",")
 
end
 
return val
 
end
 
 
 
function wd.isHere(searchset, val)
 
for i, j in pairs(searchset) do
 
if val == j then
 
return true
 
end
 
end
 
return false
 
end
 
 
 
 
 
local function wikidataLink(entity)
 
local name =':d:'
 
 
if type(entity) == 'string' then
 
if entity:match("P[0-9+]") then
 
entity = "Property:" .. entity
 
end
 
return name .. entity
 
elseif type(entity) == 'table' then
 
if entity["type"] == "property" then
 
name = ":d:Property:"
 
end
 
return name .. entity.id
 
elseif type(entity) == nil then
 
return formatError('entity-not-found')
 
end
 
end
 
 
 
function wd.siteLink(entity, project, lang)
 
-- returns 3 values: a sitelink (with the relevant prefix) a project name and a language
 
lang = lang or defaultlang
 
if (type(project) ~= 'string') then
 
project = 'wiki'
 
end
 
project = project:lower()
 
if project == 'wikipedia' then
 
project = 'wiki'
 
end
 
if type(entity) == 'string' and (project == 'wiki') and ( (not lang or lang == defaultlang) ) then -- évite de charger l'élément entier
 
return  mw.wikibase.sitelink(entity), 'wiki', defaultlang
 
end
 
if project == 'wikidata' then
 
return wikidataLink(entity), 'wikidata'
 
end
 
local projects = {
 
-- nom = {préfixe sur Wikidata, préfix pour les liens sur Wikipédia, ajouter préfixe de langue}
 
wiki = {'wiki', nil, true}, -- wikipedia
 
commons = {'commonswiki', 'commons', false},
 
commonswiki = {'commonswiki', 'commons', false},
 
wikiquote = {'wikiquote', 'q', true},
 
wikivoyage = {'wikivoyage', 'voy', true},
 
wikibooks = {'wikibooks', 'b', true},
 
wikinews = {'wikinews', 'n', true},
 
wikiversity = {'wikiversity', 'v', true},
 
wikisource = {'wikisource', 's', true},
 
wiktionary = {'wiktionary', 'wikt', true},
 
specieswiki = {'specieswiki', 'species', false},
 
metawiki = {'metawiki', 'm', false},
 
incubator = {'incubator', 'incubator', false},
 
outreach = {'outreach', 'outreach', false},
 
mediawiki = {'mediawiki', 'mw', false}
 
}
 
 
 
entity = wd.getEntity(entity)
 
if not entity then
 
return nil
 
end
 
 
 
local projectdata = projects[project:lower()]
 
if not projectdata then -- defaultlink might be in the form "dewiki" rather than "project: 'wiki', lang: 'de' "
 
for k, v in pairs(projects) do
 
if project:match( k .. '$' )
 
and mw.language.isKnownLanguageTag(project:sub(1, #project-#k))
 
then
 
lang = project:sub(1, #project-#k)
 
project = project:sub(#lang + 1, #project)
 
projectdata = projects[project]
 
break
 
end
 
end
 
if not mw.language.isKnownLanguageTag(lang) then
 
return --formatError('invalid-project-code', projet or 'nil')
 
end
 
end
 
if not projectdata then
 
return -- formatError('invalid-project-code', projet or 'nil')
 
end
 
 
local linkcode = projectdata[1]
 
local prefix = projectdata[2]
 
local multiversion = projectdata[3]
 
if multiversion then
 
linkcode = lang .. linkcode
 
end
 
local link = entity:getSitelink(linkcode)
 
if not link then
 
return nil
 
end
 
 
if prefix then
 
link = prefix .. ':' .. link
 
end
 
if multiversion then
 
link = ':' .. lang .. ':' .. link
 
end
 
return link, project, lang
 
 
end
 
end
  
-- add new values to a list, avoiding duplicates
+
function wd.formatEntity(entity, args)
function wd.addNewValues(olditems, newitems, maxnum, stopval)
+
return modules.entities.formatEntity(entity, args)
if not newitems then
 
return olditems
 
end
 
for _, qid in pairs(newitems) do
 
if stopval and (qid == stopval) then
 
table.insert(olditems, qid)
 
return olditems
 
end
 
if maxnum and (#olditems >= maxnum) then
 
return olditems
 
end
 
if not wd.isHere(olditems, qid) then
 
table.insert(olditems, qid)
 
end
 
end
 
return olditems
 
 
end
 
end
  
--=== FILTER CLAIMS ACCORDING TO VARIOUS CRITERIA : FUNCTION GETCLAIMS et alii ===
+
function wd.addtrackingcat(prop, cat) -- doit parfois être appelé par d'autres modules
 
 
local function notSpecial(claim)
 
local snack = claim.mainsnak or snack
 
return snack.snaktype == 'value'
 
end
 
 
 
local function hasTargetValue(claim, targets) -- retourne true si la valeur est dans la liste des target, ou si c'est une valeur spéciale filtrée séparément par excludespecial
 
local id = wd.getMainId(claim)
 
local targets = wd.splitStr(targets)
 
return wd.isHere(targets, id) or wd.isSpecial(claim.mainsnak)
 
end
 
 
 
local function excludeValues(claim, values) -- true si la valeur n'est pas dans la liste, ou si c'est une valeur spéciale (filtrée à part par excludespecial)
 
return wd.isSpecial(claim.mainsnak) or not ( hasTargetValue(claim, values) )
 
end
 
 
 
local function bestRanked(claims)
 
if not claims then
 
return nil
 
end
 
local preferred, normal = {}, {}
 
for i, j in pairs(claims) do
 
if j.rank == 'preferred' then
 
table.insert(preferred, j)
 
elseif j.rank == 'normal' then
 
table.insert(normal, j)
 
end
 
end
 
if #preferred > 0 then
 
return preferred
 
else
 
return normal
 
end
 
end
 
 
 
local function withRank(claims, target)
 
if target == 'best' then
 
return bestRanked(claims)
 
end
 
local newclaims = {}
 
for pos, claim in pairs(claims)  do
 
if target == 'valid' then
 
if claim.rank ~= 'deprecated' then
 
table.insert(newclaims, claim)
 
end
 
elseif claim.rank == target then
 
table.insert(newclaims, claim)
 
end
 
end
 
return newclaims
 
end
 
 
 
function wd.hasQualifier(claim, acceptedqualifs, acceptedvals, excludequalifiervalues)
 
local claimqualifs = claim.qualifiers
 
 
if (not claimqualifs) then
 
return false
 
end
 
 
 
acceptedqualifs = wd.splitStr(acceptedqualifs)
 
acceptedvals = wd.splitStr( acceptedvals)
 
 
 
 
 
local function ok(qualif) -- vérification pour un qualificatif individuel
 
if not claimqualifs[qualif] then
 
return false
 
end
 
if not (acceptedvals) then  -- si aucune valeur spécifique n'est demandée, OK
 
return true
 
end
 
for i, wanted in pairs(acceptedvals) do
 
for j, actual in pairs(claimqualifs[qualif]) do
 
if wd.getId(actual) == wanted then
 
return true
 
end
 
end
 
end
 
end
 
 
 
for i, qualif in pairs(acceptedqualifs) do
 
if ok(qualif) then
 
return true
 
end
 
end
 
return false
 
end
 
 
 
local function hasSource(claim, targetsource, sourceproperty)
 
sourceproperty = sourceproperty or 'P248'
 
if targetsource == "-" then
 
return true
 
end
 
if (not claim.references) then return
 
false
 
end
 
local candidates = claim.references[1].snaks[sourceproperty] -- les snaks utilisant la propriété demandée
 
if (not candidates) then
 
return false
 
end
 
if (targetsource == "any") then -- si n'importe quelle valeur est acceptée tant qu'elle utilise en ref la propriété demandée
 
return true
 
end
 
targetsource = wd.splitStr(targetsource)
 
for _, source in pairs(candidates) do
 
local s = wd.getId(source)
 
for i, target in pairs(targetsource) do
 
if s == target then return true end
 
end
 
end
 
return false
 
end
 
 
 
local function excludeQualifier(claim, qualifier, qualifiervalues)
 
return not wd.hasQualifier(claim, qualifier, qualifiervalues)
 
end
 
 
 
local function hasDate(claim)
 
local claimqualifs = claims.qualifiers
 
if not claimqualifs then
 
return false
 
end
 
for _, qualif in pairs(claimqualifs) do
 
if claimsqualifs[qualif] and claimsqualifs[qualif][1].snaktype == 'value' then
 
return true
 
end
 
end
 
return false
 
end
 
 
 
local function hasLink(claim, site, lang)
 
if (claim.mainsnak.snaktype ~= 'value') then -- ne pas supprimer les valeurs spéciales, il y a une fonction dédiée pour ça
 
return true
 
end
 
local id = wd.getMainId(claim)
 
local link = wd.siteLink(id, site, lang)
 
if link then
 
return true
 
end
 
end
 
 
 
local function isInLanguage(claim, lang) -- ne fonctionne que pour les monolingualtext / étendre aux autres types en utilisant les qualifiers ?
 
if type(lang) == 'table' then -- si c'est une table de language séparées par des virgules, on les accepte toutes
 
for i, l in pairs(lang) do
 
local v = isInLanguage(claim, l)
 
if v then
 
return true
 
end
 
end
 
end
 
if type(lang) ~= ('string') then
 
return --?
 
end
 
 
 
if (lang == '-') then
 
return true
 
end
 
if (lang == 'locallang') then
 
lang =  mw.getContentLanguage():getCode()
 
end
 
 
-- pour les monolingual text
 
local snak = claim.mainsnak or claim
 
if snak.snaktype == 'value' and snak.datavalue.type == 'monolingualtext' then
 
if snak.datavalue.value.language == lang then
 
return true
 
end
 
return false
 
end
 
 
 
-- pour les autres types de données : recherche dans les qualificatifs
 
if (lang == 'fr') then
 
lang = 'Q150'
 
elseif (lang == 'en') then
 
lang = 'Q1860'
 
else
 
lang = invertedlangcodes[lang]
 
end
 
if claim.qualifiers and claim.qualifiers.P407 then
 
if wd.hasQualifier(claim, {'P407'}, {lang}) then
 
return true
 
else
 
return false
 
end
 
end
 
 
 
return true -- si on ne ne sait pas la langue, on condière que c'est bon
 
end
 
 
 
local function firstVals(claims, numval) -- retourn les numval premières valeurs de la table claims
 
    local numval = tonumber(numval) or 0 -- raise a error if numval is not a positive integer ?
 
    if not claims then
 
    return nil
 
    end
 
    while (#claims > numval) do
 
    table.remove(claims)
 
    end
 
    return claims
 
end
 
 
 
local function timeFromQualifs(claim, qualifs)
 
local claimqualifs = claim.qualifiers
 
if not claimqualifs then
 
return nil
 
end
 
for i, qualif in pairs(qualifs or timequalifiers) do
 
local vals = claimqualifs[qualif]
 
if vals and (vals[1].snaktype == 'value') then
 
return vals[1].datavalue.value.time
 
end
 
end
 
end
 
 
 
local function atDate(claim, mydate)
 
if mydate == "today" then
 
mydate = os.date("!%Y-%m-%dT%TZ")
 
end
 
local newclaims = {}
 
local mindate = timeFromQualifs(claim, {'P580'})
 
local maxdate = timeFromQualifs(claim, {'P582'})
 
if modules.formatDate.before(mydate, mindate) and  modules.formatDate.before(maxdate, mydate) then
 
return true
 
end
 
end
 
 
 
local function check(claim, condition)
 
if type(condition) == 'function' then -- cas standard
 
return condition(claim)
 
end
 
return formatError('invalid type', 'function', type(condition))
 
end
 
 
 
local function minPrecision(claim, minprecision)
 
local snack
 
if claim.qualifiers then -- si une date est donnée en qualificatif, c'est elle qu'on utilise de préférence au mainsnak
 
for i, j in ipairs(datequalifiers) do
 
if claim.qualifiers[j] then
 
snack = claim.qualifiers[j][1]
 
break
 
end
 
end
 
end
 
if not snack then
 
snack = claim.mainsnak or claim
 
end
 
if (snack.snaktype == 'value') and (snack.datatype == 'time') and (snack.datavalue.value.precision < minprecision) then
 
return false
 
end
 
return true
 
end
 
 
 
function wd.sortClaims(claims, sorttype)
 
if not claims then
 
return nil
 
end
 
if sorttype == 'chronological' then
 
return wd.chronoSort(claims)
 
elseif sorttype == 'inverted' then
 
return wd.chronoSort(claims, true)
 
elseif sorttype == 'order' then
 
return wd.chronoSort(claims)
 
elseif type(sorttype) == 'function' then
 
table.sort(claims, sorttype)
 
return claims
 
end
 
return claims
 
end
 
local alreadyFiltered = false
 
function wd.filterClaims(claims, args) --retire de la tables de claims celles qui sont éliminés par un des filters de la table des filters
 
 
 
local function filter(condition, filterfunction, funargs)
 
if not args[condition] then
 
return
 
end
 
for i = #claims, 1, -1 do
 
if not( filterfunction(claims[i], args[funargs[1]], args[funargs[2]], args[funargs[3]]) ) then
 
table.remove(claims, i)
 
end
 
end
 
end
 
filter('isinlang', isInLanguage, {'isinlang'} )
 
filter('excludespecial', notSpecial, {} )
 
filter('condition', check, {'condition'} )
 
if claims[1] and claims[1].mainsnak then
 
filter('targetvalue', hasTargetValue, {'targetvalue'} )
 
filter('atdate', atDate, {'atdate'} )
 
filter('qualifier', wd.hasQualifier, {'qualifier', 'qualifiervalue'} )
 
filter('excludequalifier', excludeQualifier, {'excludequalifier', 'excludequalifiervalue'} )
 
filter('withsource', hasSource, {'withsource', 'sourceproperty'} )
 
filter('withdate', hasDate, {} )
 
filter('excludevalues', excludeValues, {'excludevalues'})
 
filter('withlink', hasLink, {'withlink', 'linklang'} )
 
filter('minprecision', minPrecision, {'minprecision'} )
 
claims = withRank(claims, args.rank or 'best')
 
end
 
if args.sorttype then
 
claims = wd.sortClaims(claims, args.sorttype)
 
end
 
if #claims == 0 then
 
return nil
 
end
 
if args.numval then
 
claims = firstVals(claims, args.numval)
 
end
 
return claims
 
 
 
end
 
 
 
function wd.loadEntity(entity, cache)
 
if type(entity) ~= 'table' then
 
if cache then
 
if not cache[entity] then
 
cache[entity] = mw.wikibase.getEntity(entity)
 
mw.log("cached")
 
    end
 
return cache[entity]
 
        else
 
if entity == '' or (entity == '-') then
 
entity = nil
 
end
 
        return mw.wikibase.getEntity(entity)
 
        end
 
    else
 
    return entity
 
    end
 
end
 
 
 
 
 
function wd.getClaims( args ) -- returns a table of the claims matching some conditions given in args
 
if args.claims then -- if claims have already been set, return them
 
return args.claims
 
end
 
local properties = args.property
 
if type(properties) == 'string' then
 
properties = wd.splitStr(string.upper(args.property))
 
end
 
if not properties then
 
return formatError( 'property-param-not-provided' )
 
end
 
 
 
--Get entity
 
local entity = args.entity
 
if type(entity) == 'string' then
 
if entity == '' then
 
entity = nil
 
end
 
elseif type(entity) == 'table' then
 
entity = entity.id
 
end
 
        if (not entity) then
 
            entity = mw.wikibase.getEntityIdForCurrentPage()
 
        end
 
if (not entity) or (entity == '-') then
 
return nil
 
end
 
local claims = {}
 
 
 
if #properties == 1 then
 
claims = mw.wikibase.getAllStatements(entity, properties[1]) -- do not use mw.wikibase.getBestStatements at this stage, as it may remove the best ranked values that match other criteria in the query
 
else
 
for i, prop in ipairs(properties) do
 
local newclaims = mw.wikibase.getAllStatements(entity, prop)
 
if newclaims and #newclaims > 0 then
 
for j, claim in ipairs(newclaims) do
 
table.insert(claims, claim)
 
end
 
end
 
end
 
end
 
 
 
 
 
if (not claims) or (#claims == 0) then
 
return nil
 
end
 
return wd.filterClaims(claims, args)
 
end
 
 
 
--=== ENTITY FORMATTING ===
 
 
 
function wd.getLabel(entity, lang, labelformat)
 
if (not entity) then
 
return nil -- ou option de gestion des erreurs ?
 
end
 
 
 
lang = lang or defaultlang
 
 
 
if type(labelformat) == 'function' then
 
return labelformat(entity)
 
end
 
 
if (type(entity) == 'string') and (lang == defaultlang) then -- le plus économique
 
local str = mw.wikibase.label(entity)
 
if str then -- mw.wikibase.label() ne fonctionne pas avec les redirect https://phabricator.wikimedia.org/T157868
 
return str
 
end
 
end
 
 
 
if type(entity) == 'string' then
 
entity = mw.wikibase.getEntity(entity)
 
end
 
 
if entity and entity.labels and entity.labels[lang] then
 
return entity.labels[lang].value, true
 
end
 
end
 
 
 
function wd.formatEntity( entity, params )
 
 
 
if (not entity) then
 
return nil --formatError('entity-not-found')
 
end
 
local id = entity
 
if type(id) == 'table' then
 
id = id.id
 
end
 
 
 
params = params or {}
 
local lang = params.lang or defaultlang
 
local speciallabels = params.speciallabels
 
local displayformat = params.displayformat
 
local labelformat = params.labelformat
 
local defaultlabel = params.defaultlabel or id
 
local linktype = params.link
 
local defaultlink = params.defaultlink
 
local defaultlinkquery = params.defaultlinkquery
 
 
 
if speciallabels and speciallabels[id] then --speciallabels override the standard label + link combination
 
return speciallabels[id]
 
end
 
if params.displayformat == 'raw' then
 
return id
 
end
 
 
 
local link, label
 
 
label = wd.getLabel(entity, lang, labelformat)
 
 
-- détermination du fait qu'on soit ou non en train de rendre l'élément sur la page de son article
 
local rendering_entity_on_its_page = wd.isPageOfQId(id)
 
 
 
 
if not label then
 
if (defaultlabel == '-') then
 
return nil
 
end
 
link = wd.siteLink(id, 'wikidata')
 
return '[[' .. link .. '|' .. id .. ']]' .. addCat(modules.i18n['to translate'])
 
-- si pas de libellé, on met un lien vers Wikidata pour qu'on comprenne à quoi ça fait référence
 
end
 
 
 
if (linktype == '-') or rendering_entity_on_its_page then
 
return label
 
end
 
 
 
local link = wd.siteLink(entity, linktype, lang)
 
 
 
-- defaultlinkquery will try to link to another page on this Wiki
 
if (not link) and defaultlinkquery then
 
if type(defaultlinkquery) == 'string' then
 
defaultlinkquery = {property = defaultlinkquery}
 
end
 
defaultlinkquery.excludespecial = true
 
defaultlinkquery.entity = entity
 
local claims = wd.getClaims(defaultlinkquery)
 
if claims then
 
for i, j in pairs(claims) do
 
local id = wd.getMainId(j)
 
link = wd.siteLink(id, linktype, lang)
 
if link then
 
break
 
end
 
end
 
end
 
end
 
 
 
if link then
 
return '[[' .. link .. '|' .. label .. ']]'
 
end
 
 
 
-- if not link, you can use defaultlink: a sidelink to another Wikimedia project
 
if (not defaultlink) then
 
defaultlink = {'enwiki'}
 
end
 
if defaultlink and (defaultlink ~= '-') then
 
local linktype
 
local sidelink, site, langcode
 
 
if type(defaultlink) == 'string' then
 
defaultlink = {defaultlink}
 
end
 
for i, j in ipairs(defaultlink) do
 
sidelink, site, langcode = wd.siteLink(entity, j, lang)
 
if sidelink then
 
break
 
end
 
end
 
if not sidelink then
 
sidelink, site = wd.siteLink(entity, 'wikidata')
 
end
 
 
local icon, class, title = site, nil, nil -- le texte affiché du lien
 
if site == 'wiki' then
 
icon, class, title = langcode, "indicateur-langue", wd.translate('see-another-language', mw.language.fetchLanguageName(langcode, defaultlang))
 
elseif site == 'wikidata' then
 
icon, class, title = 'd',  "indicateur-langue", wd.translate('see-wikidata')
 
else
 
title = wd.translate('see-another-project', site)
 
end
 
local val = '[[' .. sidelink .. '|' .. '<span class = "' .. (class or '').. '" title = "' .. (title or '') .. '">' .. icon .. '</span>]]'
 
return label .. ' <small>(' .. val .. ')</small>'
 
end
 
return label
 
end
 
 
 
 
 
function wd.addTrackingCat(prop, cat) -- doit parfois être appelé par d'autres modules
 
 
if type(prop) == 'table' then
 
if type(prop) == 'table' then
 
prop = prop[1] -- devrait logiquement toutes les ajouter
 
prop = prop[1] -- devrait logiquement toutes les ajouter
 
end
 
end
 
if not prop and not cat then
 
if not prop and not cat then
return formatError("property-param-not-provided")
+
return error("no property provided")
 
end
 
end
 
if not cat then
 
if not cat then
cat = wd.translate('trackingcat', prop or 'P??')
+
cat = translate('trackingcat', prop or 'P??')
 
end
 
end
return addCat(cat )
+
return tools.addcat(cat )
 
end
 
end
  
local function unknownValue(snak, label)
+
local function removeblanks(args)
 +
for i, j in pairs(args) do
 +
if j == '' then args[i] = nil end
 +
end
 +
return args
 +
end
 +
 
 +
local function unknownvalue(snak, label)
 
local str = label
 
local str = label
  
Ligne 791 : Ligne 69 :
 
if (not str) then
 
if (not str) then
 
if snak.datatype == 'time' then
 
if snak.datatype == 'time' then
str = wd.translate('sometime')
+
str = translate('sometime')
 
else
 
else
str = wd.translate('somevalue')
+
str = translate('somevalue')
 
end
 
end
 
end
 
end
  
 
if type(str) ~= "string" then
 
if type(str) ~= "string" then
return formatError(snak.datatype)
+
return tools.formatError(snak.datatype)
 
end
 
end
 
return str
 
return str
 
end
 
end
  
local function noValue(displayformat)
+
local function novalue(displayformat)
 
if not displayformat then
 
if not displayformat then
return wd.translate('novalue')
+
return translate('novalue')
 
end
 
end
 
if type(displayformat) == 'string' then
 
if type(displayformat) == 'string' then
 
return displayformat
 
return displayformat
 
end
 
end
return formatError()
+
return tools.formatError()
 
end
 
end
  
local function getLangCode(entityid)
+
local function getlangcode(entityid)
 
return modules.langcodes[tonumber(entityid:sub(2))]
 
return modules.langcodes[tonumber(entityid:sub(2))]
 
end
 
end
  
local function showLang(statement) -- retourne le code langue entre paranthèse avant la valeur (par exemple pour les biblios et liens externes)
+
local function showlang(statement) -- retourne le code langue entre paranthèse avant la valeur (par exemple pour les biblios et liens externes)
 
local mainsnak = statement.mainsnak
 
local mainsnak = statement.mainsnak
 
if mainsnak.snaktype ~= 'value' then
 
if mainsnak.snaktype ~= 'value' then
Ligne 830 : Ligne 108 :
 
for i, j in pairs( statement.qualifiers.P407 ) do
 
for i, j in pairs( statement.qualifiers.P407 ) do
 
if  j.snaktype == 'value' then
 
if  j.snaktype == 'value' then
local langentity = wd.getId(j)
+
local langentity = tools.getId(j)
local langcode =  getLangCode(langentity)
+
local langcode =  getlangcode(langentity)
 
table.insert(langlist, langcode)
 
table.insert(langlist, langcode)
 
end
 
end
Ligne 841 : Ligne 119 :
 
end
 
end
  
 +
local function formattable(statements, params) -- transform a table of claims into a table of formatted values
 +
for i, j in pairs(statements) do
 +
j = wd.formatStatement(j, params)
 +
end
 +
return statements
 +
end
 +
 +
function wd.tableToText(values, params) -- takes a list of already formatted values and make them a text
 +
if not values then
 +
return nil
 +
end
 +
 +
-- filters out nil values to avoid error in subsequent table.concat
 +
local filteredValues = {}
 +
for i, v in ipairs(values) do
 +
if v ~= nil then
 +
table.insert(filteredValues, v)
 +
end
 +
end
 +
 +
return modules.linguistic.quickconj( filteredValues, params.conjtype)--modules.linguistic.conj( values, params.lang, params.conjtype )
 +
end
  
-- === DATE HANDLING ===
 
  
 
function wd.addStandardQualifs(str, statement)
 
function wd.addStandardQualifs(str, statement)
Ligne 849 : Ligne 148 :
 
end
 
end
 
if not str then
 
if not str then
return error()-- what's that ?
+
return tools.formatError("adding qualifs to a nil str !")-- il y a un problème
 
end
 
end
 
 
if statement.qualifiers.P1480 then
 
if statement.qualifiers.P1480 then
 
for i, j in pairs(statement.qualifiers.P1480) do
 
for i, j in pairs(statement.qualifiers.P1480) do
local v = wd.getId(j)
+
local v = tools.getId(j)
 
if (v == "Q21818619") then
 
if (v == "Q21818619") then
str = wd.translate('approximate-place', str)
+
str = str .. " (ou environs)"
 
elseif (v == "Q18122778") or (v == "Q18912752") then
 
elseif (v == "Q18122778") or (v == "Q18912752") then
str = wd.translate('uncertain-information', str)
+
str = str.. " (?)"
elseif (v == "Q5727902") then
+
elseif (v == "Q5727902") then -- traité séparément, car plus complexe séparément
if (statement.mainsnak.datatype == 'time') then
 
str = modules.formatDate.fuzzydate(str)
 
else
 
str = wd.translate('approximate-value', str)
 
end
 
 
end
 
end
 
end
 
end
Ligne 871 : Ligne 164 :
 
end
 
end
  
local function rangeObject(begin, ending, params)
+
function wd.rangeobject(begin, ending, params)
 
--[[
 
--[[
 
objet comportant un timestamp pour le classement chronologique et deux dateobject (begin et ending)
 
objet comportant un timestamp pour le classement chronologique et deux dateobject (begin et ending)
Ligne 884 : Ligne 177 :
 
end
 
end
  
local function dateObject(orig, params)
+
function wd.dateobject(orig, params)
 
--[[ transforme un snak en un nouvel objet utilisable par Module:Date complexe
 
--[[ transforme un snak en un nouvel objet utilisable par Module:Date complexe
 
{type = 'dateobject', timestamp = str, era = '+' ou '-', year = number, month = number, day = number, calendar = calendar}
 
{type = 'dateobject', timestamp = str, era = '+' ou '-', year = number, month = number, day = number, calendar = calendar}
Ligne 892 : Ligne 185 :
 
end
 
end
 
 
local newobj = modules.formatDate.splitDate(orig.time, orig.calendarmodel)
+
local newobj = modules.formatDate.splitDate(orig.time, orig.calendar)
 
 
 
newobj.precision = params.precision or orig.precision
 
newobj.precision = params.precision or orig.precision
Ligne 899 : Ligne 192 :
 
end
 
end
  
local function objectToText(obj, params)
+
function wd.objecttotext(obj, params)
 
if obj.type == 'dateobject' then
 
if obj.type == 'dateobject' then
 
return modules.formatDate.simplestring(obj, params)
 
return modules.formatDate.simplestring(obj, params)
Ligne 907 : Ligne 200 :
 
end
 
end
  
local function getDateFromQualif(statement, qualif)
+
local function getDatefromQualif(statement, qualif)
 
if (not statement) or (not statement.qualifiers) or not (statement.qualifiers[qualif]) then
 
if (not statement) or (not statement.qualifiers) or not (statement.qualifiers[qualif]) then
 
return nil
 
return nil
Ligne 915 : Ligne 208 :
 
return nil
 
return nil
 
end
 
end
return dateObject(v.datavalue.value)
+
return wd.dateobject(v.datavalue.value)
 
end
 
end
  
 
function wd.getDate(statement)
 
function wd.getDate(statement)
local period = getDateFromQualif(statement, 'P585') -- retourne un dateobject
+
local period = getDatefromQualif(statement, 'P585') -- retourne un dateobject
 
if period then
 
if period then
 
return period
 
return period
 
end
 
end
local begin, ending = getDateFromQualif(statement, 'P580'),  getDateFromQualif(statement, 'P582')
+
local begin, ending = getDatefromQualif(statement, 'P580'),  getDatefromQualif(statement, 'P582')
 
if begin or ending then
 
if begin or ending then
return rangeObject(begin, ending) -- retourne un rangeobject fait de deux dateobject
+
return wd.rangeobject(begin, ending) -- retourne un rangeobject fait de deux dateobject
 
end
 
end
 
end
 
end
 
+
function wd.getFormattedDate(statement, params, useallqualifiers)
function wd.getFormattedDate(statement, params)
 
 
if not statement then
 
if not statement then
 
return nil
 
return nil
Ligne 935 : Ligne 227 :
 
local str
 
local str
  
 +
local fuzzy = modules.getData.hasqualifier(statement, {"P1480"}, {"Q5727902"})
 +
if fuzzy then
 +
fuzzy = true
 +
end
  
 
--cherche la date avec les qualifs P580/P582
 
--cherche la date avec les qualifs P580/P582
 
local datetable = wd.getDate(statement)
 
local datetable = wd.getDate(statement)
 
if datetable then
 
if datetable then
str = objectToText(datetable, params)
+
str = wd.objecttotext(datetable, params)
 
end
 
end
 
 
 
-- puis limite intérieur / supérieur
 
-- puis limite intérieur / supérieur
 
if not str then
 
if not str then
local start, ending = getDateFromQualif(statement, 'P1319'), getDateFromQualif(statement, 'P1326')
+
local start, ending = getDatefromQualif(statement, 'P1319'), getDatefromQualif(statement, 'P1326')
 
str = modules.formatDate.between(start, ending, params)
 
str = modules.formatDate.between(start, ending, params)
 
end
 
end
Ligne 951 : Ligne 247 :
 
if (not str) and (statement.mainsnak.datatype == 'time') then
 
if (not str) and (statement.mainsnak.datatype == 'time') then
 
local mainsnak = statement.mainsnak
 
local mainsnak = statement.mainsnak
if (mainsnak.snaktype == 'value') or (mainsnak.snaktype == 'somevalue') then
+
if
str = wd.formatSnak(mainsnak, params)
+
(mainsnak.snaktype == 'value' and mainsnak.datavalue.value.precision > 7)
 +
or
 +
(mainsnak.snaktype == 'somevalue')
 +
then
 +
str = wd.formatSnak(mainsnak, params)
 
end
 
end
 
end
 
end
  
if str and params and (params.addstandardqualifs ~= '-') then
+
-- ajouter le qualificatif "environ"
 +
if fuzzy then
 +
str = modules.formatDate.fuzzydate(str)
 +
end
 +
-- autres valeurs de qualité de l'information
 +
if str and (useallqualifiers ~= "-") then
 
str = wd.addStandardQualifs(str, statement)
 
str = wd.addStandardQualifs(str, statement)
 +
if params.showqualifiers then
 +
str = wd.showQualifiers(str, statement, params)
 +
end
 
end
 
end
 
return str
 
return str
Ligne 965 : Ligne 273 :
 
-- Une clé de tri nomée « dateSortKey » est ajouté à chaque claim.
 
-- Une clé de tri nomée « dateSortKey » est ajouté à chaque claim.
 
-- Si des clés de tri de ce nom existent déjà, elles sont utilisées sans modification.
 
-- Si des clés de tri de ce nom existent déjà, elles sont utilisées sans modification.
function wd.chronoSort( claims, inverted )
+
function wd.sortDateClaims( claims )
 
for _, claim in ipairs( claims ) do
 
for _, claim in ipairs( claims ) do
 
if not claim.dateSortKey then
 
if not claim.dateSortKey then
local snack = claim.mainsnak or claim
+
local iso = wd.formatSnak( claim.mainsnak, { displayformat = 'raw' } )  
local iso
 
if (snack.snaktype == 'value') and (snack.datatype == 'time') then
 
iso = snack.datavalue.value.time
 
else
 
iso = timeFromQualifs(claim, datequalifiers) or '0'
 
end
 
 
-- transformation en nombre (indication de la base car gsub retourne deux valeurs)
 
-- transformation en nombre (indication de la base car gsub retourne deux valeurs)
iso = tonumber( iso:gsub( '(%d)%D', '%1' ), 10 )
+
iso = tonumber( iso:gsub( '(%d)%D', '%1' ), 10 ) or 0
 
claim.dateSortKey = iso
 
claim.dateSortKey = iso
 
end
 
end
Ligne 983 : Ligne 285 :
 
claims,
 
claims,
 
function ( c1, c2 )
 
function ( c1, c2 )
if inverted then
 
return c2.dateSortKey < c1.dateSortKey
 
end
 
 
return c1.dateSortKey < c2.dateSortKey
 
return c1.dateSortKey < c2.dateSortKey
 
end
 
end
 
)
 
)
return claims
 
 
end
 
end
  
 +
function wd.wikidataDate(prop, item, params)
 +
local claims = wd.getClaims{entity = item, property = prop}
 +
if not claims then
 +
return nil
 +
end
 +
wd.sortDateClaims( claims )
 +
params = params or {}
 +
local vals = {}
 +
for i, j in ipairs(claims) do
 +
local v = wd.getFormattedDate(j, params)
 +
if v then
 +
table.insert(vals, v)
 +
end
 +
end
 +
 +
local str = modules.linguistic.conj(vals, params.conjtype or 'or')
 +
 +
if not str then
 +
return
 +
end
 +
 +
if params.addcat ~= '-' then
 +
str = str .. wd.addtrackingcat(prop)
 +
end
 +
 +
if params.linkback ~= '-' then
 +
str = wd.addLinkback(str, item, prop)
 +
end
 +
return str
 +
end
  
-- ===================
 
 
function wd.getReferences(statement)
 
function wd.getReferences(statement)
 
local refdata = statement.references
 
local refdata = statement.references
 
if not refdata then
 
if not refdata then
 
return nil
 
return nil
 +
end
 +
 +
local function firstsnak(prop)
 +
return wd.formatSnak(prop[1])
 
end
 
end
  
 
local refs = {}
 
local refs = {}
local hashes = {}
 
 
for i, ref in pairs(refdata) do
 
for i, ref in pairs(refdata) do
 
local s
 
local s
Ligne 1 021 : Ligne 351 :
 
accessdate = wd.formatSnak(ref.snaks.P813[1])
 
accessdate = wd.formatSnak(ref.snaks.P813[1])
 
end
 
end
local sourceId = wd.getId(source)
+
s = modules.reference.citeitem(tools.getId(source), {['page'] = page, ['accessdate'] = accessdate})
s = modules.reference.citeitem(sourceId, {['page'] = page, ['accessdate'] = accessdate})
 
 
table.insert(refs, s)
 
table.insert(refs, s)
table.insert(hashes, ref.hash .. sourceId)
 
 
end
 
end
 
end
 
end
 
 
elseif hasValue('P854') then -- P854: reference url
+
elseif hasValue('P854') and hasValue('P1476') then
local url, title, author, publisher, accessdate, publishdate, publishlang
+
local url, title, accessdate, publishdate, publishlang
 
+
url, title = wd.formatSnak(ref.snaks.P854[1], {text = "-"}), wd.formatSnak(ref.snaks.P1476[1])
url = wd.formatSnak(ref.snaks.P854[1], {text = "-"})
 
if hasValue('P1476') then
 
title = wd.formatSnak(ref.snaks.P1476[1])
 
else
 
title = mw.ustring.gsub(url, '^[Hh][Tt][Tt][Pp]([Ss]?):(/?)([^/])', 'http%1://%3')
 
end
 
 
 
--todo : handle multiple values for author, etc.
 
 
if hasValue('P813') then
 
if hasValue('P813') then
 
accessdate = wd.formatSnak(ref.snaks.P813[1])
 
accessdate = wd.formatSnak(ref.snaks.P813[1])
end
 
if hasValue('P50') then  -- author (item type)
 
author = wd.formatSnak(ref.snaks.P50[1])
 
elseif hasValue('P2093') then -- author (string type)
 
author = wd.formatSnak(ref.snaks.P2093[1])
 
end
 
if hasValue('P123') then
 
publisher = wd.formatSnak(ref.snaks.P123[1])
 
 
end
 
end
 
-- publishdate avec P577 ? (ne semble pas vraiment correspondre)  
 
-- publishdate avec P577 ? (ne semble pas vraiment correspondre)  
 
if hasValue('P407') then
 
if hasValue('P407') then
local id = wd.getId(ref.snaks.P407[1])
+
local id = tools.getId(ref.snaks.P407[1])
publishlang = getLangCode(id)
+
publishlang = getlangcode(id)
 
end
 
end
s = modules.cite.lienWeb{titre = title, url = url, auteur = author, editeur = publisher, langue = publishlang, ['en ligne le'] = publishdate, ['consulté le'] = accessdate}
+
s = modules.cite.lienWeb{titre = title, url = url, langue = publishlang, ['en ligne le'] = publishdate, ['consulté le'] = accessdate}
table.insert(hashes, ref.hash)
 
 
table.insert(refs, s)
 
table.insert(refs, s)
 
elseif ref.snaks.P854 and ref.snaks.P854[1].snaktype == 'value' then
 
elseif ref.snaks.P854 and ref.snaks.P854[1].snaktype == 'value' then
 
s = wd.formatSnak(ref.snaks.P854[1], {text = "-"})
 
s = wd.formatSnak(ref.snaks.P854[1], {text = "-"})
table.insert(hashes, ref.snaks.P854[1].hash)
 
 
table.insert(refs, s)
 
table.insert(refs, s)
 
end
 
end
 
end
 
end
 
if #refs > 0 then
 
if #refs > 0 then
if #hashes == #refs then
 
return refs, hashes
 
end
 
 
return refs
 
return refs
 
end
 
end
 
end
 
end
  
function wd.sourceStr(sources, hashes)
+
function wd.getDatavalue(snak, params)
if not sources or (#sources == 0) then
 
return nil
 
end
 
local useHashes = hashes and #hashes == #sources
 
for i, j in ipairs(sources) do
 
local refArgs = {name = 'ref', content = j}
 
if useHashes and hashes[i] ~= '-' then
 
refArgs.args = {name = 'wikidata-' .. hashes[i]}
 
end
 
sources[i] = mw.getCurrentFrame():extensionTag(refArgs)
 
end
 
return table.concat(sources, '<sup class="reference cite_virgule">,</sup>')
 
end
 
 
 
function wd.getDataValue(snak, params)
 
 
if not params then
 
if not params then
 
params = {}
 
params = {}
Ligne 1 106 : Ligne 398 :
  
 
if datatype == 'wikibase-item' then
 
if datatype == 'wikibase-item' then
return wd.formatEntity(wd.getId(snak), params)
+
return modules.entities.formatEntity(tools.getId(snak), params)
 
end
 
end
  
Ligne 1 130 : Ligne 422 :
 
 
 
if datatype == 'time' then -- format example: +00000001809-02-12T00:00:00Z
 
if datatype == 'time' then -- format example: +00000001809-02-12T00:00:00Z
 +
local precision = params.precision -- degré de précision à afficher ('day', 'month', 'year'), inférieur ou égal à value.precision
 
if displayformat == 'raw' then
 
if displayformat == 'raw' then
 
return value.time
 
return value.time
 
else
 
else
local dateobject = dateObject(value, {precision = params.precision})
+
return wd.objecttotext(wd.dateobject(value, {precision = precision}), {linktopic = params.linktopic})
return objectToText(dateobject, params)
 
 
end
 
end
 
end
 
end
Ligne 1 158 : Ligne 450 :
 
end
 
end
  
 +
local showunit = params.showunit or true
 +
if showunit == '-' then
 +
showunit = false
 +
end
 
local raw
 
local raw
 
if displayformat == "raw" then
 
if displayformat == "raw" then
Ligne 1 163 : Ligne 459 :
 
end
 
end
 
return modules.formatNum.displayvalue(amount, unit,
 
return modules.formatNum.displayvalue(amount, unit,
{targetunit = params.targetunit, raw = raw, rounding = params.rounding, showunit = params.showunit or 'short', showlink = params.showlink}
+
{targetunit = params.targetunit, raw = raw, rounding = params.rounding, showunit = showunit}
 
)
 
)
 
end
 
end
Ligne 1 173 : Ligne 469 :
 
end
 
end
 
end
 
end
return formatError('unknown-datavalue-type' )
+
return tools.formatError('unknown-datavalue-type' )
  
 
end
 
end
 +
 +
function wd.getClaims( args ) -- returns a table of the claims matching some conditions given in args
 +
return modules.getData.getClaims(args)
 +
end
 +
  
 
function wd.stringTable(args) -- like getClaims, but get a list of string rather than a list of snaks, for easier manipulation
 
function wd.stringTable(args) -- like getClaims, but get a list of string rather than a list of snaks, for easier manipulation
 +
 
local claims = args.claims
 
local claims = args.claims
 
if not claims then
 
if not claims then
Ligne 1 191 : Ligne 493 :
 
end
 
end
 
if args.removedupes and (args.removedupes ~= '-') then
 
if args.removedupes and (args.removedupes ~= '-') then
claims = wd.addNewValues({}, claims) -- devrait aussi supprimer de props celles qui ne sont pas utilisées
+
claims = tools.addnewvalues({}, claims) -- devrait aussi supprimer de props celles qui ne sont pas utilisées
 
end
 
end
 
return claims, props
 
return claims, props
Ligne 1 202 : Ligne 504 :
 
local vals = {}
 
local vals = {}
 
if type(qualifs) == 'string' then
 
if type(qualifs) == 'string' then
qualifs = wd.splitStr(qualifs)
+
qualifs = tools.splitStr(qualifs)
 
end
 
end
 
for i, j in pairs(qualifs) do
 
for i, j in pairs(qualifs) do
Ligne 1 223 : Ligne 525 :
 
return nil
 
return nil
 
end
 
end
qualiftable = wd.filterClaims(qualiftable, params) or {}
 
 
for i, j in pairs(qualiftable) do
 
for i, j in pairs(qualiftable) do
 
qualiftable[i] = wd.formatSnak(j, params)
 
qualiftable[i] = wd.formatSnak(j, params)
Ligne 1 230 : Ligne 531 :
 
end
 
end
  
function wd.showQualifiers(str, statement, args)
+
function wd.showQualifiers(str, statement, args) -- utilisée par formatStatement et par wikidatadate
 
local qualifs =  args.showqualifiers
 
local qualifs =  args.showqualifiers
 
if not qualifs then
 
if not qualifs then
Ligne 1 236 : Ligne 537 :
 
end
 
end
 
if type(qualifs) == 'string' then
 
if type(qualifs) == 'string' then
qualifs = wd.splitStr(qualifs)
+
qualifs = tools.splitStr(qualifs)
 
end
 
end
 
local qualifargs = args.qualifargs or {}
 
local qualifargs = args.qualifargs or {}
Ligne 1 243 : Ligne 544 :
 
qualifargs.labelformat = args.qualiflabelformat or args.labelformat
 
qualifargs.labelformat = args.qualiflabelformat or args.labelformat
 
qualifargs.link = args.qualiflink or args.link
 
qualifargs.link = args.qualiflink or args.link
qualifargs.linktopic = args.qualiflinktopic or args.linktopic
 
 
qualifargs.conjtype = args.qualifconjtype
 
qualifargs.conjtype = args.qualifconjtype
qualifargs.defaultlink = args.qualifdefaultlink or args.defaultlink
 
qualifargs.defaultlinkquery = args.qualifdefaultlinkquery or args.defaultlinkquery
 
 
 
 
local formattedqualifs = wd.getFormattedQualifiers(statement, qualifs, qualifargs)
 
local formattedqualifs = wd.getFormattedQualifiers(statement, qualifs, qualifargs)
if formattedqualifs and formattedqualifs ~= "" then
+
if formattedqualifs and str then
str = str .. " (" .. formattedqualifs .. ")"
+
str = str .. modules.linguistic.inparentheses(formattedqualifs, defaultlang)
 
end
 
end
 
return str
 
return str
Ligne 1 256 : Ligne 554 :
  
  
function wd.formatSnak( snak, params )
+
function wd.sourceStr(sources)
if not params then params = {} end -- pour faciliter l'appel depuis d'autres modules
+
if not sources or (#sources == 0) then
if snak.snaktype == 'somevalue' then
+
return nil
return unknownValue(snak, params.unknownlabel)
+
end
elseif snak.snaktype == 'novalue' then
+
for i, j in pairs(sources) do
return noValue(params.novaluelabel)
+
sources[i] = mw.getCurrentFrame():extensionTag( "ref", j)
elseif snak.snaktype == 'value' then
 
return wd.getDataValue( snak, params)
 
else
 
return formatError( 'unknown-snak-type' )
 
 
end
 
end
 +
return table.concat(sources, '<sup class="reference cite_virgule">,</sup>')
 
end
 
end
  
Ligne 1 274 : Ligne 569 :
 
end
 
end
 
if not statement.type or statement.type ~= 'statement' then
 
if not statement.type or statement.type ~= 'statement' then
return formatError( 'unknown-claim-type' )
+
return tools.formatError( 'unknown-claim-type' )
 
end
 
end
 
local prop = statement.mainsnak.property
 
local prop = statement.mainsnak.property
Ligne 1 280 : Ligne 575 :
 
local str
 
local str
  
-- special displayformat f
+
-- partie principale
if args.statementformat and (type(args.statementformat) == 'function') then
+
if args.showonlyqualifier and (args.showonlyqualifier ~= '') then
str = args.statementformat(statement, args)
 
elseif (statement.mainsnak.datatype == 'time') and (statement.mainsnak.dateformat ~= '-') then
 
if args.displayformat == 'raw' and statement.mainsnak.snaktype == 'value' then
 
str = statement.mainsnak.datavalue.value.time
 
else
 
str = wd.getFormattedDate(statement, args)
 
end
 
elseif args.showonlyqualifier and (args.showonlyqualifier ~= '') then
 
 
str = wd.getFormattedQualifiers(statement, args.showonlyqualifier, args)
 
str = wd.getFormattedQualifiers(statement, args.showonlyqualifier, args)
 
if not str then
 
if not str then
 
return nil
 
return nil
 
end
 
end
if args.addstandardqualifs ~= '-' then
+
elseif args.statementformat and (type(args.statementformat) == 'function') then
str = wd.addStandardQualifs(str, statement)
+
str = args.statementformat(statement, args)
end
+
else  
else
 
 
str = wd.formatSnak( statement.mainsnak, args )
 
str = wd.formatSnak( statement.mainsnak, args )
if args.addstandardqualifs ~= '-' then
 
str = wd.addStandardQualifs(str, statement)
 
end
 
 
end
 
end
  
 
-- ajouts divers
 
-- ajouts divers
 
if args.showlang == true then
 
if args.showlang == true then
local indicateur = showLang(statement)
+
str = (showlang(statement) or '') .. '&#32;' .. str
if indicateur then
 
str = indicateur .. '&nbsp;' .. str
 
end
 
 
end
 
end
 
if args.showqualifiers then
 
if args.showqualifiers then
Ligne 1 316 : Ligne 596 :
  
 
if args.showdate then -- when "showdate and chronosort are both set, date retrieval is performed twice
 
if args.showdate then -- when "showdate and chronosort are both set, date retrieval is performed twice
local period = wd.getFormattedDate(statement, args, "-") -- 3 arguments indicate the we should not use additional qualifiers, alrady added by wd.formatStatement
+
local period = wd.getFormattedDate(statement, args, "-") -- 3 arguments indicate the we should not use additional qualifiers, alrady added by wd.formatStatement
if period then
+
if period then
str = str .. " <small>(" .. period .. ")</small>"
+
str = str .. " <small>(" .. period ..")</small>"
end
+
end
 
end
 
end
  
 
if args.showsource then
 
if args.showsource then
local sources, hashes = wd.getReferences(statement)
+
local sources = wd.getReferences(statement)
 
if sources then
 
if sources then
local source = wd.sourceStr(sources, hashes)
+
local source = wd.sourceStr(sources)
if source then
+
str = str .. (source or "")
str = str .. source
 
end
 
 
end
 
end
 
end
 
end
 +
if statement.qualifiers then
 +
str = wd.addStandardQualifs(str, statement)
 +
end
 +
return str
 +
end
  
return str
+
function wd.formatSnak( snak, params )
 +
if not params then params = {} end -- pour faciliter l'appel depuis d'autres modules
 +
if snak.snaktype == 'somevalue' then
 +
return unknownvalue(snak, params.unknownlabel)
 +
elseif snak.snaktype == 'novalue' then
 +
return novalue(params.novaluelabel)
 +
elseif snak.snaktype == 'value' then
 +
return wd.getDatavalue( snak, params)
 +
else
 +
return tools.formatError( 'unknown-snak-type' )
 +
end
 +
end
 +
 
 +
function wd.getDescription(entity, lang)
 +
lang = lang or defaultlang
 +
 
 +
local description
 +
if lang == defaultlang then
 +
return  mw.wikibase.descriptionl(qid)
 +
end
 +
if not entity.descriptions then
 +
return translate('no description')
 +
end
 +
local descriptions = entity.descriptions
 +
if not descriptions then
 +
return nil
 +
end
 +
if descriptions[lang] then
 +
return descriptions[delang].value
 +
end
 +
return entity.id
 
end
 
end
  
function wd.addLinkBack(str, id, property)
+
function wd.addLinkback(str, id, property)
 
if not id then
 
if not id then
id = wd.getEntity()
+
id = tools.getEntity()
 
end
 
end
 
if not id then
 
if not id then
Ligne 1 354 : Ligne 667 :
 
end
 
end
 
local icon = '[[File:Blue pencil.svg|%s|10px|baseline|class=noviewer|link=%s]]'
 
local icon = '[[File:Blue pencil.svg|%s|10px|baseline|class=noviewer|link=%s]]'
local title = wd.translate('see-wikidata-value')
+
local title = translate('see-wikidata-value')
 
local url = mw.uri.fullUrl('d:' .. id, 'uselang=fr')
 
local url = mw.uri.fullUrl('d:' .. id, 'uselang=fr')
 
url.fragment = property -- ajoute une #ancre si paramètre "property" défini
 
url.fragment = property -- ajoute une #ancre si paramètre "property" défini
Ligne 1 363 : Ligne 676 :
 
:tag('span')
 
:tag('span')
 
:addClass('noprint wikidata-linkback')
 
:addClass('noprint wikidata-linkback')
 +
:css('padding-left', '0.5em')
 
:wikitext(icon:format(title, url))
 
:wikitext(icon:format(title, url))
 
:allDone()
 
:allDone()
Ligne 1 380 : Ligne 694 :
 
)
 
)
 
end
 
end
 
--=== FUNCTIONS USING AN ENTITY AS ARGUMENT ===
 
local function formatStatementsGrouped(args, type)
 
-- regroupe les affirmations ayant la même valeur en mainsnak, mais des qualificatifs différents
 
-- (seulement pour les propriétés de type élément)
 
 
local claims = wd.getClaims(args)
 
if not claims then
 
return nil
 
end
 
local groupedClaims = {}
 
 
-- regroupe les affirmations par valeur de mainsnak
 
local function addClaim(claim)
 
local id = wd.getMainId(claim)
 
for i, j in pairs(groupedClaims) do
 
if (j.id == id) then
 
table.insert(groupedClaims[i].claims, claim)
 
return
 
end
 
end
 
table.insert(groupedClaims, {id = id, claims = {claim}})
 
end
 
for i, claim in pairs(claims) do
 
addClaim(claim)
 
end
 
 
local stringTable = {}
 
 
-- instructions ad hoc pour les paramètres concernant la mise en forme d'une déclaration individuelle
 
local funs = {
 
{param = "showqualifiers", fun = function(str, claims)
 
local qualifs = {}
 
for i, claim in pairs(claims) do
 
local news = wd.getFormattedQualifiers(claim, args.showqualifiers, args)
 
if news then
 
table.insert(qualifs, news)
 
end
 
end
 
local qualifstr = modules.linguistic.conj(qualifs, "qualif-separator")
 
if qualifstr and qualifstr ~= "" then
 
str = str .. " (" .. qualifstr .. ")"
 
end
 
return str
 
end
 
},
 
{param = "showdate", fun = function(str, claims)
 
-- toutes les dates sont regroupées à l'intérieur des mêmes parenthèses ex "médaille d'or (1922, 1924)"
 
local dates = {}
 
for i, statement in pairs(claims) do
 
local s = wd.getFormattedDate(statement, args, true)
 
if statement then table.insert(dates, s) end
 
end
 
local datestr = modules.linguistic.conj(dates)
 
if datestr and datestr ~= "" then
 
str = str .. " <small>(" .. datestr .. ")</small>"
 
end
 
return str
 
end
 
},
 
{param = "showsource", fun = function(str, claims)
 
-- les sources sont toutes affichées au même endroit, à la fin
 
-- si deux affirmations ont la même source, on ne l'affiche qu'une fois
 
local sources = {}
 
local hashes = {}
 
 
local function dupeRef(old, new)
 
for i, j in pairs(old) do
 
if j == new then
 
return true
 
end
 
end
 
end
 
for i, claim in pairs(claims) do
 
local refs, refHashes = wd.getReferences(claim)
 
if refs then
 
for i, j in pairs(refs) do
 
if not dupeRef(sources, j) then
 
table.insert(sources, j)
 
local hash = (refHashes and refHashes[i]) or '-'
 
table.insert(hashes, hash)
 
end
 
end
 
end
 
end
 
return str .. (wd.sourceStr(sources, hashes) or "")
 
end
 
}
 
}
 
 
for i, group in pairs(groupedClaims) do -- bricolage pour utiliser les arguments de formatStatements
 
local str = wd.formatEntity(group.id, args)
 
for i, fun in pairs(funs) do
 
if args[fun.param] then
 
str = fun.fun(str, group.claims, args)
 
end
 
end
 
table.insert(stringTable, str)
 
end
 
 
args.valuetable = stringTable
 
return wd.formatStatements(args)
 
end
 
 
  
 
function wd.formatStatements( args )--Format statement and concat them cleanly
 
function wd.formatStatements( args )--Format statement and concat them cleanly
Ligne 1 489 : Ligne 699 :
 
return nil
 
return nil
 
end
 
end
 
+
local valueexpl = translate("activate-query")
-- If a value is already set: use it, except if it's the special value {{WD}} (use wikidata)
+
--If a value is already set, use it
if args.value and args.value ~= '' then
+
if args.value and (args.value ~= '') and (args.value ~= valueexpl) then
local valueexpl = wd.translate("activate-query")
+
return args.value
if args.value ~= valueexpl then
+
end
return args.value
+
-- if args.expl: return something only one if explicitly required in Wikitext
end
+
if args.expl and (args.value ~= valueexpl) then
-- There is no value set, and args.expl disables wikidata on empty values
 
elseif args.expl then
 
 
return nil
 
return nil
 
end
 
end
 
+
args.entity = tools.getEntity(args.entity)
args.entity = wd.getEntity(args.entity)
 
  
 
if args.grouped and args.grouped ~= '' then
 
if args.grouped and args.grouped ~= '' then
 
args.grouped = false
 
args.grouped = false
return formatStatementsGrouped(args)
+
return wd.groupedStatements(args)
 
end
 
end
 
local valuetable = args.valuetable -- dans le cas où les valeurs sont déjà formtées
 
local valuetable = args.valuetable -- dans le cas où les valeurs sont déjà formtées
Ligne 1 513 : Ligne 720 :
 
end
 
end
  
local str = modules.linguistic.conj(valuetable, args.conjtype)
+
local str = wd.tableToText(valuetable, args)
 
if not str then
 
if not str then
return args.default
+
return nil
 
end
 
end
 
if not props then
 
if not props then
props = wd.splitStr(args.property)[1]
+
props = tools.splitStr(args.property)[1]
 
end
 
end
 
if args.ucfirst ~= '-' then
 
if args.ucfirst ~= '-' then
Ligne 1 525 : Ligne 732 :
  
 
if args.addcat and (args.addcat ~= '-') then
 
if args.addcat and (args.addcat ~= '-') then
str = str .. wd.addTrackingCat(props)
+
str = str .. wd.addtrackingcat(props)
 
end
 
end
 
if args.linkback and (args.linkback ~= '-') then
 
if args.linkback and (args.linkback ~= '-') then
str = wd.addLinkBack(str, args.entity, props)
+
str = wd.addLinkback(str, args.entity, props)
 +
end
 +
return str
 +
end
 +
 
 +
function wd.showQualifier( args )
 +
local qualifs = args.qualifiers or args.qualifier
 +
 +
if not qualifs then
 +
return tools.formatError( 'property-param-not-provided' )
 
end
 
end
if args.returnnumberofvalues then
+
if type(qualifs) == 'string' then
return str, #valuetable
+
qualifs = tools.splitStr(qualifs)
 +
end
 +
 
 +
local claims = wd.getClaims(args)
 +
if not claims then
 +
return nil
 +
end
 +
local str = ''
 +
for i, j in pairs(claims) do
 +
local new = wd.getFormattedQualifiers(j, qualifs, args) or ''
 +
str = str .. new
 
end
 
end
 
return str
 
return str
Ligne 1 540 : Ligne 766 :
 
return nil
 
return nil
 
end
 
end
args.linkback = args.linkback or true
+
args.linkback = true
 
args.addcat = true
 
args.addcat = true
 
if args.value then -- do not ignore linkback and addcat, as formatStatements do
 
if args.value then -- do not ignore linkback and addcat, as formatStatements do
if args.value == '-' then
+
local val = args.value .. wd.addtrackingcat(args.property)
return nil
+
val = wd.addLinkback(val, args.entity, args.property)
end
 
local val = args.value .. wd.addTrackingCat(args.property)
 
val = wd.addLinkBack(val, args.entity, args.property)
 
 
return val
 
return val
 
end  
 
end  
Ligne 1 570 : Ligne 793 :
 
end
 
end
 
if args.addcat == true then
 
if args.addcat == true then
val = val .. wd.addTrackingCat(args.property)
+
val = val .. wd.addtrackingcat(args.property)
 
end
 
end
val = wd.addLinkBack(val, args.entity, args.property)
+
val = wd.addLinkback(val, args.entity, args.property)
 
return val
 
return val
 
end
 
end
  
  
function wd.keyDate (event, item, params)
+
-- Complex functions using several items
params = params or {}
+
local function getids(query)
params.entity = item
 
if type(event) == 'table' then
 
for i, j in pairs(event) do
 
params.targetvalue = nil -- réinitialisation barbare des paramètres modifiés
 
local s = wd.keyDate(j, item, params)
 
if s then
 
return s
 
end
 
end
 
elseif type(event) ~= 'string' then
 
return formatError('invalid-datatype', type(event), 'string')
 
elseif string.sub(event, 1, 1) == 'Q' then -- on demande un élément utilisé dans P:P793 (événement clé)
 
params.property = 'P793'
 
params.targetvalue = event
 
params.addcat = params.addcat or true
 
return wd.getTheDate(params)
 
elseif string.sub(event, 1, 1) == 'P'  then -- on demande une propriété
 
params.property = event
 
return wd.formatAndCat(params)
 
else
 
return formatError('invalid-entity-id', event)
 
end
 
end
 
 
 
function wd.mainDate(entity)
 
-- essaye P580/P582
 
local args = {entity = entity, addcat = true}
 
 
args.property = 'P580'
 
local startpoint = wd.formatStatements(args)
 
args.property = 'P582'
 
local endpoint = wd.formatStatements(args)
 
 
 
local str
 
if (startpoint or endpoint) then
 
str = modules.formatDate.daterange(startpoint, endpoint, params)
 
str = wd.addLinkBack(str, entity, 'P582')
 
return str
 
end
 
 
 
-- défaut : P585
 
args.property = {'P585', 'P571'}
 
args.linkback = true
 
return wd.formatStatements(args)
 
end
 
 
 
-- === FUNCTIONS FOR TRANSITIVE PROPERTIES ===
 
 
 
function wd.getIds(item, query)
 
 
query.excludespecial = true
 
query.excludespecial = true
 
query.displayformat = 'raw'
 
query.displayformat = 'raw'
query.entity = item
 
query.addstandardqualifs = '-'
 
 
return wd.stringTable(query)
 
return wd.stringTable(query)
 
end
 
end
  
  
-- recursively adds a list of qid to an existing list, based on the results of a query
+
function wd.Dump(entity)
function wd.addVals(list, query, maxdepth, maxnodes, stopval)
+
entity = tools.getEntity(entity)
maxdepth = tonumber(maxdepth) or 10
+
if not entity then
maxnodes = tonumber(maxnodes) or 100
+
return tools.formatError("entity-param-not-provided")
if (maxdepth < 0) then
 
return list
 
 
end
 
end
if stopval and wd.isHere(list, stopval) then
+
return "<pre>"..mw.dumpObject(entity).."</pre>"
return list
 
end
 
local origsize = #list
 
for i = 1, origsize do
 
-- tried a  "checkpos" param instead of starting to 1 each time, but no impact on performance
 
local candidates = wd.getIds(list[i], query)
 
list = wd.addNewValues(list, candidates, maxnodes, stopval)
 
if list[#list] == stopval then
 
return list
 
end
 
if #list >= maxnodes then
 
return list
 
end
 
 
 
 
end
 
if (#list == origsize) then
 
return list
 
end
 
return wd.addVals(list, query, maxdepth - 1, maxnodes, stopval, origsize + 1)
 
 
end
 
end
  
-- returns a list of items transitively matching a query (orig item is not included in the list)
+
function wd.groupedStatements(args, type)
 +
-- regroupe les affirmations ayant la même valeur en mainsnak, mais des qualificatifs différents
 +
-- (seulement pour les propriétés de type élément)
  
function wd.transitiveVals(item, query, maxdepth, maxnodes, stopval)
+
local claims = wd.getClaims(args)
maxdepth = tonumber(maxdepth) or 5
+
if not claims then
if type(query) == "string" then
 
query = {property = query}
 
end
 
 
 
-- récupération des valeurs
 
local vals = wd.getIds(item, query)
 
if not vals then
 
return nil
 
end
 
local v = wd.addVals(vals, query, maxdepth - 1, maxnodes, stopval)  
 
if not v then
 
 
return nil
 
return nil
 
end
 
end
 +
local groupedClaims = {}
  
-- réarrangement des valeurs
+
-- regroupe les affirmations par valeur de mainsnak
if query.valorder == "inverted" then
+
local function addClaim(claim)
local a = {}
+
local id = tools.getMainId(claim)
for i, j in pairs(v) do
+
for i, j in pairs(groupedClaims) do  
table.insert(a, 1, j)
+
if (j.id == id) then
 +
table.insert(groupedClaims[i].claims, claim)
 +
return
 +
end
 
end
 
end
v = a
+
table.insert(groupedClaims, {id = id, claims = {claim}})
 
end
 
end
 
+
for i, claim in pairs(claims) do
return v
+
addClaim(claim)
end
 
 
 
-- returns true if an item is the value of a query, transitively
 
function wd.inTransitiveVals(searchedval, sourceval, query, maxdepth, maxnodes )
 
local vals = wd.transitiveVals(sourceval, query, maxdepth, maxnodes, searchedval )
 
if (not vals) then
 
return false
 
 
end
 
end
for _, val in ipairs(vals) do
 
if (val == searchedval) then
 
return true
 
end
 
end
 
return false
 
end
 
  
-- returns true if an item is a superclass of another, based on P279
+
local stringTable = {}
function wd.isSubclass(class, item, maxdepth)
 
local query = {property = 'P279'}
 
if class == item then -- item is a subclass of itself iff it is a class
 
if wd.getIds(item, query) then
 
return true
 
end
 
return false
 
end
 
return wd.inTransitiveVals(class, item, query, maxdepth )
 
end
 
  
-- returns true if one of the best ranked P31 values of an item is the target or a subclass of the target
+
-- instructions ad hoc pour les paramètres concernant la mise en forme d'une déclaration individuelle
-- rank = 'valid' would seem to make sense, but it would need to check for date qualifiers as some P31 values have begin or end date
+
local funs = {
function wd.isInstance(targetclass, item, maxdepth)
+
{param = "showqualifiers", fun = function(str, claims)
maxdepth = maxdepth or 10
+
local qualifs = {}
local directclasses = wd.transitiveVals(item, {property = 'P31'}, 1)
+
for i, claim in pairs(claims) do
if not directclasses then
+
local news = wd.getFormattedQualifiers(claim, args.showqualifiers, args)
return false
+
if news then
end  
+
table.insert(qualifs, news)
for i, class in pairs(directclasses) do
+
end
if wd.isSubclass(targetclass, class, maxdepth - 1) then
+
end
return true
+
local qualifstr = modules.linguistic.conj(qualifs, " ; ") -- point virgule pour séparer les années
end
+
if not qualifstr then
end
+
return str
return false
+
end
end
+
return str .. " " .. modules.linguistic.inparentheses(qualifstr)
 +
end
 +
},
 +
{param = "showdate", fun = function(str, claims)
 +
-- toutes les dates sont regroupées à l'intérieur des mêmes parenthèses ex "médaille d'or (1922, 1924)"
 +
local dates = {}
 +
for i, statement in pairs(claims) do
 +
local s = wd.getFormattedDate(statement, args, true) -- l'option useallqualifiers ne doit pas être désactivée dans ce cas
 +
if statement then table.insert(dates, s) end
 +
end
 +
local datestr = modules.linguistic.conj(dates)
 +
if not datestr then
 +
return str
 +
end
 +
return str .. "<small>" .. modules.linguistic.inparentheses(datestr) .. "</small>"
 +
end
 +
},
 +
{param = "showsource", fun = function(str, claims)
 +
-- les sources sont toutes affichées au même endroit, à la fin
 +
-- si deux affirmations ont la même source, on ne l'affiche qu'une fois
 +
local sources = {}
 +
 +
local function dupeRef(old, new)
 +
for i, j in pairs(old) do
 +
if j == new then
 +
return true
 +
end
 +
end
 +
end
 +
for i, claim in pairs(claims) do
 +
local refs = wd.getReferences(claim)
 +
if refs then
 +
for i, j in pairs(refs) do
 +
if not dupeRef(sources, j) then
 +
table.insert(sources, j)
 +
end
 +
end
 +
end
 +
end
 +
return str .. (wd.sourceStr(sources) or "")
 +
end
 +
}
 +
}
  
-- return the first value in a transitive query that belongs to a particular class. For instance find a value of P131 that is a province of Canada
+
for i, group in pairs(groupedClaims) do -- bricolage pour utiliser les arguments de formatStatements
function wd.findVal(sourceitem, targetclass, query, recursion, instancedepth)
+
local str = wd.formatEntity(group.id, args)
if type(query) == "string" then
+
for i, fun in pairs(funs) do
query = {property = query}
+
if args[fun.param] then
end
+
str = fun.fun(str, group.claims, args)
local candidates = wd.getIds(sourceitem, query)
 
if candidates then
 
for i, j in pairs(candidates) do
 
if wd.isInstance(targetclass, j, instancedepth) then
 
return j
 
 
end
 
end
 
end
 
end
if not recursion then
+
table.insert(stringTable, str)
recursion = 3
 
else
 
recursion = recursion - 1
 
end
 
if recursion < 0 then
 
return nil
 
end
 
for i, candidate in pairs(candidates) do
 
return wd.findVal(candidate, targetclass, query, recursion, instancedepth)
 
end
 
 
end
 
end
 +
 +
args.valuetable = stringTable
 +
return wd.formatStatements(args)
 
end
 
end
 
 
-- === VARIA ===
 
function wd.getDescription(entity, lang)
 
lang = lang or defaultlang
 
 
local description
 
if lang == defaultlang then
 
return  mw.wikibase.descriptionl(qid)
 
end
 
if not entity.descriptions then
 
return wd.translate('no description')
 
end
 
local descriptions = entity.descriptions
 
if not descriptions then
 
return nil
 
end
 
if descriptions[lang] then
 
return descriptions[delang].value
 
end
 
return entity.id
 
end
 
 
 
function wd.Dump(entity)
 
entity = wd.getEntity(entity)
 
if not entity then
 
return formatError("entity-param-not-provided")
 
end
 
return "<pre>"..mw.dumpObject(entity).."</pre>"
 
end
 
 
function wd.frameFun(frame)
 
local args = frame.args
 
local funname = args[1]
 
table.remove(args, 1)
 
return wd[funname](args)
 
end
 
 
  
 
return wd
 
return wd

Version actuelle datée du 28 décembre 2020 à 11:45

La documentation pour ce module peut être créée à Module:Wikidata/doc

--script that retrieves basic data stored in Wikidata, for the datamodel, see https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua

local wd = {}

local modules = { }
local modulesNames = {
	reference = 'Module:Wikidata/Références',
	linguistic = 'Module:Linguistique',
	formatDate = 'Module:Date complexe',
	formatNum = 'Module:Conversion',
	langmodule = 'Module:Langue',
	cite = 'Module:Biblio',
	getData = 'Module:Wikidata/Récup',
	entities = 'Module:Wikidata/Formatage entité',
	tools = 'Module:Wikidata/Outils',
	globes = 'Module:Wikidata/Globes',
	langcodes = 'Module:Dictionnaire Wikidata/Codes langue', -- gros et rarement utilisé
	weblink = 'Module:Weblink'
}

local function loadModule( t, key )
	if modulesNames[key] then
		local m = require( modulesNames[key] )
		t[key] = m
		return m
	end
end
setmetatable( modules, { __index = loadModule } )

local tools = require 'Module:Wikidata/Outils'
local translate = tools.translate
local defaultlang = mw.getContentLanguage():getCode()

function wd.getLabel(entity, args)
	modules.entities.getLabel(entity)
end

function wd.formatEntity(entity, args)
	return modules.entities.formatEntity(entity, args)
end

function wd.addtrackingcat(prop, cat) -- doit parfois être appelé par d'autres modules
	if type(prop) == 'table' then
		prop = prop[1] -- devrait logiquement toutes les ajouter
	end
	if not prop and not cat then
		return error("no property provided")
	end
	if not cat then
		cat = translate('trackingcat', prop or 'P??')
	end
	return tools.addcat(cat )
end

local function removeblanks(args)
	for i, j in pairs(args) do
		if j == '' then args[i] = nil end
	end
	return args
end

local function unknownvalue(snak, label)
	local str = label

	if type(str) == "function" then
		str = str(snak)
	end

	if (not str) then
		if snak.datatype == 'time' then
			str = translate('sometime')
		else
			str = translate('somevalue')
		end
	end

	if type(str) ~= "string" then
		return tools.formatError(snak.datatype)
	end
	return str
end

local function novalue(displayformat)
	if not displayformat then
		return translate('novalue')
	end
	if type(displayformat) == 'string' then
		return displayformat
	end
	return tools.formatError()
end

local function getlangcode(entityid)
	return modules.langcodes[tonumber(entityid:sub(2))]
end

local  function showlang(statement) -- retourne le code langue entre paranthèse avant la valeur (par exemple pour les biblios et liens externes)
	local mainsnak = statement.mainsnak
	if mainsnak.snaktype ~= 'value' then
		return nil
	end
	local langlist = {}
	if mainsnak.datavalue.type == 'monolingualtext' then
		langlist = {mainsnak.datavalue.value.language}
	elseif (not statement.qualifiers) or (not statement.qualifiers.P407) then
		return
	else
		for i, j in pairs( statement.qualifiers.P407 ) do
			if  j.snaktype == 'value' then
				local langentity = tools.getId(j)
				local langcode =  getlangcode(langentity)
				table.insert(langlist, langcode)
			end
		end
	end
	if (#langlist > 1) or (#langlist == 1 and langlist[1] ~= defaultlang) then -- si c'est en français, pas besoin de le dire
		return modules.langmodule.indicationMultilingue(langlist)
	end
end

local function formattable(statements, params) -- transform a table of claims into a table of formatted values
	for i, j in pairs(statements) do
		j = wd.formatStatement(j, params)
	end
	return statements
end

function wd.tableToText(values, params) -- takes a list of already formatted values and make them a text
	if not values then
		return nil
	end

	-- filters out nil values to avoid error in subsequent table.concat
	local filteredValues = {}
	for i, v in ipairs(values) do
		if v ~= nil then
			table.insert(filteredValues, v)
		end
	end

	return modules.linguistic.quickconj( filteredValues, params.conjtype)--modules.linguistic.conj( values, params.lang, params.conjtype )
end


function wd.addStandardQualifs(str, statement)
	if (not statement) or (not statement.qualifiers) then
		return str
	end
	if not str then
		return tools.formatError("adding qualifs to a nil str !")-- il y a un problème
	end
	if statement.qualifiers.P1480 then
		for i, j in pairs(statement.qualifiers.P1480) do
			local v = tools.getId(j)
			if (v == "Q21818619") then
				str = str .. " (ou environs)"
			elseif (v == "Q18122778") or (v == "Q18912752") then
				str = str.. " (?)"
			elseif (v == "Q5727902") then -- traité séparément, car plus complexe séparément
			end			
		end
	end
	return str
end

function wd.rangeobject(begin, ending, params)
	--[[
		objet comportant un timestamp pour le classement chronologique et deux dateobject (begin et ending)
	]]-- 
	local timestamp
	if begin then
		timestamp = begin.timestamp
	else
		timestamp = ending.timestamp
	end
	return {begin = begin, ending = ending, timestamp = timestamp, type = 'rangeobject'}
end

function wd.dateobject(orig, params)
	--[[ transforme un snak en un nouvel objet utilisable par Module:Date complexe
		{type = 'dateobject', timestamp = str, era = '+' ou '-', year = number, month = number, day = number, calendar = calendar}
	]]-- 
	if not params then
		params = {}
	end
	
	local newobj = modules.formatDate.splitDate(orig.time, orig.calendar)
	
	newobj.precision = params.precision or orig.precision
	newobj.type = 'dateobject'
	return newobj
end

function wd.objecttotext(obj, params)
	if obj.type == 'dateobject' then
		return modules.formatDate.simplestring(obj, params)
	elseif obj.type == 'rangeobject' then
		return modules.formatDate.daterange(obj.begin, obj.ending, params)
	end
end

local function getDatefromQualif(statement, qualif)
	if (not statement) or (not statement.qualifiers) or not (statement.qualifiers[qualif]) then
		return nil
	end
	local v = statement.qualifiers[qualif][1]
	if v.snaktype ~= 'value' then -- que faire dans ce cas ?
		return nil
	end
	return wd.dateobject(v.datavalue.value)
end

function wd.getDate(statement)
	local period = getDatefromQualif(statement, 'P585') -- retourne un dateobject
	if period then
		return period
	end
	local begin, ending = getDatefromQualif(statement, 'P580'),  getDatefromQualif(statement, 'P582')
	if begin or ending then
		return wd.rangeobject(begin, ending) -- retourne un rangeobject fait de deux dateobject
	end
end
function wd.getFormattedDate(statement, params, useallqualifiers)
	if not statement then
		return nil
	end
	local str

	local fuzzy = modules.getData.hasqualifier(statement, {"P1480"}, {"Q5727902"})
	if fuzzy then
		fuzzy = true
	end

	--cherche la date avec les qualifs P580/P582
	local datetable = wd.getDate(statement)
	if datetable then
		str = wd.objecttotext(datetable, params)
	end
	
	-- puis limite intérieur / supérieur
	if not str then
		local start, ending = getDatefromQualif(statement, 'P1319'), getDatefromQualif(statement, 'P1326')
		str = modules.formatDate.between(start, ending, params)
	end

	 -- sinon, le mainsnak, pour les données de type time
	if (not str) and (statement.mainsnak.datatype == 'time') then
		local mainsnak = statement.mainsnak
		if
			(mainsnak.snaktype == 'value' and mainsnak.datavalue.value.precision > 7)
			or
			(mainsnak.snaktype == 'somevalue')
		then
		str = wd.formatSnak(mainsnak, params)
		end
	end

	-- ajouter le qualificatif "environ"
	if fuzzy then
		str = modules.formatDate.fuzzydate(str)
	end
	-- autres valeurs de qualité de l'information
	if str and (useallqualifiers ~= "-") then
		str = wd.addStandardQualifs(str, statement)
		if params.showqualifiers then
			str = wd.showQualifiers(str, statement, params)
		end
	end
	return str
end

-- Fonction qui trie des Claims de type time selon l'ordre chronologique
-- Une clé de tri nomée « dateSortKey » est ajouté à chaque claim.
-- Si des clés de tri de ce nom existent déjà, elles sont utilisées sans modification.
function wd.sortDateClaims( claims )
	for _, claim in ipairs( claims ) do
		if not claim.dateSortKey then
			local iso = wd.formatSnak( claim.mainsnak, { displayformat = 'raw' } ) 
			-- transformation en nombre (indication de la base car gsub retourne deux valeurs)
			iso = tonumber( iso:gsub( '(%d)%D', '%1' ), 10 ) or 0
			claim.dateSortKey = iso
		end
	end
	table.sort( 
		claims,
		function ( c1, c2 )
			return c1.dateSortKey < c2.dateSortKey
		end
	)
end

function wd.wikidataDate(prop, item, params)
	local claims = wd.getClaims{entity = item, property = prop}
	if not claims then
		return nil
	end
	wd.sortDateClaims( claims )
	params = params or {}
	local vals = {}
	for i, j in ipairs(claims) do
		local v = wd.getFormattedDate(j, params)
		if v then
			table.insert(vals, v)
		end
	end
	
	local str = modules.linguistic.conj(vals, params.conjtype or 'or')

	if not str then
		return
	end

	if params.addcat ~= '-' then
		str = str .. wd.addtrackingcat(prop)
	end

	if params.linkback ~= '-' then
		str = wd.addLinkback(str, item, prop)
	end
	return str
end

function wd.getReferences(statement)
	local refdata = statement.references
	if not refdata then
		return nil
	end

	local function firstsnak(prop)
		return wd.formatSnak(prop[1])
	end

	local refs = {}
	for i, ref in pairs(refdata) do
		local s
		local function hasValue(prop) -- checks that the prop is here with valid value
			if ref.snaks[prop] and ref.snaks[prop][1].snaktype == 'value' then
				return true
			end
			return false
		end		

		if ref.snaks.P248 then
			for j, source in pairs(ref.snaks.P248) do
				if source.snaktype == 'value' then
					local page, accessdate
					if hasValue('P304') then
						page = wd.formatSnak(ref.snaks.P304[1])
					end
					if hasValue('P813') then
						accessdate = wd.formatSnak(ref.snaks.P813[1])
					end
					s = modules.reference.citeitem(tools.getId(source), {['page'] = page, ['accessdate'] = accessdate})
					table.insert(refs, s)
				end
			end
	
		elseif hasValue('P854') and hasValue('P1476') then
			local url, title, accessdate, publishdate, publishlang
			url, title = wd.formatSnak(ref.snaks.P854[1], {text = "-"}), wd.formatSnak(ref.snaks.P1476[1])
			if hasValue('P813') then
				accessdate = wd.formatSnak(ref.snaks.P813[1])
			end
			-- publishdate avec P577 ? (ne semble pas vraiment correspondre) 
			if hasValue('P407') then
				local id = tools.getId(ref.snaks.P407[1])
				publishlang = getlangcode(id)
			end
			s = modules.cite.lienWeb{titre = title, url = url, langue = publishlang, ['en ligne le'] = publishdate, ['consulté le'] = accessdate}
			table.insert(refs, s)			
		elseif ref.snaks.P854 and ref.snaks.P854[1].snaktype == 'value' then
			s = wd.formatSnak(ref.snaks.P854[1], {text = "-"})
			table.insert(refs, s)
		end
	end
	if #refs > 0 then
		return refs
	end
end

function wd.getDatavalue(snak, params)
	if not params then
		params = {}
	end
	local speciallabels = params.speciallabels -- parfois on a besoin de faire une liste d'éléments pour lequel le libellé doit être changé, pas très pratique d'utiliser une fonction pour ça

	if snak.snaktype ~= 'value' then
		return nil
	end

	local datatype = snak.datatype
	local value = snak.datavalue.value
	
	local displayformat = params.displayformat
	if type(displayformat) == 'function' then
		return displayformat(snak, params)
	end

	if datatype == 'wikibase-item' then
		return modules.entities.formatEntity(tools.getId(snak), params)
	end

	if datatype == 'url' then
		return modules.weblink.makelink(value, params.text)
	end

	if datatype == 'math' then
		return mw.getCurrentFrame():extensionTag( "math", value)
	end

	if (datatype == 'string') or (datatype == 'external-id') or (datatype == 'commonsMedia') then -- toutes les données de type string sauf "math"
		if params.urlpattern then
			local urlpattern = params.urlpattern
			if type(urlpattern) == 'function' then
				urlpattern = urlpattern(value)
			end
			local url = mw.ustring.gsub(urlpattern, '$1', (value:gsub('%%', '%%%%'))):gsub(' ', '%%20')
			value = '[' .. url .. ' ' .. (params.text or value) .. ']'
		end
		return value
	end
	
	if datatype == 'time' then -- format example: +00000001809-02-12T00:00:00Z
		local precision = params.precision -- degré de précision à afficher ('day', 'month', 'year'), inférieur ou égal à value.precision
		if displayformat == 'raw' then
			return value.time
		else
			return wd.objecttotext(wd.dateobject(value, {precision = precision}), {linktopic = params.linktopic})
		end
	end

	if datatype == 'globe-coordinate' then
		-- retourne une table avec clés latitude, longitude, précision et globe à formater par un autre module (à changer ?)
		if displayformat == 'latitude' then
			return value.latitude
		elseif displayformat == 'longitude' then
			return value.longitude
		else
			local coordvalue = mw.clone( value )
			coordvalue.globe = modules.globes[value.globe] -- transforme l'ID du globe en nom anglais utilisable par geohack
			return coordvalue -- note : les coordonnées Wikidata peuvent être utilisée depuis Module:Coordinates. Faut-il aussi autoriser à appeler Module:Coordiantes ici ?
		end
	end

	if datatype == 'quantity' then -- todo : gérer les paramètre précision
		local amount, unit = value.amount, value.unit

		if unit then
			unit = unit:match('Q%d+')
		end

		local showunit = params.showunit or true		
		if showunit == '-' then
			showunit = false
		end
		local raw	
		if displayformat == "raw" then
			raw = true
		end
		return modules.formatNum.displayvalue(amount, unit,
			{targetunit = params.targetunit, raw = raw, rounding = params.rounding, showunit = showunit}
		)
	end
	if datatype == 'monolingualtext' then
		if value.language == defaultlang then
			return value.text
		else
			return modules.langmodule.langue({value.language, value.text})
		end
	end	
	return tools.formatError('unknown-datavalue-type' )

end

function wd.getClaims( args ) -- returns a table of the claims matching some conditions given in args
	return modules.getData.getClaims(args)
end


function wd.stringTable(args) -- like getClaims, but get a list of string rather than a list of snaks, for easier manipulation
	
	local claims = args.claims
	if not claims then
		claims = wd.getClaims(args)
	end
	if not claims or claims == {} then
		return nil
	end
	local props = {} -- liste des propriétés associété à chaque string pour catégorisation et linkback
	for i, j in pairs(claims) do
		claims[i] = wd.formatStatement(j, args)
		table.insert(props, j.mainsnak.property)
	end
	if args.removedupes and (args.removedupes ~= '-') then
		claims = tools.addnewvalues({}, claims) -- devrait aussi supprimer de props celles qui ne sont pas utilisées
	end
	return claims, props
end

function wd.getQualifiers(statement, qualifs, params)
	if not statement.qualifiers then
		return nil
	end
	local vals = {}
	if type(qualifs) == 'string' then
		qualifs = tools.splitStr(qualifs)
	end
	for i, j in pairs(qualifs) do
		if statement.qualifiers[j] then
			for k, l in pairs(statement.qualifiers[j]) do
				table.insert(vals, l)
			end
		end
	end
	if #vals == 0 then
		return nil
	end
	return vals
end

function wd.getFormattedQualifiers(statement, qualifs, params)
	if not params then params = {} end
	local qualiftable = wd.getQualifiers(statement, qualifs)
	if not qualiftable then
		return nil
	end
	for i, j in pairs(qualiftable) do
		qualiftable[i] = wd.formatSnak(j, params)
	end
	return modules.linguistic.conj(qualiftable, params.conjtype)
end

function wd.showQualifiers(str, statement, args) -- utilisée par formatStatement et par wikidatadate
	local qualifs =  args.showqualifiers
	if not qualifs then
		return str -- or error ?
	end
	if type(qualifs) == 'string' then
			qualifs = tools.splitStr(qualifs)
	end
	local qualifargs = args.qualifargs or {}
	-- formatage des qualificatifs = args commençant par "qualif", ou à défaut, les mêmes que pour la valeur principale
	qualifargs.displayformat = args.qualifdisplayformat or args.displayformat
	qualifargs.labelformat = args.qualiflabelformat or args.labelformat
	qualifargs.link = args.qualiflink or args.link
	qualifargs.conjtype = args.qualifconjtype
			
	local formattedqualifs = wd.getFormattedQualifiers(statement, qualifs, qualifargs)
	if formattedqualifs and str then
		str = str .. modules.linguistic.inparentheses(formattedqualifs, defaultlang)
	end
	return str
end


function wd.sourceStr(sources)
	if not sources or (#sources == 0) then
		return nil
	end
	for i, j in pairs(sources) do
		sources[i] = mw.getCurrentFrame():extensionTag( "ref", j)
	end
	return table.concat(sources, '<sup class="reference cite_virgule">,</sup>')
end

function wd.formatStatement( statement, args )
	if not args then
		args = {}
	end
	if not statement.type or statement.type ~= 'statement' then
		return tools.formatError( 'unknown-claim-type' )
	end
	local prop = statement.mainsnak.property

	local str

	-- partie principale
	if args.showonlyqualifier and (args.showonlyqualifier ~= '') then
		str = wd.getFormattedQualifiers(statement, args.showonlyqualifier, args)
		if not str then
			return nil
		end
	elseif args.statementformat and (type(args.statementformat) == 'function') then
		str = args.statementformat(statement, args)
	else 
		str = wd.formatSnak( statement.mainsnak, args )
	end

	-- ajouts divers	
	if args.showlang == true then
		str = (showlang(statement) or '') .. '&#32;' .. str
	end
	if args.showqualifiers then
		str = wd.showQualifiers(str, statement, args)
	end

	if args.showdate then -- when "showdate and chronosort are both set, date retrieval is performed twice
	local period = wd.getFormattedDate(statement, args, "-") -- 3 arguments indicate the we should not use additional qualifiers, alrady added by wd.formatStatement
	if period then
		str = str .. " <small>(" .. period ..")</small>"
	end
	end

	if args.showsource then
		local sources = wd.getReferences(statement)
		if sources then
			local source = wd.sourceStr(sources)
			str = str .. (source or "")
		end
	end
	if statement.qualifiers then
		str = wd.addStandardQualifs(str, statement)
	end
	return str
end

function wd.formatSnak( snak, params )
	if not params then params = {} end -- pour faciliter l'appel depuis d'autres modules
	if snak.snaktype == 'somevalue' then
		return unknownvalue(snak, params.unknownlabel)
	elseif snak.snaktype == 'novalue' then
		return novalue(params.novaluelabel)
	elseif snak.snaktype == 'value' then
		return wd.getDatavalue( snak, params)
	else
		return tools.formatError( 'unknown-snak-type' )
	end
end

function wd.getDescription(entity, lang)
	lang = lang or defaultlang

	local description
	if lang == defaultlang then
		return  mw.wikibase.descriptionl(qid)
	end
	if not entity.descriptions then
		return translate('no description')
	end
	local descriptions = entity.descriptions
	if not descriptions then
		return nil
	end
	if descriptions[lang] then
		return descriptions[delang].value
	end
	return entity.id
end

function wd.addLinkback(str, id, property)
	if not id then
		id = tools.getEntity()
	end
	if not id then
		return str
	end
	if type(property) == 'table' then
		property = property[1]
	end
	if type(id) == 'table' then
		id = id.id
	end
	
	local class = ''
	if property then
		class = 'wd_' .. string.lower(property)
	end
	local icon = '[[File:Blue pencil.svg|%s|10px|baseline|class=noviewer|link=%s]]'
	local title = translate('see-wikidata-value')
	local url = mw.uri.fullUrl('d:' .. id, 'uselang=fr')
	url.fragment = property -- ajoute une #ancre si paramètre "property" défini
	url = tostring(url)
	local v = mw.html.create('span')
		:addClass(class)
		:wikitext(str)
		:tag('span')
			:addClass('noprint wikidata-linkback')
			:css('padding-left', '0.5em')
			:wikitext(icon:format(title, url))
		:allDone()
	return tostring(v)
end

function wd.addRefAnchor(str, id)
--[[
	Insère une ancre pour une référence générée à partir d'un élément wd.
	L'id Wikidata sert d'identifiant à l'ancre, à utiliser dans les modèles type "harvsp"
--]]
	return tostring(
		mw.html.create('span')
			:attr('id', id)
			:attr('class', "ouvrage")
			:wikitext(str)
	)
end

function wd.formatStatements( args )--Format statement and concat them cleanly
	if args.value == '-' then
		return nil
	end
	local valueexpl = translate("activate-query")
	--If a value is already set, use it
	if args.value and (args.value ~= '') and (args.value ~= valueexpl) then
		return args.value
	end
	-- if args.expl: return something only one if explicitly required in Wikitext
	if args.expl and (args.value ~= valueexpl) then
		return nil
	end
	args.entity = tools.getEntity(args.entity)

	if args.grouped and args.grouped ~= '' then
		args.grouped = false
		return wd.groupedStatements(args)
	end
	local valuetable = args.valuetable -- dans le cas où les valeurs sont déjà formtées
	local props -- les prorpriétés réellement utilisées (dans certainse cas, ce ne sont pas toutes celles de ags.property
	if not valuetable then -- cas le plus courant
		valuetable, props = wd.stringTable(args)
	end

	local str = wd.tableToText(valuetable, args)
	if not str then
		return nil
	end
	if not props then
		props = tools.splitStr(args.property)[1]
	end
	if args.ucfirst ~= '-' then
		str = modules.linguistic.ucfirst(str)
	end

	if args.addcat and (args.addcat ~= '-') then
		str = str .. wd.addtrackingcat(props)
	end
	if args.linkback and (args.linkback ~= '-') then
		str = wd.addLinkback(str, args.entity, props)
	end
	return str
end

function wd.showQualifier( args )
	local qualifs = args.qualifiers or args.qualifier
	
	if not qualifs then
		return tools.formatError( 'property-param-not-provided' )
	end
	if type(qualifs) == 'string' then
		qualifs = tools.splitStr(qualifs)
	end

	local claims = wd.getClaims(args)
	if not claims then
		return nil
	end
	local str = ''
	for i, j in pairs(claims) do
		local new = wd.getFormattedQualifiers(j, qualifs, args) or ''
		str = str .. new
	end
	return str
end

function wd.formatAndCat(args)
	if not args then
		return nil
	end
	args.linkback = true
	args.addcat = true
	if args.value then -- do not ignore linkback and addcat, as formatStatements do
		local val = args.value .. wd.addtrackingcat(args.property)
		val = wd.addLinkback(val, args.entity, args.property)
		return val
	end 
	return wd.formatStatements( args )
end

function wd.getTheDate(args)
	local claims = wd.getClaims(args)
	if not claims then
		return nil
	end
	local formattedvalues = {}
	for i, j in pairs(claims) do
		local v = wd.getFormattedDate(j, args)
		if v then
			table.insert(formattedvalues, v )
		end
	end
	local val = modules.linguistic.conj(formattedvalues)
	if not val then
		return nil
	end
	if args.addcat == true then
		val = val .. wd.addtrackingcat(args.property)
	end
	val = wd.addLinkback(val, args.entity, args.property)
	return val
end


-- Complex functions using several items
local function getids(query)
	query.excludespecial = true
	query.displayformat = 'raw'
	return wd.stringTable(query)
end


function wd.Dump(entity)
	entity = tools.getEntity(entity)
	if not entity then
		return tools.formatError("entity-param-not-provided")
	end
	return "<pre>"..mw.dumpObject(entity).."</pre>"
end

function wd.groupedStatements(args, type)
	-- regroupe les affirmations ayant la même valeur en mainsnak, mais des qualificatifs différents
	-- (seulement pour les propriétés de type élément)

	local claims = wd.getClaims(args)
	if not claims then
		return nil
	end
	local groupedClaims = {}

	-- regroupe les affirmations par valeur de mainsnak
	local function addClaim(claim)
		local id = tools.getMainId(claim)
		for i, j in pairs(groupedClaims) do 
			if (j.id == id) then
				table.insert(groupedClaims[i].claims, claim)
				return
			end
		end
		table.insert(groupedClaims, {id = id, claims = {claim}})
	end
	for i, claim in pairs(claims) do	
		addClaim(claim)
	end

	local stringTable = {}

	-- instructions ad hoc pour les paramètres concernant la mise en forme d'une déclaration individuelle
	local funs = {
		{param = "showqualifiers", fun = function(str, claims)
			local qualifs = {}
			for i, claim in pairs(claims) do
				local news = wd.getFormattedQualifiers(claim, args.showqualifiers, args)
				if news then
					table.insert(qualifs, news)
				end
			end
			local qualifstr = modules.linguistic.conj(qualifs, " ; ") -- point virgule pour séparer les années
			if not qualifstr then
				return str
			end
			return str .. " " .. modules.linguistic.inparentheses(qualifstr)
			end
		},
		{param = "showdate", fun = function(str, claims)
			-- toutes les dates sont regroupées à l'intérieur des mêmes parenthèses ex "médaille d'or (1922, 1924)"
			local dates = {}
			for i, statement in pairs(claims) do
				local s = wd.getFormattedDate(statement, args, true) -- l'option useallqualifiers ne doit pas être désactivée dans ce cas
				if statement then table.insert(dates, s) end
			end
			local datestr = modules.linguistic.conj(dates)
			if not datestr then
				return str
			end
			return str .. "<small>" .. modules.linguistic.inparentheses(datestr) .. "</small>"
			end
		},
		{param = "showsource", fun = function(str, claims)
			-- les sources sont toutes affichées au même endroit, à la fin
			-- si deux affirmations ont la même source, on ne l'affiche qu'une fois
			local sources = {}
		
			local function dupeRef(old, new)
				for i, j in pairs(old) do
					if j == new then
						return true
					end
				end
			end
			for i, claim in pairs(claims) do
				local refs = wd.getReferences(claim)
				if refs then
					for i, j in pairs(refs) do
						if not dupeRef(sources, j) then
							table.insert(sources, j)
						end
					end
				end
			end
			return str .. (wd.sourceStr(sources) or "")
			end
		}
	}

	for i, group in pairs(groupedClaims) do -- bricolage pour utiliser les arguments de formatStatements
		local str = wd.formatEntity(group.id, args)
		for i, fun in pairs(funs) do
			if args[fun.param] then
				str = fun.fun(str, group.claims, args)
			end
		end
		table.insert(stringTable, str)
	end
				
	args.valuetable = stringTable
	return wd.formatStatements(args)
end

return wd