Module:Wikidata: Difference between revisions

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Content deleted Content added
return date as a year or Isodate
fix rendering for snaktype 'somevalue' and 'novalue'
 
(29 intermediate revisions by 8 users not shown)
Line 4: Line 4:


local linguistic = require('Module:Linguistic')
local linguistic = require('Module:Linguistic')
local selectClaims = require('Module:Wikidata/GetClaims')
--local formatDate = require('Module:Complex date') only loaded when needed to save memory in large pages like Wikidata:List of properties/all
local tools = require('Module:Wikidata/Tools')
local entities = require('Module:Wikidata/FormatEntity')
local dates = require('Module:Wikidata/Dates')
local weblink = require('Module:Weblink')

local langSwitch = require('Module:LangSwitch')._langSwitch
local fb = require('Module:Fallback')
local fb = require('Module:Fallback')
local i18nmessages = mw.loadData('Module:i18n/wikidata')
local i18nmessages = mw.loadData('Module:i18n/wikidata')


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


Line 17: Line 23:
return message
return message
end
end
return fb._langSwitch(message, lang or defaultlang)
return langSwitch(message, lang or defaultlang)
end
end


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


p.getClaims = selectClaims.getClaims
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)
local function removeBlanks(args)
for i, j in pairs(args) do -- does not work ??
for i = #args, 1, -1 do
if (j == '') or (j == '-') then args[i] = nil end
if (args[i] == '') or (args[i] == '-') then
table.remove(args, i)
end
end
end
return args
return args
end
end


local function getQualifiers(statement, qualifs, params)
local function formatTheUnknown() -- voir si on peut accorder/adapter l'usage de "inconnu"
if not statement.qualifiers then
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
return nil
end
end
if type(qualifs) == 'string' then
local formatDate = require('Module:Complex date')
qualifs = mw.text.split(qualifs, ',')
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
local vals = {}
return nil
for i, j in pairs(qualifs) do
end
j = string.upper(j)

if statement.qualifiers[j] then
local function formatDaterange(obj, params) --TODO
local inserted = false
local begin = formatDatepoint(obj.begin, params) or ''
if statement.qualifiers[j][1].datatype == 'monolingualtext' then
local ending = formatDatepoint(obj.ending, params) or ''
local in_preferred_lang
return begin .. '-' .. ending
for _, language in pairs(fb.fblist(params.lang or defaultlang)) do
end
for _, snak in pairs(statement.qualifiers[j]) do

if isInLanguage(snak, language) then
local function objectToText(obj, params)
in_preferred_lang = snak
if obj.type == 'dateobject' then
break
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
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
end
end
if #vals == 0 then
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
return nil
end
end
return vals
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.formatSnak(snak, params)
-- special values: 'novalue' and 'somevalue'
if isSpecial(snak) then
local datatype = snak.datatype
return nil
if snak.snaktype == 'somevalue' then
return i18n('somevalue')
elseif snak.snaktype == 'novalue' then
return i18n('novalue')
end
end
local value = snak.datavalue.value

-- user-defined displayformat
if not params then
params = {}
end

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

if datatype == 'wikibase-entityid' then
if datatype == 'wikibase-item' or datatype == 'wikibase-property' then
return entities.formatEntity(tools.getId(snak), params)
if type(displayformat) == 'function' then
elseif datatype == 'url' then
return displayformat(snak, params)
return weblink.makelink(value, params.text, params.displayformat)
end
elseif datatype == 'math' then
local prefix = 'Q'
return mw.getCurrentFrame():extensionTag('math', value)
if snak.datavalue.value["entity-type"] == 'property' then
elseif datatype == 'string' or datatype == 'external-id' or datatype == 'commonsMedia' then
prefix = 'P'
if params.urlpattern then
end
local urlpattern = params.urlpattern
local id = prefix .. tostring(value['numeric-id'])
if displayformat == 'raw' then
if type(urlpattern) == 'function' then
urlpattern = urlpattern(value)
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
value = '[' .. mw.ustring.gsub(urlpattern, '$1', value) .. ' ' .. (params.text or value) .. ']'
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
end
return value
return value
elseif datatype == 'time' then -- format example: +00000001809-02-12T00:00:00Z

return dates.formatTimeSnak(snak, params)
elseif datatype == 'time' then -- format example: +1809-02-12T00:00:00Z
if displayformat == 'raw' then
elseif datatype == 'globe-coordinate' then
-- default return a table with latitude, longitude, precision and globe that can be used by another module
return value.time
elseif displayformat == 'year' then
obj = dateObject(value);
return obj.year
elseif displayformat == 'isodate' then
obj = dateObject(value);
return obj.year .. '-' .. obj.month .. '-' .. obj.day
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
if displayformat == 'latitude' then
return value.latitude
return value.latitude
Line 478: Line 122:
return value.longitude
return value.longitude
elseif displayformat == 'qualifier' then
elseif displayformat == 'qualifier' then
local coord = require 'Module:Coordinates'
local coord = require('Module:Coordinates')
value.globe = require('Module:Wikidata/Globes')[value.globe]
value.globe = require('Module:Wikidata/Globes')[value.globe]
value.precision = nil
value.precision = nil
return coord._coord(value)
return coord._coord(value)
else
else
value.globe = require('Module:Wikidata/Globes')[value.globe] -- transforme l'ID du globe en nom anglais utilisable par geohack
value.globe = require('Module:Wikidata/Globes')[value.globe] -- get English name for geohack
return value
return value -- note : les coordonnées Wikidata peuvent être utilisée depuis Module:Coordinates. Faut-il aussi autoriser à appeler Module:Coordiantes ici ?
end
end
elseif datatype == 'quantity' then

-- TODO: handle precision parameters
elseif datatype == 'quantity' then -- todo : gérer les paramètre précision
if displayformat == 'raw' then
if displayformat == 'raw' then
return tonumber(value.amount)
return tonumber(value.amount)
else
else
local formatNum = require 'Module:Formatnum'
local formatNum = require('Module:Formatnum')
local number = formatNum.formatNum(value.amount)
local number = formatNum.formatNum(value.amount)
local unit = mw.ustring.match(value.unit, '(Q%d+)')
local unit = mw.ustring.match(value.unit, '(Q%d+)')
if unit then
if unit then
number = number .. '&nbsp;' .. p.formatEntity(unit, params)
number = number .. '&nbsp;' .. entities.formatEntity(unit, params)
end
end
return number
return number
end
end
elseif datatype == 'monolingualtext' then
elseif datatype == 'monolingualtext' then
if displayformat == 'raw' then
return '<span lang="' .. value.language .. '">' .. value.text .. '</span>'
-- Don't use HTML
else
local byte, char = string.byte, mw.ustring.char
return formatError( 'unknown-datavalue-type', datatype )
local lang = value.language:lower()
end
local tag = {}
end
table.insert(tag, char(0x2068)) -- U+2068: First Strong Isolate (FSI)

table.insert(tag, char(0xE0001)) -- U+E0001: Language tag
local function getMultipleClaims(args)
for i = 1, #lang do
local newargs = args
local claims = {}
local b = byte(lang, i)
if b >= 0x61 and b <= 0x7A or b == 0x2D or b == 0x5F then -- 'a'..'z', '-', '_'
for i, j in pairs(args.property) do
table.insert(tag, char(0xE0000 + b)) -- U+E0020..U+E007E: Tag characters (remaps ASCII only)
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
table.insert(tag, value.text)
table.insert(tag, char(0xE007F)) -- U+E007F: Cancel Tag
table.insert(tag, char(0x2069)) -- U+2069: Pop Directional Isolate (PDI)
return table.concat(tag)
else
return '<bdi lang="' .. value.language .. '">' .. value.text .. '</bdi>'
end
end
else
return formatError('unknown-datavalue-type', datatype )
end
end
if #vals == 0 then
return nil
end
return vals
end
end


function p.getFormattedQualifiers(statement, qualifs, params)
function p.formatStatementQualifiers(statement, qualifs, params)
if not params then params = {} end
if not params then params = {} end
local qualiftable = getQualifiers(statement, qualifs, params)
local qualiftable = getQualifiers(statement, qualifs, params)
Line 675: Line 185:
end
end


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


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


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


if args.showsource and statement.references then
if args.showsource and statement.references then
local cite = require 'Module:Cite'
local cite = require('Module:Cite')
local frame = mw.getCurrentFrame()
local frame = mw.getCurrentFrame()
local sourcestring = ''
local sourcestring = ''
Line 717: Line 222:
if ref.snaks.P248 then
if ref.snaks.P248 then
for j, source in pairs(ref.snaks.P248) do
for j, source in pairs(ref.snaks.P248) do
if not isSpecial(source) then
if not tools.isSpecial(source) then
local page
local page
if ref.snaks.P304 and not isSpecial(ref.snaks.P304[1]) then
if ref.snaks.P304 and not tools.isSpecial(ref.snaks.P304[1]) then
page = ref.snaks.P304[1].datavalue.value
page = ref.snaks.P304[1].datavalue.value
end
end
local s = cite.citeitem('Q' .. source.datavalue.value['numeric-id'], lang, page)
local s = cite.citeitem('Q' .. source.datavalue.value['numeric-id'], lang, page)
s = frame:extensionTag( 'ref', s )
s = frame:extensionTag('ref', s)
sourcestring = sourcestring .. s
sourcestring = sourcestring .. s
end
end
end
end
elseif ref.snaks.P854 and not isSpecial(ref.snaks.P854[1]) then
elseif ref.snaks.P854 and not tools.isSpecial(ref.snaks.P854[1]) then
s = frame:extensionTag( 'ref', p.getDatavalue(ref.snaks.P854[1]) )
s = frame:extensionTag('ref', p.getDatavalue(ref.snaks.P854[1]))
sourcestring = sourcestring .. s
sourcestring = sourcestring .. s
end
end
Line 735: Line 240:
end
end
return str
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 ( (not lang) or (lang == defaultlang) ) and (type(entity) == "string") then
return mw.wikibase.label(entity)
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
end


Line 781: Line 246:
return i18n('no description')
return i18n('no description')
end
end
if type(entity) ~= 'table' then
if type(entity) == 'string' and (not lang or lang == defaultlang) then
entity = p.getEntity(entity)
return mw.wikibase.description(entity)
end
end
entity = mw.wikibase.getEntity(entity)
local descriptions = entity.descriptions
local descriptions = entity.descriptions
if not descriptions then
if not descriptions then
return i18n('no description')
return i18n('no description')
end
if descriptions[lang] then
return descriptions[lang].value
end
end
local langlist = fb.fblist(lang or defaultlang) -- list of fallback languages if no label in the desired language
local langlist = fb.fblist(lang or defaultlang) -- list of fallback languages if no label in the desired language
Line 800: Line 263:
end
end


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


function p.stringTable(args) -- find the relevant claims, and formats them as a list of strings
local function getLink(entity, typelink, lang)
-- Get claims
if not typelink or typelink == '-' then
local claims = p.getClaims(args)
if not claims then
return nil
return nil
end
end
-- Define the formatter function
if not lang then
local formatterFun = p.formatStatement
lang = defaultlang
if args.type == 'date' then
formatterFun = dates.getFormattedDate
elseif args.type == 'qualifiers' then
formatterFun = formatStatementQualifiers
end
end
-- Format claims

for i = #claims, 1, -1 do
if typelink == 'wikidata' then
claims[i] = formatterFun(claims[i], args)
if entity.type == 'property' then
if not claims[i] then
return 'd:P:' .. entity.id
table.remove(claims, i)
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
end
end
return nil
return claims
end
end


function p.formatStatements(args)--Format statements and concat them cleanly
local function formattedLabel(label, entity, args)
-- If a value is already set, use it
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
if args.value == '-' then
return nil
return nil
end
end
if args.value then
--If a value is already set, use it
if args.value and args.value ~= '' then
return args.value
return args.value
end
end
-- Obsolete parameters
local valuetable = p.stringTable(args)
if args.item then
return tableToText(valuetable, args)
args.entity = args.item
end

function p.showQualifier( args )
local qualifs = args.qualifiers or args.qualifier
if type(qualifs) == 'string' then
qualifs = mw.text.split(qualifs, ',')
end
end
local values = p.stringTable(args) -- gets statements, and format each of them
if not qualifs then
if not values then
return formatError( 'property-param-not-provided' )
end
local claims = p.getClaims(args)
if not claims then
return nil
return nil
end
end
return linguistic.conj(values, args.lang or defaultlang, args.conjtype) -- concatenate them
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


-- == FRAME FUNCTIONS ==
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|}}}
function p.getDescription(frame) -- simple for simple templates like {{Q|}}}
local entity = frame.args.entity
local entity = frame.args.entity
local lang = frame.args.lang
local lang = frame.args.lang
return p._getDescription(entity, lang)

return p._getDescription(entity, lang) or i18n('invalid-id')
end

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


Line 982: Line 325:
args = frame
args = frame
end
end
args = removeBlanks(args)
return p._formatStatements( args )
return p.formatStatements(args)
end
end


function p.formatAndCat(frame)
function p._getLabel(entity, args)
return entities.formatEntity(entity, args)
local args = {}
end
if frame == mw.getCurrentFrame() then
function p.getLabel(frame) -- simple for simple templates like {{Q|}}}
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
local args = removeBlanks(frame.args)
local entity = args.entity or args[1]
args[k] = v
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
return entities.formatEntity(entity, args)
end

function p._wikidataDate(prop, item, params)
local claims = p.getClaims{entity = item, property = prop}
if not claims then
return nil
end
params = params or {}
local vals = {}
for i, j in pairs(claims) do
local v = dates.getFormattedDate(j, params)
if v then
table.insert(vals, v)
end
end
else
args = frame
end
end
return p._formatAndCat( args )
return linguistic.conj(vals, params.conjtype or 'or')
end

function p.wikidataDate(frame)
p._wikidataDate(frame.args[property], frame.args[params], frame.args)
end

function p._main_date(entity)
-- First try with P580/P582
local startpoint = p._wikidataDate('P580', entity)
local endpoint = p._wikidataDate('P582', entity)
if startpoint or endpoint then
return (startpoint or '') .. '&#x202F;–&#x2009;' .. (endpoint or '')
end
-- Else use P585
return p._wikidataDate('P585', entity)
end

function p.main_date(frame)
return p._main_date(frame.args[1])
end
end


-- returns the page id (Q...) of the current page or nothing of the page is not connected to Wikidata
function p.getEntityFromId(id)
function p.pageId(frame)
return p.getEntity(id)
local entity = mw.wikibase.getEntityObject()
return entity and entity.id
end
end



Latest revision as of 11:07, 3 May 2021

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}} Q12069631 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}} oil paint and poplar panel (painting support) 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}} 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}} 64-17-5[1][2][3]
{{#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}} unknown unknown value
{{#invoke:Wikidata|formatStatementsE|property=P19|item=Q43650835}} not applicable no value

Code

  1. ChEBI (title not provided in Wikidata), European Bioinformatics Institute, Creative Commons Attribution 3.0 Unported
  2. Global Substance Registration System (title not provided in Wikidata)
  3. CAS Common Chemistry
--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 selectClaims = require('Module:Wikidata/GetClaims')
local tools = require('Module:Wikidata/Tools')
local entities = require('Module:Wikidata/FormatEntity')
local dates = require('Module:Wikidata/Dates')
local weblink = require('Module:Weblink')

local langSwitch = require('Module:LangSwitch')._langSwitch
local fb = require('Module:Fallback')
local i18nmessages = mw.loadData('Module:i18n/wikidata')

-- Wiki-specific parameters
local defaultlang = mw.getCurrentFrame():callParserFunction('Int', 'Lang')
local defaultlink = 'wikidata'

local function i18n(str, lang)
	local message = i18nmessages[str]
	if type(message) == 'string' then
		return message
	end
	return langSwitch(message, lang or defaultlang)
end

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

p.getClaims = selectClaims.getClaims

local function removeBlanks(args)
	for i = #args, 1, -1 do
		if (args[i] == '') or (args[i] == '-') then
			table.remove(args, i)
		end
	end
	return args
end

local function getQualifiers(statement, qualifs, params)
	if not statement.qualifiers then
		return nil
	end
	if type(qualifs) == 'string' then
		qualifs = mw.text.split(qualifs, ',')
	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.formatSnak(snak, params)
	-- special values: 'novalue' and 'somevalue'
	local datatype = snak.datatype
	if snak.snaktype == 'somevalue' then
		return i18n('somevalue')
	elseif snak.snaktype == 'novalue' then
		return i18n('novalue')
	end
	local value = snak.datavalue.value
	-- user-defined displayformat
	local displayformat = params.displayformat
	if type(displayformat) == 'function' then
		return displayformat(snak, params)
 	end
	if datatype == 'wikibase-item' or datatype == 'wikibase-property' then
		return entities.formatEntity(tools.getId(snak), params)
	elseif datatype == 'url' then
		return weblink.makelink(value, params.text, params.displayformat)
	elseif datatype == 'math' then
		return mw.getCurrentFrame():extensionTag('math', value)
	elseif datatype == 'string' or datatype == 'external-id' or datatype == 'commonsMedia' then
		if params.urlpattern then
			local urlpattern = params.urlpattern
			if type(urlpattern) == 'function' then
				urlpattern = urlpattern(value)
			end
			value = '[' .. mw.ustring.gsub(urlpattern, '$1', value) .. ' ' .. (params.text or value) .. ']'
		end
		return value
	elseif datatype == 'time' then -- format example: +00000001809-02-12T00:00:00Z
		return dates.formatTimeSnak(snak, params) 
	elseif datatype == 'globe-coordinate' then
		-- default return a table with latitude, longitude, precision and globe that can be used by another module
		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] -- get English name for geohack
			return value
		end
	elseif datatype == 'quantity' then
		-- TODO: handle precision parameters
		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;' .. entities.formatEntity(unit, params)
			end
			return number
		end
	elseif datatype == 'monolingualtext' then
		if displayformat == 'raw' then
			-- Don't use HTML
			local byte, char = string.byte, mw.ustring.char
			local lang = value.language:lower()
			local tag = {}
			table.insert(tag, char(0x2068)) -- U+2068: First Strong Isolate (FSI)
			table.insert(tag, char(0xE0001)) -- U+E0001: Language tag
			for i = 1, #lang do
				local b = byte(lang, i)
				if b >= 0x61 and b <= 0x7A or b == 0x2D or b == 0x5F then -- 'a'..'z', '-', '_'
					table.insert(tag, char(0xE0000 + b)) -- U+E0020..U+E007E: Tag characters (remaps ASCII only)
				end
			end
			table.insert(tag, value.text)
			table.insert(tag, char(0xE007F)) -- U+E007F: Cancel Tag
			table.insert(tag, char(0x2069)) -- U+2069: Pop Directional Isolate (PDI)
			return table.concat(tag)
		else
			return '<bdi lang="' .. value.language .. '">' .. value.text .. '</bdi>'
		end
	else
		return formatError('unknown-datavalue-type', datatype )
	end
end

function p.formatStatementQualifiers(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)

	local qualifs = args.showqualifiers
	if qualifs then
		local qualifStr = p.formatStatementQualifiers(statement, qualifs, args)
		if qualifStr then
			if args.delimiter then
				str = str .. args.delimiter .. qualifStr
			else
				-- str = str .. linguistic.inparentheses(qualifStr, lang)
				str = str .. ' (' .. qualifStr .. ')'
			end
		end
	end

	if args.showdate then -- when `showdate` and `p.chronosort` are both set, date retrieval is performed twice
		local params
		local date = dates.getFormattedDate(statement, params)
		if date then
			-- str = str .. ' <small>' .. linguistic.inparentheses(date, lang) .. '</small>'
			str = str .. ' <small>(' .. date ..')</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 tools.isSpecial(source) then
						local page
						if ref.snaks.P304 and not tools.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 tools.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._getDescription(entity, lang)
	if not entity then
		return i18n('no description')
	end
	if type(entity) == 'string' and (not lang or lang == defaultlang) then
		return mw.wikibase.description(entity)
	end
	entity = mw.wikibase.getEntity(entity)
	local descriptions = entity.descriptions
	if not descriptions then
		return i18n('no description')
	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

-- == FUNCTIONS HANDLING SEVERAL STATEMENTS ===

function p.stringTable(args) -- find the relevant claims, and formats them as a list of strings
	-- Get claims
	local claims = p.getClaims(args)
	if not claims then
		return nil
	end
	-- Define the formatter function
	local formatterFun = p.formatStatement
	if args.type == 'date' then
		formatterFun = dates.getFormattedDate
	elseif args.type == 'qualifiers' then
		formatterFun = formatStatementQualifiers
	end
	-- Format claims
	for i = #claims, 1, -1 do
		claims[i] = formatterFun(claims[i], args)
		if not claims[i] then
			table.remove(claims, i)
		end
	end
	return claims
end

function p.formatStatements(args)--Format statements and concat them cleanly
	-- If a value is already set, use it
	if args.value == '-' then
		return nil
	end
	if args.value then
		return args.value
	end
	-- Obsolete parameters 
	if args.item then
		args.entity = args.item
	end
	local values = p.stringTable(args) -- gets statements, and format each of them
	if not values then
		return nil
	end
	return linguistic.conj(values, args.lang or defaultlang, args.conjtype) -- concatenate them
end

-- == FRAME FUNCTIONS ==

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)
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
	args = removeBlanks(args)
	return p.formatStatements(args)
end

function p._getLabel(entity, args) 
	return entities.formatEntity(entity, args)
end
function p.getLabel(frame) -- simple for simple templates like {{Q|}}}
	local args = removeBlanks(frame.args)
	local entity = args.entity or args[1]
	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
	return entities.formatEntity(entity, args)
end

function p._wikidataDate(prop, item, params)
	local claims = p.getClaims{entity = item, property = prop}
	if not claims then
		return nil
	end
	params = params or {}
	local vals = {}
	for i, j in pairs(claims) do
		local v = dates.getFormattedDate(j, params)
		if v then
			table.insert(vals, v)
		end
	end
	return linguistic.conj(vals, params.conjtype or 'or')
end

function p.wikidataDate(frame)
	 p._wikidataDate(frame.args[property], frame.args[params], frame.args)
end

function p._main_date(entity)	
	-- First try with P580/P582
	local startpoint = p._wikidataDate('P580', entity)
	local endpoint = p._wikidataDate('P582', entity)
	if startpoint or endpoint then
		return (startpoint or '') .. '&#x202F;–&#x2009;' .. (endpoint or '')
	end
	-- Else use P585
	return p._wikidataDate('P585', entity)
end

function p.main_date(frame)
	return p._main_date(frame.args[1])
end

-- returns the page id (Q...) of the current page or nothing of the page is not connected to Wikidata
function p.pageId(frame)
	local entity = mw.wikibase.getEntityObject()
	return entity and entity.id
end

return p