Modul:Wikidata

Seba na module şıma şenê yû pela dokumani vırazê Modul:Wikidata/dok

--script that retrieves basic data stored in Wikidata, for the datamodel, see https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lualocal wd = {}local modules = { }local modulesNames = {reference = 'Module:Wikidata/Références',i18n = 'Module:Wikidata/I18n',globes = 'Module:Wikidata/Globes',langcodes = 'Module:Dictionnaire Wikidata/Codes langue', -- big, infrequently usedainvertedlangcodes = 'Module:Dictionnaire Wikidata/Codes langue/inversé',linguistic = 'Module:Linguistique',formatDate = 'Module:Date complexe',formatNum = 'Module:Conversion',langmodule = 'Module:Langue',cite = 'Module:Biblio',weblink = 'Module:Weblink'}local function loadModule( t, key )if modulesNames[key] thenlocal m = require( modulesNames[key] )t[key] = mreturn mendendsetmetatable( modules, { __index = loadModule } )local datequalifiers = {'P585', 'P571', 'P580', 'P582', 'P1319', 'P1326'}-- === I18n ===local defaultlang = mw.getContentLanguage():getCode()function wd.translate(str, rep1, rep2)str = modules.i18n[str] or strif rep1 and (type (rep1) == 'string') thenstr = str:gsub('$1', rep1)endif rep2 and (type (rep2) == 'string')thenstr = str:gsub('$2', rep2)endreturn strendlocal function addCat(cat, sortkey)if sortkey thenreturn  '[[Category:' .. cat .. '|' .. sortkey .. ']]'endreturn '[[Category:' .. cat  .. ']]'endlocal 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)    endend-- function wd.isSpecial(snak)return (snak.snaktype ~= 'value')endfunction wd.getId(snak)if (snak.snaktype == 'value') thenreturn 'Q' .. snak.datavalue.value['numeric-id']endendfunction wd.getNumericId(snak)if (snak.snaktype == 'value') thenreturn snak.datavalue.value['numeric-id']endendfunction wd.getMainId(claim)return wd.getId(claim.mainsnak)endfunction wd.entityId(entity)if type(entity) == 'string' thenreturn entityelseif type(entity) == 'table' thenreturn entity.idendendfunction 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 pagefunction wd.isPageOfQId(qid) local self_id = mw.wikibase.getEntityIdForCurrentPage() return self_id ~= nil and qid == self_id endfunction wd.getEntity( val ) if type(val) == 'table' thenreturn valendif val == '-' thenreturn nilendif val == '' thenval = nilendreturn mw.wikibase.getEntity(val)endfunction wd.splitStr(val) -- transforme en table les chaînes venant du Wikitexte qui utilisent des virgules de séparationif type(val) == 'string' thenval = mw.text.split(val, ",")endreturn valendfunction wd.isHere(searchset, val)for i, j in pairs(searchset) doif val == j thenreturn trueendendreturn falseendlocal function wikidataLink(entity)local name =':d:'if type(entity) == 'string' thenif entity:match("P[0-9+]") thenentity = "Property:" .. entityendreturn name .. entityelseif type(entity) == 'table' thenif entity["type"] == "property" thenname = ":d:Property:"endreturn name .. entity.idelseif type(entity) == nil thenreturn formatError('entity-not-found')endendfunction wd.siteLink(entity, project, lang)-- returns 3 values: a sitelink (with the relevant prefix) a project name and a languagelang = lang or defaultlangif (type(project) ~= 'string') thenproject = 'wiki'endproject = project:lower()if project == 'wikipedia' thenproject = 'wiki'endif type(entity) == 'string' and (project == 'wiki') and ( (not lang or lang == defaultlang) ) then -- évite de charger l'élément entierreturn  mw.wikibase.sitelink(entity), 'wiki', defaultlangendif project == 'wikidata' thenreturn wikidataLink(entity), 'wikidata'endlocal projects = {-- nom = {préfixe sur Wikidata, préfix pour les liens sur Wikipédia, ajouter préfixe de langue}wiki = {'wiki', nil, true}, -- wikipediacommons = {'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}}local entityid = entity.id or entitylocal projectdata = projects[project:lower()]if not projectdata then -- defaultlink might be in the form "enwiki" rather than "project: 'wiki', lang: 'en' "for k, v in pairs(projects) doif project:match( k .. '$' ) and mw.language.isKnownLanguageTag(project:sub(1, #project-#k))thenlang = project:sub(1, #project-#k)project = project:sub(#lang + 1, #project)projectdata = projects[project]breakendendif not mw.language.isKnownLanguageTag(lang) thenreturn --formatError('invalid-project-code', projet or 'nil')endendif not projectdata thenreturn -- formatError('invalid-project-code', projet or 'nil')endlocal linkcode = projectdata[1]local prefix = projectdata[2]local multiversion = projectdata[3]if multiversion thenlinkcode = lang .. linkcodeendlocal link = mw.wikibase.getSitelink(entityid, linkcode)if not link thenreturn nilendif prefix thenlink = prefix .. ':' .. linkendif multiversion thenlink = ':' .. lang .. ':' .. linkendreturn link, project, langend-- add new values to a list, avoiding duplicatesfunction wd.addNewValues(olditems, newitems, maxnum, stopval)if not newitems thenreturn olditemsendfor _, qid in pairs(newitems) doif stopval and (qid == stopval) thentable.insert(olditems, qid)return olditemsendif maxnum and (#olditems >= maxnum) thenreturn olditemsendif not wd.isHere(olditems, qid) thentable.insert(olditems, qid)endendreturn olditemsend--=== FILTER CLAIMS ACCORDING TO VARIOUS CRITERIA : FUNCTION GETCLAIMS et alii ===local function notSpecial(claim)local typeif claim.mainsnak ~= nil thentype = claim.mainsnak.snaktypeelse-- condition respectée quand showonlyqualifier est un paramètre renseigné-- dans ce cas, claim n'est pas une déclaration entière, mais UNE snak qualifiée du main snaktype = claim.snaktypeendreturn type == 'value'endlocal 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 excludespeciallocal id = wd.getMainId(claim)local targets = wd.splitStr(targets)return wd.isHere(targets, id) or wd.isSpecial(claim.mainsnak)endlocal 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) )endlocal function bestRanked(claims)if not claims thenreturn nilendlocal preferred, normal = {}, {}for i, j in pairs(claims) doif j.rank == 'preferred' thentable.insert(preferred, j)elseif j.rank == 'normal' thentable.insert(normal, j)endendif #preferred > 0 thenreturn preferredelsereturn normalendendlocal function withRank(claims, target)if target == 'best' thenreturn bestRanked(claims)endlocal newclaims = {}for pos, claim in pairs(claims)  doif target == 'valid' thenif claim.rank ~= 'deprecated' thentable.insert(newclaims, claim)endelseif claim.rank == target thentable.insert(newclaims, claim)endendreturn newclaimsendfunction wd.hasQualifier(claim, acceptedqualifs, acceptedvals, excludequalifiervalues)local claimqualifs = claim.qualifiersif (not claimqualifs) thenreturn falseendacceptedqualifs = wd.splitStr(acceptedqualifs)acceptedvals = wd.splitStr( acceptedvals)local function ok(qualif) -- vérification pour un qualificatif individuelif not claimqualifs[qualif] thenreturn falseendif not (acceptedvals) then  -- si aucune valeur spécifique n'est demandée, OKreturn trueendfor i, wanted in pairs(acceptedvals) dofor j, actual in pairs(claimqualifs[qualif]) doif wd.getId(actual) == wanted thenreturn trueendendendendfor i, qualif in pairs(acceptedqualifs) doif ok(qualif) thenreturn trueendendreturn falseendfunction wd.hasQualifierNumber(claim, acceptedqualifs, acceptedvals, excludequalifiervalues)local claimqualifs = claim.qualifiersif (not claimqualifs) thenreturn falseendacceptedqualifs = wd.splitStr(acceptedqualifs)acceptedvals = wd.splitStr( acceptedvals)local function ok(qualif) -- vérification pour un qualificatif individuelif not claimqualifs[qualif] thenreturn falseendif not (acceptedvals) then  -- si aucune valeur spécifique n'est demandée, OKreturn trueendfor i, wanted in pairs(acceptedvals) dofor j, actual in pairs(claimqualifs[qualif]) doif mw.wikibase.renderSnak(actual) == wanted thenreturn trueendendendendfor i, qualif in pairs(acceptedqualifs) doif ok(qualif) thenreturn trueendendreturn falseendlocal function hasSource(claim, targetsource, sourceproperty)sourceproperty = sourceproperty or 'P248'if targetsource == "-" thenreturn trueendif (not claim.references) then returnfalseendlocal candidates = claim.references[1].snaks[sourceproperty] -- les snaks utilisant la propriété demandéeif (not candidates) thenreturn falseendif (targetsource == "any") then -- si n'importe quelle valeur est acceptée tant qu'elle utilise en ref la propriété demandéereturn trueendtargetsource = wd.splitStr(targetsource)for _, source in pairs(candidates) dolocal s = wd.getId(source)for i, target in pairs(targetsource) doif s == target then return true endendendreturn falseendlocal function excludeQualifier(claim, qualifier, qualifiervalues)return not wd.hasQualifier(claim, qualifier, qualifiervalues)endfunction wd.hasDate(claim)if not claim thenreturn false --error() ?endif wd.getDateFromQualif(claim, 'P585') or wd.getDateFromQualif(claim, 'P580') or wd.getDateFromQualif(claim, 'P582') thenreturn trueendreturn falseendlocal 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 çareturn trueendlocal id = wd.getMainId(claim)local link = wd.siteLink(id, site, lang)if link thenreturn trueendendlocal 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 toutesfor i, l in pairs(lang) dolocal v = isInLanguage(claim, l)if v thenreturn trueendendendif type(lang) ~= ('string') thenreturn --?endif (lang == '-') thenreturn trueendif (lang == 'locallang') thenlang =  mw.getContentLanguage():getCode()end-- pour les monolingual textlocal snak = claim.mainsnak or claimif snak.snaktype == 'value' and snak.datavalue.type == 'monolingualtext' thenif snak.datavalue.value.language == lang thenreturn trueendreturn falseend-- pour les autres types de données : recherche dans les qualificatifsif (lang == 'diq') thenlang = 'Q150'elseif (lang == 'en') thenlang = 'Q1860'elselang = invertedlangcodes[lang]endif claim.qualifiers and claim.qualifiers.P407 thenif wd.hasQualifier(claim, {'P407'}, {lang}) thenreturn trueelsereturn falseendendreturn true -- si on ne ne sait pas la langue, on condière que c'est bonendlocal 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 claimsendlocal function lastVals(claims, numval2) -- retourn les valeurs de la table claims à partir de numval2    local numval2 = tonumber(numval2) or 0 -- raise a error if numval is not a positive integer ?    if not claims then    return nil    end    for i=1,numval2 do    table.remove(claims, 1)    end    return claimsendlocal function timeFromQualifs(claim, qualifs)local claimqualifs = claim.qualifiersif not claimqualifs thenreturn nilendfor i, qualif in pairs(qualifs or timequalifiers) dolocal vals = claimqualifs[qualif]if vals and (vals[1].snaktype == 'value') thenreturn vals[1].datavalue.value.timeendendendlocal function atDate(claim, mydate, precision)if mydate == "today" thenmydate = os.date("!%Y-%m-%dT%TZ")end-- with point in timelocal d = timeFromQualifs(claim, {'P585'})if d thenreturn modules.formatDate.equal(mydate, d, precision)end-- with start or end datelocal mindate = timeFromQualifs(claim, {'P580'}) local maxdate = timeFromQualifs(claim, {'P582'})if modules.formatDate.before(mydate, mindate) and  modules.formatDate.before(maxdate, mydate) thenreturn trueendreturn falseendlocal function check(claim, condition)if type(condition) == 'function' then -- cas standardreturn condition(claim)endreturn formatError('invalid type', 'function', type(condition)) endlocal function minPrecision(claim, minprecision)local snackif claim.qualifiers then -- si une date est donnée en qualificatif, c'est elle qu'on utilise de préférence au mainsnakfor i, j in ipairs(datequalifiers) doif claim.qualifiers[j] thensnack = claim.qualifiers[j][1] breakendendendif not snack thensnack = claim.mainsnak or claimendif (snack.snaktype == 'value') and (snack.datatype == 'time') and (snack.datavalue.value.precision < minprecision) thenreturn falseendreturn trueendfunction wd.sortClaims(claims, sorttype)if not claims thenreturn nilendif sorttype == 'chronological' thenreturn wd.chronoSort(claims)elseif sorttype == 'inverted' thenreturn wd.chronoSort(claims, true)elseif sorttype == 'order' thenreturn wd.chronoSort(claims)elseif type(sorttype) == 'function' thentable.sort(claims, sorttype)return claimselseif type(sorttype) == 'string' and sorttype:sub(1, 1) == 'P' thenreturn wd.numericPropertySort(claims, sorttype)endreturn claimsendlocal alreadyFiltered = falsefunction wd.filterClaims(claims, args) --retire de la tables de claims celles qui sont éliminés par un des filters de la table des filterslocal function filter(condition, filterfunction, funargs)if not args[condition] thenreturnendfor i = #claims, 1, -1 doif not( filterfunction(claims[i], args[funargs[1]], args[funargs[2]], args[funargs[3]]) ) thentable.remove(claims, i)endendendfilter('isinlang', isInLanguage, {'isinlang'} )filter('excludespecial', notSpecial, {} )filter('condition', check, {'condition'} )if claims[1] and claims[1].mainsnak thenfilter('targetvalue', hasTargetValue, {'targetvalue'} )filter('atdate', atDate, {'atdate'} )filter('qualifier', wd.hasQualifier, {'qualifier', 'qualifiervalue'} )filter('qualifiernumber', wd.hasQualifierNumber, {'qualifiernumber', 'qualifiernumbervalue'} )filter('excludequalifier', excludeQualifier, {'excludequalifier', 'excludequalifiervalue'} )filter('withsource', hasSource, {'withsource', 'sourceproperty'} )filter('withdate', wd.hasDate, {} )filter('excludevalues', excludeValues, {'excludevalues'})filter('withlink', hasLink, {'withlink', 'linklang'} )filter('minprecision', minPrecision, {'minprecision'} )claims = withRank(claims, args.rank or 'best')endif args.sorttype thenclaims = wd.sortClaims(claims, args.sorttype)endif #claims == 0 thenreturn nilendif args.numval2 thenclaims = lastVals(claims, args.numval2)endif args.numval thenclaims = firstVals(claims, args.numval)endreturn claimsendfunction wd.loadEntity(entity, cache)if type(entity) ~= 'table' thenif cache thenif not cache[entity] then cache[entity] = mw.wikibase.getEntity(entity)mw.log("cached")     endreturn cache[entity]        elseif entity == '' or (entity == '-') thenentity = nilend        return mw.wikibase.getEntity(entity)        end    else     return entity    endendfunction wd.getClaims( args ) -- returns a table of the claims matching some conditions given in argsif args.claims then -- if claims have already been set, return themreturn args.claimsendlocal properties = args.propertyif type(properties) == 'string' thenproperties = wd.splitStr(string.upper(args.property))endif not properties thenreturn formatError( 'property-param-not-provided' )end--Get entitylocal entity = args.entityif type(entity) == 'string' thenif entity == '' thenentity = nilendelseif type(entity) == 'table' thenentity = entity.idend        if (not entity) then            entity = mw.wikibase.getEntityIdForCurrentPage()        endif (not entity) or (entity == '-') thenreturn nilendlocal claims = {}if #properties == 1 thenclaims = 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 queryelsefor i, prop in ipairs(properties) dolocal newclaims = mw.wikibase.getAllStatements(entity, prop) if newclaims and #newclaims > 0 thenfor j, claim in ipairs(newclaims) dotable.insert(claims, claim)endendendendif (not claims) or (#claims == 0) thenreturn nilendreturn wd.filterClaims(claims, args)end--=== ENTITY FORMATTING ===function wd.getLabel(entity, lang, labelformat)if (not entity) thenreturn nil -- ou option de gestion des erreurs ?endlang = lang or defaultlangif type(labelformat) == 'function' thenreturn labelformat(entity)endentity = entity.id or ( type(entity) == "string" and entity)if (type(entity) == 'string') and (lang == defaultlang) then -- le plus économiquelocal str = mw.wikibase.label(entity)if str then -- mw.wikibase.label() ne fonctionne pas avec les redirect https://phabricator.wikimedia.org/T157868return strendendreturn mw.wikibase.getLabelByLang(entity, lang)endfunction wd.formatEntity( entity, params )if (not entity) thenreturn nil --formatError('entity-not-found')endlocal id = entityif type(id) == 'table' thenid = id.idendparams = params or {}local lang = params.lang or defaultlanglocal speciallabels = params.speciallabelslocal displayformat = params.displayformatlocal labelformat = params.labelformatlocal defaultlabel = params.defaultlabel or idlocal linktype = params.linklocal defaultlink = params.defaultlinklocal defaultlinkquery = params.defaultlinkqueryif speciallabels and speciallabels[id] then --speciallabels override the standard label + link combinationreturn speciallabels[id]endif params.displayformat == 'raw' thenreturn idendlocal link, labellabel = 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 articlelocal rendering_entity_on_its_page = wd.isPageOfQId(id)if not label thenif (defaultlabel == '-') then return nilendlink = 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érenceendif (linktype == '-') or rendering_entity_on_its_page thenreturn labelendlocal link = wd.siteLink(entity, linktype, lang)-- defaultlinkquery will try to link to another page on this Wikiif (not link) and defaultlinkquery thenif type(defaultlinkquery) == 'string' thendefaultlinkquery = {property = defaultlinkquery}enddefaultlinkquery.excludespecial = truedefaultlinkquery.entity = entitylocal claims = wd.getClaims(defaultlinkquery)if claims thenfor i, j in pairs(claims) dolocal id = wd.getMainId(j)link = wd.siteLink(id, linktype, lang)if link thenbreakendendendendif link then return '[[' .. link .. '|' .. label .. ']]'end-- if not link, you can use defaultlink: a sidelink to another Wikimedia projectif (not defaultlink) thendefaultlink = {'enwiki'}endif defaultlink and (defaultlink ~= '-') thenlocal linktypelocal sidelink, site, langcodeif type(defaultlink) == 'string' thendefaultlink = {defaultlink}endfor i, j in ipairs(defaultlink) dosidelink, site, langcode = wd.siteLink(entity, j, lang)if sidelink thenbreakendendif not sidelink thensidelink, site = wd.siteLink(entity, 'wikidata')endlocal icon, class, title = site, nil, nil -- le texte affiché du lienif site == 'wiki' thenicon, class, title = langcode, "indicateur-langue", wd.translate('see-another-language', mw.language.fetchLanguageName(langcode, defaultlang))elseif site == 'wikidata' thenicon, class, title = 'd',  "indicateur-langue", wd.translate('see-wikidata')elsetitle = wd.translate('see-another-project', site)endlocal val = '[[' .. sidelink .. '|' .. ']]'return label .. ''end return labelendfunction wd.addTrackingCat(prop, cat) -- doit parfois être appelé par d'autres modulesif type(prop) == 'table' thenprop = prop[1] -- devrait logiquement toutes les ajouterendif not prop and not cat thenreturn formatError("property-param-not-provided")endif not cat thencat = wd.translate('trackingcat', prop or 'P??')endreturn addCat(cat )endlocal function unknownValue(snak, label)local str = labelif type(str) == "function" thenstr = str(snak)endif (not str) thenif snak.datatype == 'time' thenstr = wd.translate('sometime')elsestr = wd.translate('somevalue')endendif type(str) ~= "string" thenreturn formatError(snak.datatype)endreturn strendlocal function noValue(displayformat)if not displayformat thenreturn wd.translate('novalue')endif type(displayformat) == 'string' thenreturn displayformatendreturn formatError()endlocal function getLangCode(entityid)return modules.langcodes[tonumber(entityid:sub(2))]endlocal function showLang(statement) -- retourne le code langue entre paranthèse avant la valeur (par exemple pour les biblios et liens externes)local mainsnak = statement.mainsnakif mainsnak.snaktype ~= 'value' thenreturn nilendlocal langlist = {}if mainsnak.datavalue.type == 'monolingualtext' thenlanglist = {mainsnak.datavalue.value.language}elseif (not statement.qualifiers) or (not statement.qualifiers.P407) thenreturnelsefor i, j in pairs( statement.qualifiers.P407 ) doif  j.snaktype == 'value' thenlocal langentity = wd.getId(j)local langcode =  getLangCode(langentity)table.insert(langlist, langcode)endendendif (#langlist > 1) or (#langlist == 1 and langlist[1] ~= defaultlang) then -- si c'est en français, pas besoin de le direreturn modules.langmodule.indicationMultilingue(langlist)endend-- === DATE HANDLING ===function wd.addStandardQualifs(str, statement)if (not statement) or (not statement.qualifiers) thenreturn strendif not str thenreturn error()-- what's that ?endif statement.qualifiers.P1480 thenfor i, j in pairs(statement.qualifiers.P1480) dolocal v = wd.getId(j)if (v == "Q21818619") thenstr = wd.translate('approximate-place', str)elseif (v == "Q18122778") or (v == "Q18912752") thenstr = wd.translate('uncertain-information', str)elseif (v == "Q5727902") thenif (statement.mainsnak.datatype == 'time') thenstr = modules.formatDate.fuzzydate(str)elsestr = wd.translate('approximate-value', str)endendendendreturn strendlocal function rangeObject(begin, ending, params)--[[objet comportant un timestamp pour le classement chronologique et deux dateobject (begin et ending)]]-- local timestampif begin thentimestamp = begin.timestampelsetimestamp = ending.timestampendreturn {begin = begin, ending = ending, timestamp = timestamp, type = 'rangeobject'}endlocal function 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 thenparams = {}endlocal newobj = modules.formatDate.splitDate(orig.time, orig.calendarmodel)newobj.precision = params.precision or orig.precisionnewobj.type = 'dateobject'return newobjendlocal function objectToText(obj, params)if obj.type == 'dateobject' thenreturn modules.formatDate.simplestring(obj, params)elseif obj.type == 'rangeobject' thenreturn modules.formatDate.daterange(obj.begin, obj.ending, params)endendfunction wd.getDateFromQualif(statement, qualif)if (not statement) or (not statement.qualifiers) or not (statement.qualifiers[qualif]) thenreturn nilendlocal v = statement.qualifiers[qualif][1]if v.snaktype ~= 'value' then -- que faire dans ce cas ?return nilendreturn dateObject(v.datavalue.value)endfunction wd.getDate(statement)local period = wd.getDateFromQualif(statement, 'P585') -- retourne un dateobjectif period thenreturn periodendlocal begin, ending = wd.getDateFromQualif(statement, 'P580'),  wd.getDateFromQualif(statement, 'P582')if begin or ending thenreturn rangeObject(begin, ending) -- retourne un rangeobject fait de deux dateobjectendreturn nilendfunction wd.getFormattedDate(statement, params)if not statement thenreturn nilendlocal str--cherche la date avec les qualifs P580/P582local datetable = wd.getDate(statement)if datetable thenstr = objectToText(datetable, params)end-- puis limite intérieur / supérieurif not str thenlocal start, ending = wd.getDateFromQualif(statement, 'P1319'), wd.getDateFromQualif(statement, 'P1326')str = modules.formatDate.between(start, ending, params)end -- sinon, le mainsnak, pour les données de type timeif (not str) and (statement.mainsnak.datatype == 'time') thenlocal mainsnak = statement.mainsnakif (mainsnak.snaktype == 'value') or (mainsnak.snaktype == 'somevalue') thenstr = wd.formatSnak(mainsnak, params)endendif str and params and (params.addstandardqualifs ~= '-') thenstr = wd.addStandardQualifs(str, statement)endreturn strend-- 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.chronoSort( claims, inverted )for _, claim in ipairs( claims ) doif not claim.dateSortKey thenlocal snack = claim.mainsnak or claimlocal isoif (snack.snaktype == 'value') and (snack.datatype == 'time') theniso = snack.datavalue.value.timeelseiso = timeFromQualifs(claim, datequalifiers) or '0'end-- transformation en nombre (indication de la base car gsub retourne deux valeurs)iso = tonumber( iso:gsub( '(%d)%D', '%1' ), 10 )claim.dateSortKey = isoendendtable.sort( claims,function ( c1, c2 )if inverted thenreturn c2.dateSortKey < c1.dateSortKeyendreturn c1.dateSortKey < c2.dateSortKeyend)return claimsend-- Fonction qui trie des Claims de type value selon l'ordre de la propriété fournit-- 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.numericPropertySort( claims, propertySort )for _, claim in ipairs( claims ) doif not claim.dateSortKey thenlocal vallocal claimqualifs = claim.qualifiersif claimqualifs thenlocal vals = claimqualifs[propertySort]if vals and vals[1].snaktype == 'value' thenval = vals[1].datavalue.valueendendclaim.dateSortKey = tonumber(val or 0)endendtable.sort(claims,function ( c1, c2 )return c1.dateSortKey < c2.dateSortKeyend)return claimsend-- ===================function wd.getReferences(statement)local refdata = statement.referencesif not refdata thenreturn nilendlocal refs = {}local hashes = {}for i, ref in pairs(refdata) dolocal slocal function hasValue(prop) -- checks that the prop is here with valid valueif ref.snaks[prop] and ref.snaks[prop][1].snaktype == 'value' thenreturn trueendreturn falseendif ref.snaks.P248 then -- cas lorsque P248 (affirmé dans) est utilisé for j, source in pairs(ref.snaks.P248) doif source.snaktype == 'value' thenlocal page, accessdate, quotationif hasValue('P304') then -- page page = wd.formatSnak(ref.snaks.P304[1])endif hasValue('P813') then -- date de consultationaccessdate = wd.formatSnak(ref.snaks.P813[1])endif hasValue('P1683') then -- citationquotation = wd.formatSnak(ref.snaks.P1683[1])endlocal sourceId = wd.getId(source)s = modules.reference.citeitem(sourceId, {['page'] = page, ['accessdate'] = accessdate, ['citation'] = quotation})table.insert(refs, s)table.insert(hashes, ref.hash .. sourceId)endendelseif hasValue('P854') then -- cas lorsque P854 (URL de la référence) est utilisélocal url, title, author, publisher, accessdate, publishdate, publishlang, quotationurl = wd.formatSnak(ref.snaks.P854[1], {text = "-"})if hasValue('P1476') thentitle = wd.formatSnak(ref.snaks.P1476[1])elsetitle = mw.ustring.gsub(url, '^[Hh][Tt][Tt][Pp]([Ss]?):(/?)([^/])', 'http%1://%3')end--todo : handle multiple values for author, etc.if hasValue('P813') then -- date de consultationaccessdate = wd.formatSnak(ref.snaks.P813[1])endif 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])endif hasValue('P123') then -- éditeurpublisher = wd.formatSnak(ref.snaks.P123[1])endif hasValue('P1683') then -- citationquotation = wd.formatSnak(ref.snaks.P1683[1])endif hasValue('P577') then -- date de publicationpublishdate = wd.formatSnak(ref.snaks.P577[1])end if hasValue('P407') then -- langue de l'œuvrelocal id = wd.getId(ref.snaks.P407[1])publishlang = getLangCode(id)ends = modules.cite.lienWeb{titre = title, url = url, auteur = author, editeur = publisher, langue = publishlang, ['en ligne le'] = publishdate, ['consulté le'] = accessdate, ['citation'] = quotation}table.insert(hashes, ref.hash)table.insert(refs, s)elseif ref.snaks.P854 and ref.snaks.P854[1].snaktype == 'value' thens = wd.formatSnak(ref.snaks.P854[1], {text = "-"})table.insert(hashes, ref.snaks.P854[1].hash)table.insert(refs, s)endendif #refs > 0 thenif #hashes == #refs thenreturn refs, hashesendreturn refsendendfunction wd.sourceStr(sources, hashes)if not sources or (#sources == 0) thenreturn nilendlocal useHashes = hashes and #hashes == #sourcesfor i, j in ipairs(sources) dolocal refArgs = {name = 'ref', content = j}if useHashes and hashes[i] ~= '-' thenrefArgs.args = {name = 'wikidata-' .. hashes[i]}endsources[i] = mw.getCurrentFrame():extensionTag(refArgs)endreturn table.concat(sources)endfunction wd.getDataValue(snak, params)if not params thenparams = {}endlocal 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 çaif snak.snaktype ~= 'value' thenreturn nilendlocal datatype = snak.datatypelocal value = snak.datavalue.valuelocal displayformat = params.displayformatif type(displayformat) == 'function' thenreturn displayformat(snak, params)endif datatype == 'wikibase-item' thenreturn wd.formatEntity(wd.getId(snak), params)endif datatype == 'url' thenreturn modules.weblink.makelink(value, params.text)endif datatype == 'math' thenreturn mw.getCurrentFrame():extensionTag( "math", value)endif (datatype == 'string') or (datatype == 'external-id') or (datatype == 'commonsMedia') then -- toutes les données de type string sauf "math"if params.urlpattern thenlocal urlpattern = params.urlpatternif type(urlpattern) == 'function' thenurlpattern = urlpattern(value)endlocal url = mw.ustring.gsub(urlpattern, '$1', (value:gsub('%%', '%%%%'))):gsub(' ', '%%20')value = '[' .. url .. ' ' .. (params.text or value) .. ']'endreturn valueendif datatype == 'time' then -- format example: +00000001809-02-12T00:00:00Zif displayformat == 'raw' thenreturn value.timeelselocal dateobject = dateObject(value, {precision = params.precision})return objectToText(dateobject, params)endendif 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' thenreturn value.latitudeelseif displayformat == 'longitude' thenreturn value.longitudeelselocal coordvalue = mw.clone( value )coordvalue.globe = modules.globes[value.globe] -- transforme l'ID du globe en nom anglais utilisable par geohackreturn coordvalue -- note : les coordonnées Wikidata peuvent être utilisée depuis Module:Coordinates. Faut-il aussi autoriser à appeler Module:Coordiantes ici ?endendif datatype == 'quantity' then -- todo : gérer les paramètres précisionlocal amount, unit = value.amount, value.unitif unit thenunit = unit:match('Q%d+')endlocal rawif displayformat == "raw" thenraw = trueendreturn modules.formatNum.displayvalue(amount, unit,{targetunit = params.targetunit, raw = raw, rounding = params.rounding, showunit = params.showunit or 'short', showlink = params.showlink})endif datatype == 'monolingualtext' thenif value.language == defaultlang thenreturn value.textelsereturn modules.langmodule.langue({value.language, value.text})endendreturn formatError('unknown-datavalue-type' )endfunction wd.stringTable(args) -- like getClaims, but get a list of string rather than a list of snaks, for easier manipulationlocal claims = args.claimsif not claims thenclaims = wd.getClaims(args)endif not claims or claims == {} thenreturn {}, {}endlocal props = {} -- liste des propriétés associété à chaque string pour catégorisation et linkbackfor i, j in pairs(claims) doclaims[i] = wd.formatStatement(j, args)table.insert(props, j.mainsnak.property)endif args.removedupes and (args.removedupes ~= '-') thenclaims = wd.addNewValues({}, claims) -- devrait aussi supprimer de props celles qui ne sont pas utiliséesendreturn claims, propsendfunction wd.getQualifiers(statement, qualifs, params)if not statement.qualifiers thenreturn nilendlocal vals = {}if type(qualifs) == 'string' thenqualifs = wd.splitStr(qualifs)endfor i, j in pairs(qualifs) doif statement.qualifiers[j] thenfor k, l in pairs(statement.qualifiers[j]) dotable.insert(vals, l)endendendif #vals == 0 thenreturn nilendreturn valsendfunction wd.getFormattedQualifiers(statement, qualifs, params)if not params then params = {} endlocal qualiftable = wd.getQualifiers(statement, qualifs)if not qualiftable thenreturn nilendqualiftable = wd.filterClaims(qualiftable, params) or {}for i, j in pairs(qualiftable) doqualiftable[i] = wd.formatSnak(j, params)endreturn modules.linguistic.conj(qualiftable, params.conjtype)endfunction wd.showQualifiers(str, statement, args)local qualifs =  args.showqualifiersif not qualifs thenreturn str -- or error ?endif type(qualifs) == 'string' thenqualifs = wd.splitStr(qualifs)endlocal qualifargs = args.qualifargs or {}-- formatage des qualificatifs = args commençant par "qualif", ou à défaut, les mêmes que pour la valeur principalequalifargs.displayformat = args.qualifdisplayformat or args.displayformatqualifargs.labelformat = args.qualiflabelformat or args.labelformatqualifargs.link = args.qualiflink or args.linkqualifargs.linktopic = args.qualiflinktopic or args.linktopicqualifargs.conjtype = args.qualifconjtypequalifargs.defaultlink = args.qualifdefaultlink or args.defaultlinkqualifargs.defaultlinkquery = args.qualifdefaultlinkquery or args.defaultlinkquerylocal formattedqualifs = wd.getFormattedQualifiers(statement, qualifs, qualifargs)if formattedqualifs and formattedqualifs ~= "" thenstr = str .. " (" .. formattedqualifs .. ")"endreturn strendfunction wd.formatSnak( snak, params )if not params then params = {} end -- pour faciliter l'appel depuis d'autres modulesif snak.snaktype == 'somevalue' thenreturn unknownValue(snak, params.unknownlabel)elseif snak.snaktype == 'novalue' thenreturn noValue(params.novaluelabel)elseif snak.snaktype == 'value' thenreturn wd.getDataValue( snak, params)elsereturn formatError( 'unknown-snak-type' )endendfunction wd.formatStatement( statement, args )if not args thenargs = {}endif not statement.type or statement.type ~= 'statement' thenreturn formatError( 'unknown-claim-type' )endlocal prop = statement.mainsnak.propertylocal str-- special displayformat fif args.statementformat and (type(args.statementformat) == 'function') thenstr = args.statementformat(statement, args)elseif (statement.mainsnak.datatype == 'time') and (statement.mainsnak.dateformat ~= '-') thenif args.displayformat == 'raw' and statement.mainsnak.snaktype == 'value' thenstr = statement.mainsnak.datavalue.value.timeelsestr = wd.getFormattedDate(statement, args)endelseif args.showonlyqualifier and (args.showonlyqualifier ~= '') thenstr = wd.getFormattedQualifiers(statement, args.showonlyqualifier, args)if not str thenreturn nilendif args.addstandardqualifs ~= '-' thenstr = wd.addStandardQualifs(str, statement)endelsestr = wd.formatSnak( statement.mainsnak, args )if args.addstandardqualifs ~= '-' thenstr = wd.addStandardQualifs(str, statement)endend-- ajouts diversif args.showlang == true thenlocal indicateur = showLang(statement)if indicateur thenstr = indicateur .. '&nbsp;' .. strendendif args.showqualifiers thenstr = wd.showQualifiers(str, statement, args)endif args.showdate then -- when "showdate and chronosort are both set, date retrieval is performed twicelocal period = wd.getFormattedDate(statement, args, "-") -- 3 arguments indicate the we should not use additional qualifiers, alrady added by wd.formatStatementif period thenstr = str .. " <small>(" .. period .. ")</small>"endendif args.showsource thenlocal sources, hashes = wd.getReferences(statement)if sources thenlocal source = wd.sourceStr(sources, hashes)if source thenstr = str .. sourceendendendreturn strendfunction wd.addLinkBack(str, id, property)if not id thenid = wd.getEntityIdForCurrentPage()endif not id thenreturn strendif type(property) == 'table' thenproperty = property[1]endid = wd.entityId(id)local class = ''if property thenclass = 'wd_' .. string.lower(property)endlocal icon = '[[File:Blue pencil.svg|%s|10px|baseline|class=noviewer|link=%s]]'local title = wd.translate('see-wikidata-value')local url = mw.uri.fullUrl('d:' .. id, 'uselang=diq')url.fragment = property -- ajoute une #ancre si paramètre "property" définiurl = tostring(url)local v = mw.html.create('span'):addClass(class):wikitext(str):tag('span'):addClass('noprint wikidata-linkback'):wikitext(icon:format(title, url)):allDone()return tostring(v)endfunction 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--=== 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 thenreturn nilendlocal groupedClaims = {}-- regroupe les affirmations par valeur de mainsnaklocal function addClaim(claim)local id = wd.getMainId(claim)for i, j in pairs(groupedClaims) do if (j.id == id) thentable.insert(groupedClaims[i].claims, claim)returnendendtable.insert(groupedClaims, {id = id, claims = {claim}})endfor i, claim in pairs(claims) doaddClaim(claim)endlocal stringTable = {}-- instructions ad hoc pour les paramètres concernant la mise en forme d'une déclaration individuellelocal funs = {{param = "showqualifiers", fun = function(str, claims)local qualifs = {}for i, claim in pairs(claims) dolocal news = wd.getFormattedQualifiers(claim, args.showqualifiers, args)if news thentable.insert(qualifs, news)endendlocal qualifstr = modules.linguistic.conj(qualifs, "qualif-separator")if qualifstr and qualifstr ~= "" thenstr = str .. " (" .. qualifstr .. ")"endreturn strend},{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) dolocal s = wd.getFormattedDate(statement, args, true)if statement then table.insert(dates, s) endendlocal datestr = modules.linguistic.conj(dates)if datestr and datestr ~= "" thenstr = str .. " <small>(" .. datestr .. ")</small>"endreturn strend},{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 foislocal sources = {}local hashes = {}local function dupeRef(old, new)for i, j in pairs(old) doif j == new thenreturn trueendendendfor i, claim in pairs(claims) dolocal refs, refHashes = wd.getReferences(claim)if refs thenfor i, j in pairs(refs) doif not dupeRef(sources, j) thentable.insert(sources, j)local hash = (refHashes and refHashes[i]) or '-'table.insert(hashes, hash)endendendendreturn str .. (wd.sourceStr(sources, hashes) or "")end}}for i, group in pairs(groupedClaims) do -- bricolage pour utiliser les arguments de formatStatementslocal str = wd.formatEntity(group.id, args)for i, fun in pairs(funs) doif args[fun.param] thenstr = fun.fun(str, group.claims, args)endendtable.insert(stringTable, str)endargs.valuetable = stringTablereturn wd.formatStatements(args)endfunction wd.formatStatements( args )--Format statement and concat them cleanlyif args.value == '-' thenreturn nilend-- If a value is already set: use it, except if it's the special value {{WD}} (use wikidata)if args.value and args.value ~= '' thenlocal valueexpl = wd.translate("activate-query")if args.value ~= valueexpl thenreturn args.valueend-- There is no value set, and args.expl disables wikidata on empty valueselseif args.expl thenreturn nilendif args.grouped and args.grouped ~= '' thenargs.grouped = falsereturn formatStatementsGrouped(args)endlocal valuetable = args.valuetable -- dans le cas où les valeurs sont déjà formtéeslocal props -- les prorpriétés réellement utilisées (dans certainse cas, ce ne sont pas toutes celles de ags.propertyif not valuetable then -- cas le plus courantvaluetable, props = wd.stringTable(args)endlocal str = modules.linguistic.conj(valuetable, args.conjtype)if not str thenreturn args.defaultendif not props thenprops = wd.splitStr(args.property)[1]endif args.ucfirst ~= '-' thenstr = modules.linguistic.ucfirst(str)endif args.addcat and (args.addcat ~= '-') thenstr = str .. wd.addTrackingCat(props)endif args.linkback and (args.linkback ~= '-') thenstr = wd.addLinkBack(str, args.entity, props)endif args.returnnumberofvalues thenreturn str, #valuetableendreturn strendfunction wd.formatAndCat(args)if not args thenreturn nilendargs.linkback = args.linkback or trueargs.addcat = trueif args.value then -- do not ignore linkback and addcat, as formatStatements doif args.value == '-' thenreturn nilendlocal val = args.value .. wd.addTrackingCat(args.property)val = wd.addLinkBack(val, args.entity, args.property)return valend return wd.formatStatements( args )endfunction wd.getTheDate(args)local claims = wd.getClaims(args)if not claims thenreturn nilendlocal formattedvalues = {}for i, j in pairs(claims) dolocal v = wd.getFormattedDate(j, args)if v thentable.insert(formattedvalues, v )endendlocal val = modules.linguistic.conj(formattedvalues)if not val thenreturn nilendif args.addcat == true thenval = val .. wd.addTrackingCat(args.property)endval = wd.addLinkBack(val, args.entity, args.property)return valendfunction wd.keyDate (event, item, params)params = params or {}params.entity = itemif type(event) == 'table' thenfor i, j in pairs(event) doparams.targetvalue = nil -- réinitialisation barbare des paramètres modifiéslocal s = wd.keyDate(j, item, params)if s thenreturn sendendelseif 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 = eventparams.addcat = params.addcat or truereturn wd.getTheDate(params)elseif string.sub(event, 1, 1) == 'P'  then -- on demande une propriétéparams.property = eventreturn wd.formatAndCat(params)elsereturn formatError('invalid-entity-id', event)endendfunction wd.mainDate(entity)-- essaye P580/P582local args = {entity = entity, addcat = true}args.property = 'P580'local startpoint = wd.formatStatements(args)args.property = 'P582'local endpoint = wd.formatStatements(args)local strif (startpoint or endpoint) thenstr = modules.formatDate.daterange(startpoint, endpoint, params)str = wd.addLinkBack(str, entity, 'P582')return strend-- défaut : P585args.property = {'P585', 'P571'}args.linkback = truereturn wd.formatStatements(args)end-- === FUNCTIONS FOR TRANSITIVE PROPERTIES ===function wd.getIds(item, query)query.excludespecial = truequery.displayformat = 'raw'query.entity = itemquery.addstandardqualifs = '-'return wd.stringTable(query)end-- recursively adds a list of qid to an existing list, based on the results of a queryfunction wd.addVals(list, query, maxdepth, maxnodes, stopval)maxdepth = tonumber(maxdepth) or 10maxnodes = tonumber(maxnodes) or 100if (maxdepth < 0) thenreturn listendif stopval and wd.isHere(list, stopval) thenreturn listendlocal origsize = #listfor i = 1, origsize do-- tried a  "checkpos" param instead of starting to 1 each time, but no impact on performancelocal candidates = wd.getIds(list[i], query)list = wd.addNewValues(list, candidates, maxnodes, stopval)if list[#list] == stopval thenreturn listendif #list >= maxnodes thenreturn listendendif (#list == origsize) thenreturn listendreturn wd.addVals(list, query, maxdepth - 1, maxnodes, stopval, origsize + 1)end-- returns a list of items transitively matching a query (orig item is not included in the list)function wd.transitiveVals(item, query, maxdepth, maxnodes, stopval)maxdepth = tonumber(maxdepth) or 5if type(query) == "string" thenquery = {property = query}end-- récupération des valeurslocal vals = wd.getIds(item, query)if not vals thenreturn nilendlocal v = wd.addVals(vals, query, maxdepth - 1, maxnodes, stopval) if not v thenreturn nilend-- réarrangement des valeursif query.valorder == "inverted" thenlocal a = {}for i, j in pairs(v) dotable.insert(a, 1, j)endv = aendreturn vend-- returns true if an item is the value of a query, transitivelyfunction wd.inTransitiveVals(searchedval, sourceval, query, maxdepth, maxnodes )local vals = wd.transitiveVals(sourceval, query, maxdepth, maxnodes, searchedval )if (not vals) then return falseendfor _, val in ipairs(vals) doif (val == searchedval) thenreturn trueendendreturn falseend-- returns true if an item is a superclass of another, based on P279function wd.isSubclass(class, item, maxdepth)local query = {property = 'P279'}if class == item then -- item is a subclass of itself iff it is a classif wd.getIds(item, query) thenreturn trueendreturn falseendreturn 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-- rank = 'valid' would seem to make sense, but it would need to check for date qualifiers as some P31 values have begin or end datefunction wd.isInstance(targetclass, item, maxdepth)maxdepth = maxdepth or 10local directclasses = wd.transitiveVals(item, {property = 'P31'}, 1)if not directclasses thenreturn falseend for i, class in pairs(directclasses) doif wd.isSubclass(targetclass, class, maxdepth - 1) thenreturn trueendendreturn falseend-- 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 Canadafunction wd.findVal(sourceitem, targetclass, query, recursion, instancedepth)if type(query) == "string" thenquery = {property = query}endlocal candidates = wd.getIds(sourceitem, query)if candidates thenfor i, j in pairs(candidates) doif wd.isInstance(targetclass, j,  instancedepth) thenreturn jendendif not recursion thenrecursion = 3elserecursion = recursion - 1endif recursion < 0 thenreturn nilendfor i, candidate in pairs(candidates) doreturn wd.findVal(candidate, targetclass, query, recursion, instancedepth)endendend-- === VARIA ===function wd.getDescription(entity, lang)lang = lang or defaultlanglocal descriptionif lang == defaultlang thenreturn  mw.wikibase.descriptionl(qid)endif not entity.descriptions thenreturn wd.translate('no description')endlocal descriptions = entity.descriptionsif not descriptions thenreturn nilendif descriptions[lang] thenreturn descriptions[delang].valueendreturn entity.idendfunction wd.Dump(entity)entity = wd.getEntity(entity)if not entity thenreturn formatError("entity-param-not-provided")endreturn "<pre>"..mw.dumpObject(entity).."</pre>"endfunction wd.frameFun(frame)local args = frame.argslocal funname = args[1]table.remove(args, 1)return wd[funname](args)endreturn wd