Modul:LinkedWiki: Unterschied zwischen den Versionen

Aus ÖsterreichWiki
Zur Navigation springen Zur Suche springen
K (1 Version importiert: Bei Wikipedia von Löschung bedroht)
Keine Bearbeitungszusammenfassung
Zeile 1: Zeile 1:
-- module local variables
--script that retrieves basic data stored in Wikidata, for the datamodel, see https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua
local wiki =
{
langcode = mw.language.getContentLanguage().code
}


-- internationalisation
local p = {}
local i18n =
{
["errors"] =
{
["property-not-found"] = "Eigenschaft nicht gefunden.",
["entity-not-found"] = "Wikidata-Eintrag nicht gefunden.",
["unknown-claim-type"] = "Unbekannter Aussagentyp.",
["unknown-entity-type"] = "Unbekannter Entity-Typ.",
["qualifier-not-found"] = "Qualifikator nicht gefunden.",
["site-not-found"] = "Wikimedia-Projekt nicht gefunden.",
["invalid-parameters"] = "Ungültige Parameter.",
["module-not-loaded"] = "Loading of additional module failed."
},
["datetime"] =
{
-- $1 is a placeholder for the actual number
[0] = "$1 Mrd. Jahren", -- precision: billion years
[1] = "$100 Mio. Jahren", -- precision: hundred million years
[2] = "$10 Mio. Jahren", -- precision: ten million years
[3] = "$1 Mio. Jahren", -- precision: million years
[4] = "$100.000 Jahren", -- precision: hundred thousand years
[5] = "$10.000 Jahren", -- precision: ten thousand years
[6] = "$1. Jahrtausend", -- precision: millenium
[7] = "$1. Jahrhundert", -- precision: century
[8] = "$1er", -- precision: decade
-- the following use the format of #time parser function
[9]  = "Y", -- precision: year,
[10] = "F Y", -- precision: month
[11] = "j. F Y", -- precision: day
[12] = 'j. F Y, G "Uhr"', -- precision: hour
[13] = "j. F Y G:i", -- precision: minute
[14] = "j. F Y G:i:s", -- precision: second
["beforenow"] = "vor $1", -- how to format negative numbers for precisions 0 to 5
["afternow"] = "in $1", -- how to format positive numbers for precisions 0 to 5
["bc"] = '$1 "v.Chr."', -- how print negative years
["ad"] = "$1" -- how print positive years
},
["monolingualtext"] = '<span lang="%language">%text</span>',
["FETCH_WIKIDATA"] = "ABFRAGE_WIKIDATA"
}


--important properties
local linguistic = require('Module:Linguistic')
local propertyId =
--local formatDate = require('Module:Complex date') only loaded when needed to save memory in large pages like Wikidata:List of properties/all
{
local fb = require('Module:Fallback')
["starttime"] = "P580",
local i18nmessages = mw.loadData('Module:i18n/wikidata')
["endtime"] = "P582"
}


local formatchar =
-- Wiki-specific parameters
{
local defaultlang = mw.getCurrentFrame():preprocess("{{int:lang}}")
[10] = {"n","m","M","F","xg"}, --precision: month
local defaultlink = 'wikidata'
[11] = {"W","j","d","z","D","l","N","w"}, --precision: day
[12] = {"a","A","g","h","G","H"}, --precision: hour
[13] = {"i"}, --precision: minute
[14] = {"s","U"} --precision: second
}


local function printError(code)
local function i18n(str)
return '<span class="error">' .. (i18n.errors[code] or code) .. '</span>'
local message = i18nmessages[str]
if type(message) == 'string' then
return message
end
return fb._langSwitch(message, defaultlang) .. ''
end
end


-- the "qualifiers" and "snaks" field have a respective "qualifiers-order" and "snaks-order" field
local function formatError( key, text )
-- use these as the second parameter and this function instead of the built-in "pairs" function
return error(i18n(key) .. (text or ''))
-- to iterate over all qualifiers and snaks in the intended order.
end
local function orderedpairs(array, order)
if not order then return pairs(array) end


-- return iterator function
local function addTrackingCat(prop, cat)
local i = 0
if not prop and not cat then
return function()
return error("no property provided")
i = i + 1
end
if order[i] then
if not cat then
return order[i], array[order[i]]
cat = i18nmessages.trackingcat .. '/' .. string.upper(prop)
end
end
end
return '[[Category:' .. cat .. ']]'
end
end


-- Function to check whether a certain item is a parent of a given item.
local function removeBlanks(args)
-- If pExitItem is reached without finding the searched parent item, the search stops.
for i, j in pairs(args) do -- does not work ??
-- A parent is connected via P31 or P279.
if (j == '') or (j == '-') then args[i] = nil end
-- Attention: very intensive function, use carefully!
end
local function isParent(pItem, pParent, pExitItem, pMaxDepth, pDepth)
return args
if not pDepth then pDepth = 0 end
end


if type(pItem) == "number" then pItem = "Q" .. pItem end
local function formatTheUnknown() -- voir si on peut accorder/adapter l'usage de "inconnu"
return i18n('somevalue')
end


local entity = mw.wikibase.getEntity(pItem)
local function isSpecial(snak)
if not entity then return false end
return snak.snaktype ~= 'value'
end


local claims31
local function sameValue(snak, target)
local claims279
return not isSpecial(snak) and p.getRawvalue(snak) == target
if entity.claims then
end
claims31 = entity.claims[mw.wikibase.resolvePropertyId('P31')]
 
claims279 = entity.claims[mw.wikibase.resolvePropertyId('P279')]
local function showLang(statement, str) -- TODO (not yet in proper format)
--adds a lang indication at the start of the string, based on data in statement
local mainsnak = statement.mainsnak
if isSpecial(mainsnak) then
return str
end
 
local langlist = {}
if mainsnak.datavalue.type == 'monolingualtext' then
langlist = {mainsnak.datavalue.value.language}
elseif statement.qualifiers and statement.qualifiers.P407 then
local convertlangcode = mw.loadData('Module:Dictionary/lang codes')
for i, j in pairs( statement.qualifiers.P407 ) do
if not isSpecial(j) then
local val = convertlangcode[j.datavalue.value['numeric-id']]
table.insert(langlist, val)
end
end
end
if #langlist == 0 then
return str
else
else
return false
return '(' .. table.concat(langlist) .. ')' .. str
end
end
if not claims31 and not claims279 then return false end
end


local parentIds = {}
function p.getEntity( val )
if claims31 and #claims31 > 0 then
if type(val) == 'table' then
for i, v in ipairs(claims31) do parentIds[#parentIds+1] = getSnakValue(v.mainsnak, "numeric-id") end
return val
end
end
if claims279 and #claims279 > 0 then
return mw.wikibase.getEntityObject(val)
for i, v in ipairs(claims279) do parentIds[#parentIds+1] = getSnakValue(v.mainsnak, "numeric-id") end
end
 
-- DATE FUNCTIONS
local function splitTimestamp(timestamp, calendar)
local pattern = "(%W)(%d+)%-(%d+)%-(%d+)"
local era, year, month, day = timestamp:match(pattern)
 
if calendar == 'julian' then
--todo  year, month, day = formatdate.gregorianToJulian( era .. year, month, day )
end
end


-- check if searched parent or exit item is reached or do recursive call
return {day = day, month = month, year = year, era = era, timestamp = timestamp, type = 'dateobject'}
if not parentIds[1] or #parentIds == 0 then return false end
end
local itemString = ""
local result = nil
for i, v in ipairs(parentIds) do
if not v then return false end
itemString = "Q" .. v


if itemString == pParent then
local function rangeObject(begin, ending)
-- successful!
local timestamp
return true
if begin then
elseif itemString == pExitItem or itemString == "Q35120" then
timestamp = begin.timestamp
-- exit if either "exit item" or node item (Q35120) is reached
elseif ending then
return false
timestamp = ending.timestamp
else
end
if pDepth+1 < pMaxDepth then
return {begin = begin, ending = ending, timestamp = timestamp, type = 'rangeobject'}
result = isParent(itemString, pParent, pExitItem, pMaxDepth, pDepth+1)
end
else return false end


if result == true then return result end
local function dateObject(orig, params) -- transforme un snak en un nouvel objet utilisable par Module:Date complexe
end
if not params then
params = {}
end
end
do return false end
 
local newobj = splitTimestamp(orig.time, orig.calendar) -- initalise l'object en mettant la valeur des dates
 
newobj.precision = params.precision or orig.precision
newobj.type = 'dateobject'
return newobj
end
end


local function printDatavalueCoordinate(data, parameter)
local function formatDatepoint(obj, params) -- TO IMPROVE
-- data fields: latitude [double], longitude [double], altitude [double], precision [double], globe [wikidata URI, usually http://www.wikidata.org/entity/Q2 [earth]]
if not obj then
if parameter then
return nil
if parameter == "globe" then data.globe = mw.ustring.match(data.globe, "Q%d+") end -- extract entity id from the globe URI
end
return data[parameter]
local formatDate = require('Module:Complex date')
else
local lang = params.lang or defaultlang
return data.latitude .. "/" .. data.longitude -- combine latitude and longitude, which can be decomposed using the #titleparts wiki function
local precision = math.min(obj.precision, params.precision or 15) -- if we don't want to show the value to its full detail
if precision >= 11 then
return formatDate.complex_date{args={date1 = obj.year .. '-' .. obj.month .. '-' .. obj.day, lang= lang}}
elseif precision == 10 then
return formatDate.complex_date{args={date1 = obj.year .. '-' .. obj.month, lang= lang}}
elseif precision == 9 then
return formatDate.complex_date{args={date1 = tostring(obj.year), lang= lang}}
elseif precision == 8 then
return formatDate.complex_date{args={date1 = string.sub(tostring(obj.year), 1, 3) .. '0', lang = lang, precision = 'decade'}}
elseif precision == 7 then
return formatDate.complex_date{args={date1 = string.sub(tostring(obj.year + 100), 1, 2), lang = lang, precision = 'century'}}
end
end
return nil
end
local function formatDaterange(obj, params) --TODO
local begin = formatDatepoint(obj.begin, params) or ''
local ending = formatDatepoint(obj.ending, params) or ''
return begin .. '-' .. ending
end
end


local function printDatavalueQuantity(data, parameter)
local function objectToText(obj, params)
-- data fields: amount [number], unit [string], upperBound [number], lowerBound [number]
if obj.type == 'dateobject' then
if not parameter or parameter == "amount" then
return formatDatepoint(obj, params)
return tonumber(data.amount)
elseif obj.type == 'rangeobject' then
elseif parameter == "unit" then
return formatDaterange(obj, params)
return mw.ustring.match(data.unit, "Q%d+")
else
return data[parameter]
end
end
return nil
end
end


local function normalizeDate(date)
local function tableToText(values, params) -- takes a list of already formatted values and make them a text
date = mw.text.trim(date, "+")
if not values or #values == 0 then
-- extract year
return nil
local yearstr = mw.ustring.match(date, "^\-?%d+")
end
local year = tonumber(yearstr)
return linguistic.conj(values, params.lang or defaultlang, params.conjtype)--linguistic.conj( values, params.lang, params.conjtype )
-- remove leading zeros of year
return year .. mw.ustring.sub(date, #yearstr + 1), year
end
end


-- precision: 0 - billion years, 1 - hundred million years, ..., 6 - millenia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second
function p.getDate(obj)
function formatDate(date, precision, timezone, formatstr)
--[[
precision = precision or 11
returns an object containing a timestamp for easy sorting, and other data
date, year = normalizeDate(date)
possible types of object:
date = string.gsub(date, "-00%f[%D]", "-01")
dateobject
if year == 0 and precision <= 9 then return "" end
{timestamp = string, year = number, month = number, day = number, calendar = string}
rangeobject
{timestamp = string, begin = dateobject, ending = dateobject}
]]--
if not obj then
return nil
end
if type(obj) == 'string' then
obj = p.getEntity(obj)
end


-- precision is 10000 years or more
-- if obj is a statement with date, get it
if precision <= 5 then
if obj.mainsnak and not isSpecial(obj.mainsnak) and obj.mainsnak.datatype == 'time' then
local factor = 10 ^ ((5 - precision) + 4)
return dateObject(obj.mainsnak.datavalue.value)
local y2 = math.ceil(math.abs(year) / factor)
end
local relative = mw.ustring.gsub(i18n.datetime[precision], "$1", tostring(y2))
 
if year < 0 then
-- else preload relevant data
relative = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative)
local qualifs = obj.qualifiers -- when obj is a statement, look in qualifiers
else
local claims = obj.claims -- when obj is an item, look in claims
relative = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative)
 
local pointprop = {'P585', 'P571'} -- dates corresponding to a punctual fact
local beginprop = {'P580', 'P569'} -- start date, birth date == start of a date range
local endingprop = {'P582', 'P570'}
 
local function getval(prop)
local val
if claims and claims[prop] and not isSpecial(claims[prop][1].mainsnak) then
val = claims[prop][1].mainsnak.datavalue.value
elseif qualifs and qualifs[prop] and not isSpecial(qualifs[prop][1]) then
val = qualifs[prop][1].datavalue.value
end
if val then
return dateObject(val)
end
end
return relative
return nil
end
end


-- precision is decades, centuries and millenia
for i, prop in pairs(pointprop) do
local era
local val = getval(prop)
if precision == 6 then era = mw.ustring.gsub(i18n.datetime[6], "$1", tostring(math.floor((math.abs(year) - 1) / 1000) + 1)) end
if val then return val end
if precision == 7 then era = mw.ustring.gsub(i18n.datetime[7], "$1", tostring(math.floor((math.abs(year) - 1) / 100) + 1)) end
if precision == 8 then era = mw.ustring.gsub(i18n.datetime[8], "$1", tostring(math.floor(math.abs(year) / 10) * 10)) end
if era then
if year < 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.bc, '"', ""), "$1", era)
elseif year > 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.ad, '"', ""), "$1", era) end
return era
end
end
 
--if no date has not been found, look for startdate or enddate
-- precision is years or less
local begin, ending
if precision >= 9 then
for i, prop in pairs(beginprop) do
--[[ the following code replaces the UTC suffix with the given negated timezone to convert the global time to the given local time
begin = getval(prop)
timezone = tonumber(timezone)
if begin then
if timezone and timezone ~= 0 then
break
timezone = -timezone
timezone = string.format("%.2d%.2d", timezone / 60, timezone % 60)
if timezone[1] ~= '-' then timezone = "+" .. timezone end
date = mw.text.trim(date, "Z") .. " " .. timezone
end
end
]]--
end
if formatstr then
for i, prop in pairs(endingprop) do
for i=(precision+1), 14 do
ending = getval(prop)
for _, ch in pairs(formatchar[i]) do
if ending then
if formatstr:find(ch) then
break
formatstr = i18n.datetime[precision]
end
end
end
else
formatstr = i18n.datetime[precision]
end
end
if year == 0 then formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], "")
end
elseif year < 0 then
if begin or ending then
-- Mediawiki formatDate doesn't support negative years
return rangeObject(begin, ending)
date = mw.ustring.sub(date, 2)
end
formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], mw.ustring.gsub(i18n.datetime.bc, "$1", i18n.datetime[9]))
return nil
elseif year > 0 and i18n.datetime.ad ~= "$1" then
end
formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], mw.ustring.gsub(i18n.datetime.ad, "$1", i18n.datetime[9]))
 
end
function p.getFormattedDate(statement, params)
return mw.language.new(wiki.langcode):formatDate(formatstr, date)
local datetable = p.getDate(statement)
if not datetable then
return nil
end
return 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')
else
return claim.rank == target
end
end
end
end


local function printDatavalueTime(data, parameter)
local function bestRanked(claims)
-- data fields: time [ISO 8601 time], timezone [int in minutes], before [int], after [int], precision [int], calendarmodel [wikidata URI]
if not claims then
--  precision: 0 - billion years, 1 - hundred million years, ..., 6 - millenia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second
return nil
--  calendarmodel: e.g. http://www.wikidata.org/entity/Q1985727 for the proleptic Gregorian calendar or http://www.wikidata.org/wiki/Q11184 for the Julian calendar]
end
if parameter then
local preferred, normal = {}, {}
para, formatstr = parameter:match("([^:]+):([^:]+)")
for _, j in ipairs(claims) do
if parameter == "calendarmodel" then
if j.rank == 'preferred' then
data.calendarmodel = string.match(data.calendarmodel, "Q%d+") -- extract entity id from the calendar model URI
table.insert(preferred, j)
elseif para and para == "time" then
elseif j.rank == 'normal' then
return formatDate(data.time, data.precision, data.timezone,formatstr)
table.insert(normal, j)
elseif parameter == "time" then
data.time = normalizeDate(data.time)
end
end
return data[parameter]
end
if #preferred > 0 then
return preferred
else
else
return formatDate(data.time, data.precision, data.timezone)
return normal
end
end
end
end


local function printDatavalueEntity(data, parameter)
local function hasQualifier(claim, qualifier, qualifiervalues)
-- data fields: entity-type [string], numeric-id [int, Wikidata id]
if not qualifier then -- si aucun qualificatif est demandé, ça passe
local id
return true
end
 
qualifier = string.upper(qualifier)
if not claim.qualifiers or not claim.qualifiers[qualifier] then
return false
end


if data["entity-type"] == "item" then id = "Q" .. data["numeric-id"]
if type(qualifiervalues) == 'string' then
elseif data["entity-type"] == "property" then id = "P" .. data["numeric-id"]
qualifiervalues = mw.text.split(qualifiervalues, ',')
else return printError("unknown-entity-type")
end
end


if parameter then
if (not qualifiervalues) or (qualifiervalues == {}) then
if parameter == "link" then
return true -- si aucune valeur spécifique n'est exigée
local linkTarget = mw.wikibase.sitelink(id)
end
local linkName = mw.wikibase.label(id)
 
if linkTarget then
for _, j in ipairs(claim.qualifiers[qualifier]) do
local link = linkTarget
for _, l in ipairs(qualifiervalues) do
-- if there is a local Wikipedia article linking to it, use the label or the article title
if p.sameValue(j, l) then
if linkName and (linkName ~= linkTarget) then link = link .. "|" .. linkName end
return true
return "[[" .. link .. "]]"
else
-- if there is no local Wikipedia article output the label or link to the Wikidata object to input a proper label
if linkName then return linkName else return "[[:d:" .. id .. "|" .. id .. "]]" end
end
end
else
return data[parameter]
end
end
else
return mw.wikibase.label(id) or id
end
end
end
return false
end


local function printDatavalueMonolingualText(data, parameter)
local function hasSource(statement, source, sourceproperty)
-- data fields: language [string], text [string]
if not statement.references then
if parameter then
return false
return data[parameter]
else
local result = mw.ustring.gsub(mw.ustring.gsub(i18n.monolingualtext, "%%language", data["language"]), "%%text", data["text"])
return result
end
end
end
sourceproperty = string.upper(sourceproperty or 'P248')
 
local sourcevalue = string.upper(source or '')
function getSnakValue(snak, parameter)
for _, ref in ipairs(statement.references) do
-- snaks have three types: "novalue" for null/nil, "somevalue" for not null/not nil, or "value" for actual data
for prop, content in pairs(ref.snaks) do
if snak.snaktype == "value" then
if prop == sourceproperty then
-- call the respective snak parser
if sourcevalue == '' then
if snak.datavalue.type == "string" then return snak.datavalue.value
return true
elseif snak.datavalue.type == "globecoordinate" then return printDatavalueCoordinate(snak.datavalue.value, parameter)
else
elseif snak.datavalue.type == "quantity" then return printDatavalueQuantity(snak.datavalue.value, parameter)
for _, k in ipairs(content) do
elseif snak.datavalue.type == "time" then return printDatavalueTime(snak.datavalue.value, parameter)
if sameValue(k, source) then
elseif snak.datavalue.type == "wikibase-entityid" then return printDatavalueEntity(snak.datavalue.value, parameter)
return true
elseif snak.datavalue.type == "monolingualtext" then return printDatavalueMonolingualText(snak.datavalue.value, parameter)
end
end
end
end
end
end
end
end
return mw.wikibase.renderSnak(snak)
return false
end
end


function getQualifierSnak(claim, qualifierId)
local function hasDate(statement)
-- a "snak" is Wikidata terminology for a typed key/value pair
if not statement.qualifiers then
-- a claim consists of a main snak holding the main information of this claim,
return false
-- as well as a list of attribute snaks and a list of references snaks
end
if qualifierId then
local dateprops = {'P580', 'P585', 'P582'}
-- search the attribute snak with the given qualifier as key
for i, prop in pairs(dateprops) do
if claim and claim.qualifiers then
if statement.qualifiers[prop] then
local qualifier = claim.qualifiers[qualifierId]
return true
if qualifier then return qualifier[1] end
end
end
return nil, printError("qualifier-not-found")
else
-- otherwise return the main snak
return claim.mainsnak
end
end
return false
end
end


local function datavalueTimeToDateObject(data)
local function isInLanguage(snak, lang) -- ne fonctionne que pour les monolingualtext / étendre aux autres types en utilisant les qualifiers ?
local sign, year, month, day, hour, minute, second = string.match(data.time, "(.)(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)Z")
return not isSpecial(snak) and snak.datavalue.type == 'monolingualtext' and snak.datavalue.value.language == lang
local result =
{
year = tonumber(year),
month = tonumber(month),
day = tonumber(day),
hour = tonumber(hour),
min = tonumber(minute),
sec = tonumber(second),
timezone = data.timezone,
julian = data.calendarmodel and string.match(data.calendarmodel, "Q11184$")
}
if sign == "-" then result.year = -result.year end
return result
end
end


function julianDay(dateObject)
local function numval(claims, numval) -- retourn les numval premières valeurs de la table claims
local year = dateObject.year
local numval = tonumber(numval) or 0 -- raise an error if numval is not a positive integer ?
local month = dateObject.month or 0
if #claims <= numval then
local day = dateObject.day or 0
return claims
end
local newclaims = {}
while #newclaims < numval do
table.insert(newclaims, claims[#newclaims + 1])
end
return newclaims
end


if month == 0 then month = 1 end
local function wikipediaLink(entity, lang)
if day == 0 then day = 1 end
local link
if month <= 2 then
if type(entity) == 'table' then
year = year - 1
link = entity:getSitelink(lang .. 'wiki')
month = month + 12
else
link = mw.wikibase.getSitelink(entity, lang .. 'wiki')
end
if link then
return ':' .. lang .. ':' .. link
end
end
return nil
end
local function getLink(entity, typelink, lang)
if typelink == 'wikidata' then
if type(entity) == 'table' then
if entity.type == 'property' then
return 'd:P:' .. entity.id
elseif entity.type == 'lexeme' then
return 'd:L:' .. entity.id
else
return 'd:' .. entity.id
end
else
if string.sub(entity, 1, 1) == 'P' then
return 'd:P:' .. entity
elseif string.sub(entity, 1, 1) == 'L' then
return 'd:L:' .. entity
else
return 'd:' .. entity
end
end


local time = ((((dateObject.sec or 0) / 60 + (dateObject.min or 0) + (dateObject.timezone or 0)) / 60) + (dateObject.hour or 0)) / 24
elseif typelink == 'wikipedia' then
return wikipediaLink(entity, lang or defaultlang)


local b
elseif typelink == 'anywikipedia' then
if dateObject.julian then b = 0 else
for _, lg in ipairs(fb.fblist(lang or defaultlang, true)) do
local century = math.floor(year / 100)
local link = wikipediaLink(entity, lg)
b = 2 - century + math.floor(century / 4)
if link then
return link
end
end
end
end
return nil
end


return math.floor(365.25 * (year + 4716)) + math.floor(30.6001 * (month + 1)) + day + time + b - 1524.5
function p.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
return false
end
end


function getQualifierSortValue(claim, qualifierId)
function p.chronosort(objs, inverted)
local snak = getQualifierSnak(claim, qualifierId)
table.sort(objs, function(a, b)
if snak and snak.snaktype == "value" then
local timeA = p.getDate(a)
if snak.datavalue.type == "time" then
local timeB = p.getDate(b)
return julianDay(datavalueTimeToDateObject(snak.datavalue.value))
if inverted then
return p.comparedate(timeB, timeA)
else
else
return getSnakValue(snak)
return p.comparedate(timeA, timeB)
end
end
end)
return objs
end
function p.sortclaims(claims, sorttype)
if type(sorttype) == 'function' then
table.sort(claims, sorttype)
elseif sorttype == 'chronological' then
return p.chronosort(claims)
elseif sorttype == 'inverted' then
return p.chronosort(claims, true)
end
end
return claims
end
function p.getRawvalue(snak)
return p.getDatavalue(snak, { displayformat = 'raw' })
end
end


function getValueOfClaim(claim, qualifierId, parameter)
function p.showentity(entity, lang)
local error
if not entity then
local snak
return nil
snak, error = getQualifierSnak(claim, qualifierId)
end
if snak then
local label, link, id = p._getLabel(entity, lang), getLink(entity, 'wikidata')
return getSnakValue(snak, parameter)
if type(entity) == 'table' then
id = entity.id
else
else
return nil, error
id = entity
end
end
return '[[' .. link .. '|' .. label .. ']] <small>(' .. id .. ')</small>'
end
end


function p.getDatavalue(snak, params)
if isSpecial(snak) then
return nil
end


function formatReference(ref)
if not params then
-- "imported from"-references are useless, skip them:
params = {}
if ref["P143"] then return nil end
-- load [[Modul:Zitation]]
local ZitationSuccess, r = pcall(require, "Modul:Zitation")
if type(r) == "table" then
Zitation = r.Zitation()
end
end
-- assert (ZitationSuccess, i18n["errors"]["module-not-loaded"])
 
local displayformat = params.displayformat
-- assignments of Wikidata properties to Zitation parameters
local valuetype = snak.datavalue.type
local wdZmap = {
local value = snak.datavalue.value
P1433 = {"bas", "Werk"},
 
P248  = {"bas", "Werk"},
if valuetype == 'wikibase-entityid' then
P1476 = {"bas", "Titel"},
if type(displayformat) == 'function' then
P1680 = {"bas", "TitelErg"},
return displayformat(snak, params)
P407  = {"bas", "Sprache"},
end
P364  = {"bas", "Sprache"},
local id = snak.datavalue.value.id
P2439 = {"bas", "Sprache"},
if displayformat == 'raw' then
P123  = {"bas", "Verlag"},
return id
P577  = {"bas", "Datum"},
elseif displayformat == 'wikidatastyle' then
P98  = {"bas", "Hrsg"},
return p.showentity(id, params.lang)
P2093 = {"bas", "Autor"},
else
P50  = {"bas", "Autor"},
return p.formatEntity(id, params)
P1683 = {"bas", "Zitat"},
end
P854  = {"www", "URL"},
 
P813  = {"www", "Abruf"},
elseif valuetype == 'string' then
P1065 = {"www", "ArchivURL"},
local showntext = params.showntext
P2960 = {"www", "ArchivDatum"},
if displayformat == 'weblink' then
P2701 = {"www", "Format"},
if showntext then
P393  = {"print", "Auflage"},
return '[' .. value .. ' ' .. showntext .. ']'
P291  = {"print", "Ort"},
else
P304  = {"fragment", "Seiten"},
return value
P792  = {"fragment", "Kapitel"},
P629  = {"orig", "Titel"}
}
for prop, value in pairs(ref) do
if wdZmap[prop] then
if type(value) == "table" then
-- More snaks with same property, we concatenate using a comma
value = table.concat(value, ", ")
end
-- value should be string now, so we can call Zitation
if type(value) == "string" and string.len(value) > 0 then
Zitation.fill(wdZmap[prop][1], wdZmap[prop][2], value, prop)
end
end
end
end
end
if ({['math'] = 1, ['musical-notation'] = 1})[snak.datatype] == 1 and displayformat ~= 'raw' then
-- if no title on Wikidata, try to use the URL as title
value = mw.wikibase.formatValue(snak)
if (not ref["P1476"]) and ref["P854"] then
else
local URLutil = Zitation.fetch("URLutil")
if params.urlpattern then
Zitation.fill("bas", "Titel", URLutil.getHost(ref["P854"]))
showntext = mw.text.nowiki(showntext or value)
elseif not (ref["P1476"]) then
value = mw.ustring.gsub(value, '%%', '%%%%') -- escape '%'
Zitation.fill("bas", "Titel", ref["P854"])
value = '[' .. mw.ustring.gsub(mw.ustring.gsub(params.urlpattern, '$1', value), ' ', '%%20') .. ' ' .. showntext .. ']'
end
elseif params.pattern then
refFormatted, f = Zitation.format()
local pattern = mw.ustring.gsub(params.pattern, '%%', '%%%%')
return refFormatted
value = mw.ustring.gsub(value, '%%', '%%%%')
end
value = mw.ustring.gsub(pattern, '$1', value)
 
function getReferences(frame, claim)
local result = ""
-- traverse through all references
for ref in pairs(claim.references or {}) do
local refTable = {}
for snakkey, snakval in orderedpairs(claim.references[ref].snaks or {}, claim.references[ref]["snaks-order"]) do
if #snakval == 1 then
refTable[snakkey] = getSnakValue(snakval[1])
else
else
--
if displayformat ~= 'raw' then
multival = {}
value = mw.text.nowiki(value)
for snakidx = 1, #snakval do
table.insert(multival, getSnakValue(snakval[snakidx]))
end
end
refTable[snakkey] = multival
end
end
end
end
local formattedRef = formatReference(refTable)
return value
if formattedRef then result = result .. frame:extensionTag("ref", formattedRef) end
 
end
elseif valuetype == 'time' then -- format example: +00000001809-02-12T00:00:00Z
return result
if displayformat == 'raw' then
end
return value.time
else
return objectToText(dateObject(value), params)
end


local function hasqualifier(claim, qualifierproperty)
elseif valuetype == 'globecoordinate' then
local invert
-- retourne une table avec clés latitude, longitude, précision et globe à formater par un autre module (à changer ?)
if string.sub(qualifierproperty, 1, 1) == "!" then invert = true else invert = false end
if displayformat == 'latitude' then
if not claim.qualifiers and not invert then return false end
return value.latitude
if not claim.qualifiers and invert then return true end
elseif displayformat == 'longitude' then
if qualifierproperty == '' then return true end
return value.longitude
if not invert and not claim.qualifiers[qualifierproperty] then return false end
elseif displayformat == 'qualifier' then
if invert and claim.qualifiers[string.sub(qualifierproperty, 2)] then return false end
local coord = require 'Module:Coordinates'
return true
value.globe = mw.loadData('Module:Wikidata/Globes')[value.globe]
end
value.precision = nil
return coord._coord(value)
else
value.globe = mw.loadData('Module:Wikidata/Globes')[value.globe] -- transforme l'ID du globe en nom anglais utilisable par geohack
return value -- note : les coordonnées Wikidata peuvent être utilisée depuis Module:Coordinates. Faut-il aussi autoriser à appeler Module:Coordiantes ici ?
end


local function qualifierhasvalue(claim, property, value)
elseif valuetype == 'quantity' then -- todo : gérer les paramètre précision
if not claim.qualifiers then return false end
if displayformat == 'raw' then
if not claim.qualifiers[property] then return false end
return tonumber(value.amount)
for key, snak in pairs(claim.qualifiers[property]) do
else
if snak.snaktype == "value" then
local formatNum = require 'Module:Formatnum'
if snak.datavalue.type == "wikibase-entityid" then
local number = formatNum.formatNum(value.amount, params.lang)
if snak.datavalue.value.id == value then
local unit = mw.ustring.match(value.unit, '(Q%d+)')
return true
if unit then
end
number = number .. '&nbsp;' .. p.formatEntity(unit, params)
--TODO: elseif other types
end
end
return number
end
end
elseif valuetype == 'monolingualtext' then
return '<span lang="' .. value.language .. '">' .. value.text .. '</span>'
else
return formatError( 'unknown-datavalue-type', valuetype )
end
end
return false
end
end


local function hassource(claim, sourceproperty)
local function getMultipleClaims(args)
if not claim.references then return false end
local newargs = args
if sourceproperty == '' then return true end
local claims = {}
if string.sub(sourceproperty,1,1) ~= "!" then
for i, j in pairs(args.property) do
for _, source in pairs(claim.references) do
newargs.property = j
if source.snaks[sourceproperty] then return true end
local newclaims = p.getClaims(args)
end
if newclaims then
return false
for k, l in pairs(newclaims) do
else
table.insert(claims, l)
for _, source in pairs(claim.references) do
for key in pairs(source.snaks) do
if key ~= string.sub(sourceproperty,2) then return true end
end
end
end
end
return false
end
end
return claims
end
end


function atdate(claim, mydate)
function p.getClaims( args ) -- returns a table of the claims matching some conditions given in args
local refdate
args = removeBlanks(args)
if not mydate or mydate == "" then
if not args.property then
refdate = os.date("!*t")
return formatError( 'property-param-not-provided' )
end
if type(args.property) == 'table' then
return getMultipleClaims(args)
end
--Get entity
if args.item then -- synonyms
args.entity = args.item
end
local property = string.upper(args.property)
local allClaims
local entity = args.entity
if type(entity) == 'table' then
allClaims = (entity and entity.claims and entity.claims[property]) or {}
else
else
if string.match(mydate, "^%d+$") then
allClaims = mw.wikibase.getAllStatements(entity, property)
refdate = { year = tonumber(mydate) }
end
else
if #allClaims == 0 then
refdate = datavalueTimeToDateObject({ time = mw.language.getContentLanguage():formatDate("+Y-m-d\\TH:i:s\\Z", mydate) })
return nil
end
 
if not args.rank then
args.rank = 'best'
end
local claims = {}
for _, statement in ipairs(allClaims) 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.qualifiervalues or args.qualifiervalue)
)
and
(
not args.withsource or args.withsource == '-'
or
hasSource(statement, args.withsource, 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, args.rank)
)
then
table.insert(claims, statement)
end
end
end
end
local refjd = julianDay(refdate)
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


local mindate = getQualifierSortValue(claim, propertyId["starttime"])
if args.numval then
local maxdate = getQualifierSortValue(claim, propertyId["endtime"])
return numval(claims, args.numval)
end
return claims
end


if mindate and mindate > refjd then return false end
function p.formatClaimList(claims, args)
if maxdate and maxdate < refjd then return false end
if not claims then
return nil
end
for i, j in pairs(claims) do
claims[i] = p.formatStatement(j, args)
end
return claims
end


return true
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
end


--returns a table of claims excluding claims not passed the filters
local function getQualifiers(statement, qualifs, params)
function filterClaims(frame, claims)
if not statement.qualifiers then
local function filter(condition, filterfunction)
return nil
if not frame.args[condition] then
end
return
local vals = {}
end
for i, j in pairs(qualifs) do
local newclaims = {}
j = string.upper(j)
for i, claim in pairs(claims) do
if statement.qualifiers[j] then
if filterfunction(claim, frame.args[condition]) then
local inserted = false
table.insert(newclaims, claim)
if statement.qualifiers[j][1].datatype == 'monolingualtext' then
local in_preferred_lang
for _, language in ipairs(fb.fblist(params.lang or defaultlang, true)) do
for _, snak in ipairs(statement.qualifiers[j]) do
if isInLanguage(snak, language) then
in_preferred_lang = snak
break
end
end
if in_preferred_lang then
break
end
end
if in_preferred_lang then
table.insert(vals, in_preferred_lang)
inserted = true
end
end
if not inserted then
for _, snak in pairs(statement.qualifiers[j]) do
table.insert(vals, snak)
end
end
end
end
end
claims = newclaims
end
end
if #vals == 0 then
return nil
end
return vals
end


filter('hasqualifier', hasqualifier)
function p.getFormattedQualifiers(statement, qualifs, params)
filter('hassource', hassource)
if not params then params = {} end
filter('atdate', atdate)
local qualiftable = getQualifiers(statement, qualifs, params)
if not qualiftable then
for key, val in pairs(frame.args) do
return nil
if type(key) == "number" and key > 2 and key % 2 == 1 then
end
-- key = 3, 5, 7 and so on
for i, j in pairs(qualiftable) do
local newclaims = {}
local params = params
for i, claim in pairs(claims) do
if j.datatype == 'globe-coordinate' then
if qualifierhasvalue(claim, frame.args[key - 1], frame.args[key]) then
params.displayformat = 'qualifier'
table.insert(newclaims, claim)
end
end
claims = newclaims
end
end
qualiftable[i] = p.formatSnak(j, params)
end
end
return linguistic.conj(qualiftable, params.lang or defaultlang, params.conjtype)
return claims
end
end


local p = {}
function p.formatStatement( statement, args )
if not statement.type or statement.type ~= 'statement' then
return formatError( 'unknown-claim-type', statement.type )
end
if not args then args = {} end
local lang = args.lang or defaultlang
local str = p.formatSnak( statement.mainsnak, args )
if args.showlang == true then
str = showLang(statement, str)
end


function p.isSubclass(frame)
local qualifs = args.showqualifiers
if not frame.args["parent"] then return "" end
if qualifs then
if type(qualifs) == 'string' then
qualifs = mw.text.split(qualifs, ',')
end
local foundvalues = p.getFormattedQualifiers(statement, qualifs, args)
if foundvalues then
if args.delimiter then
str = str .. args.delimiter .. foundvalues
else
str = str .. linguistic.inparentheses(foundvalues, lang)
end
end
end


local maxDepth
if args.showdate then -- when "showdate and p.chronosort are both set, date retrieval is performed twice
maxDepth = frame.args["maxDepth"] or 5
local timedata = p.getDate(statement)
if not type(maxDepth) == "number" then maxDepth = 5 end
if timedata then
local formatteddate = objectToText(timedata, args)
formatteddate = linguistic.inparentheses(formatteddate, lang)
str = str .. '<small>' .. formatteddate ..'</small>'
end
end


local result
if args.showsource and statement.references then
result = isParent(frame.args["id"], frame.args["parent"], frame.args["exitItem"], maxDepth)
local cite = require 'Module:Cite'
local frame = mw.getCurrentFrame()
if frame.args["returnInt"] then
local sourcestring = ''
if result == true then return 1 else return "" end
local s
else
for _, ref in ipairs(statement.references) do
if result then return result else return false end
if ref.snaks.P248 then
for j, source in pairs(ref.snaks.P248) do
if not isSpecial(source) then
local page
if ref.snaks.P304 and not isSpecial(ref.snaks.P304[1]) then
page = ref.snaks.P304[1].datavalue.value
end
s = cite.citeitem(source.datavalue.value.id, lang, page)
s = frame:extensionTag( 'ref', s )
sourcestring = sourcestring .. s
end
end
elseif ref.snaks.P854 and not isSpecial(ref.snaks.P854[1]) then
s = frame:extensionTag( 'ref', p.getDatavalue(ref.snaks.P854[1]) )
sourcestring = sourcestring .. s
end
end
str = str .. sourcestring
end
end
return str
end
end


function p.descriptionIn(frame)
function p.getmainid(claim)
local langcode = frame.args[1]
if claim and not isSpecial(claim.mainsnak) then
local id = frame.args[2]
return claim.mainsnak.datavalue.value.id
-- return description of a Wikidata entity in the given language or the default language of this Wikipedia site
local entity = mw.wikibase.getEntity(id)
if entity and entity.descriptions then
local desc = entity.descriptions[langcode or wiki.langcode]
if desc then return desc.value end
else
return "";
end
end
return nil
end
end


function p.labelIn(frame)
function p.formatSnak(snak, params)
local langcode = frame.args[1]
--local params = params or {} pour faciliter l'appel depuis d'autres modules
local id = frame.args[2]
if snak.snaktype == 'value' then
-- return label of a Wikidata entity in the given language or the default language of this Wikipedia site
return p.getDatavalue(snak, params)
local entity = mw.wikibase.getEntity(id)
elseif snak.snaktype == 'somevalue' then
if entity and entity.labels then
return formatTheUnknown()
local label = entity.labels[langcode or wiki.langcode]
elseif snak.snaktype == 'novalue' then
if label then return label.value end
return i18n('novalue') --todo
else
else
return "";
return formatError( 'unknown-snak-type', snak.snaktype )
end
end
end
end


function p.claim(frame)
local function defaultLabel(entity, displayformat) -- label when no label is available
local property = frame.args[1] or ""
if displayformat == 'id' then
local id = frame.args["id"]
if type(entity) ~= 'table' then
local qualifierId = frame.args["qualifier"]
return entity
local parameter = frame.args["parameter"]
else
local language = frame.args["language"]
return entity.id
local list = frame.args["list"]
end
local includeempty = frame.args["includeempty"]
end
local listMaxItems = tonumber(frame.args["listMaxItems"]) or 0
return i18n('no-label')
local references = frame.args["references"]
end
local sort = frame.args["sort"]
local sortInItem = frame.args["sortInItem"]
local inverse = frame.args["inverse"]
local showerrors = frame.args["showerrors"]
local default = frame.args["default"]
if default then showerrors = nil end


-- get wikidata entity
function p._getLabel(entity, lang, default, fallback)
local entity = mw.wikibase.getEntity(id)
if not entity then
if not entity then
if showerrors then return printError("entity-not-found") else return default end
return nil
end
end
-- fetch the first claim of satisfying the given property
if not lang then
local claims
lang = defaultlang
if entity.claims then claims = entity.claims[mw.wikibase.resolvePropertyId(property)] end
if not claims or not claims[1] then
if showerrors then return printError("property-not-found") else return default end
end
end
 
if type(entity) ~= 'table' and lang == defaultlang then
--filter claims
local label, lg = mw.wikibase.getLabelWithLang(entity)
claims = filterClaims(frame, claims)
if label and (fallback ~= '-' or lg == lang) then
if not claims[1] then return default end
return label
 
end
-- get initial sort indices
else
local sortindices = {}
entity = p.getEntity(entity)
for idx in pairs(claims) do
if entity and entity.labels then
sortindices[#sortindices + 1] = idx
if fallback ~= '-' then
end
for _, lg in ipairs(fb.fblist(lang, true)) do
 
if entity.labels[lg] then
local comparator
return entity.labels[lg].value
if sort then
end
-- sort by time qualifier
end
comparator = function(a, b)
local timea = getQualifierSortValue(claims[a], sort)
local timeb = getQualifierSortValue(claims[b], sort)
if type(timea) ~= type(timeb) and not (tonumber(timea) and tonumber(timeb)) then
if tonumber(timea) then return true
elseif tonumber(timeb) then return false
elseif tostring(timea) and tostring(timeb) then
if inverse then return tostring(timea) > tostring(timeb) else return tostring(timea) < tostring(timeb) end
else return false end -- different types, neither numbers nor strings, no chance to compare => random result to avoid script error
elseif tonumber(timea) and tonumber(timeb) then
timea = tonumber(timea)
timeb = tonumber(timeb)
end
if inverse then
return timea > timeb
else
else
return timea < timeb
if entity.labels[lang] then
return entity.labels[lang].value
end
end
end
end
end
elseif sortInItem then
end
-- fill table sortkeys
return defaultLabel(entity, default)
local sortkeys = {}
end
local snakSingle
 
local sortkeyValueId
function p._getDescription(entity, lang, fallback)
local claimContainingValue
if not entity then
for idx, claim in pairs(claims) do
return i18n('no description')
snakSingle = getQualifierSnak(claim)
end
sortkeyValueId = "Q" .. getSnakValue(snakSingle, "numeric-id")
if not lang then
claimContainingValue = mw.wikibase.getEntity(sortkeyValueId).claims[mw.wikibase.resolvePropertyId(sortInItem)]
lang = defaultlang
if claimContainingValue then
end
sortkeys[#sortkeys + 1] = getValueOfClaim(claimContainingValue[1])
if type(entity) ~= 'table' and lang == defaultlang then
else
local description, lg = mw.wikibase.getDescriptionWithLang(entity)
sortkeys[#sortkeys + 1] = ""
if description and (fallback ~= '-' or lg == lang) then
end
return description
end
end
comparator = function(a, b)
else
if inverse then
entity = p.getEntity(entity)
return sortkeys[a] > sortkeys [b]
if entity and entity.descriptions then
if fallback ~= '-' then
for _, lg in ipairs(fb.fblist(lang, true)) do
if entity.descriptions[lg] then
return entity.descriptions[lg].value
end
end
else
else
return sortkeys[a] < sortkeys [b]
if entity.descriptions[lang] then
return entity.descriptions[lang].value
end
end
end
end
end
end
return i18n('no description')
end
local function formattedLabel(label, entity, args)
local link = getLink(entity, args.link, args.lang)
if not link then
link = getLink(entity, defaultlink, args.lang)
end
if not link then
return label
else
else
-- sort by claim rank
return '[[' .. link .. '|' .. label .. ']]'
comparator = function(a, b)
end
local rankmap = { deprecated = 2, normal = 1, preferred = 0 }
end
local ranka = rankmap[claims[a].rank or "normal"] .string.format("%08d", a)
 
local rankb = rankmap[claims[b].rank or "normal"] ..  string.format("%08d", b)
function p.formatEntity( entity, args )
return ranka < rankb
if not entity then
end
return nil
end
if not args then
args = {}
end
local label = p._getLabel(entity, args.lang, 'id', args.fallback)
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 = args.lang
if not entity then
return i18n('invalid-id')
end
 
if string.sub(entity, 1, 10) == 'Property:P' then
entity = string.sub(entity, 10)
elseif string.sub(entity, 1, 8) == 'Lexeme:L' then
entity = string.sub(entity, 8)
elseif not ({L = 1, P = 1, Q = 1})[string.sub(entity, 1, 1)] or not tonumber(string.sub(entity, 2)) then
return i18n('invalid-id')
end
end
table.sort(sortindices, comparator)


local result
if not args.link or args.link == '' or args.link == '-' then -- by default: no link
local error
if lang == '' then
if list then
lang = defaultlang
list = string.gsub(list, "\\n", "\n") -- if a newline is provided (whose backslash will be escaped) unescape it
local value
-- iterate over all elements and return their value (if existing)
result = {}
for idx in pairs(claims) do
local claim = claims[sortindices[idx]]
value, error =  getValueOfClaim(claim, qualifierId, parameter)
if not value and value ~= 0 and showerrors then value = error end
if not value and value ~= 0 and includeempty then value = "" end
if value and references then value = value .. getReferences(frame, claim) end
result[#result + 1] = value
end
if listMaxItems and listMaxItems > 0 then
result = table.concat(result, list, 1, math.min(table.getn(result), listMaxItems))
else
result = table.concat(result, list)
end
end
return p._getLabel(entity, lang, args.default, args.fallback)
else
else
-- return first element
return p.formatEntity(entity, args)
end
end


local claim = claims[sortindices[1]]
function p._formatStatements( args )--Format statements and concat them cleanly
if language and claim.mainsnak.datatype == "monolingualtext" then
if args.value == '-' then
-- iterate over claims to find adequate language
return nil
for idx, claim in pairs(claims) do
if claim.mainsnak.datavalue.value.language == language then
result, error = getValueOfClaim(claim, qualifierId, parameter)
break
end
end
else
result, error = getValueOfClaim(claim, qualifierId, parameter)
end
if references == "only" then
result = getReferences(frame, claim)
elseif result and references then  
result = result .. getReferences(frame, claim)
end
end
end
 
--If a value is already set, use it
if result then return result else
if args.value and args.value ~= '' then
if showerrors then return error else return default end
return args.value
end
end
local valuetable = p.stringTable(args)
return tableToText(valuetable, args)
end
end


function p.getValue(frame)
function p.showQualifier( args )
local param = frame.args[2]
local qualifs = args.qualifiers or args.qualifier
if param == "FETCH_WIKIDATA" or param == i18n["FETCH_WIKIDATA"] then return p.claim(frame) else return param end
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 = ''
local new
for _, cl in ipairs(claims) do
new = p.getFormattedQualifiers(cl, qualifs, args) or ''
str = str .. new
end
return str
end
end


function p.pageId(frame)
function p._formatAndCat(args)
local id = frame.args[1]
local val = p._formatStatements(args)
local entity = mw.wikibase.getEntity(id)
if val then
if not entity then return "" else return entity.id end
return val .. addTrackingCat(args.property)
end
return nil
end
end


function p.labelOf(frame)
function p.getTheDate(args)
local id = frame.args[1]
local claims = p.getClaims(args)
-- returns the label of the given entity/property id
if not claims then
-- if no id is given, the one from the entity associated with the calling Wikipedia article is used
return nil
if not id then
end
local entity = mw.wikibase.getEntity()
local formattedvalues = {}
if not entity then return printError("entity-not-found") end
for _, cl in ipairs(claims) do
id = entity.id
table.insert(formattedvalues, p.getFormattedDate(cl))
end
local val = linguistic.conj(formattedvalues)
if val and args.addcat == true then
return val .. addTrackingCat(args.property)
else
return val
end
end
return mw.wikibase.label(id)
end
---FONCTIONS depuis le FRAME
function p.getaDate(frame)
return p.getTheDate(frame.args)
end
end


function p.sitelinkOf(frame)
function p.getQualifier(frame)
local id = frame.args[1]
return p.showQualifier(frame.args)
-- returns the Wikipedia article name of the given entity
-- if no id is given, the one from the entity associated with the calling Wikipedia article is used
if not id then
local entity = mw.wikibase.getEntity()
if not entity then return printError("entity-not-found") end
id = entity.id
end
return mw.wikibase.sitelink(id)
end
end


function p.badges(frame)
function p.getDescription(frame) -- simple for simple templates like {{Q|}}}
local site = frame.args[1]
local entity = frame.args.entity
local id = frame.args[2]
if not entity then
if not site then return printError("site-not-found") end
return i18n('invalid-id')
local entity = mw.wikibase.getEntity(id)
if not entity then return printError("entity-not-found") end
local badges = entity.sitelinks[site].badges
if badges then
local result
for idx = 1, #badges do
if result then result = result .. "/" .. badges[idx] else result = badges[idx] end
end
return result
end
end
local lang = frame.args.lang
local fallback = frame.args.fallback
return p._getDescription(entity, lang, fallback)
end
end


function p.sitelinkCount(frame)
function p.formatStatements( args )
local filter = "^.*" .. (frame.args[1] or "") .. "$"
return p._formatStatements( args )
local id = frame.args[2]
end


local entity = mw.wikibase.getEntity(id)
function p.formatStatementsE(frame)
local count = 0
local args = {}
if entity and entity.sitelinks then
if frame == mw.getCurrentFrame() then
for project, _ in pairs(entity.sitelinks) do
args = frame:getParent().args -- paramètres du modèle appelant (est-ce vraiment une bonne idée ?)
if string.find(project, filter) then count = count + 1 end
for k, v in pairs(frame.args) do
args[k] = v
end
end
else
args = frame
end
end
return count
return p._formatStatements( args )
end
end


-- call this in cases of script errors within a function instead of {{#invoke:Wikidata|<method>|...}} call {{#invoke:Wikidata|debug|<method>|...}}
function p.formatAndCat(frame)
function p.debug(frame)
local args = {}
local func = frame.args[1]
if frame == mw.getCurrentFrame() then
if func then
args = frame:getParent().args -- paramètres du modèle appelant (est-ce vraiment une bonne idée ?)
-- create new parameter set, where the first parameter with the function name is removed
for k, v in pairs(frame.args) do
local newargs = {}
args[k] = v
for key, val in pairs(frame.args) do
if type(key) == "number" then
if key > 1 then newargs[key - 1] = val end
else
newargs[key] = val
end
end
end
frame.args = newargs
local status, result = pcall(p[func], frame)
-- if status then return tostring(result) or "" else return '<span class="error">' .. result .. '</span>' end -- revert
if status then return result else return '<span class="error">' .. result .. '</span>' end
else
else
return printError("invalid-parameters")
args = frame
end
end
return p._formatAndCat( args )
end
end


function p.printEntity(frame)
function p.getEntityFromId(id)
local id = frame.args[1]
return p.getEntity(id)
local entity = mw.wikibase.getEntity(id)
if entity then return "<pre>" .. mw.text.jsonEncode(entity, mw.text.JSON_PRETTY) .. "</pre>" end
end
end


return p
return p

Version vom 14. April 2021, 09:46 Uhr

Die Dokumentation für dieses Modul kann unter Modul:LinkedWiki/Doku erstellt werden

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

local p = {}

local linguistic = require('Module:Linguistic')
--local formatDate = require('Module:Complex date') only loaded when needed to save memory in large pages like Wikidata:List of properties/all
local fb = require('Module:Fallback')
local i18nmessages = mw.loadData('Module:i18n/wikidata')

-- Wiki-specific parameters
local defaultlang = mw.getCurrentFrame():preprocess("{{int:lang}}")
local defaultlink = 'wikidata'

local function i18n(str)
	local message = i18nmessages[str]
	if type(message) == 'string' then
		return message
	end
	return fb._langSwitch(message, defaultlang) .. ''
end

local function formatError( key, text )
	return error(i18n(key) .. (text or ''))
end

local function addTrackingCat(prop, cat)
	if not prop and not cat then
		return error("no property provided")
	end
	if not cat then
		cat = i18nmessages.trackingcat .. '/' .. string.upper(prop)
	end
	return '[[Category:' .. cat .. ']]'
end

local function removeBlanks(args)
	for i, j in pairs(args) do -- does not work ??
		if (j == '') or (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 isSpecial(snak)
	return snak.snaktype ~= 'value'
end

local function sameValue(snak, target)
	return not isSpecial(snak) and p.getRawvalue(snak) == target
end

local function showLang(statement, str) -- TODO (not yet in proper format)
	--adds a lang indication at the start of the string, based on data in statement
	local mainsnak = statement.mainsnak
	if isSpecial(mainsnak) then
		return str
	end

	local langlist = {}
	if mainsnak.datavalue.type == 'monolingualtext' then
		langlist = {mainsnak.datavalue.value.language}
	elseif statement.qualifiers and statement.qualifiers.P407 then
		local convertlangcode = mw.loadData('Module:Dictionary/lang codes')
		for i, j in pairs( statement.qualifiers.P407 ) do
			if not isSpecial(j) then
				local val = convertlangcode[j.datavalue.value['numeric-id']]
				table.insert(langlist, val)
			end
		end
	end
	if #langlist == 0 then
		return str
	else
		return '(' .. table.concat(langlist) .. ')' .. str
	end
end

function p.getEntity( val )
	if type(val) == 'table' then
		return val
	end
	return mw.wikibase.getEntityObject(val)
end

-- DATE FUNCTIONS
local function splitTimestamp(timestamp, calendar)
	local pattern = "(%W)(%d+)%-(%d+)%-(%d+)"
	local era, year, month, day = timestamp:match(pattern)

	if calendar == 'julian' then
	--todo  year, month, day = formatdate.gregorianToJulian( era .. year, month, day )
	end

	return {day = day, month = month, year = year, era = era, timestamp = timestamp, type = 'dateobject'}
end

local function rangeObject(begin, ending)
	local timestamp
	if begin then
		timestamp = begin.timestamp
	elseif ending then
		timestamp = ending.timestamp
	end
	return {begin = begin, ending = ending, timestamp = timestamp, type = 'rangeobject'}
end

local function dateObject(orig, params) -- transforme un snak en un nouvel objet utilisable par Module:Date complexe
	if not params then
		params = {}
	end

	local newobj = splitTimestamp(orig.time, orig.calendar) -- initalise l'object en mettant la valeur des dates

	newobj.precision = params.precision or orig.precision
	newobj.type = 'dateobject'
	return newobj
end

local function formatDatepoint(obj, params) -- TO IMPROVE
	if not obj then
		return nil
	end
	local formatDate = require('Module:Complex date')
	local lang = params.lang or defaultlang
	local precision = math.min(obj.precision, params.precision or 15) -- if we don't want to show the value to its full detail
	if precision >= 11 then
		return formatDate.complex_date{args={date1 = obj.year .. '-' .. obj.month .. '-' .. obj.day, lang= lang}}
	elseif precision == 10 then
		return formatDate.complex_date{args={date1 = obj.year .. '-' .. obj.month, lang= lang}}
	elseif precision == 9 then
		return formatDate.complex_date{args={date1 = tostring(obj.year), lang= lang}}
	elseif precision == 8 then
		return formatDate.complex_date{args={date1 = string.sub(tostring(obj.year), 1, 3) .. '0', lang = lang, precision = 'decade'}}
	elseif precision == 7 then
		return formatDate.complex_date{args={date1 = string.sub(tostring(obj.year + 100), 1, 2), lang = lang, precision = 'century'}}
	end
	return nil
end

local function formatDaterange(obj, params) --TODO
	local begin = formatDatepoint(obj.begin, params) or ''
	local ending = formatDatepoint(obj.ending, params) or ''
	return begin .. '-' .. ending
end

local function objectToText(obj, params)
	if obj.type == 'dateobject' then
		return formatDatepoint(obj, params)
	elseif obj.type == 'rangeobject' then
		return formatDaterange(obj, params)
	end
	return nil
end

local function tableToText(values, params) -- takes a list of already formatted values and make them a text
	if not values or #values == 0 then
		return nil
	end
	return linguistic.conj(values, params.lang or defaultlang, params.conjtype)--linguistic.conj( values, params.lang, params.conjtype )
end

function p.getDate(obj)
--[[
returns an object containing a timestamp for easy sorting, and other data
	possible types of object:
		dateobject
			{timestamp = string, year = number, month = number, day = number, calendar = string}
		rangeobject
			{timestamp = string, begin = dateobject, ending = dateobject}
]]--
	if not obj then
		return nil
	end
	if type(obj) == 'string' then
		obj = p.getEntity(obj)
	end

	-- if obj is a statement with date, get it
	if obj.mainsnak and not isSpecial(obj.mainsnak) and obj.mainsnak.datatype == 'time' then
		return dateObject(obj.mainsnak.datavalue.value)
	end

	-- else preload relevant data
	local qualifs = obj.qualifiers -- when obj is a statement, look in qualifiers
	local claims = obj.claims -- when obj is an item, look in claims

	local pointprop = {'P585', 'P571'} -- dates corresponding to a punctual fact
	local beginprop = {'P580', 'P569'} -- start date, birth date == start of a date range
	local endingprop = {'P582', 'P570'}

	local function getval(prop)
		local val
		if claims and claims[prop] and not isSpecial(claims[prop][1].mainsnak) then
			val = claims[prop][1].mainsnak.datavalue.value
		elseif qualifs and qualifs[prop] and not isSpecial(qualifs[prop][1]) then
			val = qualifs[prop][1].datavalue.value
		end
		if val then
			return dateObject(val)
		end
		return nil
	end

	for i, prop in pairs(pointprop) do
		local val = getval(prop)
		if val then return val end
	end
	--if no date has not been found, look for startdate or enddate
	local begin, ending
	for i, prop in pairs(beginprop) do
		begin = getval(prop)
		if begin then
			break
		end
	end
	for i, prop in pairs(endingprop) do
		ending = getval(prop)
		if ending then
			break
		end
	end
	if begin or ending then
		return rangeObject(begin, ending)
	end
	return nil
end

function p.getFormattedDate(statement, params)
	local datetable = p.getDate(statement)
	if not datetable then
		return nil
	end
	return 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')
	else
		return claim.rank == target
	end
end

local function bestRanked(claims)
	if not claims then
		return nil
	end
	local preferred, normal = {}, {}
	for _, j in ipairs(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)
	if not qualifier then -- si aucun qualificatif est demandé, ça passe
		return true
	end

	qualifier = string.upper(qualifier)
	if not claim.qualifiers or not claim.qualifiers[qualifier] then
		return false
	end

	if type(qualifiervalues) == 'string' then
		qualifiervalues = mw.text.split(qualifiervalues, ',')
	end

	if (not qualifiervalues) or (qualifiervalues == {}) then
		return true -- si aucune valeur spécifique n'est exigée
	end

	for _, j in ipairs(claim.qualifiers[qualifier]) do
		for _, l in ipairs(qualifiervalues) do
			if p.sameValue(j, l) then
				return true
			end
		end
	end
	return false
 end

local function hasSource(statement, source, sourceproperty)
	if not statement.references then
		return false
	end
	sourceproperty = string.upper(sourceproperty or 'P248')
	local sourcevalue = string.upper(source or '')
	for _, ref in ipairs(statement.references) do
		for prop, content in pairs(ref.snaks) do
			if prop == sourceproperty then
				if sourcevalue == '' then
					return true
				else
					for _, k in ipairs(content) do
						if sameValue(k, source) then
							return true
						end
					end
				end
			end
		end
	end
	return false
end

local function hasDate(statement)
	if not statement.qualifiers then
		return false
	end
	local dateprops = {'P580', 'P585', 'P582'}
	for i, prop in pairs(dateprops) do
		if statement.qualifiers[prop] then
			return true
		end
	end
	return false
end

local function isInLanguage(snak, lang) -- ne fonctionne que pour les monolingualtext / étendre aux autres types en utilisant les qualifiers ?
	return not isSpecial(snak) and snak.datavalue.type == 'monolingualtext' and snak.datavalue.value.language == lang
end

local function numval(claims, numval) -- retourn les numval premières valeurs de la table claims
	local numval = tonumber(numval) or 0 -- raise an 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 wikipediaLink(entity, lang)
	local link
	if type(entity) == 'table' then
		link = entity:getSitelink(lang .. 'wiki')
	else
		link = mw.wikibase.getSitelink(entity, lang .. 'wiki')
	end
	if link then
		return ':' .. lang .. ':' .. link
	end
	return nil
end

local function getLink(entity, typelink, lang)
	if typelink == 'wikidata' then
		if type(entity) == 'table' then
			if entity.type == 'property' then
				return 'd:P:' .. entity.id
			elseif entity.type == 'lexeme' then
				return 'd:L:' .. entity.id
			else
				return 'd:' .. entity.id
			end
		else
			if string.sub(entity, 1, 1) == 'P' then
				return 'd:P:' .. entity
			elseif string.sub(entity, 1, 1) == 'L' then
				return 'd:L:' .. entity
			else
				return 'd:' .. entity
			end
		end

	elseif typelink == 'wikipedia' then
		return wikipediaLink(entity, lang or defaultlang)

	elseif typelink == 'anywikipedia' then
		for _, lg in ipairs(fb.fblist(lang or defaultlang, true)) do
			local link = wikipediaLink(entity, lg)
			if link then
				return link
			end
		end
	end
	return nil
end

function p.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
	return false
end

function p.chronosort(objs, inverted)
	table.sort(objs, function(a, b)
		local timeA = p.getDate(a)
		local timeB = p.getDate(b)
		if inverted then
			return p.comparedate(timeB, timeA)
		else
			return p.comparedate(timeA, timeB)
		end
	end)

	return objs
end

function p.sortclaims(claims, sorttype)
	if type(sorttype) == 'function' then
		table.sort(claims, sorttype)
	elseif sorttype == 'chronological' then
		return p.chronosort(claims)
	elseif sorttype == 'inverted' then
		return p.chronosort(claims, true)
	end
	return claims
end

function p.getRawvalue(snak)
	return p.getDatavalue(snak, { displayformat = 'raw' })
end

function p.showentity(entity, lang)
	if not entity then
		return nil
	end
	local label, link, id = p._getLabel(entity, lang), getLink(entity, 'wikidata')
	if type(entity) == 'table' then
		id = entity.id
	else
		id = entity
	end
	return '[[' .. link .. '|' .. label .. ']] <small>(' .. id .. ')</small>'
end

function p.getDatavalue(snak, params)
	if isSpecial(snak) then
		return nil
	end

	if not params then
		params = {}
	end

	local displayformat = params.displayformat
	local valuetype = snak.datavalue.type
	local value = snak.datavalue.value

	if valuetype == 'wikibase-entityid' then
		if type(displayformat) == 'function' then
			return displayformat(snak, params)
		end
		local id = snak.datavalue.value.id
		if displayformat == 'raw' then
			return id
		elseif displayformat == 'wikidatastyle' then
			return p.showentity(id, params.lang)
		else
			return p.formatEntity(id, params)
		end

	elseif valuetype == 'string' then
		local showntext = params.showntext
		if displayformat == 'weblink' then
			if showntext then
				return '[' .. value .. ' ' .. showntext .. ']'
			else
				return value
			end
		end
		if ({['math'] = 1, ['musical-notation'] = 1})[snak.datatype] == 1 and displayformat ~= 'raw' then
			value = mw.wikibase.formatValue(snak)
		else
			if params.urlpattern then
				showntext = mw.text.nowiki(showntext or value)
				value = mw.ustring.gsub(value, '%%', '%%%%') -- escape '%'
				value = '[' .. mw.ustring.gsub(mw.ustring.gsub(params.urlpattern, '$1', value), ' ', '%%20') .. ' ' .. showntext .. ']'
			elseif params.pattern then
				local pattern = mw.ustring.gsub(params.pattern, '%%', '%%%%')
				value = mw.ustring.gsub(value, '%%', '%%%%')
				value = mw.ustring.gsub(pattern, '$1', value)
			else
				if displayformat ~= 'raw' then
					value = mw.text.nowiki(value)
				end
			end
		end
		return value

	elseif valuetype == 'time' then -- format example: +00000001809-02-12T00:00:00Z
		if displayformat == 'raw' then
			return value.time
		else
			return objectToText(dateObject(value), params)
		end

	elseif valuetype == 'globecoordinate' 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
		elseif displayformat == 'qualifier' then
			local coord = require 'Module:Coordinates'
			value.globe = mw.loadData('Module:Wikidata/Globes')[value.globe]
			value.precision = nil
			return coord._coord(value)
		else
			value.globe = mw.loadData('Module:Wikidata/Globes')[value.globe] -- transforme l'ID du globe en nom anglais utilisable par geohack
			return value -- note : les coordonnées Wikidata peuvent être utilisée depuis Module:Coordinates. Faut-il aussi autoriser à appeler Module:Coordiantes ici ?
		end

	elseif valuetype == 'quantity' then -- todo : gérer les paramètre précision
		if displayformat == 'raw' then
			return tonumber(value.amount)
		else
			local formatNum = require 'Module:Formatnum'
			local number = formatNum.formatNum(value.amount, params.lang)
			local unit = mw.ustring.match(value.unit, '(Q%d+)')
			if unit then
				number = number .. '&nbsp;' .. p.formatEntity(unit, params)
			end
			return number
		end
	elseif valuetype == 'monolingualtext' then
		return '<span lang="' .. value.language .. '">' .. value.text .. '</span>'
	else
		return formatError( 'unknown-datavalue-type', valuetype )
	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)
		if newclaims then
			for k, l in pairs(newclaims) do
				table.insert(claims, l)
			end
		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 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 -- synonyms
		args.entity = args.item
	end
	local property = string.upper(args.property)
	local allClaims
	local entity = args.entity
	if type(entity) == 'table' then
		allClaims = (entity and entity.claims and entity.claims[property]) or {}
	else
		allClaims = mw.wikibase.getAllStatements(entity, property)
	end
	if #allClaims == 0 then
		return nil
	end

	if not args.rank then
		args.rank = 'best'
	end
	local claims = {}
	for _, statement in ipairs(allClaims) 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.qualifiervalues or args.qualifiervalue)
		)
		and
		(
			not args.withsource or args.withsource == '-'
			or
			hasSource(statement, args.withsource, 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, args.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
		j = string.upper(j)
		if statement.qualifiers[j] then
			local inserted = false
			if statement.qualifiers[j][1].datatype == 'monolingualtext' then
				local in_preferred_lang
				for _, language in ipairs(fb.fblist(params.lang or defaultlang, true)) do
					for _, snak in ipairs(statement.qualifiers[j]) do
						if isInLanguage(snak, language) then
							in_preferred_lang = snak
							break
						end
					end
					if in_preferred_lang then
						break
					end
				end
				if in_preferred_lang then
					table.insert(vals, in_preferred_lang)
					inserted = true
				end
			end
			if not inserted then
				for _, snak in pairs(statement.qualifiers[j]) do
					table.insert(vals, snak)
				end
			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, params)
	if not qualiftable then
		return nil
	end
	for i, j in pairs(qualiftable) do
		local params = params
		if j.datatype == 'globe-coordinate' then
			params.displayformat = 'qualifier'
		end
		qualiftable[i] = p.formatSnak(j, params)
	end
	return linguistic.conj(qualiftable, params.lang or defaultlang, params.conjtype)
end

function p.formatStatement( statement, args )
	if not statement.type or statement.type ~= 'statement' then
		return formatError( 'unknown-claim-type', statement.type )
	end
	if not args then args = {} end
	local lang = args.lang or defaultlang
	local str = p.formatSnak( statement.mainsnak, args )
	if args.showlang == true then
		str = showLang(statement, str)
	end

	local qualifs = args.showqualifiers
	if qualifs then
		if type(qualifs) == 'string' then
			qualifs = mw.text.split(qualifs, ',')
		end
		local foundvalues = p.getFormattedQualifiers(statement, qualifs, args)
		if foundvalues then
			if args.delimiter then
				str = str .. args.delimiter .. foundvalues
			else
				str = str .. linguistic.inparentheses(foundvalues, lang)
			end
		end
	end

	if args.showdate then -- when "showdate and p.chronosort are both set, date retrieval is performed twice
		local timedata = p.getDate(statement)
		if timedata then
			local formatteddate = objectToText(timedata, args)
			formatteddate = linguistic.inparentheses(formatteddate, lang)
			str = str .. '<small>' .. formatteddate ..'</small>'
		end
	end

	if args.showsource and statement.references then
		local cite = require 'Module:Cite'
		local frame = mw.getCurrentFrame()
		local sourcestring = ''
		local s
		for _, ref in ipairs(statement.references) do
			if ref.snaks.P248 then
				for j, source in pairs(ref.snaks.P248) do
					if not isSpecial(source) then
						local page
						if ref.snaks.P304 and not isSpecial(ref.snaks.P304[1]) then
							page = ref.snaks.P304[1].datavalue.value
						end
						s = cite.citeitem(source.datavalue.value.id, lang, page)
						s = frame:extensionTag( 'ref', s )
						sourcestring = sourcestring .. s
					end
				end
			elseif ref.snaks.P854 and not isSpecial(ref.snaks.P854[1]) then
				s = frame:extensionTag( 'ref', p.getDatavalue(ref.snaks.P854[1]) )
				sourcestring = sourcestring .. s
			end
		end
		str = str .. sourcestring
	end
	return str
end

function p.getmainid(claim)
	if claim and not isSpecial(claim.mainsnak) then
		return claim.mainsnak.datavalue.value.id
	end
	return nil
end

function p.formatSnak(snak, params)
	--local params = params or {} pour faciliter l'appel depuis d'autres modules
	if snak.snaktype == 'value' then
		return p.getDatavalue(snak, params)
	elseif snak.snaktype == 'somevalue' then
		return formatTheUnknown()
	elseif snak.snaktype == 'novalue' then
		return i18n('novalue') --todo
	else
		return formatError( 'unknown-snak-type', snak.snaktype )
	end
end

local function defaultLabel(entity, displayformat) -- label when no label is available
	if displayformat == 'id' then
		if type(entity) ~= 'table' then
			return entity
		else
			return entity.id
		end
	end
	return i18n('no-label')
end

function p._getLabel(entity, lang, default, fallback)
	if not entity then
		return nil
	end
	if not lang then
		lang = defaultlang
	end
	if type(entity) ~= 'table' and lang == defaultlang then
		local label, lg = mw.wikibase.getLabelWithLang(entity)
		if label and (fallback ~= '-' or lg == lang) then
			return label
		end
	else
		entity = p.getEntity(entity)
		if entity and entity.labels then
			if fallback ~= '-' then
				for _, lg in ipairs(fb.fblist(lang, true)) do
					if entity.labels[lg] then
						return entity.labels[lg].value
					end
				end
			else
				if entity.labels[lang] then
					return entity.labels[lang].value
				end
			end
		end
	end
	return defaultLabel(entity, default)
end

function p._getDescription(entity, lang, fallback)
	if not entity then
		return i18n('no description')
	end
	if not lang then
		lang = defaultlang
	end
	if type(entity) ~= 'table' and lang == defaultlang then
		local description, lg = mw.wikibase.getDescriptionWithLang(entity)
		if description and (fallback ~= '-' or lg == lang) then
			return description
		end
	else
		entity = p.getEntity(entity)
		if entity and entity.descriptions then
			if fallback ~= '-' then
				for _, lg in ipairs(fb.fblist(lang, true)) do
					if entity.descriptions[lg] then
						return entity.descriptions[lg].value
					end
				end
			else
				if entity.descriptions[lang] then
					return entity.descriptions[lang].value
				end
			end
		end
	end
	return i18n('no description')
end

local function formattedLabel(label, entity, args)
	local link = getLink(entity, args.link, args.lang)
	if not link then
		link = getLink(entity, defaultlink, args.lang)
	end
	if not link then
		return label
	else
		return '[[' .. link .. '|' .. label .. ']]'
	end
end

function p.formatEntity( entity, args )
	if not entity then
		return nil
	end
	if not args then
		args = {}
	end
	local label = p._getLabel(entity, args.lang, 'id', args.fallback)
	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 = args.lang
	if not entity then
		return i18n('invalid-id')
	end

	if string.sub(entity, 1, 10) == 'Property:P' then
		entity = string.sub(entity, 10)
	elseif string.sub(entity, 1, 8) == 'Lexeme:L' then
		entity = string.sub(entity, 8)
	elseif not ({L = 1, P = 1, Q = 1})[string.sub(entity, 1, 1)] or not tonumber(string.sub(entity, 2)) then
		return i18n('invalid-id')
	end

	if not args.link or args.link == '' or args.link == '-' then -- by default: no link
		if lang == '' then
			lang = defaultlang
		end
		return p._getLabel(entity, lang, args.default, args.fallback)
	else
		return p.formatEntity(entity, args)
	end
end

function p._formatStatements( args )--Format statements 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
	local valuetable = p.stringTable(args)
	return tableToText(valuetable, args)
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 = ''
	local new
	for _, cl in ipairs(claims) do
		new = p.getFormattedQualifiers(cl, qualifs, args) or ''
		str = str .. new
	end
	return str
end

function p._formatAndCat(args)
	local val = p._formatStatements(args)
	if val then
		return val .. addTrackingCat(args.property)
	end
	return nil
end

function p.getTheDate(args)
	local claims = p.getClaims(args)
	if not claims then
		return nil
	end
	local formattedvalues = {}
	for _, cl in ipairs(claims) do
		table.insert(formattedvalues, p.getFormattedDate(cl))
	end
	local val = linguistic.conj(formattedvalues)
	if val and args.addcat == true then
		return val .. addTrackingCat(args.property)
	else
		return val
	end
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 not entity then
		return i18n('invalid-id')
	end
	local lang = frame.args.lang
	local fallback = frame.args.fallback

	return p._getDescription(entity, lang, fallback)
end

function p.formatStatements( args )
	return p._formatStatements( args )
end

function p.formatStatementsE(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.getEntityFromId(id)
	return p.getEntity(id)
end

return p