Module:Wikidata: Difference between revisions

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Content deleted Content added
No edit summary
fix rendering for snaktype 'somevalue' and 'novalue'
 
(60 intermediate revisions by 8 users not shown)
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
local formatDate = require( 'Module:Date')
local formatCoord = require( 'Module:Coordinates/Test')


local i18n = {
local p = {}
["errors"] = {
["property-param-not-provided"] = "Paramètre propriété non renseigné.",
["qualifier-param-not-provided"] = "Paramètre qualifier non renseigné.",
["entity-not-found"] = "Entité non trouvée.",
["unknown-claim-type"] = "type d'affirmation inconnu.",
["unknown-snak-type"] = "Type de snak inconnu.",
["unknown-datavalue-type"] = "Type de donnée non reconnu.",
["unknown-entity-type"] = "Type d'entité non reconnu.",
["unknown-value-module"] = "You must set both value-module and value-function parameters.",
["value-module-not-found"] = "The module pointed by value-module not found.",
["value-function-not-found"] = "The function pointed by value-function not found.",
["ambigous"] = "Ambigu : plusieurs valeurs possible",
},
["somevalue"] = "''unknown value''",
["novalue"] = "''no value''"
}


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
function getEntityFromId( id )
local fb = require('Module:Fallback')
return mw.wikibase.getEntity() --TODO support for getting other entities
local i18nmessages = mw.loadData('Module:i18n/wikidata')
end


-- Wiki-specific parameters
function getId( id ) -- semble nécessaire pour récupérer l'ID correspondant à la page Wikipédia active
local defaultlang = mw.getCurrentFrame():callParserFunction('Int', 'Lang')
entity = getEntityFromId( id )
local defaultlink = 'wikidata'
return entity.id
end


function formatError( key )
local function i18n(str, lang)
local message = i18nmessages[str]
return '<span class="error">' .. i18n.errors[key] .. '</span>'
if type(message) == 'string' then
return message
end
return langSwitch(message, lang or defaultlang)
end
end


function getClaims( options )
local function formatError(key, text)
return error(i18n(key) .. ' ' .. (text or ''), 2)
if not options.property then
end
return formatError( 'property-param-not-provided' )
end
--Get entity
local entity = nil
local property = string.lower(options.property)
if options.entity and type( options.entity ) == "table" then
entity = options.entity
else
entity = getEntityFromId( options.entityId )
end


p.getClaims = selectClaims.getClaims
if not entity or not entity.claims or not entity.claims[property] then
return nil
end
claims = entity.claims[property]


local function removeBlanks(args)
if options.excludespecial == 'true' then
for i = #args, 1, -1 do
oldclaims = claims
if (args[i] == '') or (args[i] == '-') then
claims = {}
table.remove(args, i)
for i, statement in pairs(oldclaims) do
if statement.mainsnak.snaktype == 'value' then
table.insert(claims, statement)
end
end
end
end
end
return args
if options.targetvalue then
end
targetvalue = options.targetvalue

oldclaims = claims
local function getQualifiers(statement, qualifs, params)
claims = {}
for i, statement in pairs(oldclaims) do
if not statement.qualifiers then
return nil
if formatDatavalue(statement.mainsnak) == targetvalue then
table.insert(claims, statement)
end
end
end
end
if type(qualifs) == 'string' then
if options.qualifier then -- ne marche que pour les propriétés de type item
qualifs = mw.text.split(qualifs, ',')
qualifier = options.qualifier
end
qualifiervalue = options.qualifiervalue
local vals = {}
oldclaims = claims
for i, j in pairs(qualifs) do
claims = {}
j = string.upper(j)
for i, statement in pairs(oldclaims) do
if statement.qualifiers and statement.qualifiers[qualifier] then
if statement.qualifiers[j] then
local inserted = false
if qualifiervalue then
for j, qualif in pairs(statement.qualifiers[qualifier]) do
if statement.qualifiers[j][1].datatype == 'monolingualtext' then
local in_preferred_lang
if formatDatavalue(qualif) == qualifiervalue then
for _, language in pairs(fb.fblist(params.lang or defaultlang)) do
table.insert(claims, statement)
for _, snak in pairs(statement.qualifiers[j]) do
if isInLanguage(snak, language) then
in_preferred_lang = snak
break
end
end
end
end
if in_preferred_lang then
else
break
table.insert(claims, statement)
end
end
if in_preferred_lang then
table.insert(vals, in_preferred_lang)
inserted = true
end
end
end
end
if not inserted then
end
for _, snak in pairs(statement.qualifiers[j]) do
end
table.insert(vals, snak)
if options.source then
if options.sourceproperty then
sourceproperty = options.sourceproperty
else
sourceproperty = "p248"
end
sourcevalue = options.source
oldclaims = claims
claims = {}
for i, statement in pairs(oldclaims) do
if statement.references then
for j, reference in pairs(statement.references) do
for k, prop in pairs(reference.snaks) do
if k == sourceproperty then
for l, m in pairs(prop) do
if formatDatavalue(m) == "Q" .. sourcevalue then
table.insert(claims, statement)
end
end
end
end
end
end
end
end
end
end
end
end
if #vals == 0 then
return claims
return nil
end
end
return vals
end


function formatDatavalue(snak, formatting)
function p.formatSnak(snak, params)
-- special values: 'novalue' and 'somevalue'
datatype = snak.datavalue.type
local datatype = snak.datatype
if datatype == 'wikibase-entityid' then
if snak.snaktype == 'somevalue' then
return i18n('somevalue')
if formatting then
elseif snak.snaktype == 'novalue' then
return formatEntityId("Q" .. tostring(snak.datavalue.value['numeric-id']))
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
else
value.globe = require('Module:Wikidata/Globes')[value.globe] -- get English name for geohack
return "Q" .. tostring(snak.datavalue.value['numeric-id'])
return value
end
end
elseif datatype == 'quantity' then
-- TODO: handle precision parameters
elseif datatype == 'string' then
if displayformat == 'raw' then
return snak.datavalue.value
return tonumber(value.amount)
else
elseif datatype == 'time' then
local formatNum = require('Module:Formatnum')
d = snak.datavalue.value.time
precision = snak.datavalue.value.precision
local number = formatNum.formatNum(value.amount)
local unit = mw.ustring.match(value.unit, '(Q%d+)')
era = ''
if unit then
if string.sub(d,1,1) == '-' then -- Before Christ for negative year or year 0
number = number .. '&nbsp;' .. entities.formatEntity(unit, params)
era = '-'
elseif string.sub(d,2,12) == '00000000000' then
era = '-'
end
year = ''
if precision >= 9 then
year = string.sub(d, 9, 12)
if era == '-' then -- remove one Year for BC years because of year 0 in the datamodel
year = tostring(tonumber(year) + 1)
end
end
return number
end
end
elseif datatype == 'monolingualtext' then
month = ''
if precision >= 10 then
if displayformat == 'raw' then
-- Don't use HTML
month = string.sub(d, 14, 15)
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
end
else
if precision >= 11 then
return formatError('unknown-datavalue-type', datatype )
day = string.sub(d, 17, 18)
end
return formatDate.modeleDate({day, month, era .. year})
elseif datatype == 'globecoordinate' then
latitude = tostring(snak.datavalue.value.latitude)
longitude = tostring(snak.datavalue.value.longitude)
-- precision = snak.datavalue.value.precision
--globe = to do transform string like http://www.wikidata.org/entity/Q2 into the globe parameter of Module:Coordinates
return formatCoord.coord2({latitude, longitude})
else return formatError( 'unknown-datavalue-type' )
end
end
end
end


function numOfClaims( claims )
function p.formatStatementQualifiers(statement, qualifs, params)
if not params then params = {} end
if type(claims) ~= "table" then
local qualiftable = getQualifiers(statement, qualifs, params)
return 0
elseif claims == {} then
if not qualiftable then
return 0
return nil
end
elseif claims[0] then -- table Wikibase non modifiée avec une clé 0 (buggy)
for i, j in pairs(qualiftable) do
return #claims + 1
local params = params
else
if j.datatype == 'globe-coordinate' then
return #claims
params.displayformat = 'qualifier'
end
qualiftable[i] = p.formatSnak(j, params)
end
end
return linguistic.conj(qualiftable, params.lang or defaultlang)
end
end


function p.formatStatement(statement, args)
function getClaimedIDs( options ) -- devrait plutôt être une option de function formatStatements
if not statement.type or statement.type ~= 'statement' then
claims = getClaims( options )
return formatError('unknown-claim-type', statement.type)
if type(claims) ~= 'table' then
return formatError( 'unidentified-error' )
end
end
if not args then args = {} end
local idlist = ''
local lang = args.lang or defaultlang
for i, claim in pairs(claims) do
local str = p.formatSnak(statement.mainsnak, args)
if options.rank == 'one' then

return "Q" .. claims[0].mainsnak.datavalue.value["numeric-id"]
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
else
-- str = str .. linguistic.inparentheses(qualifStr, lang)
idlist = idlist .. " Q" .. claim.mainsnak.datavalue.value["numeric-id"] -- liste simple permettant de retrouver facilement un ID donné
str = str .. ' (' .. qualifStr .. ')'
end
end
end
end
end
return idlist

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
end


function getQualifier( options )
function p._getDescription(entity, lang)
if not entity then
claims = getClaims( options )
return i18n('no description')
if options.qualifier then
qualifier = options.qualifier
else
return formatError( 'qualifier-param-not-provided' )
end
end
if not claims or numOfClaims(claims) == 0 then
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
return nil
end
elseif numOfClaims(claims) > 1 then
-- Define the formatter function
return formatError( 'ambiguous' )
local formatterFun = p.formatStatement
else
if args.type == 'date' then
qualifiers = {}
formatterFun = dates.getFormattedDate
for i, j in pairs( claims[1].qualifiers[qualifier] ) do
elseif args.type == 'qualifiers' then
table.insert(qualifiers, formatDatavalue(j, 'standard'))
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 mw.text.listToText(qualifiers)
end
end
return claims
end
end


function formatStatements( options )--Format statement and concat them cleanly
function p.formatStatements(args)--Format statements and concat them cleanly
-- If a value is already set, use it
local formattedStatements = {}
if args.value == '-' then
local rawStatements = getClaims( options )
return nil
if notrawStatements or numOfClaims(rawStatements) == 0 then
end
return nil
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
end
return linguistic.conj(values, args.lang or defaultlang, args.conjtype) -- concatenate them
for i, statement in pairs( rawStatements ) do
if options.rank == 'one' then
return formatStatement( statement, options ) --Output only one value
else
table.insert( formattedStatements, formatStatement( statement, options ) )
end
end
return mw.text.listToText( formattedStatements, options.separator, options.conjunction )
end
end


-- == FRAME FUNCTIONS ==
function formatStatement( statement, options )
if not statement.type or statement.type ~= 'statement' then
return formatError( 'unknown-claim-type' )
end


function p.getDescription(frame) -- simple for simple templates like {{Q|}}}
return formatSnak( statement.mainsnak, options )
local entity = frame.args.entity
--TODO reference and qualifiers
local lang = frame.args.lang
return p._getDescription(entity, lang)
end
end


function formatSnak( snak, options )
function p.formatStatementsE(frame)
local args = {}
if snak.snaktype == 'somevalue' then
if frame == mw.getCurrentFrame() then
return i18n['somevalue']
args = frame:getParent().args -- paramètres du modèle appelant (est-ce vraiment une bonne idée ?)
elseif snak.snaktype == 'novalue' then
for k, v in pairs(frame.args) do
return i18n['novalue']
args[k] = v
elseif snak.snaktype == 'value' then
end
return formatDatavalue( snak, true)
else
else
args = frame
return formatError( 'unknown-snak-type' )
end
end
args = removeBlanks(args)
return p.formatStatements(args)
end
end


function formatEntityId( entityId, options )
function p._getLabel(entity, args)
return entities.formatEntity(entity, args)
local label = mw.wikibase.label( entityId )
local link = mw.wikibase.sitelink( entityId )
if link then
if label then
return '[[' .. link .. '|' .. label .. ']]'
else
return '[[' .. link .. ']]'
end
else
return label --TODO what if no links and label + fallback language?
end
end
end
function p.getLabel(frame) -- simple for simple templates like {{Q|}}}

local args = removeBlanks(frame.args)
function formatFromPattern( str, options )
local entity = args.entity or args[1]
return mw.ustring.gsub( options.pattern, '$1', str ) .. '' --Hack to get only the first result of the function
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
end


function p._wikidataDate(prop, item, params)
local p = {}
local claims = p.getClaims{entity = item, property = prop}

if not claims then
function p.id(frame)
return nil
return getId(frame.args[1])
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
end


function p.formatStatements( frame )
function p.wikidataDate(frame)
local args = frame.args
p._wikidataDate(frame.args[property], frame.args[params], frame.args)

--If a value if already set, use it
if args.value and args.value ~= '' then
return args.value
end
return formatStatements( frame.args )
end
end


function p.formatStatementsFromLua( options )
function p._main_date(entity)
-- First try with P580/P582
--If a value if already set, use it
local startpoint = p._wikidataDate('P580', entity)
if options.value and options.value ~= '' then
local endpoint = p._wikidataDate('P582', entity)
return options.value
if startpoint or endpoint then
end
return (startpoint or '') .. '&#x202F;–&#x2009;' .. (endpoint or '')
return formatStatements( options )
end
-- Else use P585
return p._wikidataDate('P585', entity)
end
end


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

function p.getClaimedIDs(frame)
return getClaimedIDs(frame.args)
end
end


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

return p
return p

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