Và al contegnud

Mòdul:Wikidata

De Wikipedia

La documentazione per questo modulo può essere creata in Mòdul:Wikidata/man

--[[
* Modulo per implementare le funzionalità dei template:
* {{Wikidata}}, {{WikidataQ}}, {{WikidataIdx}}, {{WikidataN}},
* {{WikidataLabel}}, {{WikidataLink}}, {{WikidataTipo}} e {{WikidataId}}.
* Permette di accedere a Wikidata in modo più avanzato rispetto a {{#property}}.

* Il modulo è stato importato inizialmente da:
* http://test2.wikipedia.org/w/index.php?title=Module:Wikidata&oldid=52322
]]

require('Module:No globals')

local getArgs = require('Module:Arguments').getArgs
local mConvert = require('Module:Conversione')
local mLanguages = require('Module:Linguaggi')

-- Categoria per le pagine con errori
local errorCategory = '[[Categoria:Voci con errori del modulo Wikidata]]'

local p = {}

-- Messaggi di errore
local i18n = {
	["errors"] = {
		["entityid-param-not-provided"] = "Parametro ''entityid'' non fornito",
		["property-param-not-provided"] = "Parametro ''property'' non fornito",
		["qualifier-param-not-provided"] = "Parametro ''qualifier'' non fornito",
		["value-param-not-provided"] = "Parametro ''valore'' da ricercare non fornito",
		["entity-not-found"] = "Entità non trovata",
		["unknown-claim-type"] = "Tipo asserzione sconosciuta",
		["unknown-snak-type"] = "Tipo di snak sconosciuto",
		["unknown-datavalue-type"] = "Tipo di dato sconosciuto",
		["unknown-entity-type"] = "Tipo di entità sconosciuta"
	},
	["somevalue"] = "''valore sconosciuto''",
	["novalue"] = "''nessun valore''"
}

-------------------------------------------------------------------------------
--                             Formatters
-------------------------------------------------------------------------------

local function errhandler(msg)
	local cat = mw.title.getCurrentTitle().namespace == 0 and errorCategory or ''
	return string.format('<span class="error">%s</span>%s', msg, cat)
end

local function formatList(values, ordered)
	local fmt = ordered and '<ol><li>%s</li></ol>' or '<ul><li>%s</li></ul>'
	return #values > 0 and string.format(fmt, mw.text.listToText(values, '</li><li>', '</li><li>')) or ''
end

local function formatExtLink(url)
	local protocols = { ftp = true, http = true, https = true }

	local success, uri = pcall(function() return mw.uri.new(url) end)
	if success and uri.protocol and protocols[uri.protocol] then
		local dest = tostring(uri)
		return string.format('<div style="word-break: break-all;">[%s %s]</div>', dest, dest:gsub(uri.protocol .. '://', ''))	
	else
		return url
	end
end

local function formatEntityId(entityId)
	local label = mw.wikibase.label(entityId)
	local link = mw.wikibase.sitelink(entityId)
	if link then
		if label and label ~= link then
			return '[[' .. link .. '|' .. label .. ']]'
		else
			return '[[' .. link .. ']]'
		end
	else
		return label or ''
	end
end

local function formatMonolingualtext(value, args)
	local ret = ''
	if not args.includelang or args.includelang:match('%f[a-z]' .. value.language .. '%f[^a-z]') then
		if not args.excludelang or not args.excludelang:match('%f[a-z]' .. value.language .. '%f[^a-z]') then
			ret = value.text
			if args.showlang then
				ret = mLanguages.lingue( { value.language } ) .. '&nbsp;' .. ret
			end
		end
	end
	return ret
end

local function formatTimeWithPrecision(time, precision)
	local months = {
		'genar', 'febrar', 'marz', 'april', 'magg', 'giugn',
		'luj', 'agost', 'settember', 'ottober', 'november', 'dicember'
	}
	local ret, year, month, day
 
	year, month, day = time:match('(%d+)%-(%d%d)%-(%d%d).+')
	year, month, day = tonumber(year), tonumber(month), tonumber(day)
	if precision == 9 then
		ret = year
	elseif precision == 10 then
		ret = months[month] .. ' ' .. year
	elseif precision == 11 then
		ret = day .. ' ' .. months[month] .. ' ' .. year
		ret = ret:gsub('^1%s', '1º ')
	end
	if precision >= 9 and precision <= 11 then
		ret = ret .. (time:sub(1, 1) == '-' and ' a.C.' or '')
	end

	return ret
end

local function formatTime(value, args)
	local ret
 
	if args.time == 'precision' then
		ret = value.precision
	elseif args.time == 'calendarmodel' then
		ret = value.calendarmodel
	elseif args.time == 'year' and value.precision >= 9 then
		ret = formatTimeWithPrecision(value.time, 9)
	elseif args.time == 'month' and value.precision >= 10 then
		ret = formatTimeWithPrecision(value.time, 10)
	elseif args.time == 'day' and value.precision >= 11 then
		ret = formatTimeWithPrecision(value.time, 11)
	elseif not args.time then
		ret = formatTimeWithPrecision(value.time, value.precision)
	end

	return ret or ''
end

local function formatGlobecoordinate(value, args)
	local ret
	if args.coord == 'latitude' then
		ret = value.latitude
	elseif args.coord == 'longitude' then
		ret = value.longitude
	elseif args.coord == 'globe' then
		ret = value.globe
	else
		ret = value.latitude .. ', ' .. value.longitude
	end
	return ret
end

local function formatFromPattern(str, args)
	local pattern = args.pattern
	pattern = mw.ustring.gsub(pattern, '\\{', '{')
	pattern = mw.ustring.gsub(pattern, '\\}', '}')
	return mw.getCurrentFrame():preprocess(mw.message.newRawMessage(pattern, str):plain())
end

local function formatUserValue(value, args)
	if args.extlink then
		value = formatExtLink(value)
	end
	return args.pattern and formatFromPattern(value, args) or value
end

local function getEntityIdFromValue(value)
	local prefix = ''
	if value['entity-type'] == 'item' then
		prefix = 'Q'
	elseif value['entity-type'] == 'property' then
		prefix = 'P'
	else
		error(i18n.errors['unknown-entity-type'])
	end
	return prefix .. value['numeric-id']
end

local function formatUnitSymbol(entityId, args)
	local ret = p._getProperty( { 'P558', n = 1, from = entityId } )
	local space = ret == '°' and '' or ' '
	if ret and args.showunitlink then
		local link = mw.wikibase.sitelink(entityId)
		if link then
			ret = string.format('[[%s|%s]]', link, ret)
		end
	end
	return ret and (space .. ret) or ''
end

-- http://lua-users.org/wiki/SimpleRound
local function round(num, idp)
	local mult = 10 ^ (idp or 0)
	return math.floor(num * mult + 0.5) / mult
end


local function formatQuantity(value, args)
	local ret = tonumber(value.amount)

	if (args.unit or args.showunit) and value.unit ~= '1' then
		local unitId = mw.ustring.match(value.unit, 'Q%d+')
		if args.unit then
			local opts = {
				showunit = args.showunit,
				showunitlink = args.showunitlink,
				formatnum = args.formatnum,
				rounding = args.rounding				
			}
			ret = mConvert._main(ret, unitId, args.unit, opts)
		else
			-- se è richiesto solo il simbolo dell'unità
			-- senza la conversione lo ottiene da P558
			ret = args.rounding and round(ret, args.rounding) or ret
			if args.formatnum then
				ret = mw.language.getContentLanguage():formatNum(ret)
			end
			ret = ret .. formatUnitSymbol(unitId, args)
		end
	elseif args.formatnum then
		ret = args.rounding and round(ret, args.rounding) or ret
		ret = mw.language.getContentLanguage():formatNum(ret)
	elseif args.formatduration and value.unit ~= '1' then
		local unitId = mw.ustring.match(value.unit, 'Q%d+')
		ret = mConvert._main(ret, unitId, 'second')
		ret = ret and mw.language.getContentLanguage()
				:formatDuration(tonumber(ret), { 'days', 'hours', 'minutes', 'seconds' } )
	end

	return ret
end

local function formatDatavalue(datavalue, snakdatatype, args)
	local ret

	if datavalue.type == 'wikibase-entityid' then
		local entityId = getEntityIdFromValue(datavalue.value)
		if args.showprop then
			ret = p._getProperty( { args.showprop, n = 1, from = entityId } ) or ''
		else
			ret = args.formatting == 'raw' and entityId or formatEntityId(entityId)
		end
	elseif datavalue.type == 'string' then
		ret = datavalue.value
		if args.extlink and snakdatatype == 'url' then
			ret = formatExtLink(ret)
		elseif args.urlencode then
			ret = mw.uri.encode(ret)
		end
	elseif datavalue.type == 'monolingualtext' then
		ret = formatMonolingualtext(datavalue.value, args)
	elseif datavalue.type == 'time' then
		if args.formatting == 'raw' then
			ret = datavalue.value.time
		else
			ret = formatTime(datavalue.value, args)
		end
	elseif datavalue.type == 'globecoordinate' then
		ret = formatGlobecoordinate(datavalue.value, args)
	elseif datavalue.type == 'quantity' then
		ret = formatQuantity(datavalue.value, args)
	else
		error(i18n.errors['unknown-datavalue-type'])
	end

	return ret
end

local function formatSnak(snak, args)
	if snak.snaktype == 'somevalue' then
		return i18n['somevalue']
	elseif snak.snaktype == 'novalue' then
		return i18n['novalue']
	elseif snak.snaktype == 'value' then
		return formatDatavalue(snak.datavalue, snak.datatype, args)
	else
		error(i18n.errors['unknown-snak-type'])
	end
end

-- È al plurale perché anche i qualifier possono avere più di un valore
-- (si ottiene inserendo due volte lo stesso qualifier)
local function formatQualifiers(claim, qualifier, args, rawTable, retTable)
	local formattedQualifiers = retTable or {}

	if claim.qualifiers and claim.qualifiers[qualifier] then
		local qualifiers = claim.qualifiers[qualifier]
		-- con args.nq seleziona solo l'n-esimo qualifier
		if args.nq then
			local n = tonumber(args.nq)
			qualifiers = (n and n <= #qualifiers) and { qualifiers[n] } or {}
		end
		for _, q in pairs(qualifiers) do
			local formattedQualifier = formatSnak(q, args)
			if formattedQualifier ~= '' then
				if args.pattern then
					formattedQualifier = formatFromPattern(formattedQualifier, args)
				end
				table.insert(formattedQualifiers, formattedQualifier)
			end
		end
	end

	if rawTable then
		return formattedQualifiers
	end

	return #formattedQualifiers > 0 and
		   mw.text.listToText(formattedQualifiers, args.separator, args.conjunction) or nil
end

local function appendQualifiers(statement, text, args)
	local formattedQualifiers = {}
	local qualifiers = mw.text.split(args.showqualifiers, ',')
	for _, qualifier in ipairs(qualifiers) do
		if statement.qualifiers[qualifier] then
			local formattedQualifier = formatQualifiers(statement, qualifier, args)
			table.insert(formattedQualifiers, formattedQualifier)
		end
	end
	if #formattedQualifiers > 0 then
		text = string.format('%s (%s)', text, mw.text.listToText(formattedQualifiers, ', ', ', '))
	end
	return text
end

local function formatStatement(statement, args)
	if not statement.type or statement.type ~= 'statement' then
		error(i18n.errors['unknown-claim-type'])
	end
 
	local ret = formatSnak(statement.mainsnak, args)
	-- eventuale showqualifiers
	if args.showqualifiers and statement.qualifiers then
		ret = appendQualifiers(statement, ret, args)
	end

	return ret
end

local function formatStatements(claims, args, rawTable)
	local formattedStatements = {}

	for i, claim in pairs(claims) do
		local formattedStatement = formatStatement(claim, args)
		if formattedStatement ~= '' then
			-- eventuale pattern
			if args.pattern then
				formattedStatement = formatFromPattern(formattedStatement, args)
			end
			table.insert(formattedStatements, formattedStatement)
		end
	end
	if rawTable then
		return formattedStatements
	end

	return ((args.list or args.orderedlist) and #formattedStatements > 1) and
		   formatList(formattedStatements, args.orderedlist ~= nil) or 
		   mw.text.listToText(formattedStatements, args.separator, args.conjunction)
end

-------------------------------------------------------------------------------
--                      Lettura e selezione statement
-------------------------------------------------------------------------------

-- Ritorna true se lo statement contiene il qualifier richiesto con un dato valore
local function hasQualifierValue(statement, qualifierId, qualifierValue)
	local ret = false
	for i, qualifier in pairs(statement.qualifiers[qualifierId]) do
		local isItem = qualifier.snaktype == 'value' and
					   qualifier.datavalue.type == 'wikibase-entityid'
		-- per le proprietà di tipo item il confronto è eseguito sull'id
		if formatSnak(qualifier, isItem and { formatting = 'raw' } or {} ) == qualifierValue then
			ret = true
			break
		end
	end
	return ret
end

-- Ritorna i claim con il rank richiesto
local function filterRankValue(claims, rank)
	local ret = {}
	for i, claim in pairs(claims) do
		if claim.rank == rank then
			table.insert(ret, claim)
		end
	end
	return ret
end

-- Ritorna una table contenente gli statement per la property richiesta,
-- oppure nil se l'entity o la proprietà non esistono.
-- Gli statement ritornati sono eventualmente filtrati in base ai parametri:
-- "rank", "qualifier", "qualifiertype" e "n"
local function getClaims(property, args)
	local entity, claims, filteredClaims
	
	-- get entity
	entity = mw.wikibase.getEntity(args.from)
	if not entity then
		return nil
	end

	if property and entity.claims and entity.claims[property] and
	   #entity.claims[property] > 0 then
	   claims = entity.claims[property]
	else
		return nil
	end

	-- statements filtrati per rank (default 'best')
	args.rank = args.rank or 'best'
	if args.rank == 'best' then
		filteredClaims = filterRankValue(claims, 'preferred')
		if #filteredClaims == 0 then
			filteredClaims = filterRankValue(claims, 'normal')
		end
	else
		filteredClaims = filterRankValue(claims, args.rank)
	end
	claims = filteredClaims

	-- statements filtrati per qualifier
	if args.qualifier then
		filteredClaims = {}
		for i, claim in pairs(claims) do
			if claim.qualifiers and claim.qualifiers[args.qualifier] then
				if args.qualifiervalue then
					if hasQualifierValue(claim, args.qualifier, args.qualifiervalue) then
						table.insert(filteredClaims, claim)
					end
				else
					table.insert(filteredClaims, claim)
				end
			end
		end
		claims = filteredClaims
	end

	-- statements filtrati per essere senza un qualifier
	if args.noqualifier then
		filteredClaims = {}
		for i, claim in pairs(claims) do
			if not (claim.qualifiers and claim.qualifiers[args.noqualifier]) then
				table.insert(filteredClaims, claim)
			end
		end
		claims = filteredClaims
	end

	-- statements filtrati per non avere un certo valore a un certo qualifier opzionale
	if args.qualifieroptnovalue and args.qualifiervalue then
		filteredClaims = {}
		for i, claim in pairs(claims) do
			if claim.qualifiers and claim.qualifiers[args.qualifieroptnovalue] then
				if not hasQualifierValue(claim, args.qualifieroptnovalue, args.qualifiervalue) then
					table.insert(filteredClaims, claim)
				end
			else
				table.insert(filteredClaims, claim)
			end
		end
		claims = filteredClaims
	end

	-- con args.qualifiertype=latest ritorna solo il più recente
	if args.qualifier and args.qualifiertype == 'latest' then
		local latest, latestTime
		for i, claim in pairs(claims) do
			if claim.qualifiers and claim.qualifiers[args.qualifier] then
				for j, qualifier in pairs(claim.qualifiers[args.qualifier]) do
					if qualifier.datavalue.type == 'time' then
						if not latestTime or qualifier.datavalue.value.time > latestTime then
							latest = claim
							latestTime = qualifier.datavalue.value.time
						end
					end
				end
			end
		end
		claims = latest and {latest} or {}
	end

	-- con args.n ritorna solo l'n-esimo elemento
	if args.n then
		local n = tonumber(args.n)
		claims = (n and n <= #claims) and { claims[n] } or {}
	end

	return claims
end

-------------------------------------------------------------------------------
--                               API
-------------------------------------------------------------------------------

function p._getClaims(property, args)
	return getClaims(property, args or {})
end

function p._formatStatement(statement, args)
	return formatStatement(statement, args or {})
end

function p._formatQualifiers(claim, qualifier, args, rawTable, retTable)
	return formatQualifiers(claim, qualifier, args or {}, rawTable, retTable)
end

-- Ritorna il valore di una proprietà di Wikidata oppure nil se l'entity o
-- la proprietà non esistono, o se per parametri di selezione gli statement sono zero.
function p._getProperty(args, rawTable)
	local property, value, claims, ret

	-- parametri posizionali
	property = args[1] and string.upper(args[1]) or nil
	if not property then
		error(i18n.errors['property-param-not-provided'], 2)
	end
	value = args[2]
	-- fix uppercase
	args.qualifier = args.qualifier and string.upper(args.qualifier) or nil

	if value then
		ret = formatUserValue(value, args)
	elseif args.wd ~= 'no' then
		claims = getClaims(property, args)
		ret = (claims and #claims > 0) and formatStatements(claims, args, rawTable) or nil
	end

	return ret
end

-- Ritorna il valore di un qualifier di una proprietà di Wikidata,
-- o nil se l'entity o la proprietà non esistono, o se per parametri di selezione non ci sono risultati.
function p._getQualifier(args)
	local property, qualifier, value, claims, ret

	-- parametri posizionali
	property = args[1] and string.upper(args[1]) or nil
	if not property then
		error(i18n.errors['property-param-not-provided'], 2)
	end
	qualifier = args[2] and string.upper(args[2]) or nil
	if not qualifier then
		error(i18n.errors['qualifier-param-not-provided'], 2)
	end
	value = args[3]

	if value then
		ret = formatUserValue(value, args)
	elseif args.wd ~= 'no' then
		claims = getClaims(property, args)
		if claims and #claims > 0 then
			local formattedQualifiers = {}
			for _, claim in pairs(claims) do
				formattedQualifiers = formatQualifiers(claim, qualifier, args, true, formattedQualifiers)
			end
			ret = #formattedQualifiers > 0 and
				  mw.text.listToText(formattedQualifiers, args.separator, args.conjunction) or nil
		end
	end

	return ret
end

-- Ritorna l'indice dello statement con il valore richiesto, o nil se non trovato.
function p._indexOf(args)
	local ret, property, value, claims

	-- parametri posizionali
	property = args[1] and string.upper(args[1]) or nil
	if not property then
		error(i18n.errors['property-param-not-provided'], 2)
	end
	value = args[2]
	if not value then
		error(i18n.errors['value-param-not-provided'], 2)
	end

	claims = getClaims(property, args)
	if claims and #claims > 0 then
		args.formatting = 'raw'
		for i, claim in pairs(claims) do
			if formatStatement(claim, args) == value then
				ret = i
				break
			end
		end
	end

	return ret
end

-- Ritorna il numero di statement di una proprietà di Wikidata.
function p._N(args) 
	local property, claims

	-- parametri posizionali
	property = args[1] and string.upper(args[1]) or nil
	if not property then
		error(i18n.errors['property-param-not-provided'], 2)
	end
	-- get claims
	claims = getClaims(property, args)

	return claims and #claims or 0
end


--[[
* La proprietà specificata ha come valore almeno uno tra gli entityId specificati?
*
* @param string property Wikidata Property ID
* @param table args values (mixed with .from)
* @return boolean
]]
function p._propertyHasEntity(property, args)
	local statements = p._getProperty( {property, from = args.from, formatting = 'raw' }, true)
	if statements then
		for _, statement in ipairs(statements) do
			for _, entityId in ipairs(args) do
				if statement == entityId then
					return true
				end
			end
		end
	end

	return false
end

-- Ritorna true se la proprietà P31 (instance of) ha come valore almeno uno tra gli entityId specificati
function p._instanceOf(args)
	return p._propertyHasEntity('P31', args)
end

-- Ritorna true se la proprietà P279 (subclass of) ha come valore almeno uno tra gli entityId specificati
function p._subclassOf(args)
	return p._propertyHasEntity('P279', args)
end

-- Ritorna l'etichetta di un item o di una proprietà Wikidata.
function p._getLabel(args) 
	-- parametri posizionali
	local entityId = args[1] and string.upper(args[1]) or nil
	local langCode = args[2]

	local entity = mw.wikibase.getEntity(entityId)
	if not entity then
		error(i18n.errors['entity-not-found'], 2)
	end

	return entity:getLabel(langCode)
end

-- Ritorna il titolo della pagina collegata a un dato item Wikidata.
function p._getLink(args) 
	-- parametri posizionali
	local entityId = args[1] and string.upper(args[1]) or nil
	if not entityId then
		error(i18n.errors['entityid-param-not-provided'], 2)
	end
	
	return entityId:sub(1, 1) == 'Q' and formatEntityId(entityId) or nil
end

-- Ritorna il datatype di una proprietà Wikidata.
function p._getDatatype(args) 
	local ret, property, entity, datatype

	-- parametri posizionali
	property = args[1] and string.upper(args[1]) or nil
	if not property then
		error(i18n.errors['property-param-not-provided'], 2)
	end

	entity = mw.wikibase.getEntity(property)
	if not entity then
		error(i18n.errors['entity-not-found'], 2)
	end

	datatype = entity.datatype
	if datatype == 'commonsMedia' then
		ret = 'file multimediale su Commons'
	elseif datatype == 'globe-coordinate' then
		ret = 'coordinate geografiche'
	elseif datatype == 'monolingualtext' then
		ret = 'testo monolingua'
	elseif datatype == 'quantity' then
		ret = 'quantità'
	elseif datatype == 'string' then
		ret = 'stringa'
	elseif datatype == 'time' then
		ret = 'data e ora'
	elseif datatype == 'url' then
		ret = 'URL'
	elseif datatype == 'external-id' then
		ret = 'identificatore esterno'
	elseif datatype == 'wikibase-item' then
		ret = 'elemento'
	elseif datatype == 'wikibase-property' then
		ret = 'proprietà'
	elseif datatype == 'math' then
		ret = 'espressione matematica'
	else
		error(i18n.errors['unknown-datavalue-type'], 2)
	end

	return ret
end

-- Ritorna l'ID dell'item Wikidata collegato alla pagina corrente.
function p._getId()
	return mw.wikibase.getEntityIdForCurrentPage()
end

-- Entry-point per {{Wikidata}}
function p.getProperty(frame)
	return select(2, xpcall(function()
		return p._getProperty(getArgs(frame, {parentOnly = true}))
	end, errhandler))
end

-- Entry-point per {{WikidataQ}}
function p.getQualifier(frame)
	return select(2, xpcall(function()
		return p._getQualifier(getArgs(frame, {parentOnly = true}))
	end, errhandler))
end

-- Entry-point per {{WikidataIdx}}
function p.indexOf(frame)
	return select(2, xpcall(function()
		return p._indexOf(getArgs(frame, {parentOnly = true}))
	end, errhandler))
end

-- Entry-point per {{WikidataN}}
function p.N(frame)
	return select(2, xpcall(function()
		return p._N(getArgs(frame, {parentOnly = true}))
	end, errhandler))
end

-- Entry-point per {{WikidataLabel}}
function p.getLabel(frame)
	return select(2, xpcall(function()
		return p._getLabel(getArgs(frame, {parentOnly = true}))
	end, errhandler))
end

-- Entry-point per {{WikidataLink}}
function p.getLink(frame)
	return select(2, xpcall(function()
		return p._getLink(getArgs(frame, {parentOnly = true}))
	end, errhandler))
end

-- Entry-point per {{WikidataIstanza}}
function p.instanceOf(frame)
	return select(2, xpcall(function()
		return p._instanceOf(getArgs(frame, {parentOnly = true})) and 1 or ''
	end, errhandler))
end

-- Entry-point per {{WikidataTipo}}
function p.getDatatype(frame)
	return select(2, xpcall(function()
		return p._getDatatype(getArgs(frame, {parentOnly = true}))
	end, errhandler))
end

-- Entry-point per {{WikidataId}}
function p.getId(frame)
	return select(2, xpcall(function()
		return p._getId()
	end, errhandler))
end

return p
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy