Modulo:Wikidata
Aspekto
Dokumentado por ĉi tiu modulo povas esti kreata ĉe Modulo:Wikidata/dokumentado
local i18n = {
errors = {
["entity-not-found"] = "Ento ne trovita",
["invalid-date"] = "Nevalida dato %s",
["invalid-field"] = "Nevalida kampo %s",
["param-not-provided"] = "Neprovizita parametro %s",
["unknown-claim-type"] = "Nekonata speco de aserto: %s",
["unknown-datavalue-type"] = "Nekonata speco de datumvaloro: %s",
["unknown-entity-type"] = "Nekonata speco de ento: %s",
["unknown-snak-type"] = "Nekonata speco de snako: %s",
["unknown-value-module"] = "Vi devas agordi kaj la parametron value-module kaj value-function",
["value-function-not-found"] = "Funkcio montrata de la parametro value-function ne trovita",
["value-module-not-found"] = "Modulo montrata de la parametro value-module ne trovita"
},
lang = mw.language.getContentLanguage(),
novalue = "''sen valoro''",
somevalue = "''valoro ne konata''"
}
function wrapWithSpan(str, param, value)
-- TODO mw.html ?
return '<span ' .. param .. '="' .. value .. '">' .. str .. '</span>'
end
function formatError( key, ... )
return wrapWithSpan(string.format( i18n.errors[key], ... ), 'class', 'error')
end
function getEntityFromId( id )
return mw.wikibase.getEntityObject( id )
end
function getEntityIdFromValue( value )
local entityType = value['entity-type']
if entityType == 'item' then
return 'Q' .. value['numeric-id']
elseif entityType == 'property' then
return 'P' .. value['numeric-id']
else
return formatError( 'unknown-entity-type', entityType )
end
end
function findEntity(options)
local entity
if options.entity and type(options.entity) == "table" then
entity = options.entity
end
if not entity then
if options.id and options.id ~= '' then
entity = getEntityFromId(options.id)
else
entity = getEntityFromId()
end
end
if options.of and options.of ~= '' then
local property = options.of:upper()
if entity and entity.claims and entity.claims[property] then
local Filtered = filterStatements(entity.claims[property], { limit = 1 })
for _, statement in pairs(Filtered) do
if statement.mainsnak.datatype == 'wikibase-item' or statement.mainsnak.datatype == 'wikibase-property' then
local id = getEntityIdFromValue( statement.mainsnak.datavalue.value )
entity = string.find(id, '^[PQ]%d+$') and getEntityFromId( id ) or nil
return entity
end
end
end
return nil
else
return entity
end
end
function getSitelink(options)
local site = nil
if options.site and options.site ~= '' then
site = options.site
elseif options[1] and options[1] ~= '' then
site = mw.text.trim(options[1])
end
local entity = findEntity(options)
if not entity or not entity.sitelinks then
return ''
end
local sitelink = entity:getSitelink(site)
if not sitelink then
return ''
elseif options.pattern and options.pattern ~= '' then
return formatFromPattern(sitelink, options)
else
return sitelink
end
end
function formatStatements( options )
if not options.property or options.property == '' then
return formatError( 'param-not-provided', 'property' )
end
--Get entity
local entity = findEntity(options)
if not entity then
return '' --TODO error?
else
options.entity = entity
end
if not entity.claims or not entity.claims[options.property:upper()] then
return '' --TODO error?
end
options.limit = returnLimit(options.limit, 100, 1)
local Statements = filterStatements(entity.claims[options.property:upper()], options)
-- Format statements and concat them cleanly
local formattedStatements = {}
local see_more = '… více na [[d:' .. entity.id .. '#' .. options.property:upper() .. '|Wikidatech]]'
local add_more = false
if #Statements == options.limit then
Statements[#Statements] = nil
if options.showmore and options.showmore == 'true' then
add_more = true
end
end
for _, statement in pairs(Statements) do
table.insert( formattedStatements, formatStatement( statement, options ) )
end
if add_more then table.insert( formattedStatements, see_more ) end
return mw.text.listToText( formattedStatements, options.separator, options.conjunction )
end
function filterStatements(claims, options)
local Statements, oldStatements = claims, {}
-- apply filter by rank
if options.rank ~= "all" then
oldStatements, Statements = Statements, {}
local hasPref = false
if not options.rank or options.rank == "normal" then
for _, statement in pairs(oldStatements) do
if statement.rank == "preferred" then
hasPref = true
break
end
end
end
for _, statement in pairs(oldStatements) do
if options.rank == "deprecated" or options.rank == "preferred" then
if statement.rank == options.rank then
table.insert(Statements, statement)
end
elseif hasPref then
if statement.rank == "preferred" then
table.insert(Statements, statement)
end
elseif statement.rank == "preferred" or statement.rank == "normal" then
table.insert(Statements, statement)
end
end
end
-- apply filter by snak type
if not options.showspecial or options.showspecial ~= "true" then
oldStatements, Statements = Statements, {}
for _, statement in pairs(oldStatements) do
if statement.mainsnak.snaktype == "value" then
table.insert(Statements, statement)
end
end
end
-- apply filter by qualifier property
if options.withqualifier and options.withqualifier ~= '' then
oldStatements, Statements = Statements, {}
for _, statement in pairs(oldStatements) do
if hasQualifierValue(statement.qualifiers, options.withqualifier:upper()) then
table.insert(Statements, statement)
end
end
end
if options.withlang and options.withlang ~= '' then
local datatype = getEntityFromId(options.property).datatype
if datatype == 'monolingualtext' then
oldStatements, Statements = Statements, {}
for _, statement in pairs(oldStatements) do
if statement.mainsnak.snaktype == "value" then
if statement.mainsnak.datavalue.value.language == options.withlang then
table.insert(Statements, statement)
end
end
end
end
end
-- apply filter by time
if options.date and options.date ~= '' then
local lang = i18n.lang
local date
if options.date == '#now' then
date = tonumber(lang:formatDate( 'Ymd' ))
else
date = timeToNumber(options.date)
end
if date then
oldStatements, Statements = Statements, {}
local temp_value
local Time = require 'Module:Time'
for _, statement in pairs(oldStatements) do
if statement.qualifiers then
local P580 = hasQualifierValue(statement.qualifiers, "P580")
local P582 = hasQualifierValue(statement.qualifiers, "P582")
local P585 = hasQualifierValue(statement.qualifiers, "P585")
if P585 then
local value = Time.newFromWikidataValue( statement.qualifiers.datavalue.value )
value = timeToNumber(value)
if value and value <= date then
if not temp_value then temp_value = value end
if temp_value > value then
temp_value = value
Statements = { statement }
end
end
elseif P580 then
local value = Time.newFromWikidataValue( statement.qualifiers.datavalue.value )
value = timeToNumber(value)
if value and value <= date then
if not P582 then
table.insert(Statements, statement)
else
local value = Time.newFromWikidataValue( statement.qualifiers.datavalue.value )
value = timeToNumber(value)
if value and date <= value then
table.insert(Statements, statement)
end
end
end
elseif P582 then
local value = Time.newFromWikidataValue( statement.qualifiers.datavalue.value )
value = timeToNumber(value)
if value and date <= value then
table.insert(Statements, statement)
end
end
end
end
else
return {} --formatError( 'invalid-date', options.date )
end
end
-- apply filter by limit
if #Statements > options.limit then
oldStatements, Statements = Statements, {}
for i, statement in pairs(oldStatements) do
if i <= options.limit then
table.insert( Statements, statement )
else break end
end
end
return Statements
end
function timeToNumber(date)
if not (
mw.ustring.find(date, "^%d%d?%d?%d?$")
or mw.ustring.find(date, "^%d%d?%d?%d?-%d%d$")
or mw.ustring.find(date, "^%d%d?%d?%d?-%d%d-%d%d$")
) then return nil end
if mw.ustring.sub(date, -3, -3) == '-' then
if mw.ustring.sub(date, -6, -6) == '-' then
date = mw.ustring.gsub(date, '-', '')
else
date = mw.ustring.gsub(date, '-', '') .. '00'
end
else
date = date .. '0000'
end
return tonumber(date)
end
function formatStatement( statement, options )
if not statement.type or statement.type ~= 'statement' then
return formatError( 'unknown-claim-type', statement.type )
end
local mainsnak = formatSnak( statement.mainsnak, options )
local qualifiers, references
if statement.qualifiers and options.showqualifier and options.showqualifier ~= '' then
local QualList = {}
for Pid in mw.text.gsplit( options.showqualifier, ",", true ) do
local ValuesList = {}
Pid = Pid:upper()
local options = {
autoformat = "true",
entity = options.entity,
property = Pid
}
if statement.qualifiers[Pid] then
for _, qualData in pairs(statement.qualifiers[Pid]) do
if qualData.snaktype == "value" then
if qualData.datavalue.type == "time" then
options["value-module"] = "Wikidata/datum"
options["value-function"] = "formatDate"
end
table.insert( ValuesList, formatDatavalue(qualData.datavalue, options) )
end
end
elseif Pid == "TIME" then
options["value-module"] = "Wikidata/datum"
options["value-function"] = "formatDate"
local P580 = hasQualifierValue(statement.qualifiers, "P580")
local P582 = hasQualifierValue(statement.qualifiers, "P582")
local P585 = hasQualifierValue(statement.qualifiers, "P585")
if P580 or P582 or P585 then
if P585 then
table.insert( ValuesList, formatDatavalue(statement.qualifiers.P585[P585].datavalue, options) )
elseif P580 then
if P582 then
local connector = ' – '
if statement.qualifiers.P580[P580].datavalue.value.precision == 9
and statement.qualifiers.P582[P582].datavalue.value.precision == 9 then
connector = '–'
end
table.insert( ValuesList, formatDatavalue(statement.qualifiers.P580[P580].datavalue, options) .. connector .. formatDatavalue(statement.qualifiers.P582[P582].datavalue, options) )
else
table.insert( ValuesList, 'od ' .. formatDatavalue(statement.qualifiers.P580[P580].datavalue, options) )
end
elseif P582 then
table.insert( ValuesList, 'do ' .. formatDatavalue(statement.qualifiers.P582[P582].datavalue, options) )
end
end
end
if #ValuesList > 0 then
table.insert( QualList, mw.text.listToText(ValuesList) )
end
end
if #QualList > 0 then
qualifiers = table.concat( QualList, '; ' )
end
end
if statement.references and options.showsource and options.showsource == 'true' then
local useModule = require 'Module:Wikidata/cite'
if useModule then
local useFunction = "formatSource"
if useModule[useFunction] then
references = useModule[useFunction](statement.references, options)
end
end
end
if qualifiers then
mainsnak = mainsnak .. ' (' .. qualifiers .. ')'
end
if references then
mainsnak = mainsnak .. references
end
return mainsnak
end
function formatSnak( snak, options )
if snak.snaktype == 'value' then
return formatDatavalue( snak.datavalue, options )
elseif snak.snaktype == 'somevalue' or snak.snaktype == 'novalue' then
return i18n[snak.snaktype]
else
return formatError( 'unknown-snak-type', snak.snaktype )
end
end
function formatDatavalue( datavalue, options )
--Use the customize handler if provided
if options['value-module'] or options['value-function'] then
if not options['value-module'] or not options['value-function'] then
return formatError( 'unknown-value-module' )
end
local formatter = require ('Module:' .. options['value-module'])
if not formatter then
return formatError( 'value-module-not-found' )
end
local fun = formatter[options['value-function']]
if not fun then
return formatError( 'value-function-not-found' )
end
return fun( datavalue.value, options )
end
--Default formatters
if datavalue.type == 'wikibase-entityid' then
return formatEntityId( getEntityIdFromValue( datavalue.value ), options )
elseif datavalue.type == 'string' then
if options.pattern and options.pattern ~= '' then
return formatFromPattern( datavalue.value, options )
elseif options.autoformat and options.autoformat == 'true' then
local pattern = findPattern(options.property)
if pattern ~= '' then
return formatFromPattern( datavalue.value, { pattern = '[' .. pattern .. ' $1]' } )
end
else
return datavalue.value
end
elseif datavalue.type == 'time' then
local Time = require 'Module:Time'
return Time.newFromWikidataValue( datavalue.value ):toString()
elseif datavalue.type == 'globecoordinate' then
if not options.field or options.field == '' then
-- return formatError( 'param-not-provided', 'field' )
return formatCoordinateValue(datavalue, 'dms')
elseif options.field == "latitude" or options.field == "longitude" then
return formatCoordinateValue(datavalue, options.typeOfCoordinates, options.field)
elseif options.field == "precision" or options.field == "globe" then
return datavalue.value[options.field]
else
return formatError( 'invalid-field', options.field )
end
elseif datavalue.type == 'monolingualtext' then
return wrapWithSpan(datavalue.value.text, 'lang', datavalue.value.language)
elseif datavalue.type == 'quantity' then
return tonumber( datavalue.value.amount )
else
return formatError( 'unknown-datavalue-type', datavalue.type )
end
end
function hasQualifierValue( Qualifiers, Pid )
if Qualifiers and Qualifiers[Pid] then
for number, qualData in pairs(Qualifiers[Pid]) do
if qualData.snaktype == "value" then return number end
end
end
return false
end
function formatCoordinateValue(datavalue, typeOfValue, field)
-- type bude nepovinny - tj. "nil"
-- priklad pouze -- je tam asi X chyb (mimo jine N vs S a podobne)
local value = ""
local latdmsText, londmsText
if typeOfValue == 'dms' then
local latDms = fastConvertDdToDms(datavalue.value.latitude)
local lonDms = fastConvertDdToDms(datavalue.value.longitude)
latdmsText = "N " .. latDms["degrees"] .. "° " .. latDms["minutes"] .. "' " .. latDms["seconds"] .. '"'
londmsText = "E " .. lonDms["degrees"] .. "° " .. lonDms["minutes"] .. "' " .. lonDms["seconds"] .. '"'
if field then
if field == 'latitude' then
value = latdmsText
elseif field == 'longitude' then
value = londmsText
end
else
value = latdmsText .. " " .. londmsText
end
elseif typeOfValue == 'dd' then
latdmsText = tonumber(datavalue.value.latitude)
londmsText = tonumber(datavalue.value.longitude)
if field then
if field == 'latitude' then
value = latdmsText
elseif field == 'longitude' then
value = londmsText
end
else
value = latdmsText .. " " .. londmsText
end
else
value = datavalue.value.latitude .. ' / ' .. datavalue.value.longitude .. ' (přesnost: ' .. datavalue.value.precision .. ')'
end
return value
end
function fastConvertDdToDms(ddValue)
local dmsArr = {
degrees = 0,
minutes = 0,
seconds = 0.0
}
if ddValue then
dmsArr["degrees"] = math.floor(tonumber(ddValue))
dmsArr["minutes"] = math.floor((tonumber(ddValue) - dmsArr["degrees"]) * 60)
dmsArr["seconds"] = (tonumber(ddValue) - dmsArr["degrees"] - dmsArr["minutes"]/60) * 3600
end
return dmsArr
end
function formatEntityId( entityId, options )
local formatter = require 'Modul:Wikidata/item'
return formatter.formatEntityId( entityId, options )
end
function findPattern(property)
return property and formatStatements({
entity = getEntityFromId(property:upper()),
property = 'P1630',
limit = 1
}) or ''
end
function returnLimit(limit, default, add)
if tonumber(limit) then
return tonumber(add) and (tonumber(limit) + tonumber(add)) or tonumber(limit)
end
return tonumber(default) or 100
end
function formatFromPattern( str, options )
return mw.ustring.gsub( options.pattern, '$1', str ) .. '' --Hack to get only the first result of the function
end
local p = {}
function p.dumpWikidataEntity( frame )
local args = frame and frame.args or nil
return mw.dumpObject( getEntityFromId( args and args.id or nil ) )
end
function p.getBadges( frame )
local args = frame and frame.args or {}
local site = args and args.site or nil
local entity = findEntity( args )
local Badges = {}
if entity and entity.sitelinks and site and entity.sitelinks[site] then
for _, badge in pairs( entity.sitelinks[site].badges ) do
table.insert( Badges, formatEntityId( badge ) )
end
end
return table.concat( Badges, ', ' ) or ''
end
function p.getLabel( frame )
local args = frame and frame.args or {}
local lang = args and args.lang or nil
local entity = findEntity( args )
if not lang or lang == '' then
lang = i18n.lang.code
end
if not entity or not entity.labels or not entity.labels[lang] then
return ''
end
return entity.labels[lang].value
end
function p.getDescription( frame )
local args = frame and frame.args or {}
local lang = args and args.lang or nil
local entity = findEntity( args )
if not lang or lang == '' then
lang = i18n.lang.code
end
if not entity or not entity.descriptions or not entity.descriptions[lang] then
return ''
end
return entity.descriptions[lang].value
end
function p.getAliases( frame )
local args = frame and frame.args or {}
local lang = args and args.lang or nil
local entity = findEntity( args )
if not lang or lang == '' then
lang = i18n.lang.code
end
if not entity or not entity.aliases or not entity.aliases[lang] then
return ''
end
local limit = returnLimit(args.limit)
local Aliases = {}
for i, alias in pairs( entity.aliases[lang] ) do
if i <= limit then
table.insert( Aliases, alias.value )
else break end
end
return mw.text.listToText( Aliases, args.separator, args.conjunction ) or ''
end
function p.getCount( frame )
local args = frame and frame.args or {}
if not args.property or args.property == '' then
return formatError( 'param-not-provided', 'property' )
end
local entity = findEntity( args )
if not entity or not entity.claims or not entity.claims[args.property:upper()] then
return 0
end
args.limit = returnLimit(args.limit, 100)
local Statements = filterStatements(entity.claims[args.property:upper()], args)
return #Statements or 0
end
function p.getSitelink( frame )
return getSitelink( frame.args )
end
function p.getSitelinkFromLua( options )
return getSitelink( options )
end
function p.filterStatementsFromLua( claims, options )
options.limit = returnLimit(options.limit, 100)
return filterStatements( claims, options )
end
function p.formatStatements( frame )
local args = frame and frame.args or {}
--If a value is already set, use it
if args.value and args.value ~= '' then
return args.value
end
return formatStatements( frame.args )
end
function p.formatStatementsFromLua( options )
--If a value is already set, use it
if options.value and options.value ~= '' then
return options.value
end
return formatStatements( options )
end
return p