Module:Wikidata: Difference between revisions

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Content deleted Content added
essentially a copy of d:Module:Wikidata
synch with current version of d:Module:Wikidata
Line 1: Line 1:
--script that retrieves basic data stored in Wikidata, for the datamodel, see https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua
--script that retrieves basic data stored in Wikidata, for the datamodel, see https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua



local p = {}
local p = {}

local fb = require('Module:Fallback')
local linguistic = require('Module:Linguistic')
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 i18n = {
local fb = require('Module:Fallback')
["errors"] = {
local i18nmessages = mw.loadData('Module:i18n/wikidata')
["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-type"] = "unknown snak type",
["unknown-datavalue-type"] = "unknown datavalue type.",
["unknown-entity-type"] = "unknown entity type",
["invalid-id"] = "invalid ID"
},
["no-label"] = "no label",
['no description'] = "no description",
["novalue"] = "not applicable"
}


-- Wiki-specific parameters
local function wikibasedate(datedata, lang, case, class, trim_year)
local defaultlang = mw.getCurrentFrame():preprocess("{{int:lang}}")
-- convert Wikibase date string into year month day minute seconds
local defaultlink = 'wikidata'
if not datedata then

return nil
local function i18n(str, lang)
local message = i18nmessages[str]
if type(message) == 'string' then
return message
end
end
return fb._langSwitch(message, lang or defaultlang)
if not lang then
end
return error("language not provided")

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
end
if type(datedata) ~= 'table' or (not datedata.time) then
if not cat then
cat = i18nmessages.trackingcat .. '/' .. string.upper(prop)
return error("invalid date structure, should be a Wikibase time-type snak")
end
end
return '[[Category:' .. cat .. ']]'
local datestring = datedata.time
end
local precision = tonumber(datedata.precision)

local calendar = 'gregorian'
local function removeBlanks(args)
local datetable, era = p.parsedatestring(datestring)
for i, j in pairs(args) do -- does not work ??
if datedata.calendarmodel == 'http://www.wikidata.org/entity/Q1985786' then --todo here: convert calendards
if (j == '') or (j == '-') then args[i] = nil end
calendar = 'julian'
end
end
return args
end


local function formatTheUnknown() -- voir si on peut accorder/adapter l'usage de "inconnu"
-- adapt value to precision (should be done after the conversion of the string so that we can later have finer treatment like around 1957 rather than "1950s"
return i18n('somevalue')
if precision < 10 then
end
datetable[2] = ''

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
end

if precision < 11 then
local langlist = {}
datetable[3] = ''
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
end
if precision < 12 then
if #langlist == 0 then
return str
datetable[4] = ''
else
return '('.. table.concat(langlist) .. ')' .. str
end
end
end
if precision < 13 then

datetable[5] = ''
function p.getEntity( val )
if type(val) == 'table' then
return val
end
end
return mw.wikibase.getEntityObject(val)
if precision < 14 then
end
datetable[6] = ''

end
-- DATE FUNCTIONS
local datestr = p._Date(datetable, lang, case, (class or 'dtstart'), trim_year)
local function splitTimestamp(timestamp, calendar)
if era == '-' then
local pattern = "(%W)(%d+)%-(%d+)%-(%d+)"
return p.BC(datestr, lang)
local era, year, month, day = timestamp:match(pattern)
else

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

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


local function formatError( key )
local function rangeObject(begin, ending)
local timestamp
return '<span class="error">' .. i18n.errors[key] .. '</span>'
if begin then
timestamp = begin.timestamp
elseif ending then
timestamp = ending.timestamp
end
return {begin = begin, ending = ending, timestamp = timestamp, type = 'rangeobject'}
end
end


local function dateObject(orig, params) -- transforme un snak en un nouvel objet utilisable par Module:Date complexe
local function formatLink(value)
if not params then
local link = value
params = {}
local showntext = value
end
if string.sub(showntext, 1, 7) == "http://" then

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

if string.sub(showntext, -1) == '/' then -- when last char is a char, should possibly be fixed directly in the item instead ?
newobj.precision = params.precision or orig.precision
showntext = string.sub(showntext, 1, #showntext - 1)
newobj.type = 'dateobject'
end
return newobj
return '[' .. link .. ' ' .. showntext .. ']'
end
end


local function samevalue(snak, target)
local function formatDatepoint(obj, params) -- TO IMPROVE
if not obj then
if snak.snaktype == 'value' and p.getRawvalue(snak) == target then
return true
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
end
return nil
end
end


local function getEntityFromId( id )
local function formatDaterange(obj, params) --TODO
local begin = formatDatepoint(obj.begin, params) or ''
return mw.wikibase.getEntityObject(id)
local ending = formatDatepoint(obj.ending, params) or ''
return begin .. '-' .. ending
end
end


local function formattable(statements, params) -- transform a table of claims into a table of formatted values
local function objectToText(obj, params)
if obj.type == 'dateobject' then
for i, j in pairs(statements) do
j = p.formatStatement(j, params)
return formatDatepoint(obj, params)
elseif obj.type == 'rangeobject' then
end
return formatDaterange(obj, params)
return statements
end
return nil
end
end


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


local function getDate(statement)
function p.getDate(obj)
--[[
--[[
returns an object containing a timestamp for easy sorting, and other data
return a "timedata" object as used by the date modules with the date of an item from the p580, p582 and p585 qualifiers
possible types of object:
object format:
dateobject
* timestamp 28 char string value for the timepoint, or if non the beginning or if none, the end (for easy sorting)
{timestamp = string, year = number, month = number, day = number, calendar = string}
* timepoint: snak
rangeobject
* begin: snak
{timestamp = string, begin = dateobject, ending = dateobject}
]]--
]]--
if not obj then
local timedata = {}
local q = statement.qualifiers
if not q or not (q.P585 or q.P580 or q.P582) then
return nil
return nil
end
end
if q.P582 and q.P582[1].snaktype == 'value' then
if type(obj) == 'string' then
obj = p.getEntity(obj)
timedata.endpoint = q.P582[1].datavalue.value
timedata.timestamp = q.P582[1].datavalue.value.time
end
end

if q.P580 and q.P580[1].snaktype == 'value' then
-- if obj is a statement with date, get it
timedata.startpoint = q.P580[1].datavalue.value
if obj.mainsnak and not isSpecial(obj.mainsnak) and obj.mainsnak.datatype == 'time' then
timedata.timestamp = q.P580[1].datavalue.value.time
return dateObject(obj.mainsnak.datavalue.value.time)
end
end

if q.P585 and q.P585[1].snaktype == 'value' then
-- else preload relevant data
timedata.timepoint = q.P585[1].datavalue.value
local qualifs = obj.qualifiers -- when obj is a statement, look in qualifiers
timedata.timestamp = q.P585[1].datavalue.value.time
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
end
return timedata
return nil
end
end


local function withtargetvalue(claims, targetvalue)
function p.getFormattedDate(statement, params)
local datetable = p.getDate(statement)
targetvalue = string.upper(targetvalue)
if not datetable then
local oldclaims = claims
return nil
local claims = {}
end
for i, statement in pairs(oldclaims) do
return objectToText(datetable, params)
if samevalue(statement.mainsnak, targetvalue) then
table.insert(claims, statement)
end
end
return claims
end
end


local function validclaims(claims)
local function hasTargetValue(claim, target)
if target == nil then
local oldclaims = claims
return true
local claims = {}
end
for i, statement in pairs(oldclaims) do
return sameValue(claim.mainsnak, target)
if statement.rank == 'preferred' or statement.rank == 'normal' then
table.insert(claims, statement)
end
end
return claims
end
end


local function withrank(claims, rank)
local function hasRank(claim, target)
if rank == 'best' then
if target == 'valid' then
local preferred = withrank(claims, 'preferred')
return hasRank(claim, 'preferred') or hasRank(claim, 'normal')
else
if #preferred > 0 then
return claim.rank == target
return preferred
end
else
return withrank(claims, 'normal')
end
end
if rank == 'valid' then
return validclaims(claims)
end
local oldclaims = claims
local claims = {}
for i, statement in pairs(oldclaims) do
if statement.rank == rank then
table.insert(claims, statement)
end
end
return claims
end
end


local function withqualifier(claims, qualifier, qualifiervalue)
local function bestRanked(claims)
if not claims then
qualifier, qualifiervalue = string.upper(qualifier), string.upper(qualifiervalue or '')
return nil
end
local oldclaims = claims
local preferred, normal = {}, {}
local claims = {}
for i, statement in pairs(oldclaims) do
for i, j in pairs(claims) do
if j.rank == 'preferred' then
if statement.qualifiers and statement.qualifiers then
table.insert(preferred, j)
if qualifiervalue ~= '' then
elseif j.rank == 'normal' then
for j, qualif in pairs(statement.qualifiers[qualifier]) do
table.insert(normal, j)
if p.getRawvalue(qualif) == qualifiervalue then
end
table.insert(claims, statement)
end
end
if #preferred > 0 then
end
return preferred
else
else
table.insert(claims, statement)
return normal
end
end
end
end
return claims
end
end

local function withsource(claims, source, sourceproperty)
local function hasQualifier(claim, qualifier, qualifiervalues)
local oldclaims = claims
if not qualifier then -- si aucun qualificatif est demandé, ça passe
local claims = {}
return true
sourceproperty = string.upper(sourceproperty or 'P248')
end
local sourcevalue = string.upper(source or '')

for i, statement in pairs(oldclaims) do
qualifier = string.upper(qualifier)
local success
if not claim.qualifiers or not claim.qualifiers[qualifier] then
if statement.references then
return false
for j, reference in pairs(statement.references) do
end
if success then break end -- sp that it does not return twice the same reference when the property is used twice

for prop, content in pairs(reference.snaks) do
if type(qualifiervalues) == 'string' then
if prop == sourceproperty then
qualifiervalues = mw.text.split(qualifiervalues, ',')
if sourcevalue == '' then
end
table.insert(claims, statement)

success = true
if (not qualifiervalues) or (qualifiervalues == {}) then
else
return true -- si aucune valeur spécifique n'est exigée
for l, m in pairs(content) do
end
if p.getRawvalue(m) == source then

table.insert(claims, statement)
for i, j in pairs(claim.qualifiers[qualifier]) do
success = true
for k, l in pairs(qualifiervalues) do
end
if p.getRawvalue(j) == l then
end
return true
end
end
end
end
end
end
end
return false
end
end
end

return claims
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 i, ref in pairs(statement.references) do
for prop, content in pairs(ref.snaks) do
if prop == sourceproperty then
if sourcevalue == '' then
return true
else
for j, k in pairs(content) do
if p.getRawvalue(k) == source then
return true
end
end
end
end
end
end
return false
end
end


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


local function comparedate(a, b) -- returns true if a is earlier than B or if a has a date but not b
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 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

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
if a and b then
return a.timestamp < b.timestamp
return a.timestamp < b.timestamp
Line 247: Line 358:
return true
return true
end
end
return false
end
end


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

)
return claims
return objs
end
end


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


local function numval(claims, numval)
function p.getRawvalue(snak)
return p.getDatavalue(snak, {displayformat = 'raw'})
local numval = tonumber(numval) or 0 -- raise a error if numval is not a positive integer ?
local newclaims = {}
for i, j in pairs(claims) do
if #newclaims == numval then
return newclaims
end
table.insert(newclaims,j)
end
return newclaims
end
end


function p.getRawvalue(snak)
function p.showentity(entity, lang)
if not entity then
return p.getDatavalue(snak, {format = 'raw'})
return nil
end
if type(entity) == 'string' then
entity = p.getEntity(entity)
end
if not entity or not entity.type then
return formatError('entity-not-found')
end

local label = p._getLabel(entity, lang)
local id = entity.id
local link = id
if entity.type == 'property' then
link = 'Property:' .. link
end

return '[[' .. link .. '|' .. label .. ']] <small>(' .. id .. ')</small>'
end
end


function p.getDatavalue(snak, params)
function p.getDatavalue(snak, params)
if snak.snaktype ~= 'value' then
if isSpecial(snak) then
return nil
return nil
end
end
local datatype = snak.datavalue.type
local value = snak.datavalue.value
local displayformat = params.format
if datatype == 'wikibase-entityid' then
if displayformat == 'raw' then
return "Q" .. tostring(value['numeric-id'])
else
return p.formatEntityId("Q" .. tostring(value['numeric-id']), params)
end
elseif datatype == 'string' then
if displayformat == 'weblink' then
return formatLink(value)
end
return value


if not params then
params = {}
elseif datatype == 'time' then -- format example: +00000001809-02-12T00:00:00Z
end
if displayformat == 'raw' then
return value.time
else
return require('Module:Date').Wikibasedate(value, params.lang)
end


local displayformat = params.displayformat
elseif datatype == 'globecoordinate' then
local datatype = snak.datavalue.type
local latitude, longitude = value.latitude, value.longitude
local value = snak.datavalue.value
local globe = require('Module:Wikidata/Globes')[value.globe] -- transforme l'ID du globe en nom anglais utilisable par geohack

local precision = value.precision
if precision < 0.00016 then
if datatype == 'wikibase-entityid' then
if type(displayformat) == 'function' then
precision = 'dmsand2'
return displayformat(snak, params)
elseif precision < 0.016 then
end
precision = 'dms'
local prefix = 'Q'
elseif precision < 1 then
if snak.datavalue.value["entity-type"] == 'property' then
precision = 'dm'
prefix = 'P'
end
local id = prefix .. tostring(value['numeric-id'])
if displayformat == 'raw' then
return id
elseif displayformat == 'wikidatastyle' then
return p.showentity(id, params.lang)
else
else
return p.formatEntity(id, params)
precision = 'd'
end
end
return require('Module:Coordinates')._coord{latitude = latitude, longitude = longitude, globe = globe, precision = precision}


elseif datatype == 'string' then
local showntext = params.showntext
if displayformat == 'weblink' then
if showntext then
return '[' .. value .. ' ' .. showntext .. ']'
else
return value
end
end
if snak.datatype == 'math' and displayformat ~= 'raw' then
value = mw.getCurrentFrame():extensionTag('math', value)
end
if params.urlpattern then
value = '[' .. mw.ustring.gsub(mw.ustring.gsub(params.urlpattern, '$1', value), ' ', '%%20') .. ' ' .. (showntext or value) .. ']'
end
return value


elseif datatype == 'quantity' then
elseif datatype == 'time' then -- format example: +00000001809-02-12T00:00:00Z
if displayformat == 'raw' then
if displayformat == 'raw' then
return value.amount
return value.time
else
else
return objectToText(dateObject(value), params)
local str = string.sub(value.amount,2)
end
return require('Module:Formatnum').formatnum(str, {lang= params.lang})

end
elseif datatype == 'globecoordinate' then
else
-- retourne une table avec clés latitude, longitude, précision et globe à formater par un autre module (à changer ?)
return formatError( 'unknown-datavalue-type' )
if displayformat == 'latitude' then
end
return value.latitude
elseif displayformat == 'longitude' then
return value.longitude
elseif displayformat == 'qualifier' then
local coord = require 'Module:Coordinates'
value.globe = require('Module:Wikidata/Globes')[value.globe]
value.precision = nil
return coord._coord(value)
else
value.globe = require('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 datatype == '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)
local unit = mw.ustring.match(value.unit, '(Q%d+)')
if unit then
number = number .. '&nbsp;' .. p.formatEntity(unit, params)
end
return number
end
elseif datatype == 'monolingualtext' then
return '<span lang="' .. value.language .. '">' .. value.text .. '</span>'
else
return formatError( 'unknown-datavalue-type', datatype )
end
end
end


Line 354: Line 506:
newargs.property = j
newargs.property = j
local newclaims = p.getClaims(args)
local newclaims = p.getClaims(args)
for k, l in pairs(newclaims) do
if newclaims then
for k, l in pairs(newclaims) do
table.insert(claims, l)
table.insert(claims, l)
end
end
end
end
end
return claims
return claims
end
end

function p.getClaims( args ) -- returns a table of the claims matching some conditions given in args
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' )
if not args.property then
return formatError( 'property-param-not-provided' )
end
end
if type(args.property) == 'table' then
if type(args.property) == 'table' then
return getMultipleClaims(args)
return getMultipleClaims(args)
end
end
--Get entity
--Get entity
if args.item then -- synonyms
local entity = nil
local property = string.upper(args.property)
if args.item then -- item used as a synonym for entity
args.entity = args.item
args.entity = args.item
end
end
if not args.entity then
local entity = args.entity
if type(entity) ~= 'table' then
entity = p.getEntity(entity)
end
local property = string.upper(args.property)
if not entity or not entity.claims or not entity.claims[property] then
return nil
return nil
end
end
if type( args.entity ) == "table" then
entity = args.entity
else
entity = getEntityFromId( args.entity )
end


if not args.rank then
if not entity or not entity.claims or not entity.claims[property] then
args.rank = 'best'
return nil
end
end
claims = entity.claims[property]
local claims = {}
-- mettre ~= '' pour le cas ou le paramètre est écrit mais laissé blanc ({{#invoke:formatStatements|property=pXX|targetvalue = xx}})
-- ~= '' lorsque le paramètre est écrit mais laissé blanc dans une fonction frame
for i, statement in pairs(entity.claims[property]) do
if
if args.targetvalue and args.targetvalue ~= '' then
(
claims = withtargetvalue(claims, args.targetvalue)
not args.excludespecial
end
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.qualifier and args.qualifier ~= '' then
if args.numval then
return numval(claims, args.numval)
claims = withqualifier(claims, args.qualifier, args.qualifiervalue)
end
end
return claims
if (args.source and args.source) or (args.sourceproperty and args.sourceproperty ~= '') then
end
claims = withsource(claims, args.source, args.sourceproperty)
end
if args.excludespecial and args.excludespecial ~= '' then
claims = excludespecial(claims)
end


function p.formatClaimList(claims, args)
if args.rank ~= 'all' then
if not args.rank or args.rank == '' then
if not claims then
return nil
args.rank = 'best'
end
claims = withrank(claims, args.rank)
end
if args.sorttype then
claims = p.sortclaims(claims, args.sorttype)
end
if args.numval and args.numval ~= '' then --keep at the end, after other filters have been implmented
claims = numval(claims, args.numval)
end
if #claims > 0 then
return claims
end
end
for i, j in pairs(claims) do
claims[i] = p.formatStatement(j, args)
end
return claims
end
end


function p.stringTable(args) -- like getClaims, but get a list of string rather than a list of snaks, for easier manipulation
function p.getQualifiers(args)
local claims = p.getClaims(args)
local claims = p.getClaims(args)
if not claims then
return p.formatClaimList(claims, args)
end

local function getQualifiers(statement, qualifs, params)
if not statement.qualifiers then
return nil
return nil
end
end
local targetqualif = args.qualifier
local vals = {}
for i, j in pairs(qualifs) do
local found = {}
j = string.upper(j)
for i, j in pairs(claims) do
if (not j.qualifiers) or (not j.qualifiers[targetqualif]) then
if statement.qualifiers[j] then
local inserted = false
break
if statement.qualifiers[j][1].datatype == 'monolingualtext' then
end
local in_preferred_lang
for k, l in pairs(j.qualifiers[targetqualif]) do
for _, language in pairs(fb.fblist(params.lang or defaultlang)) do
table.insert(found, l)
for _, snak in pairs(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
end
if #found > 0 then
if #vals == 0 then
return found
return nil
end
end
return vals
end
end


function p.getFormattedQualifiers(args)
function p.getFormattedQualifiers(statement, qualifs, params)
if not params then params = {} end
local qualifs = p.getQualifiers(args)
local qualiftable = getQualifiers(statement, qualifs, params)
if not qualifs then
if not qualiftable then
return nil
return nil
end
end
for i, j in pairs(qualifs) do
for i, j in pairs(qualiftable) do
local params = params
qualifs[i] = p.formatSnak(j, args)
if j.datatype == 'globe-coordinate' then
params.displayformat = 'qualifier'
end
qualiftable[i] = p.formatSnak(j, params)
end
end
return linguistic.conj(qualiftable, params.lang or defaultlang)
return tableToText(qualifs, args)
end
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
function p.formatQualifier( frame )
if qualifs then
return p.getFormattedQualifiers(frame.args)
if type(qualifs) == 'string' then
end
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
function p.formatClaimList(claims, args)
local timedata = p.getDate(statement)
if not claims then
if timedata then
return nil
local formatteddate = objectToText(timedata, args)
end
formatteddate = linguistic.inparentheses(formatteddate, lang)
for i, j in pairs(claims) do
str = str .. '<small>' .. formatteddate ..'</small>'
claims[i] = p.formatStatement(j, args)
end
end
end
return claims
end


if args.showsource and statement.references then
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)
local cite = require 'Module:Cite'
local frame = mw.getCurrentFrame()
return p.formatClaimList(claims, args)
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 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
local s = cite.citeitem('Q' .. source.datavalue.value['numeric-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
end


function p.formatSnak(snak, params)
function p.formatStatements( args )--Format statement and concat them cleanly
--local params = params or {} pour faciliter l'appel depuis d'autres modules
local valuetable = p.stringTable(args)
if snak.snaktype == 'value' then
return tableToText(valuetable, args)
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
end


local function defaultLabel(entity, lang, displayformat) -- label when no label is available
function p.formatStatement( statement, args )
if entity and displayformat == 'id' then
if not statement.type or statement.type ~= 'statement' then
return entity.id
return formatError( 'unknown-claim-type' )
end
end
return i18n('no-label', lang)

end
local str = p.formatSnak( statement.mainsnak, args )
if args.showqualifiers then -- very ugly
local targetqualif = string.upper(args.showqualifiers)
local foundvalues = {}
if statement.qualifiers and statement.qualifiers[targetqualif] then
for i, j in pairs(statement.qualifiers[targetqualif]) do
table.insert(foundvalues, p.getDatavalue(j, {lang=args.lang, link=args.link}))
end
str = str .. linguistic.inparentheses(mw.text.listToText(foundvalues), lang)
end
end
if args.withdate then -- when "withdate and chronosorty are borth set, date retrieval is performed twice
local timedata = getDate(statement)
if timedata then
local dat = require('Module:Daterange').wikibasedaterange(timedata, args.lang)
local formattteddate = linguistic.inparentheses(dat, args.lang)
str = str .. formattteddate
end
end


function p._getLabel(entity, lang, default)
if args.showsource and statement.references then -- not great
if not entity then
local sourcestring = ''
return nil
for i, ref in pairs(statement.references) do
end
if ref.snaks.P248 then
if type(entity) ~= 'table' then
for j, source in pairs(ref.snaks.P248) do
entity = p.getEntity(entity)
if source.snaktype == 'value' then
end
local page
if entity and entity.labels then
if ref.snaks.P304 and ref.snaks.P304[1].snaktype == 'value' then
for i, lg in pairs(fb.fblist(lang or defaultlang)) do
page = ref.snaks.P304[1].datavalue.value
if entity.labels[lg] then
end
return entity.labels[lg].value
local s = require('Module:Cite/sandbox').citeitem('Q' .. source.datavalue.value['numeric-id'], args.lang, page)
end
s = mw.getCurrentFrame():extensionTag( 'ref', s )
end
sourcestring = sourcestring .. s
end
end
return defaultLabel(entity, lang, default)
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
end


function p.formatSnak( snak, args )
function p._getDescription(entity, lang)
if not entity then
if not args then args = {} end -- pour faciliter l'appel depuis d'autres modules
return i18n('no description')
if snak.snaktype == 'somevalue' then
end
return fb._langSwitch(require('Module:I18n/unknown'), args.lang)
if type(entity) ~= 'table' then
elseif snak.snaktype == 'novalue' then
entity = p.getEntity(entity)
return i18n['novalue'] --todo
end
elseif snak.snaktype == 'value' then
local descriptions = entity.descriptions
return p.getDatavalue( snak, args)
if not descriptions then
else
return i18n('no description')
return formatError( 'unknown-snak-type' )
end
end
if descriptions[lang] then
return descriptions[lang].value
end
local langlist = fb.fblist(lang or defaultlang) -- list of fallback languages if no label in the desired language
for i, lg in pairs(langlist) do
if descriptions[lg] then
return descriptions[lg].value
end
end
return i18n('no description')
end
end


function p._getLabel(entity, lang, default)
local function wikipediaLink(entity, lang)
local link = entity:getSitelink(lang .. 'wiki')
if type(entity) == 'string' then
if link then
entity = mw.wikibase.getEntityObject(entity)
return ':' .. lang .. ':' .. link
end
end
if not entity then return nil end
return nil
local label = entity:getLabel(lang)
if label then
return label
end
for i, j in pairs (mw.language.getFallbacksFor(lang)) do
label = entity:getLabel(j)
if label then
return label
end
end
if default == 'nolabel' then
return i18n['no-label']
end
return entity.id
end
end


function p._getDescription(entity, lang)
local function getLink(entity, typelink, lang)
if type(entity) == 'string' then
if not typelink or typelink == '-' then
return nil
entity = mw.wikibase.getEntityObject(entity)
end
end
if not entity then return nil end
if not lang then
lang = defaultlang
if not entity.descriptions then
end
return i18n['no description']

end
if typelink == 'wikidata' then
local descriptions = entity.descriptions
if entity.type == 'property' then
if not descriptions then
return nil
return 'd:P:' .. entity.id
else
end
return 'd:' .. entity.id
if descriptions[lang] then
end
return descriptions[lang].value

end
elseif typelink == 'wikipedia' then
local fblist = require('Module:Fallback').fblist(lang) -- list of fallback languages in no label in the desired language
return wikipediaLink(entity, lang)
for i, j in pairs (mw.language.getFallbacksFor(lang)) do

if descriptions.lang then
elseif typelink == 'anywikipedia' then
return descriptions[lang].value
local fallbacklist = fb.fblist(lang)
end
for i, lg in pairs(fallbacklist) do
end
link = wikipediaLink(entity, lg)
if default == 'nolabel' then
if link then return link end
return i18n['no-label']
end
end
end
return entity.id
return nil
end
end


local function formattedLabel(label, entity, args)
local function formattedLabel(label, entity, args)
if args.link== '-' then
if not args then args = {} end
local link = getLink(entity, args.link, args.lang)
return label
if not link then
end
local lang = args.lang
link = getLink(entity, defaultlink, args.lang)
local link
if args.link == 'wikipedia' then
link = entity:getSitelink(lang .. 'wiki')
if link then
link = ':' .. lang .. ':' .. link
end
elseif args.link == 'anywikipedia' then -- if Wikipeia links does not exist in the user's language, goes through the fallbackchain
local sitelinks = entity.sitelinks
if sitelinks then
local hack = {}
for i, j in pairs(sitelinks) do
if string.sub(i, #i-4, #i) then
hack[string.sub(i, 1, #i - 4)] = j.title
end
end
if not hack['en'] then hack['en'] = '~' end
local linklang
link, linklang = require('Module:Fallback')._langSwitch(hack, lang)
if link then
link = ':' .. linklang .. ':' .. link
end
end
end
end
if not link then
if not link then
return label
link = 'd:' .. entity.id
else
return '[[' .. link .. '|' .. label .. ']]'
end
end
return '[[' .. link .. '|' .. label .. ']]'
end
end


function p.formatEntityId( entity, args )
local entity = getEntityFromId(entity)
local label = p._getLabel(entity, args.lang)
return formattedLabel(label, entity, args)
end


function p.getmainid(claim)
function p.getmainid(claim)
if claim and claim.mainsnak.snaktype == 'value' then
if claim and not isSpecial(claim.mainsnak) then
return 'Q' .. claim.mainsnak.datavalue.value['numeric-id']
return 'Q' .. claim.mainsnak.datavalue.value['numeric-id']
end
end
return nil
end
end


function p.formatEntity( entity, args )
function p.formatEntity( entity, args )
if not entity then
return nil
end
if not args then args = {} end
if type(entity) == 'string' then
if type(entity) == 'string' then
entity = mw.wikibase.getEntityObject(entity)
entity = p.getEntity(entity)
end
end
label = p._getLabel(entity, args.lang)
local label = p._getLabel(entity, args.lang)
if not label then
return formattedLabel(label, entity, args)
label = entity.id
end
return formattedLabel(label, entity, args)
end
end


function p.getLabel(frame) -- simple for simple templates like {{Q|}}}
function p.getLabel(frame) -- simple for simple templates like {{Q|}}}
local args = frame.args
local args = frame.args
local entity = args.entity
local entity = args.entity
local lang = args.lang
local lang = args.lang
if not lang or lang == '' then
if lang == '' then
lang = defaultlang
lang = frame:preprocess('{{int:lang}}')
end
end

if string.sub(entity, 1, 10) == 'Property:P' then
entity = string.sub(entity, 10)
if string.sub(entity, 1, 10) == 'Property:P' then
elseif (string.sub(entity, 1, 1) ~= 'P' and string.sub(entity, 1, 1) ~= 'Q') or (not tonumber(string.sub(entity, 2))) 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']
return i18n('invalid-id')
end
end

if not args.link or args.link == '' then -- by default: no link
args.link = '-'
if not args.link or args.link == '' then -- by default: no link
args.link = '-'
end
end
if args.link == '-' then
if args.link == '-' then
return p._getLabel(entity, lang) or i18n.errors['invalid-id']
return p._getLabel(entity, lang) or i18n('invalid-id')
else
else
args.lang = lang
return p.formatEntity(entity, args)
return p.formatEntity(entity, args)
end
end
end
end


function p.getDescription(frame) -- simple for simple templates like {{Q|}}}
function p._formatStatements( args )--Format statements and concat them cleanly
if args.value == '-' then
local entity = frame.args.entity
return nil
local lang = frame.args.lang
end
if not lang or lang == '' then
--If a value is already set, use it
lang = frame:preprocess('{{int:lang}}')
if args.value and args.value ~= '' then
end
return args.value
if (string.sub(entity, 1, 1) ~= 'P' and string.sub(entity, 1, 1) ~= 'Q') or (not tonumber(string.sub(entity, 2))) then
end
return i18n.errors['invalid-id']
local valuetable = p.stringTable(args)
end
return tableToText(valuetable, args)
return p._getDescription(entity, lang) or i18n.errors['invalid-id']
end
end


function p._formatStatements(args)
function p.showQualifier( args )
local qualifs = args.qualifiers or args.qualifier
return p.formatStatements(args)
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
end


function p.formatStatementsE( frame )
function p._formatAndCat(args)
local val = p._formatStatements(args)
local args = frame.args -- parameters not provided directly in the calling template, to avoid update issues
if not args.item then
if val then
return val .. addTrackingCat(args.property)
args = frame:getParent().args
end
end
return nil
end


function p.getTheDate(args)
if (not args.item) or (args.item == '') then
local claims = p.getClaims(args)
return "no item given"
if not claims then
return nil
end
end
local formattedvalues = {}
if (not args.property) or (args.property == '') then
for i, j in pairs(claims) do
return "no property given"
table.insert(formattedvalues, p.getFormattedDate(j))
end
end
local val = linguistic.conj(formattedvalues)
if not args.lang then
if val and args.addcat == true then
args.lang = frame:preprocess('{{int:lang}}')
return val .. addTrackingCat(args.property)
end
else
return p.formatStatements( args )
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
local lang = frame.args.lang

return p._getDescription(entity, lang) or i18n('invalid-id')
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
end



Revision as of 14:23, 27 April 2016

Lua

CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules

Module permitting basic data retrieval from Wikidata items, based on mw:Extension:Wikibase Client/Lua

Functions callable from Lua

p.getClaims returns claims in a particular item that match a particular query

  • item (required): its Qid
  • property (required) property that the claims should have
  • qualifier = qualifiers that the statement should have
  • withrank = rank of the statement ; 'preferred', 'normal', 'deprecated' or 'valid' (ie. normal and preferred). By default = preferred.
  • sourceproperty = this property should be used in the source
  • withsource = the source that should be provided in the statement (if sourceproperty is not provided, the property used is stated in (P248))
  • excludespecial = set to true if you do not want to get "novalue" and "somevalue".
  • numval = if you want to set a maximum number of values to be returned
  • sorttype = set to "chronological" to get the statements in chronological order using the start time (P580), end time (P582) and point in time (P585) qualifiers. Set to "inverted" for chronological order. From a Lua module, you can also define your own sorting criteria.
  • showsource = set to "true" if you want the source of the statement to be displayed.

p.formatStatements(args): returns a string containing the statements given in the table args. Same keys as getClaims, plus formatting arguments:

  • lang (required) for the desired language
  • displayformat = the format in which the args should be returned. For example, for a string-type property displayformat = "weblink" returns a formatted weblink.
  • conjtype = the conjunction separating the statements. For example, conj = '<br />' will make a new line between each statement.
  • showqualifiers = the qualifiers that should be shown along with the mainsnak value

p.getLabel get the label of an entity

  • entity = entity ID with its Q or P
  • lang

Functions callable from wikitext

  • p.formatStatementsE same as p.formatStatements, except that "lang" is not required. It is most conveniently used from {{Data}} that takes exactly the same arguments.

Examples

Code Render Comment
{{#invoke:Wikidata|pageId}} Script error: The function "pageId" does not exist. return wikidata q-code for pages connected to wikidata
{{#invoke:Wikidata|getLabel|entity=Q42|link=wikipedia}} Douglas Adams link to wikipedia
{{#invoke:Wikidata|getLabel|entity=Q42|link=wikipedia|format=lc}} Douglas Adams link to wikipedia but shown with lower case
{{#invoke:Wikidata|getLabel|entity=Q42|link=-}} Douglas Adams no links
{{#invoke:Wikidata|getLabel|entity=Q42|link=wikipedia|lang=ja}} ダグラス・アダムズ
{{#invoke:Wikidata|getLabel|entity=Q42|link=wikidata}} Douglas Adams link to wikidata
{{#invoke:Wikidata|getLabel|entity=Q42|link=wikidata|lang=ja}} ダグラス・アダムズ
{{#invoke:Wikidata|getDescription|entity=Q42|link=wikidata|lang=fr}} écrivain de science-fiction et humoriste anglais (1952–2001)
{{#invoke:Wikidata|formatStatementsE|item=Q42|property=p31}} human
{{#invoke:Wikidata|formatStatementsE|item=Q42|property=p31|link=-}} human
{{#invoke:Wikidata|formatStatementsE|item=Q42|property=p31|lang=ja}} ヒト
{{#invoke:Wikidata|formatStatementsE|item=Q42|property=p569}}
{{#invoke:Wikidata|formatStatementsE|item=Q42|property=p569|lang=ja}}
{{#invoke:Wikidata|formatStatementsE|item=Q12418|property=p186}} oil paint and poplar panel
{{#invoke:Wikidata|formatStatementsE|item=Q12418|property=p186|link=-}} oil paint and poplar panel
{{#invoke:Wikidata|formatStatementsE|item=Q12418|property=p186|displayformat=raw}} Q296955 and Q106857865
{{#invoke:Wikidata|formatStatementsE|item=Q12418|property=p186|lang=ja}} 油絵具およびポプラ板
{{#invoke:Wikidata|formatStatementsE|item=Q12418|property=p186|rank=valid}} oil paint, poplar panel and wood rank = "valid" accepts both "preferred" and "normal" values
{{#invoke:Wikidata|formatStatementsE|item=Q12418|property=p186|showqualifiers=p518}} Lua error in Module:Linguistic at line 111: variable 'lang' is not declared. shows the value of the p518 qualifier (if any) in addition to the main value
{{#invoke:Wikidata|formatStatementsE|item=Q83259|property=p669|showqualifiers=p670|delimiter=&#32;}} shows the value of the P670 qualifier (if any) in addition to the main value separated by a space
{{#invoke:Wikidata|formatStatementsE|item=Q12418|property=p186|qualifier=p518}} poplar panel should only display values that have a p518 qualifier
{{#invoke:Wikidata|formatStatementsE|item=Q12418|property=p186|numval=1}} oil paint returns the number of values numval (priority to those with "rank= preferred", if there are not enough of them, also accepts "rank = normal")
{{#invoke:Wikidata|formatStatementsE|item=Q12418|property=P276|sourceproperty=P854}} Salle des États, Louvre
{{#invoke:Wikidata|formatStatementsE|item=Q11879536|property=P460|withsource=Q1645493}} Lisa del Giocondo
{{#invoke:Wikidata|formatStatementsE|item=Q153|property=P231|showsource=true}} Lua error in Module:Fallback at line 62: attempt to call method 'lower' (a nil value).
{{#invoke:Wikidata|formatStatementsE|item=Q205309|property=P793|sorttype=inverted}} closure, demolition, renovation, renovation, first match and construction
{{#invoke:Wikidata|formatStatementsE|property=P625|item=Q90|displayformat=latitude}} 48.856666666667 Latitude of Paris
{{#invoke:Wikidata|formatStatementsE|property=P19|item=Q1441042}} Lua error at line 19: attempt to call field '_langSwitch' (a nil value). unknown value
{{#invoke:Wikidata|formatStatementsE|property=P19|item=Q43650835}} Lua error at line 19: attempt to call field '_langSwitch' (a nil value). no value

Code

--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, lang)
	local message = i18nmessages[str]
	if type(message) == 'string' then
		return message
	end
	return fb._langSwitch(message, lang or 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 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.time)
	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 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)
	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 i, j in pairs(claim.qualifiers[qualifier]) do
		for k, l in pairs(qualifiervalues) do
			if p.getRawvalue(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 i, ref in pairs(statement.references) do
		for prop, content in pairs(ref.snaks) do
			if prop == sourceproperty then
				if sourcevalue == '' then
					return true
				else
					for j, k in pairs(content) do
						if p.getRawvalue(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 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

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
	if type(entity) == 'string' then
		entity = p.getEntity(entity)
	end
	if not entity or not entity.type then
		return formatError('entity-not-found')
	end

	local label = p._getLabel(entity, lang)
	local id = entity.id
	local link = id
	if entity.type == 'property' then
		link = 'Property:' .. link
	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 datatype = snak.datavalue.type
	local value = snak.datavalue.value

	if datatype == 'wikibase-entityid' then
		if type(displayformat) == 'function' then
			return displayformat(snak, params)
		end
		local prefix = 'Q'
		if snak.datavalue.value["entity-type"] == 'property' then
			prefix = 'P'
		end
		local id = prefix .. tostring(value['numeric-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 datatype == 'string' then
		local showntext = params.showntext
		if displayformat == 'weblink' then
			if showntext then
				return '[' .. value .. ' ' .. showntext .. ']'
			else
				return value
			end
		end
		if snak.datatype == 'math' and displayformat ~= 'raw' then
			value = mw.getCurrentFrame():extensionTag('math', value)
		end
		if params.urlpattern then
			value = '[' .. mw.ustring.gsub(mw.ustring.gsub(params.urlpattern, '$1', value), ' ', '%%20') .. ' ' .. (showntext or value) .. ']'
		end
		return value

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

	elseif datatype == '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 = require('Module:Wikidata/Globes')[value.globe]
			value.precision = nil
			return coord._coord(value)
		else
			value.globe = require('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 datatype == '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)
			local unit = mw.ustring.match(value.unit, '(Q%d+)')
			if unit then
				number = number .. '&nbsp;' .. p.formatEntity(unit, params)
			end
			return number
		end
	elseif datatype == 'monolingualtext' then
		return '<span lang="' .. value.language .. '">' .. value.text .. '</span>'
	else
		return formatError( 'unknown-datavalue-type', datatype )
	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 entity = args.entity
	if type(entity) ~= 'table' then
		entity = p.getEntity(entity)
	end
	local property = string.upper(args.property)
	if not entity or not entity.claims or 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.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 pairs(fb.fblist(params.lang or defaultlang)) do
					for _, snak in pairs(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)
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 = ''
		for i, ref in pairs(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
						local s = cite.citeitem('Q' .. source.datavalue.value['numeric-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.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, lang, displayformat) -- label when no label is available
	if entity and displayformat == 'id' then
		return entity.id
	end
	return i18n('no-label', lang)
end

function p._getLabel(entity, lang, default)
	if not entity then
		return nil
	end
	if type(entity) ~= 'table' then
		entity = p.getEntity(entity)
	end
	if entity and entity.labels then
		for i, lg in pairs(fb.fblist(lang or defaultlang)) do
			if entity.labels[lg] then
				return entity.labels[lg].value
			end
		end
	end
	return defaultLabel(entity, lang, default)
end

function p._getDescription(entity, lang)
	if not entity then
		return i18n('no description')
	end
	if type(entity) ~= 'table' then
		entity = p.getEntity(entity)
	end
	local descriptions = entity.descriptions
	if not descriptions then
		return i18n('no description')
	end
	if descriptions[lang] then
		return descriptions[lang].value
	end
	local langlist = fb.fblist(lang or defaultlang) -- list of fallback languages if no label in the desired language
	for i, lg in pairs(langlist) do
		if descriptions[lg] then
			return descriptions[lg].value
		end
	end
	return i18n('no description')
end

local function wikipediaLink(entity, lang)
	local link = entity:getSitelink(lang .. 'wiki')
	if link then
		return ':' .. lang .. ':' .. link
	end
	return nil
end

local function getLink(entity, typelink, lang)
	if not typelink or typelink == '-' then
		return nil
	end
	if not lang then
		lang = defaultlang
	end

	if typelink == 'wikidata' then
		if entity.type == 'property' then
			return 'd:P:' .. entity.id
		else
			return 'd:' .. entity.id
		end

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

	elseif typelink == 'anywikipedia' then
		local fallbacklist = fb.fblist(lang)
		for i, lg in pairs(fallbacklist) do
			link = wikipediaLink(entity, lg)
			if link then return link end
		end
	end
	return nil
end

local function formattedLabel(label, entity, args)
	if not args then args = {} end
	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.getmainid(claim)
	if claim and not isSpecial(claim.mainsnak) then
		return 'Q' .. claim.mainsnak.datavalue.value['numeric-id']
	end
	return nil
end

function p.formatEntity( entity, args )
	if not entity then
		return nil
	end
	if not args then args = {} end
	if type(entity) == 'string' then
		entity = p.getEntity(entity)
	end
	local label = p._getLabel(entity, args.lang)
	if not label then
		label = entity.id
	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 = args.lang
	if lang == '' then
		lang = defaultlang
	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('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('invalid-id')
	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 = ''
	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)
	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 i, j in pairs(claims) do
		table.insert(formattedvalues, p.getFormattedDate(j))
	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
	local lang = frame.args.lang

	return p._getDescription(entity, lang) or i18n('invalid-id')
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