Module:Wikidata

Gikan sa Wikipedia, ang gawasnong ensiklopedya
Jump to navigation Jump to search

Module permitting basid data retrieval from items. It is mosly useful for other modules or for {{Data}}

A copy is available on test Wikidata

Functions callable from Lua[usba ang wikitext]

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
  • withqualfier = 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
  • source = the source that should be provided in the statement (if sourceproperty is not provided, the property used is 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 Plantilya:P, Plantilya:P and Plantilya:P qualifiers. Set to "inverrted 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
  • format = 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 linke between each statement.
  • showqualifier = 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[usba ang 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[usba ang wikitext]

Code render comment
{{#invoke:Wikidata|getLabel|entity=Q42|link=wikipedia}} Douglas Adams
{{#invoke:Wikidata|getLabel|entity=Q42|link=wikipedia|lang=ja}} ダグラス・アダムズ
{{#invoke:Wikidata|getLabel|entity=Q42|link=wikidata}} Douglas Adams
{{#invoke:Wikidata|getLabel|entity=Q42|link=wikidata|lang=ja}} ダグラス・アダムズ
{{#invoke:Wikidata|formatStatementsE|item=Q42|property=p31}} tawo
{{#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 paintug poplar wood
{{#invoke:Wikidata|formatStatementsE|item=Q12418|property=p186|displayformat=raw}} Q296955ug Q291034
{{#invoke:Wikidata|formatStatementsE|item=Q12418|property=p186|lang=ja}} 油絵具 および ポプラ板
{{#invoke:Wikidata|formatStatementsE|item=Q12418|property=p186|rank=valid}} oil paint, poplar woodug Kahoy rank = "valid" accepts both "preferred" and "normal" values
{{#invoke:Wikidata|formatStatementsE|item=Q12418|property=p186|showqualifiers=p518}} oil paintug poplar wood (painting surface) 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;}} rue d'Ulm 45 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 wood 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
{{#invoke:Wikidata|formatStatementsE|item=Q11879536|property=P460|withsource=Q1645493}} Lisa del Giocondo
{{#invoke:Wikidata|formatStatementsE|item=Q11879536|property=P460|withsource=Q1645493|showsource=true}} Lua error in Module:Cite at line 303: bad argument #1 to 'pairs' (table expected, got string).
{{#invoke:Wikidata|formatStatementsE|item=Q153|property=P231|showsource=true}} Lua error in Module:Cite at line 303: bad argument #1 to 'pairs' (table expected, got string).
{{#invoke:Wikidata|formatStatementsE|item=Q205309|property=P793|sorttype=inverted}} closure, demolition, renovation, renovation, first matchug construction

--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 convertlangcode = mw.loadData('Module:Dictionary/lang codes')
local formatNum = require('Module:Formatnum')
local fb = require('Module:Fallback')
local i18nmessages = mw.loadData('Module:i18n/wikidata')

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

local function i18n(str, lang)
	local message = i18nmessages[str]
	if type(message) == 'str' 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)
	local a = 1
	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 samevalue(snak, target)
	if snak.snaktype == 'value' and p.getRawvalue(snak) == target then
		return true
	end
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 mainsnak.snaktype ~= 'value' 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
		for i, j in pairs( statement.qualifiers.P407 ) do
			if j.snaktype == 'value' then
				local val = convertlangcode[j.datavalue.value['numeric-id']]
				table.insert(langlist, val)
			end
		end
	end
	if #langlist == 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, params)
	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
		local formatDate = require('Module:Complex date')
		if not obj then
			return nil
		end
		local lang = params.lang or defaultlang
		local precision = obj.precision
		if params.precision and (params.precision < obj.precision) then -- if we don't want to show the value to its full detail
			precision = params.precision
		end
		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 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
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 type(obj.mainsnak.datatype) == 'time' and (obj.mainsnak.datavalue.value) 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 claims[prop][1].mainsnak.snaktype == 'value' then
			val = claims[prop][1].mainsnak.datavalue.value
		elseif qualifs and qualifs[prop] and qualifs[prop][1].snaktype == 'value' then
			val = qualifs[prop][1].datavalue.value
		end
		if val then
			return dateobject(val)
		end
	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

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')
	end
	if claim.rank == target then
		return true
	end
	return false
end

local function bestranked(claims)
	if not claims then
		return nil
	end
	local preferred, normal = {}, {}
	for i, j in pairs(claims) do
		if j.rank == 'preferred' then
			table.insert(preferred, j)
		elseif j.rank == 'normal' then
			table.insert(normal, j)
		end
	end
	if #preferred > 0 then
		return preferred
	else
		return normal
	end
end

local function hasqualifier(claim, qualifier, qualifiervalues)
	qualifier = string.upper(qualifier)

	if not qualifier then -- si aucun qualificatif est demandé, ça passe
		return true
	end
	if not claim.qualifiers or not claim.qualifiers[qualifier] then
		return false
	end

	if type(qualifiervalues) == 'string' then
   		qualifiervalues = {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
end

local function hasdate(statement)
	local dateprops = {'P580', 'P585', 'P582'}
	if not statement.qualifiers then
		return false
	end
	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 ?
	if snak.snaktype == 'value' and snak.datavalue.type == 'monolingualtext' and snak.datavalue.value.language == lang then
		return true
	end
	return false
end

local function isSpecial(snak)
	if snak.snaktype == 'value' then
		return false
	end
	return true
end

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

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
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 sorttype == 'chronological' then
		return p.chronosort(claims)
	elseif sorttype == 'inverted' then
		return p.chronosort(claims, true)
	elseif type(sorttype) == 'function' then
		table.sort(claims, sorttype)
		return claims
	end
	return claims
end

function p.getRawvalue(snak)
	return p.getDatavalue(snak, {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
	label = p._getLabel(entity, lang) 
	local id = entity.id
	if entity.type == 'property' then
		link = 'Property:' .. id
	else
		link = id
	end
	
	return '[[' .. link .. '|' .. label .. ']] <small>(' .. id ..')</small>'
end

function p.getDatavalue(snak, params)
	if not params then
		params = {}
	end
	local displayformat = params.displayformat
	if snak.snaktype ~= 'value' then
		return nil
	end
   
	local datatype = snak.datavalue.type
	local value = snak.datavalue.value
	
	if datatype == 'wikibase-entityid' then
		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)
		elseif type(displayformat) == 'function' then
			return displayformat(snak, params)
		else
			return p.formatEntity(id, params)
		end
   
	elseif datatype == 'string' then
		if params.displayformat == 'weblink' then
			return require('Module:Weblink').makelink(value, params.showntext)
		elseif params.urlpattern then
			value = '[' .. mw.ustring.gsub(params.urlpattern, '$1', value) .. ' ' .. (params.text or value) .. ']'
		end
		return value

	elseif datatype == 'time' then -- format example: +00000001809-02-12T00:00:00Z
		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 ?)
		value.globe = require('Module:Wikidata/Globes')[value.globe] -- transforme l'ID du globe en nom anglais utilisable par geohack
		if displayformat == 'latitude' then
			return value.latitude
		elseif displayformat == 'longitude' then
			return value.longitude
		else
			return value -- note : les coordonnées Wikidata peuvent être utilisée depuis Module:Coordinates. Faut-il aussi autoriser à appeler Module:Coordiantes ici ?
		end

	elseif datatype == 'quantity' then -- todo : gérer les paramètre précision
		if displayformat == 'raw' then
			return value.amount
		else
			return formatNum.formatNum(value.amount)
		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( args.entity )
	end
	if (not entity) or (not entity.claims) then
		return nil
	end
	local property = string.upper(args.property)
	if not entity.claims[property] then
		return nil
	end
	if not args.rank then
		args.rank = 'best'
	end
	local claims = {} 
-- ~= '' lorsque le paramètre est écrit mais laissé blanc dans une fonction frame
	for i, statement in pairs(entity.claims[property]) do
		if 
			(
			not args.excludespecial
			or 
			not (isSpecial(statement.mainsnak))
		)
		and
		(
			not args.targetvalue
			or 
			hastargetvalue(statement, args.targetvalue) 
		)
		and
		(
			not args.qualifier
			or
			hasqualifier(statement, args.qualifier, args.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
			for k, l in pairs(statement.qualifiers[j]) do
				table.insert(vals, l)
			end
		end
	end
	if #vals == 0 then
		return nil
	end
	return vals
end 

function p.getFormattedQualifiers(statement, qualifs, params)
	if not params then params = {} end
	local qualiftable = getQualifiers(statement, qualifs)
	if not qualiftable then
		return nil
	end
	for i, j in pairs(qualiftable) do
		qualiftable[i] = p.formatSnak(j, params)
	end
	return linguistic.conj(qualiftable, 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
	if args.showqualifiers then
		local qualifs = args.showqualifiers
		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 sourcestring = ''
		for i, ref in pairs(statement.references) do
			if ref.snaks.P248 then
				for j, source in pairs(ref.snaks.P248) do
					if source.snaktype == 'value' then
						local page
						if ref.snaks.P304 and ref.snaks.P304[1].snaktype == 'value' then
							page = ref.snaks.P304[1].datavalue.value
						end
						local s = require('Module:Cite').citeitem('Q' .. source.datavalue.value['numeric-id'], lang, page)
						s = mw.getCurrentFrame():extensionTag( 'ref', s )
						sourcestring = sourcestring .. s
					end
				end
			elseif ref.snaks.P854 and ref.snaks.P854[1].snaktype == 'value' then
				s = mw.getCurrentFrame():extensionTag( 'ref', formatLink(ref.snaks.P854[1].datavalue.value))
				sourcestring = sourcestring .. s
			end
		end 
		str = str .. sourcestring
	end
	return str
end

function p.formatSnak( snak, params )
	if not args then args = {} end -- pour faciliter l'appel depuis d'autres modules
	if snak.snaktype == '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 displayformat == 'id' then
		return entity.id
	end
	return i18n('no-label', lang)
end

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

function p._getDescription(entity, lang)
	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) -- list of fallback languages in no label in the desired language
	for i, j in pairs (langlist) do
		if descriptions[j] then
			return descriptions[j].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
end
local function getLink(entity, typelink, lang) 
	if not typelink then
		return nil
	end
	if not lang then
		lang = defaultlang
	end
	if typelink == '-' then
		return nil

	elseif 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 = getwikipedialink(entity, lg)
			if link then return link end
		end
	end
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 claim.mainsnak.snaktype == 'value' then
		return 'Q' .. claim.mainsnak.datavalue.value['numeric-id']
	end
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 = lang
	if args.lang and args.lang ~= '' then
		lang = args.lang
	end
	if string.sub(entity, 1, 10) == 'Property:P' then 
		entity = string.sub(entity, 10)
	elseif (string.sub(entity, 1, 1) ~= 'P' and string.sub(entity, 1, 1) ~= 'Q') or (not tonumber(string.sub(entity, 2))) then
		return i18n('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
		lang = lang
		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
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
	if frame.args.lang then
		lang = frame.args.lang
	end
	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


local function computeLinkToItem(entityId, capitalize, callFunction)
	local sitelink = mw.wikibase.sitelink(StringUtils._prependIfMissing({tostring(entityId), 'Q'}))
	local label = nil
	local labelLang = nil
	local object = mw.wikibase.getEntityObject(StringUtils._prependIfMissing({tostring(entityId), 'Q'}))
	if callFunction and type(callFunction) == 'function' then
		label, labelLang = callFunction(object)
	end
	if label == nil or (labelLang and labelLang ~= lang.code) then label = mw.wikibase.label(StringUtils._prependIfMissing({tostring(entityId), 'Q'})) end
	if label == nil and object ~= nil then
		label = object:getLabel('en')
		labelLang = 'en'
	end
	if label == nil then label = StringUtils._prependIfMissing({tostring(entityId), 'Q'}) end

	if capitalize then
		label = lang:ucfirst(label)
	end
	if sitelink then
		return "[[:" .. sitelink .. "|" .. label .. "]]"
	else
		return "[[:d:" .. StringUtils._prependIfMissing({tostring(entityId), 'Q'}) .. "|" .. label .. "]]<abbr title='Articolul încă nu există în acest wiki'>[*]</abbr>"
	end
end

p.findLinkToItemWithLabel = function(entityId, label)
	return computeLinkToItem(entityId, false, function() return label end)
end

p.findLinkToItemWithCallback = function(entityId, capitalize, callBack)
	return computeLinkToItem(entityId, capitalize, callBack)
end

p.findLinkToItemWithLabelFromAnyClaim = function(entityId, capitalize, propertyIdsTable)
	local callFunction = nil
	if propertyIdsTable then
		callFunction = function(object)
			local returnValue = nil
			for propIdIdx,propertyId in pairs(propertyIdsTable) do
				if object and object.claims and object.claims[propertyId] and returnValue == nil then
					for eachClaimIdx, eachClaim in pairs(object.claims[propertyId]) do
						if eachClaim.type == 'statement' and eachClaim.mainsnak.snaktype == 'value' and eachClaim.mainsnak.datavalue then
							if eachClaim.mainsnak.datavalue.type == 'monolingualtext' then
								if eachClaim.mainsnak.datavalue.value.language == 'ro' then returnValue = eachClaim.mainsnak.datavalue.value.text end
							elseif eachClaim.mainsnak.datavalue.type == 'wikibase-entityid' then
								returnValue = mw.wikibase.label(StringUtils._prependIfMissing({tostring(eachClaim.mainsnak.datavalue.value['numeric-id']), 'Q'}))
							elseif eachClaim.mainsnak.datavalue.type == 'string' then
								returnValue = eachClaim.mainsnak.datavalue.value
							end
						end
					end
				end
			end
			return returnValue
		end
	end
	return computeLinkToItem(entityId, capitalize, callFunction)
end

p.findLinkToItemWithLabelFromClaim = function(entityId, capitalize, propertyId)
	return p.findLinkToItemWithLabelFromAnyClaim(entityId, capitalize, { propertyId })
end

p.findLinkToItem = function(entityId, capitalize, feminine, shortestAlias)
	if capitalize == nil then capitalize = false end
	local callFunction = nil
	if shortestAlias then
		callFunction = function(object)
			local returnedAlias = nil
			local returnedLang = nil
			if object and object.claims then
				local bestShortNameClaims = object.claims['P1813']
				if bestShortNameClaims then
					local shortNameEn = nil
					for shortNameIdx,shortNameClaim in pairs(bestShortNameClaims) do
						if shortNameClaim.mainsnak.datavalue.value.language == 'en' then shortNameEn = shortNameClaim.mainsnak.datavalue.value.text end
						if shortNameClaim.mainsnak.datavalue.value.language == 'ro' then returnedAlias = shortNameClaim.mainsnak.datavalue.value.text end
					end
					if returnedAlias then
						returnedLang = 'ro'
					else
						returnedAlias = shortNameEn
						returnedLang = 'en'
					end
				end
			end
			return returnedAlias, returnedLang
		end
	end
	if feminine then
		callFunction = function(object)
			if object then
				local feminineForms = object:getBestStatements('P2521')
				if feminineForms then
					for _idx, eachFForm in pairs(feminineForms) do
						if eachFForm.type == 'statement' and eachFForm.mainsnak.snaktype == 'value' and eachFForm.mainsnak.datatype == 'monolingualtext' and eachFForm.mainsnak.datavalue.type == 'monolingualtext' and eachFForm.mainsnak.datavalue.value and eachFForm.mainsnak.datavalue.value.language == 'ro' then
							return eachFForm.mainsnak.datavalue.value.text
						end
					end
				end
			end
		end
	end
	return computeLinkToItem(entityId, capitalize, callFunction)
end

p.getLinkToItem = function(frame)
	local args = getArgs(frame, {frameOnly=true})
	local entityId = args[1]
	return p.findLinkToItem(entityId, false, false, false)
end

local function snakToString(snak, feminine)
	if (snak.snaktype == "value") then
		if (snak.datavalue.type == "wikibase-entityid") then
			return p.findLinkToItem(snak.datavalue.value["numeric-id"], nil, feminine)
		end
		if (snak.datavalue.type == "time") then
			return formatDate(snak.datavalue.value.time, snak.datavalue.value.precision, snak.datavalue.value.timezone)
		end
		if (snak.datavalue.type == "monolingualtext") then
			return snak.datavalue.value.text
		end
		if (snak.datavalue.type == "quantity") then
			local unit = '1'
			local amount = snak.datavalue.value.amount
			local unitQ = mw.ustring.match(snak.datavalue.value.unit, 'Q%d+')
			if unitQ ~= nil then
				unit = p.findOneValueNoRef('P558', mw.wikibase.getEntityObject(unitQ))
				if unit == nil or mw.ustring.len(unit) == 0 then
					unit = p.findLabel(unitQ, nil)
				end
                                if unit == nil then
                                        unit = "" 
                                end
	
			end
			if tonumber(amount) ~= nil then
				amount = mw.language.new(lang.code):formatNum(tonumber(amount))
			end
			
			return amount .. (unit ~= '1' and (mw.text.decode('&nbsp;') .. unit) or '')
		end
		return snak.datavalue.value
	end
	return ""
end

-- Return only one value (with references) from whose with preferred ranks for a certain property
-- To call from modules
function p.findOneValue(propertyID, entity)
	if entity == nil then
		entity = mw.wikibase.getEntityObject()
	end
	if type(entity) == 'string' then entity = mw.wikibase.getEntityObject(entity)
	elseif type(entity) == 'number' then entity = mw.wikibase.getEntityObject('Q' .. tostring(entity)) end
	if entity == nil then
		return nil
	end
	local claims = entity:getBestStatements(propertyID)
	if claims and claims[1] then
		return snakToString(claims[1].mainsnak) .. p.outputReferences(claims[1])
	end
	return nil
end	

-- Return only one value (without references) from whose with preferred ranks for a certain property
-- To call from modules
function p.findOneValueNoRef(propertyID, entity)
	if entity == nil then
		entity = mw.wikibase.getEntityObject()
	end
	if type(entity) == 'string' then entity = mw.wikibase.getEntityObject(entity)
	elseif type(entity) == 'number' then entity = mw.wikibase.getEntityObject('Q' .. tostring(entity)) end
	if entity == nil then
		return nil
	end
	local claims = entity:getBestStatements(propertyID)
	if claims and claims[1] then
		return snakToString(claims[1].mainsnak)
	end
	return nil
end	

-- Return only one value (with references) from whose with preferred ranks for a certain property
-- To call from templates
p.getOneValue = function(frame) 
	local propertyID = mw.text.trim(frame.args[1] or "")
	local entityID = mw.text.trim(frame.args[2] or "")
	local entity = nil
	if entityID ~= "" then entity = mw.wikibase.getEntityObject(entityID) end
	if entity == nil then entity = mw.wikibase.getEntityObject() end
	
	if entity == nil then return nil end
	return p.findOneValue(propertyID, entity)
end

-- Return only one value (without references) from whose with preferred ranks for a certain property
-- To call from templates
p.getOneValueNoRef = function(frame)
	local propertyID = mw.text.trim(frame.args[1] or "")
	local entityID = mw.text.trim(frame.args[2] or "")
	local entity = nil
	if entityID ~= "" then entity = mw.wikibase.getEntityObject(entityID) end
	if entity == nil then entity = mw.wikibase.getEntityObject() end

	if entity == nil then return nil end
	return p.findOneValueNoRef(propertyID, entity)
end

return p