Modul:Wikidata
Dokumentationen för denna modul kan skapas på Modul:Wikidata/dok
local p = {}
local linguistic = require('Module:Linguistique')
local dates = require('Module:Wikidata/Dates')
local langmodule = require('Module:Langue')
local convertlangcode = require('Module:Dictionnaire Wikidata/Codes langue')
local formatText = require('Module:Format')
lang = 'sv' -- peut-être écrasé par args.lang
local i18n = {
["errors"] = {
["property-param-not-provided"] = "property parameter missing",
["qualifier-param-not-provided"] = "qualifier parameter missing",
["entity-not-found"] = "entity not found",
["unknown-claim-type"] = "unknown claim type",
["unknown-snak-typeg"] = "unknown snak type",
["unknown-datavalue-type"] = "unknown datavalue type.",
["unknown-entity-type"] = "unknown entity type",
["invalid-id"] = "invalid ID",
},
["no-label"] = "pas de libellé",
['no description'] = "pas description",
["novalue"] = "-",
["somevalue"] = "inconnu",
['to translate'] = 'Page utilisant des données de Wikidata à traduire',
["trackingcat"] = 'Page utilisant des données de Wikidata',
["warnDump"] = "[[Category:Called function 'Dump' from module Wikidata]]"
}
local function formatError( key )
return error(i18n.errors[key])
end
function p.addtrackingcat(prop, cat) -- doit parfois être appelé par d'autres modules
if not prop and not cat then
return error("no property provided")
end
if not cat then
cat = i18n.trackingcat .. '/' .. string.upper(prop)
end
return '[[Category:' .. 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 formatTheUnknown() -- voir si on peut accorder/adapter l'usage de "inconnu"
return i18n.somevalue
end
local function samevalue(snak, target)
if snak.snaktype == 'value' and p.getRawvalue(snak) == target then
return true
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 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 val = convertlangcode[j.datavalue.value['numeric-id']]
table.insert(langlist, val)
end
end
end
if (#langlist > 1) or (#langlist == 1 and langlist[1] ~= 'fr') then -- si c'est en français, pas besoin de le dire
return langmodule.indicationMultilingue(langlist)
end
end
local function getEntity( val )
if type(val) == 'table' then
return val
end
return mw.wikibase.getEntityObject(val)
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 = p.formatStatement(j, params)
end
return statements
end
local function tableToText(values, params) -- takes a list of already formatted values and make them a text
if not values then
return nil
end
return linguistic.quickconj( values, params.conjtype)--linguistic.conj( values, params.lang, params.conjtype )
end
function p.getDate(statement)
--[[
returns an object containing a timestamp for easy sorting, and other data
2 types of object
dateobject
{timestamp = string, year = number, month = number, day = number, calendar = string}
rangeobject
{timestamp = string, begin = dateobject, ending = dateobject}
]]--
local q = statement.qualifiers
if not q or not (q.P585 or q.P580 or q.P582) then
return nil
end
-- si p585 : dateobject
if q.P585 and q.P585[1].snaktype == 'value' then -- P585: punctual date
return dates.dateobject(q.P585[1].datavalue.value)
end
-- si p580 ou P582 : rangeobject avec une date de début et/ou une date de fin
if q.P582 and q.P582[1].snaktype == 'value' then
ending = dates.dateobject(q.P582[1].datavalue.value)
end
if q.P580 and q.P580[1].snaktype == 'value' then
begin = dates.dateobject(q.P580[1].datavalue.value)
end
return dates.rangeobject(begin, ending)
end
function p.getFormattedDate(statement, params)
local datetable = p.getDate(statement)
if not datetable then
return nil
end
return dates.objecttotext(datetable, params)
end
local function hastargetvalue(claim, target)
if target == nil then
return true
end
return samevalue(claim.mainsnak, target)
end
local function hasrank(claim, target)
if target == 'valid' then
return hasrank(claim, 'preferred') or hasrank(claim, 'normal')
end
if claim.rank == target then
return true
end
return false
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 hasqualifier(claim, qualifier, qualifiervalues)
qualifier = string.upper(qualifier)
if not qualifier then -- si aucun qualificatif est demandé, ça passe
return true
end
if not claim.qualifiers or not claim.qualifiers[qualifier] then
return false
end
if (not qualifiervalues) or (qualifiervalues == {}) then
return true -- si aucune valeur spécifique n'est exigée
end
if type(qualifiervalues) == 'string' then
qualifiervalues = {qualifiervalues}
end
for i, j in pairs(claim.qualifiers[qualifier]) do
local val = p.getRawvalue(j)
for k, l in pairs(qualifiervalues) do
if l == val then
return true
end
end
end
return false
end
local function hassource(statement, source, sourceproperty)
sourceproperty = string.upper(sourceproperty or 'P248')
local sourcevalue = string.upper(source or '')
if not statement.references or not statement.references[sourceproperty] then
return false
end
if not source then -- si toutes les sources sont valides, du moment qu'elles utilisent sourceproperty
return true
end
for i, j in pairs(statement.references[sourceproperty]) do
if p.getRawvalue(j) == source then
return true
end
end
return true
end
local function hasdate(statement)
if statement.qualifiers and (statement.qualifiers['P585'] or statement.qualifiers['P580'] or statement.qualifiers['P582']) then
return true
end
return false
end
local function isinlanguage(snak, lang) -- ne fonctionne que pour les monolingualtext / étendre aux autres types en utilisant les qualifiers ?
if snak.snaktype == 'value' and snak.datavalue.type == 'monolingualtext' and snak.datavalue.value.language == lang then
return true
end
return false
end
local function isSpecial(snak)
if snak.snaktype == 'value' then
return false
end
return true
end
local function numval(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 #claims <= numval then
return claims
end
local newclaims = {}
while #newclaims < numval do
table.insert(newclaims, claims[#newclaims + 1])
end
return newclaims
end
local function comparedate(a, b) -- returns true if a is earlier than B or if a has a date but not b
if a and b then
return a.timestamp < b.timestamp
elseif a then
return true
end
end
local function chronosort(claims, inverted)
table.sort(claims, function(a,b)
local timeA = p.getDate(a)
local timeB = p.getDate(b)
if inverted then
return comparedate(timeB, timeA)
else
return comparedate(timeA, timeB)
end
end
)
return claims
end
function p.sortclaims(claims, sorttype)
if sorttype == 'chronological' then
return chronosort(claims)
elseif sorttype == 'inverted' then
return chronosort(claims, true)
elseif type(sorttype) == 'function' then
table.sort(claims, sorttype)
return claims
end
return claims
end
function p.getRawvalue(snak)
return p.getDatavalue(snak, {format = 'raw'})
end
function p.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.datavalue.type
local value = snak.datavalue.value
local displayformat = params.format or params.displayformat -- params.format is deprecated
if type(displayformat) == 'function' then
return displayformat(snak, params)
end
if datatype == 'wikibase-entityid' then
if displayformat == 'raw' then
return "Q" .. tostring(value['numeric-id'])
elseif speciallabels and speciallabels['Q' .. value['numeric-id']] then
return speciallabels['Q' .. value['numeric-id']]
else
return p.formatEntity('Q' .. value['numeric-id'], params)
end
elseif datatype == 'string' then
if params.displayformat == 'weblink' then
return require('Module:Weblink').makelink(value, params.showntext)
elseif params.urlpattern then
value = '[' .. mw.ustring.gsub(params.urlpattern, '$1', value) .. ' ' .. (params.text or value) .. ']'
end
return value
elseif 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 dates.objecttotext(dates.dateobject(value, {precision = precision}), {linktopic = params.linktopic})
end
elseif datatype == 'globecoordinate' then
-- retourne une table avec clés latitude, longitude, précision et globe à formater par un autre module (à changer ?)
value.globe = require('Module:Wikidata/Globes')[value.globe] -- transforme l'ID du globe en nom anglais utilisable par geohack
if formatting == 'latitude' then
return value.latitude
elseif formatting == 'longitude' then
return value.longitude
else
return value -- note : les coordonnées Wikidata peuvent être utilisée depuis Module:Coordinates. Faut-il aussi autoriser à appeler Module:Coordiantes ici ?
end
elseif datatype == 'quantity' then -- todo : gérer les paramètre précision
if displayformat == 'raw' then
return value.amount
else
local str = string.sub(value.amount,2) --
return formatText.do_formatnum({str})
end
elseif datatype == 'monolingualtext' then
return langmodule.langue({value.language, value.text})
else
return formatError('unknown-datavalue-type' )
end
end
local function getMultipleClaims(args)
local newargs = args
local claims = {}
for i, j in pairs(args.property) do
newargs.property = j
local newclaims = p.getClaims(args)
for k, l in pairs(newclaims) do
table.insert(claims, l)
end
end
return claims
end
function p.getClaims( args ) -- returns a table of the claims matching some conditions given in args
args = removeblanks(args)
if args.claims then -- if claims have already been set, return them
return args.claims
end
if not args.property then
return formatError( 'property-param-not-provided' )
end
if type(args.property) == 'table' then
return getMultipleClaims(args)
end
--Get entity
if args.item then args.entity = args.item end -- synonyms
local entity = args.entity
if type(entity) ~= 'table' then
entity = getEntity( args.entity )
end
if (not entity) or (not entity.claims) then
return nil
end
local property = string.upper(args.property)
if not entity.claims[property] then
return nil
end
if not args.rank then
args.rank = 'best'
end
local claims = {}
-- ~= '' lorsque le paramètre est écrit mais laissé blanc dans une fonction frame
for i, statement in pairs(entity.claims[property]) do
if
(
not args.excludespecial
or
not (isSpecial(statement.mainsnak))
)
and
(
not args.targetvalue
or
hastargetvalue(statement, args.targetvalue)
)
and
(
not args.qualifier
or
hasqualifier(statement, args.qualifier, args.qualifiervalue or args.qualifiervalues)
)
and
(
not args.source
or
hassource(statement, args.source, args.sourceproperty)
)
and
not args.isinlanguage
or
isinlanguage(statement.mainsnak, args.isinlanguage)
and
args.rank == 'best' -- rank == best est traité à a fin
or
hasrank(statement, rank)
then
table.insert(claims, statement)
end
end
if #claims == 0 then
return nil
end
if args.rank == 'best' then
claims = bestranked(claims)
end
if args.sorttype then
claims = p.sortclaims(claims, args.sorttype)
end
if args.numval then
return numval(claims, args.numval)
end
return claims
end
function p.formatClaimList(claims, args)
if not claims then
return nil
end
for i, j in pairs(claims) do
claims[i] = p.formatStatement(j, args)
end
return claims
end
function p.stringTable(args) -- like getClaims, but get a list of string rather than a list of snaks, for easier manipulation
local claims = p.getClaims(args)
return p.formatClaimList(claims, args)
end
local function getQualifiers(statement, qualifs, params)
if not statement.qualifiers then
return nil
end
local vals = {}
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 p.getFormattedQualifiers(statement, qualifs, params)
if not params then params = {} end
local qualiftable = getQualifiers(statement, qualifs)
if not qualiftable then
return nil
end
for i, j in pairs(qualiftable) do
qualiftable[i] = p.formatSnak(j, params)
end
return linguistic.conj(qualiftable)
end
function p.formatStatement( statement, args )
if not args then
args = {}
end
if not statement.type or statement.type ~= 'statement' then
return formatError( 'unknown-claim-type' )
end
local str = p.formatSnak( statement.mainsnak, args )
if args.showlang == true then
str = (showlang(statement) or '') .. str
end
if args.showqualifiers then
local qualifs = args.showqualifiers
if type(qualifs) == 'string' then
qualifs = mw.text.split(qualifs, ',')
end
local qualifargs = {}
-- formatage des qualificateur = 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
local formattedqualifs = p.getFormattedQualifiers(statement, qualifs, qualifargs)
if formattedqualifs then
str = str .. linguistic.inparentheses(formattedqualifs, lang)
end
end
if args.showdate then -- when "showdate and chronosort are both set, date retrieval is performed twice
local timedata = p.getDate(statement)
if timedata then
local formatteddate = dates.objecttotext(timedata, args)
formattteddate = linguistic.inparentheses(formatteddate, lang)
str = str .. '<small>' .. formattteddate ..'</small>'
end
end
if args.showsource and statement.references then --[[needs abritrary access
local sourcestring = ''
for i, ref in pairs(statement.references) do
if ref.snaks.P248 then
for j, source in pairs(ref.snaks.P248) do
if source.snaktype == 'value' then
local page
if ref.snaks.P304 and ref.snaks.P304[1].snaktype == 'value' then
page = ref.snaks.P304[1].datavalue.value
end
local s = require('Module:Cite/sandbox').citeitem('Q' .. source.datavalue.value['numeric-id'], lang, page)
s = mw.getCurrentFrame():extensionTag( 'ref', s )
sourcestring = sourcestring .. s
end
end
elseif ref.snaks.P854 and ref.snaks.P854[1].snaktype == 'value' then
s = mw.getCurrentFrame():extensionTag( 'ref', formatLink(ref.snaks.P854[1].datavalue.value))
sourcestring = sourcestring .. s
end
end
str = str .. sourcestring ]]--
end
return str
end
function p.formatSnak( snak, params )
if not args then args = {} end -- pour faciliter l'appel depuis d'autres modules
if snak.snaktype == 'somevalue' then
return formatTheUnknown()
elseif snak.snaktype == 'novalue' then
return i18n['novalue'] --todo
elseif snak.snaktype == 'value' then
return p.getDatavalue( snak, params)
else
return formatError( 'unknown-snak-type' )
end
end
function p._getLabel(entity, default, inlanguage)
local label
if not entity then
return nil
end
if inlanguage then -- cherche dans l'élément complet s'il est déjà chargé, ou s'il faut une libellé non-français, inacessible par mw.wikibase.label
entity = getEntity(entity)
end
if type(entity) == 'table' then
if entity and entity.labels and entity.labels[lang] then
label = entity.labels[lang].value
end
else
label = mw.wikibase.label(entity)
end
if label then
return label
end
if default == 'nolabel' then
return i18n['no-label']
end
return entity.id
end
function p._getDescription(entity, lang)
if type(entity) ~= 'table' then
entity = getEntity(entity)
end
if not entity.descriptions then
return i18n['no description']
end
local descriptions = entity.descriptions
if not descriptions then
return nil
end
if descriptions[lang] then
return descriptions[lang].value
end
local fblist = require('Module:Fallback').fblist(lang) -- list of fallback languages in no label in the desired language
for i, j in pairs (mw.language.getFallbacksFor(lang)) do
if descriptions.lang then
return descriptions[lang].value
end
end
if default == 'nolabel' then
return i18n['no-label']
end
return entity.id
end
local function formattedLabel(label, entity, args)
if not args then
args = {}
end
if (args.labelformat and type(args.labelformat) == 'function') then
label = args.labelformat(label)
end
if args.link== '-' then
return label
end
local link = mw.wikibase.sitelink( entity )
if not link then
link = 'd:' .. entity
end
return '[[' .. link .. '|' .. label .. ']]'
end
function p.getid(snak)
if snak.snaktype == 'value' then
return 'Q' .. snak.datavalue.value['numeric-id']
end
end
function p.getmainid(claim)
if claim then
return p.getid(claim.mainsnak)
end
end
function p.formatEntity( entity, args )
local label = p._getLabel(entity, lang)
if not label then
label = entity
return formattedLabel(label, entity, args) .. '[[Category:' .. i18n['to translate'] .. ']]'
end
return formattedLabel(label, entity, args)
end
function p.getLabel(frame) -- simple for simple templates like {{Q|}}}
local args = frame.args
local entity = args.entity
local lang = lang
if args.lang and args.lang ~= '' then
lang = args.lang
end
if string.sub(entity, 1, 10) == 'Property:P' then
entity = string.sub(entity, 10)
elseif (string.sub(entity, 1, 1) ~= 'P' and string.sub(entity, 1, 1) ~= 'Q') or (not tonumber(string.sub(entity, 2))) then
return i18n.errors['invalid-id']
end
if not args.link or args.link == '' then -- by default: no link
args.link = '-'
end
if args.link == '-' then
return p._getLabel(entity, lang) or i18n.errors['invalid-id']
else
lang = lang
return p.formatEntity(entity, args)
end
end
function p.addLinkback(str, entity, property)
local id = entity
if not id then return
error('no entity provided')
end
if type(id) == 'table' then
id = entity.id
end
return str .. ' ' .. tostring(mw.html.create('span'):wikitext('<small><small><small>[[d:' .. id .. '#' .. property .. '|+/-]]</small></small></small>'))
end
function p._formatStatements( args )--Format statement and concat them cleanly
if args.value == '-' then
return nil
end
--If a value is already set, use it
if args.value and args.value ~= '' then
return args.value
end
args.entity = args.entity or args.item
if (type(args.entity) == 'string') or (not args.entity) then
args.entity = getEntity(entity)
end
local valuetable = p.stringTable(args)
local valuestr = tableToText(valuetable, args)
if valuestr and args.linkback then
valuestr = p.addLinkback(valuestr, args.entity, args.property)
end
return valuestr
end
function p.showQualifier( args )
local qualifs = args.qualifiers or args.qualifier
if type(qualifs) == 'string' then
qualifs = mw.text.split(qualifs, ',')
end
if not qualifs then
return formatError( 'property-param-not-provided' )
end
local claims = p.getClaims(args)
if not claims then
return nil
end
local str = ''
for i, j in pairs(claims) do
local new = p.getFormattedQualifiers(j, qualifs, args) or ''
str = str .. new
end
return str
end
function p._formatAndCat(args)
if not args then
return nil
end
args.linkback = args.linkback or true
local val = p._formatStatements( args )
if val then
return val .. p.addtrackingcat(args.property)
end
end
function p.getTheDate(args)
local claims = p.getClaims(args)
if not claims then
return nil
end
local formattedvalues = {}
for i, j in pairs(claims) do
table.insert(formattedvalues, p.getFormattedDate(j))
end
local val = linguistic.conj(formattedvalues)
if not val then
return nil
end
if args.addcat == true then
val = val .. p.addtrackingcat(args.property)
end
val = p.addLinkback(val, args.entity, args.property)
return val
end
---FONCTIONS depuis le FRAME
function p.getaDate(frame)
return p.getTheDate(frame.args)
end
function p.getQualifier( frame )
return p.showQualifier(frame.args)
end
function p.getDescription(frame) -- simple for simple templates like {{Q|}}}
local entity = frame.args.entity
if frame.args.lang then
lang = frame.args.lang
end
if (string.sub(entity, 1, 1) ~= 'P' and string.sub(entity, 1, 1) ~= 'Q') or (not tonumber(string.sub(entity, 2))) then
return i18n.errors['invalid-id']
end
return p._getDescription(entity, lang) or i18n.errors['invalid-id']
end
function p.numOfClaims(frame)
local claims = p.getClaims(frame.args)
if claims then
return #claims
else
return 0
end
end
function p.formatStatements( frame )
local args = {}
if frame == mw.getCurrentFrame() then
args = frame:getParent().args -- paramètres du modèle appelant (est-ce vraiment une bonne idée ?)
for k, v in pairs(frame.args) do
args[k] = v
end
else
args = frame
end
return p._formatStatements( args )
end
function p.formatAndCat(frame)
local args = {}
if frame == mw.getCurrentFrame() then
args = frame:getParent().args -- paramètres du modèle appelant (est-ce vraiment une bonne idée ?)
for k, v in pairs(frame.args) do
args[k] = v
end
else
args = frame
end
return p._formatAndCat( args )
end
function p.Dump(frame)
local data = mw.wikibase.getEntityObject()
if not data then
return i18n.warnDump
end
local f = frame.args[1] and frame or frame:getParent()
local i = 1
while true do
local index = f.args[i]
if not index then
return "<pre>"..mw.dumpObject(data).."</pre>".. i18n.warnDump
end
data = data[index] or data[tonumber(index)]
if not data then
return i18n.warnDump
end
i = i + 1
end
end
return p