Modul:Wikidata
Erscheinungsbild
Vorlagenprogrammierung | Diskussionen | Lua | Test | Unterseiten | ||
Modul | Deutsch
|
Modul: | Dokumentation |
Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus
local Wd = {
-- project configuration
wiki = {
langcode = mw.language.getContentLanguage().code
},
-- internationalisation
i18n = {
["errors"] = {
["property-not-found"] = "Eigenschaft nicht gefunden.",
["entity-not-found"] = "Wikidata-Eintrag nicht gefunden.",
["entity-not-valid"] = "Die an die Wikidata-Schnittstelle übergebene Item-ID ist nicht gültig.",
["unknown-claim-type"] = "Unbekannter Aussagentyp.",
["unknown-entity-type"] = "Unbekannter Entity-Typ.",
["qualifier-not-found"] = "Qualifikator nicht gefunden.",
["site-not-found"] = "Wikimedia-Projekt nicht gefunden.",
["invalid-parameters"] = "Ungültige Parameter.",
["module-not-loaded"] = "Loading of additional module failed."
},
["maintenance-pages"] = {
["entity-not-found"] = "Wikidata/Wartung/Fehlendes Datenobjekt",
["entity-not-valid"] = "Wikidata/Wartung/Ungültige Datenobjekt-Identifikationsnummer",
["property-not-existing"] = "Wikidata/Wartung/Eigenschaft existiert nicht"
},
["datetime"] = {
-- $s is a placeholder for the actual number
[0] = "%s Mrd. Jahren", -- precision: billion years
[1] = "%s00 Mio. Jahren", -- precision: hundred million years
[2] = "%s0 Mio. Jahren", -- precision: ten million years
[3] = "%s Mio. Jahren", -- precision: million years
[4] = "%s00.000 Jahren", -- precision: hundred thousand years
[5] = "%s0.000 Jahren", -- precision: ten thousand years
[6] = "%s. Jahrtausend", -- precision: millenium
[7] = "%s. Jahrhundert", -- precision: century
[8] = "%ser", -- precision: decade
-- the following use the format of #time parser function
[9] = "Y", -- precision: year,
[10] = "F Y", -- precision: month
[11] = "j. F Y", -- precision: day
[12] = 'j. F Y, G "Uhr"', -- precision: hour
[13] = "j. F Y G:i", -- precision: minute
[14] = "j. F Y G:i:s", -- precision: second
["beforenow"] = "vor %s", -- how to format negative numbers for precisions 0 to 5
["afternow"] = "in %s", -- how to format positive numbers for precisions 0 to 5
["bc"] = '%s "v.Chr."', -- how print negative years
["ad"] = "%s" -- how print positive years
},
["monolingualtext"] = '<span lang="%s">%s</span>',
["errortext"] = '<span class="error">%s</span>',
["FETCH_WIKIDATA"] = "ABFRAGE_WIKIDATA"
},
-- global value to count calls of expensive function isParent, including recursive calls
numberIsParentCalls = 0,
-- important properties
propertyId = {
["starttime"] = "P580",
["endtime"] = "P582",
["pointoftime"] = "P585"
},
formatchar = {
[10] = {"n","m","M","F","xg"}, --precision: month
[11] = {"W","j","d","z","D","l","N","w"}, --precision: day
[12] = {"a","A","g","h","G","H"}, --precision: hour
[13] = {"i"}, --precision: minute
[14] = {"s","U"} --precision: second
}
}
--[=[
==============================================================================
GENERAL HELPERS
==============================================================================
]=]
--[=[
print an error with the given error code
parameters:
- code (string): error code
- info (opt, string): additional information
returns: (string) error html
]=]
Wd.printError = function( code, info )
local text
text = Wd.i18n.errors[ code ] or code or ""
if info then
text = text .. ": " .. info
end
return mw.ustring.format( Wd.i18n.errortext, text )
end
--[=[
create a new error object
parameters:
- code (string): error code
- info (opt, string): additional information
returns: (table) error table
]=]
Wd.newError = function( code, info )
return {
code = code,
info = tostring( info )
}
end
--[=[
parse the given error object
parameters:
- error (table): error object created with Wd.newError
returns:
- (string): error code
- (string|nil): additional information if available
]=]
Wd.parseError = function( error )
return error.code, error.info
end
--[=[
the "qualifiers" and "snaks" field have a respective "qualifiers-order" and "snaks-order" field
use these as the second parameter and this function instead of the built-in "pairs" function
to iterate over all qualifiers and snaks in the intended order.
parameters:
- array (table): table to get items from
- order (opt, table): order table
returns: (function) iterator function
]=]
Wd.orderedPairs = function( array, order )
if not order then
return pairs( array )
end
-- return iterator function
local i = 0
return function()
i = i + 1
if order[ i ] then
return order[ i ], array[ order[ i ] ]
end
end
end
--[=[
return the size of the given table
parameters:
- tbl (table): table to get size of
returns: (number) number of pairs in table
]=]
Wd.getTableSize = function( tbl )
if type( tbl ) ~= "table" then
return 0
end
local count = 0
for _ in pairs( tbl ) do
count = count + 1
end
return count
end
--[=[
create a silent maintenance link
parameters:
- key (string): key to maintenance link title
returns: (boolean) link title exists or not (to record usage)
]=]
Wd.createMaintenanceLink = function( key )
if type( key ) == "string" and Wd.i18n[ "maintenance-pages" ][ key ] then
return mw.title.new( Wd.i18n[ "maintenance-pages" ][ key ], "Modul" ).exists
end
end
--[=[
get an argument of the invocation frame with options
parameters:
- frame (table): invocation frame
- key (string|number) argument key
- allowEmpty (opt, boolean): if true, empty strings count as valid arguments
- default (opt, any): default value to return if argument is invalid
returns: (boolean) link title exists or not (to record usage)
]=]
Wd.getFrameArgument = function( frame, key, allowEmpty, default )
-- default allowEmpty to false
if not allowEmpty then
allowEmpty = false
end
-- get frame argument
local arg = frame.args[ key ]
if arg and ( allowEmpty == true or ( allowEmpty == false and arg ~= "") ) then
return arg
else
return default
end
end
--[=[
==============================================================================
WIKIBASE CLIENT LUA API HELPERS
==============================================================================
]=]
--[=[
check wheter the given identifier is a valid entity id, typesafe
parameters:
- id (any): entity id
returns: (bool) entity id valid or invalid
]=]
Wd.isValidEntityIdTypesafe = function( id )
return ( type( id ) == "string" and mw.wikibase.isValidEntityId( id ) )
end
--[=[
check wheter the entity with the given id exists, typesafe
parameters:
- id (any): entity id
returns: (bool) entity id exists or not
]=]
Wd.entityExistsTypesafe = function( id )
return ( Wd.isValidEntityIdTypesafe( id ) and mw.wikibase.entityExists( id ) )
end
--[=[
return the entity id of the current page if given id is invalid
parameters:
- id (string|nil): entity id
returns: (string|nil) entity id of current page or nil if page is not connected
]=]
Wd.getConnectedEntityIdIfInvalid = function( id )
if Wd.isValidEntityIdTypesafe( id ) then
return id
else
return mw.wikibase.getEntityIdForCurrentPage()
end
end
--[=[
return the entity id of the current page if given id does not exist
parameters:
- id (string|nil): entity id
returns: (string|nil) entity id of current page or nil if page is not connected
]=]
Wd.getConnectedEntityIdIfInexisting = function( id )
if Wd.entityExistsTypesafe( id ) then
return id
else
return mw.wikibase.getEntityIdForCurrentPage()
end
end
--[=[
extract the first entity id from the given string
parameters:
- text (string): input string
- etype (opt, string): entity type, "Q" for items and "P" for properties
returns: (string|nil) entity id or nil if no qid found
]=]
Wd.extractEntityId = function( text, etype )
if not etype then
etype = "Q"
end
return mw.ustring.match( text, etype .. "%d+" )
end
--[=[
==============================================================================
DATETIME HELPERS
==============================================================================
]=]
--[=[
transform date to normalized form
parameters:
- date (string): date
returns: (string) normalized date
(int) year
]=]
Wd.normalizeDate = function( date )
date = mw.text.trim( date, "+" )
-- extract year
local yearstr = mw.ustring.match( date, "^-?%d+" )
local year = tonumber( yearstr )
-- remove leading zeros of year
return year .. mw.ustring.sub( date, #yearstr + 1 ), year
end
--[=[
create date string from date
parameters:
- date (string): date string
- precision (number): date precision
- timezone (string): date timezone
- formatstr (string): formatter string for MediaWiki's formatDate function
returns: (string) date string
]=]
Wd.formatDate = function( date, precision, timezone, formatstr )
-- precisions: 0 - billion years, 1 - hundred million years, ..., 6 - millenia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second
-- standard precision is days
precision = precision or 11
-- get normalized date and year
date, year = Wd.normalizeDate( date )
-- set to first day
date = string.gsub( date, "-00%f[%D]", "-01" )
-- min year precision required, but date has no year given
if precision <= 9 and year == 0 then
return ""
end
-- precision is 10000 years or more
if precision <= 5 then
local factor = 10 ^ ( ( 5 - precision ) + 4 )
local y2 = math.ceil( math.abs( year ) / factor )
local relative = mw.ustring.format( Wd.i18n.datetime[ precision ], tostring( y2 ) )
if year < 0 then
relative = mw.ustring.format( Wd.i18n.datetime.beforenow, relative )
else
relative = mw.ustring.format( Wd.i18n.datetime.afternow, relative )
end
return relative
end
-- precision is decades, centuries and millenia
local era
if precision == 6 then
era = mw.ustring.format( Wd.i18n.datetime[ 6 ], tostring( math.floor( ( math.abs( year ) - 1 ) / 1000 ) + 1 ) )
end
if precision == 7 then
era = mw.ustring.format( Wd.i18n.datetime[ 7 ], tostring( math.floor( ( math.abs( year ) - 1 ) / 100 ) + 1 ) )
end
if precision == 8 then
era = mw.ustring.format( Wd.i18n.datetime[ 8 ], tostring( math.floor( math.abs( year ) / 10 ) * 10 ) )
end
if era then
if year < 0 then
era = mw.ustring.format( mw.ustring.gsub( Wd.i18n.datetime.bc, '"', "" ), era )
elseif year > 0 then
era = mw.ustring.format( mw.ustring.gsub( Wd.i18n.datetime.ad, '"', "" ), era )
end
return era
end
-- precision is years or less
if precision >= 9 then
if formatstr then
for i = ( precision + 1 ), 14 do
for _, ch in pairs( Wd.formatchar[ i ] ) do
if formatstr:find( ch ) then
formatstr = Wd.i18n.datetime[ precision ]
end
end
end
else
formatstr = Wd.i18n.datetime[ precision ]
end
if year == 0 then
formatstr = mw.ustring.gsub( formatstr, Wd.i18n.datetime[ 9 ], "" )
elseif year < 0 then
-- MediaWiki's formatDate doesn't support negative years
date = mw.ustring.sub( date, 2 )
formatstr = mw.ustring.gsub( formatstr, Wd.i18n.datetime[ 9 ], mw.ustring.format( Wd.i18n.datetime.bc, Wd.i18n.datetime[ 9 ] ) )
elseif year > 0 and Wd.i18n.datetime.ad ~= "%s" then
formatstr = mw.ustring.gsub( formatstr, Wd.i18n.datetime[ 9 ], mw.ustring.format( Wd.i18n.datetime.ad, Wd.i18n.datetime[ 9 ] ) )
end
return mw.language.new( Wd.wiki.langcode ):formatDate( formatstr, date )
end
-- return empty string by default
return ""
end
--[=[
split time datavalue to separated date object
parameters:
- data (table): time datavalue object
returns: (table) date object
]=]
Wd.datavalueTimeToDateObject = function( data )
-- parse timestamp
local sign, year, month, day, hour, minute, second = string.match( data.time, "(.)(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)Z" )
-- fill date object
local result = {
year = tonumber(year),
month = tonumber(month),
day = tonumber(day),
hour = tonumber(hour),
min = tonumber(minute),
sec = tonumber(second),
timezone = data.timezone,
julian = data.calendarmodel and string.match( data.calendarmodel, "Q11184$" )
}
-- handle ac/bc
if sign == "-" then
result.year = -result.year
end
return result
end
--[=[
handle julian calendar
parameters:
- dateObject (table): date object, e.g. retrieved from Wd.datavalueTimeToDateObject()
returns: (number) datetime representation
]=]
Wd.handleJulianDay = function( dateObject )
local year = dateObject.year
local month = dateObject.month or 0
local day = dateObject.day or 0
if month == 0 then
month = 1
end
if day == 0 then
day = 1
end
if month <= 2 then
year = year - 1
month = month + 12
end
local time = ( ( ( ( dateObject.sec or 0) / 60 + ( dateObject.min or 0 ) + ( dateObject.timezone or 0 ) ) / 60 ) + ( dateObject.hour or 0 ) ) / 24
local b
if dateObject.julian then
b = 0
else
local century = math.floor( year / 100 )
b = 2 - century + math.floor( century / 4 )
end
return math.floor( 365.25 * ( year + 4716 ) ) + math.floor( 30.6001 * ( month + 1 ) ) + day + time + b - 1524.5
end
--[=[
==============================================================================
SNAK RENDERERS
==============================================================================
]=]
--[=[
print datavalue type string
parameters:
- data (table): value data
- parameter (string): data parameter to return
returns: (string) parameter value
]=]
Wd.printDatavalueString = function( data, parameter )
return data
end
--[=[
print datavalue type coordinate
parameters:
- data (table): value data
latitude [double], longitude [double], altitude [double], precision [double], globe [wikidata URI, usually http://www.wikidata.org/entity/Q2 [earth]]
- parameter (string): data parameter to return
returns: (string) parameter value
]=]
Wd.printDatavalueCoordinate = function( data, parameter )
if parameter then
if parameter == "globe" then
-- extract entity id from the globe URI
data.globe = Wd.extractEntityId( data.globe )
end
return data[ parameter ]
else
-- combine latitude and longitude, which can be decomposed using the #titleparts wiki function
return data.latitude .. "/" .. data.longitude
end
end
--[=[
print datavalue type quantity
parameters:
- data (table): value data
amount [number], unit [string], upperBound [number], lowerBound [number]
- parameter (string): data parameter to return
returns: (string) parameter value
]=]
Wd.printDatavalueQuantity = function( data, parameter )
if not parameter or parameter == "amount" then
return tonumber( data.amount )
elseif parameter == "unit" then
return Wd.extractEntityId( data.unit )
else
return data[ parameter ]
end
end
--[=[
print datavalue type time
parameters:
- data (table): value data
time [ISO 8601 time], timezone [int in minutes], before [int], after [int], precision [int], calendarmodel [wikidata URI]
precision: 0 - billion years, 1 - hundred million years, ..., 6 - millenia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second
calendarmodel: e.g. http://www.wikidata.org/entity/Q1985727 for the proleptic Gregorian calendar or http://www.wikidata.org/wiki/Q11184 for the Julian calendar
- parameter (string): data parameter to return
returns: (string) parameter value
]=]
Wd.printDatavalueTime = function( data, parameter )
if parameter then
-- check if format is specified
local para, formatstr = parameter:match( "([^:]+):([^:]+)" )
if parameter == "calendarmodel" then
-- extract entity id from the calendar model URI
data.calendarmodel = Wd.extractEntityId( data.calendarmodel )
elseif para and para == "time" then
-- output formatted time string
return Wd.formatDate( data.time, data.precision, data.timezone, formatstr )
elseif parameter == "time" then
-- output standard date format
data.time = Wd.normalizeDate( data.time )
end
return data[ parameter ]
else
-- return full date
return Wd.formatDate( data.time, data.precision, data.timezone )
end
end
--[=[
print datavalue type entity
parameters:
- data (table): value data
entity-type [string], numeric-id [int, Wikidata id]
- parameter (string): data parameter to return
returns: (string) parameter value
]=]
Wd.printDatavalueEntity = function( data, parameter )
local id
-- get entity type
if data[ "entity-type" ] == "item" then
id = "Q" .. data[ "numeric-id" ]
elseif data[ "entity-type" ] == "property" then
id = "P" .. data[ "numeric-id" ]
else
return Wd.printError( "unknown-entity-type" )
end
if parameter then
if parameter == "link" then
-- create link
local linkTarget = mw.wikibase.getSitelink( id )
local linkName = mw.wikibase.getLabel( id )
if linkTarget then
-- if there is a local Wikipedia page linking to it, use the label or the page title
local link = linkTarget
if linkName and ( linkName ~= linkTarget ) then
link = link .. "|" .. linkName
end
return "[[" .. link .. "]]"
else
-- if there is no local Wikipedia article output the label or link to the Wikidata object to input a proper label
if linkName then
return linkName
else
return "[[:d:" .. id .. "|" .. id .. "]]"
end
end
else
return data[ parameter ]
end
else
return mw.wikibase.getLabel( id ) or id
end
end
--[=[
print datavalue type monolingual text
parameters:
- data (table): value data
language [string], text [string]
- parameter (string): data parameter to return
returns: (string) parameter value
]=]
Wd.printDatavalueMonolingualText = function( data, parameter )
if parameter then
return data[ parameter ]
else
return mw.ustring.format( Wd.i18n.monolingualtext, data[ "language" ], data[ "text" ] )
end
end
--[=[
==============================================================================
CLAIMS
==============================================================================
]=]
--[=[
get the value of a snak. snaks have three types: "novalue" for null/nil,
"somevalue" for not null/not nil, or "value" for actual data
parameters:
- snak (table): snak data
- parameter (string): data parameter to return
returns: (string) rendered snak value
]=]
Wd.getSnakValue = function( snak, parameter )
if snak.snaktype == "value" then
-- define snak renderers
local snakRendererList = {
["string"] = Wd.printDatavalueString,
["globecoordinate"] = Wd.printDatavalueCoordinate,
["quantity"] = Wd.printDatavalueQuantity,
["time"] = Wd.printDatavalueTime,
["wikibase-entityid"] = Wd.printDatavalueEntity,
["monolingualtext"] = Wd.printDatavalueMonolingualText
}
-- get the respective snak renderer and call it
local snakRenderer = snakRendererList[ snak.datavalue.type ]
if snakRenderer then
return snakRenderer( snak.datavalue.value, parameter )
end
end
-- any other snak type or datavalue type
return mw.wikibase.renderSnak( snak )
end
--[=[
get the snak of the given qualifier of the claim or its mainsnak.
a "snak" is Wikidata terminology for a typed key/value pair
a claim consists of a main snak holding the main information of this claim,
as well as a list of attribute snaks and a list of references snaks
parameters:
- claim (table): claim data
- qualifierId (opt, string): qualifier to get the snak from
returns: (table|nil) snak data or nil if error
(string) error message if needed
]=]
Wd.getQualifierSnak = function( claim, qualifierId )
-- return nil if claim is invalid
if not claim then
return nil
end
if qualifierId then
-- search the attribute snak with the given qualifier as key
if claim.qualifiers then
local qualifier = claim.qualifiers[ qualifierId ]
if qualifier then
return qualifier[ 1 ]
end
end
return nil, Wd.printError( "qualifier-not-found" )
else
-- otherwise return the main snak
return claim.mainsnak
end
end
--[=[
get the sort value of the given qualifier of the claim or its mainsnak.
parameters:
- claim (table): claim data
- qualifierId (opt, string): qualifier to get the snak from
returns: (string|nil) sort value or nil if error
]=]
Wd.getQualifierSortValue = function( claim, qualifierId )
local snak = Wd.getQualifierSnak( claim, qualifierId )
if snak and snak.snaktype == "value" then
if snak.datavalue.type == "time" then
-- use datetime representation vor time datavalue
return Wd.handleJulianDay( Wd.datavalueTimeToDateObject( snak.datavalue.value ) )
else
-- use snak value for other datavalue types
return Wd.getSnakValue( snak )
end
end
end
--[=[
get the value of the mainsnak of the claim or the value of the given qualifier
parameters:
- claim (table): claim data
- qualifierId (opt, string): qualifier to get the data from
returns: (string|nil) value or nil if error
(string) error message if needed
]=]
Wd.getClaimValue = function( claim, qualifierId, parameter )
local error
local snak
snak, error = Wd.getQualifierSnak( claim, qualifierId )
if snak then
return Wd.getSnakValue( snak, parameter )
end
return nil, error
end
--[=[
checks if a claim has the given qualifier, and optionally, with the given value
parameters:
- claim (table): claim data
- property (string): qualifier property
- value (opt, string): value of qualifier
returns: (boolean) check result
]=]
Wd.claimHasQualifier = function( claim, property, value )
local invert = false
-- allow inversion of the qualifier check
if string.sub( property, 1, 1 ) == "!" then
invert = true
property = string.sub( property, 2 )
end
-- check if claim has any qualifiers.
-- if property is empty string or literal true, check for existence of any
-- qualifiers only, without inversion - invert is always false in this case
-- so will return false in the first condition
if not claim.qualifiers then
return invert
elseif property == "" or property == "true" then
return true
end
-- check if qualifier with property exists
if not claim.qualifiers[ property ] then
return invert
end
-- if check is only for qualifier existence return true or false inverted
if not value then
return ( not invert )
end
-- check for existence of the given value
local found = false
for key, snak in pairs( claim.qualifiers[ property ] ) do
if snak.snaktype == "value" then
if snak.datavalue.type == "wikibase-entityid" then
-- wikibase id
if snak.datavalue.value.id == value then
found = true
break
end
--TODO: elseif other types
end
end
end
-- return true or false inverted
return ( found ~= invert )
end
--[=[
checks if a claim has the given source
parameters:
- claim (table): claim data
- property (string): source property
returns: (boolean) check result
]=]
Wd.claimHasSource = function( claim, property )
-- check if claim has any references.
-- if property is empty string or literal true, check for existence of any
-- references only
if not claim.references then
return false
elseif property == "" or property == "true" then
return true
end
-- allow inversion of the reference property check
local invert = false
if string.sub( property, 1, 1 ) == "!" then
invert = true
property = string.sub( property, 2 )
end
-- check for existence of the given source property
for _, source in pairs( claim.references ) do
if source.snaks[ property ] then
return ( not invert )
end
end
-- return true or false inverted
return invert
end
--[=[
checks if a claim is valid at the given date
parameters:
- claim (table): claim data
- checkdate (opt, string): date to check, or current date
returns: (boolean) check result
]=]
Wd.claimValidAtDate = function( claim, checkdate )
local refdate, checkdateyear
if not checkdate or checkdate == "" then
-- date not given, use current date
refdate = os.date( "!*t" )
checkdateyear = 0
else
-- parse given date
if string.match( checkdate, "^%d+$" ) then
-- only year given
refdate = { year = tonumber( checkdate ) }
checkdateyear = tonumber( checkdate )
else
-- exact date given
refdate = Wd.datavalueTimeToDateObject( { time = mw.language.getContentLanguage():formatDate( "+Y-m-d\\TH:i:s\\Z", checkdate ) } )
checkdateyear = 0
end
end
local refjd = Wd.handleJulianDay( refdate )
-- get date qualifiers for claim
local exactdate = Wd.getQualifierSortValue( claim, Wd.propertyId[ "pointoftime" ] )
local mindate = Wd.getQualifierSortValue( claim, Wd.propertyId[ "starttime" ] )
local maxdate = Wd.getQualifierSortValue( claim, Wd.propertyId[ "endtime" ] )
if exactdate then
-- if there is an exact date in the qualifier, and the atdate parameter
-- is on year precision, mindate is the beginning of the year and maxdate
-- is 31st Dec of that particular year
local refmaxjd
if checkdateyear > 0 then
refmaxjd = Wd.handleJulianDay( { year = tonumber( checkdate ), month = 12, day = 31 } )
else
refmaxjd = refjd
end
-- return false if exact date is out of bounds
if exactdate < refjd or exactdate > refmaxjd then
return false
end
else
-- return false if date is before mindate or after maxdate
if mindate and mindate > refjd then
return false
end
if maxdate and maxdate < refjd then
return false
end
end
-- success, the claim was valid at "checkdate"
return true
end
--[=[
checks if a claim is not deprecated
parameters:
- claim (table): claim data
returns: (boolean) check result
]=]
Wd.claimNotDeprecated = function( claim )
return claim.rank ~= "deprecated"
end
--[=[
return a table of claims excluding claims not passed the filters
parameters:
- frame (table): call arguments
- claims (table): claims to filter
returns: (table) filtered claim table
]=]
Wd.filterClaims = function( frame, claims )
-- subfunction that calls the check functions and does the filtering
-- parameters:
-- - condition (string): call argument that contains the filter condition
-- - filterfunction (function): check function for the condition
local function filter( condition, filterfunction )
-- if condition not found in call arguments, do nothing
if not frame.args[ condition ] then
return
end
-- filter claim table
local newclaims = {}
for i, claim in pairs( claims ) do
-- call filter function and check for result
if filterfunction( claim, frame.args[ condition ] ) then
table.insert( newclaims, claim )
end
end
claims = newclaims
end
-- call available filters
filter( "hasqualifier", Wd.claimHasQualifier )
filter( "hassource", Wd.claimHasSource )
filter( "atdate", Wd.claimValidAtDate )
if not frame.args.includedeprecated then
frame.args.notdeprecated = true
filter( "notdeprecated", Wd.claimNotDeprecated )
end
-- use additional unnamed parameters as qualifier conditions (in pairs)
for key in pairs( frame.args ) do
if type( key ) == "number" and key > 2 and key % 2 == 1 then
-- key = 3, 5, 7 and so on
local newclaims = {}
local values = frame.args[ key ]
-- allow inversion of values
local invert = string.sub( values, 1, 1 ) == "!"
if invert then
values = string.sub( values, 2 )
end
-- filter claims
for i, claim in pairs( claims ) do
local hasvalue = false
for val in mw.text.gsplit( values, "," ) do
-- check if qualifier has current value
if Wd.claimHasQualifier( claim, frame.args[ key - 1 ], val ) then
hasvalue = true
break
end
end
if hasvalue ~= invert then
table.insert( newclaims, claim )
end
end
-- replace claims table
claims = newclaims
end
end
-- return filtered claim table
return claims
end
--[=[
convert single reference to its string representation
parameters:
- ref (table): ref data
returns: (string|nil) reference text or nil if error
]=]
Wd.formatReference = function( ref )
-- "imported from"-references are useless, skip them:
if ref[ "P143" ] or ref[ "P4656" ] then
return nil
end
-- load [[Modul:Zitation]]
local ZitationSuccess, r = pcall( require, "Modul:Zitation" )
if type( r ) == "table" then
Zitation = r.Zitation()
-- clear Zitation state from previous invocations
Zitation.o = nil
end
-- assert (ZitationSuccess, Wd.i18n["errors"]["module-not-loaded"])
-- assignments of Wikidata properties to Zitation parameters
local wdZmap = {
P1433 = { "bas", "Werk" },
P248 = { "bas", "Werk" },
P1476 = { "bas", "Titel" },
P1680 = { "bas", "TitelErg" },
P407 = { "bas", "Sprache" },
P364 = { "bas", "Sprache" },
P2439 = { "bas", "Sprache" },
P123 = { "bas", "Verlag" },
P577 = { "bas", "Datum" },
P98 = { "bas", "Hrsg" },
P2093 = { "bas", "Autor" },
P50 = { "bas", "Autor" },
P1683 = { "bas", "Zitat" },
P854 = { "www", "URL" },
P813 = { "www", "Abruf" },
P1065 = { "www", "ArchivURL" },
P2960 = { "www", "ArchivDatum" },
P2701 = { "www", "Format" },
P393 = { "print", "Auflage" },
P291 = { "print", "Ort" },
P304 = { "fragment", "Seiten" },
P792 = { "fragment", "Kapitel" },
P629 = { "orig", "Titel" }
}
for prop, value in pairs( ref ) do
if wdZmap[ prop ] then
if type( value ) == "table" then
-- more snaks with same property, we concatenate using a comma
value = table.concat( value, ", " )
end
-- value should be string now, so we can call Zitation
if type( value ) == "string" and string.len( value ) > 0 then
Zitation.fill( wdZmap[ prop ][ 1 ], wdZmap[ prop ][ 2 ], value, prop )
end
end
end
-- if no title on Wikidata, try to use the URL as title
if ( not ref[ "P1476" ] ) and ref[ "P854" ] then
local URLutil = Zitation.fetch( "URLutil" )
Zitation.fill( "bas", "Titel", URLutil.getHost( ref[ "P854" ] ) )
end
return Zitation.format()
end
--[=[
return references of the given claim as reference string
parameters:
- frame (table): invocation frame
- claim (table): claim data
returns: (string) reference text
]=]
Wd.getReferences = function( frame, claim )
local result = ""
-- loop through all references
for ref in pairs( claim.references or {} ) do
-- put snaks to table for later formatting with Wd.formatReference()
local refTable = {}
for snakkey, snakval in Wd.orderedPairs( claim.references[ ref ].snaks or {}, claim.references[ ref ][ "snaks-order" ] ) do
if #snakval == 1 then
-- only one value in snak
refTable[ snakkey ] = Wd.getSnakValue( snakval[ 1 ] )
else
-- multiple values in snak
local multival = {}
for snakidx = 1, #snakval do
table.insert( multival, Wd.getSnakValue( snakval[ snakidx ] ) )
end
refTable[ snakkey ] = multival
end
end
-- format reference with data in refTable
local formattedRef, f = Wd.formatReference( refTable )
if formattedRef and formattedRef ~= "" then
-- reference formatting successful, create hash from text to avoid double refs
local hash = mw.hash.hashValue( "fnv164", formattedRef )
-- output reference
result = result .. frame:extensionTag( "ref", formattedRef, { name = "_" .. hash } )
end
end
return result
end
--[=[
checks whether a certain item is a parent of a given item. A parent is
connected via P31 or P279. Attention: very intensive function, use carefully!
parameters:
- item (string|number): start item
- parent (string): parent item
- exitItem (string): item to exit check at
- maxDepth (number): maximum search depth
- depth (opt, number): current search depth
returns: (boolean) check result
]=]
Wd.isParent = function( item, parent, exitItem, maxDepth, depth )
-- increment call number
Wd.numberIsParentCalls = Wd.numberIsParentCalls + 1
-- initial depth is zero
if not depth then
depth = 0
end
-- cast start item number to string
if type( item ) == "number" then
item = "Q" .. item
end
-- check if start entity exists
if not Wd.entityExistsTypesafe( item ) then
return false
end
-- get entity
local entity = mw.wikibase.getEntity( item )
if not entity then
return false
end
-- if entity has no claims, end check
if not entity.claims then
return false
end
-- resolve property ids
local claims31 = mw.wikibase.resolvePropertyId( "P31" )
local claims279 = mw.wikibase.resolvePropertyId( "P279" )
if not claims31 or not claims279 then
return false
end
-- get connector claims, if both don't exist end check
claims31 = entity.claims[ claims31 ]
claims279 = entity.claims[ claims279 ]
if not claims31 and not claims279 then
return false
end
-- retrieve parent entity ids
local parentIds = {}
if claims31 and #claims31 > 0 then
for i, v in ipairs( claims31 ) do
parentIds[ #parentIds + 1] = Wd.getSnakValue( v.mainsnak, "numeric-id" )
end
end
if claims279 and #claims279 > 0 then
for i, v in ipairs( claims279 ) do
parentIds[ #parentIds + 1 ] = Wd.getSnakValue( v.mainsnak, "numeric-id" )
end
end
-- if no parent ids found, end check
if not parentIds[ 1 ] or #parentIds == 0 then
return false
end
-- check if searched parent or exit item is reached or do recursive call
local itemString = ""
local result = nil
for i, v in ipairs( parentIds ) do
-- create item string and check if it is valid
itemString = "Q" .. v
if not Wd.isValidEntityIdTypesafe( itemString ) then
return false
end
if itemString == parent then
-- parent item found
return true
elseif itemString == exitItem or itemString == "Q35120" then
-- exit if either exit item or node item "entity" (Q35120) is reached
return false
else
-- resursive call on parents if within maxDepth bounds
if depth + 1 < maxDepth then
result = Wd.isParent( itemString, parent, exitItem, maxDepth, depth + 1 )
else
return false
end
-- return true if recursive call returned true
if result == true then
return result
end
end
end
-- return false
do return false end
end
--[=[
==============================================================================
ENTITY PROPERTIES
==============================================================================
]=]
--[=[
return the sitelink name of the given entity on the given site.
parameters:
- id (opt, string): entity id
- title (opt, string): title of a local page that is connected to the entity
- site (opt, string): site name, or current site
- titleSite (opt, string): site name to look for title, or current site
One of id and title have to be specified. id takes precedence over title.
returns:
- (boolean) success indicator
- (string|table) sitelink if successful or error object
]=]
Wd.getSitelink = function( id, title, site, titleSite )
-- get id if not specified
if not id then
if title then
-- get id by title
-- if titleSite is nil, the title will be taken from the current wiki
id = mw.wikibase.getEntityIdForTitle( title, titleSite )
else
-- no id and no title given, invalid call
return false, Wd.newError( "invalid-parameters", "Wd.getSitelink" )
end
end
-- raise error if id doesn't exist
if not Wd.entityExistsTypesafe( id ) then
return false, Wd.newError( "entity-not-found", id )
end
-- if site is nil, the sitelink to the current wiki will be returned
return true, mw.wikibase.getSitelink( id, site )
end
--[=[
==============================================================================
TEMPLATE EXPORT
==============================================================================
]=]
-- export table
local p = {}
--[=[
checks whether a certain item is a subclass of a given item.
parameters (frame):
- id (string): start item
- parent (string): parent item
- exitItem (string): item to exit check at
- maxDepth (opt, string): maximum search depth
- returnInt (opt, string): if set, return 1 or empty instead of true/false
returns: (boolean|number) check result in
]=]
p.isSubclass = function( frame )
local result
-- check for validity of start and parent item
if Wd.entityExistsTypesafe( frame.args[ "id" ] ) and Wd.entityExistsTypesafe( frame.args[ "parent" ] ) then
-- check if id and parent are same
if frame.args[ "id" ] ~= frame.args[ "parent" ] then
-- set maximum depth
local maxDepth
maxDepth = tonumber( frame.args[ "maxDepth" ] ) or 5
-- getParent() call
result = Wd.isParent( frame.args[ "id" ], frame.args[ "parent" ], frame.args[ "exitItem" ], maxDepth)
-- mw.log(Wd.numberIsParentCalls) --uncomment to load number of isParent() calls into log
else
result = true
end
else
result = false
end
-- return result
if frame.args[ "returnInt" ] then
if result == true then
return 1
else
return ""
end
else
if result then
return result
else
return false
end
end
end
--[=[
get a claim
parameters (frame):
- 1 (string): property of claim
- id (string): entity id
- qualifier (string): qualifier to return
- parameter (string): value parameter to return
- language (string): value language to return
- countValues (string): return number of results instead of returning the actual result
- list (string): concatenate all results with the given separator
- listMaxItems (string): maximum items to list
- includeempty (string): also include empty values
- references (string): return also references, if "only", return references only
- sort (string): sort results
- sortEmptiesFirst (string): sort empty values first
- sortInItem (string): sort based on property value in given item
- inverse (string): inverse sorting order
- showerrors (string): show errors instead of returning empty string
- default (string): default return value if empty or error
returns: (string) claim or empty or error
]=]
p.claim = function( frame )
-- (A) PARAMETER ASSIGNMENTS --
local property = frame.args[ 1 ] or ""
local id = frame.args[ "id" ]
local qualifierId = frame.args[ "qualifier" ]
local parameter = frame.args[ "parameter" ]
local language = frame.args[ "language" ]
local countValues = frame.args[ "countValues" ]
local list = frame.args[ "list" ]
local includeempty = frame.args[ "includeempty" ]
local listMaxItems = tonumber( frame.args[ "listMaxItems" ] ) or 0
local references = frame.args[ "references" ]
local sort = frame.args[ "sort" ]
local sortEmptiesFirst = frame.args[ "sortEmptiesFirst" ]
local sortInItem = frame.args[ "sortInItem" ]
local inverse = frame.args[ "inverse" ]
local showerrors = frame.args[ "showerrors" ]
local default = frame.args[ "default" ]
-- don't show errors if default value is set
if default then
showerrors = nil
end
-- (B) VALIDITY CHECKS --
-- get wikidata entity
if id then
if not Wd.isValidEntityIdTypesafe( id ) then
-- invalid entity id
if showerrors then
return Wd.printError( "entity-not-valid" )
else
Wd.createMaintenanceLink( "entity-not-valid" )
return default
end
elseif not Wd.entityExistsTypesafe( id ) then
-- entity id does not exist
if showerrors then
return Wd.printError( "entity-not-found" )
else
-- create maintenance link
Wd.createMaintenanceLink( "entity-not-found" )
return default
end
end
end
-- get entity object
local entity = mw.wikibase.getEntity( id )
if not entity then
if showerrors then
return Wd.printError( "entity-not-found" )
else
return default
end
end
-- check if property exists
local realProp = mw.wikibase.resolvePropertyId( property )
if not realProp then
Wd.createMaintenanceLink( "property-not-existing" )
end
-- fetch the first claim satisfying the given property
local claims
if entity.claims then
claims = entity.claims[ realProp ]
end
if not claims or not claims[ 1 ] then
if countValues then
return 0
elseif showerrors then
return Wd.printError( "property-not-found" )
else
return default
end
end
-- (C) FILTERING AND SORTING --
-- filter claims
claims = Wd.filterClaims( frame, claims )
if not claims[ 1 ] then
if countValues then
return 0
else
return default
end
end
-- get initial sort indices
local sortindices = {}
for idx in pairs( claims ) do
sortindices[ #sortindices + 1 ] = idx
end
-- get comparator for sorting
local comparator
if sort then
-- comparator function for sorting statements based on qualifier value
comparator = function( a, b )
-- load qualifier values
local qualSortValA = Wd.getQualifierSortValue( claims[ a ], sort )
local qualSortValB = Wd.getQualifierSortValue( claims[ b ], sort )
-- if either of the two statements does not have this qualifer:
---- if sortEmptiesFirst=true: sort it to the beginning
---- else: always sort it to the end
if not qualSortValB then
if not qualSortValA then
-- if neither of the two statements has this qualifier, arbitrarily but consistently return a < b
return a < b
else
-- true if sortEmptiesFirst is false
return ( not sortEmptiesFirst )
end
elseif not qualSortValA then
-- true if sortEmptiesFirst is not false
return ( not not sortEmptiesFirst )
end
if type( qualSortValA ) ~= type( qualSortValB ) and not ( tonumber( qualSortValA ) and tonumber( qualSortValB ) ) then
if tonumber( qualSortValA ) then
return true
elseif tonumber( qualSortValB ) then
return false
elseif tostring( qualSortValA ) and tostring( qualSortValB ) then
if inverse then
return tostring( qualSortValA ) > tostring( qualSortValB )
else
return tostring( qualSortValA ) < tostring( qualSortValB )
end
else
-- different types, neither numbers nor strings, no chance to compare => random result to avoid script error
return false
end
elseif tonumber( qualSortValA ) and tonumber( qualSortValB ) then
qualSortValA = tonumber( qualSortValA )
qualSortValB = tonumber( qualSortValB )
end
if inverse then
return qualSortValA > qualSortValB
else
return qualSortValA < qualSortValB
end
end
elseif sortInItem then
-- get sortkeys of claims in referenced entity and given property
local sortkeys = {}
local snakSingle
local claimContainingValue
local claimContainingProperty
local claimContainingEntity
-- check if given property is valid
claimContainingProperty = mw.wikibase.resolvePropertyId( sortInItem )
if not claimContainingProperty then
if showerrors then
return Wd.printError( "property-not-found" )
else
return default
end
end
-- loop trough all claims and retrieve the sortkeys
for idx, claim in pairs( claims ) do
-- get referenced entity
snakSingle = Wd.getQualifierSnak( claim )
claimContainingEntity = "Q" .. Wd.getSnakValue( snakSingle, "numeric-id" )
-- get value if entity exists and has the property
if Wd.entityExistsTypesafe( claimContainingEntity ) then
claimContainingEntity = mw.wikibase.getEntity( claimContainingEntity )
if claimContainingEntity and claimContainingEntity.claims and claimContainingEntity.claims[ claimContainingProperty ] then
claimContainingValue = claimContainingEntity.claims[ claimContainingProperty ]
end
end
-- use value as sortkey, or empty string if no property value found
if claimContainingValue then
sortkeys[ #sortkeys + 1 ] = Wd.getClaimValue( claimContainingValue[ 1 ] )
else
sortkeys[ #sortkeys + 1 ] = ""
end
end
-- comparator function based on retrieved sortkeys
comparator = function( a, b )
if inverse then
return sortkeys[ a ] > sortkeys[ b ]
else
return sortkeys[ a ] < sortkeys[ b ]
end
end
else
-- sort by claim rank
comparator = function( a, b )
local rankmap = {
deprecated = 2,
normal = 1,
preferred = 0
}
local ranka = rankmap[ claims[ a ].rank or "normal" ] .. string.format( "%08d", a )
local rankb = rankmap[ claims[ b ].rank or "normal" ] .. string.format( "%08d", b )
return ranka < rankb
end
end
-- use comparator to sort indices table
table.sort( sortindices, comparator )
-- (D) RETURN VALUE --
local result
local error
if countValues then
-- return claim count
result = Wd.getTableSize( claims )
elseif list then
-- list claims
-- if a newline is provided (whose backslash will be escaped) unescape it
list = string.gsub( list, "\\n", "\n" )
-- loop through all elements and return their value (if existing)
local value
result = {}
for idx in pairs( claims ) do
local claim = claims[ sortindices[ idx ] ]
-- get claim value and handle result
value, error = Wd.getClaimValue( claim, qualifierId, parameter )
if not value then
if showerrors then
value = error
end
if includeempty then
-- overwrite error to empty string
value = ""
end
end
-- add references
if value and references then
value = value .. Wd.getReferences( frame, claim )
end
-- add to result list
result[ #result + 1 ] = value
end
-- concat result table to string
if listMaxItems and listMaxItems > 0 then
-- return until maximum results is reached
result = table.concat( result, list, 1, math.min( #result, listMaxItems ) )
else
-- return everything
result = table.concat( result, list )
end
else
-- return first element only
local claim = claims[ sortindices[ 1 ] ]
-- get value with language support
if language and claim.mainsnak.datatype == "monolingualtext" then
-- iterate over claims to find adequate language
for idx, claim in pairs( claims ) do
if claim.mainsnak.datavalue.value.language == language then
result, error = Wd.getClaimValue( claim, qualifierId, parameter )
break
end
end
else
-- no language given, get claim value
result, error = Wd.getClaimValue( claim, qualifierId, parameter )
end
-- handle references
if references == "only" then
-- only return references
result = Wd.getReferences( frame, claim )
elseif result and references then
-- add references
result = result .. Wd.getReferences( frame, claim )
end
end
-- return result
if result then
return result
else
if showerrors then
return error
else
return default
end
end
end
--[=[
returns the given value or runs claim() if keyword is found
parameters (frame):
- 2 (opt, string): value to return or keyword
returns: (string) value or return of claim()
]=]
p.getValue = function( frame )
local param = frame.args[ 2 ]
if param == "FETCH_WIKIDATA" or param == Wd.i18n["FETCH_WIKIDATA"] then
return p.claim( frame )
else
return param
end
end
--[=[
return the entity id of the current page, or check the given entity id
for redirects and return the redirect target, if existing
parameters (frame):
- 1 (opt, string): entity id
returns: (string) entity id or empty string
]=]
p.pageId = function( frame )
local id = frame.args[ 1 ]
-- if id not given, return the id of the current page.
-- mw.wikibase.getEntityIdForCurrentPage() loads less data than getEntity(),
-- so it's preferable to use it in this case.
if not id then
return mw.wikibase.getEntityIdForCurrentPage() or ""
end
-- get the given entity to check for redirects
local entity = mw.wikibase.getEntity( id )
if entity then
return entity:getId() or ""
end
-- return empty string by default
return ""
end
--[=[
return label of a Wikidata entity in the given language or the default language of this Wikipedia site
parameters (frame):
- 1 (opt, string): language code
- 2 (opt, string): entity id
- noFallback (opt, string): suppress language fallbacks
returns: (string) label text or empty string
]=]
p.labelIn = function( frame )
local langcode = frame.args[ 1 ]
local id = frame.args[ 2 ]
local noFallback = frame.args[ "noFallback" ]
if noFallback then
-- get label in language without fallbacks.
-- id is required mw.wikibase.getLabelByLang(), so get the connected entity
-- if not given or invalid
id = Wd.getConnectedEntityIdIfInexisting( id )
if not id then
return ""
end
return mw.wikibase.getLabelByLang( id, langcode or Wd.wiki.langcode ) or ""
elseif not langcode then
-- no langcode given, return label in wiki language with fallbacks
return mw.wikibase.getLabel( id ) or ""
else
-- get label in language with fallbacks
local entity = mw.wikibase.getEntity( id )
if entity then
return entity:getLabel( langcode ) or ""
end
end
-- return empty string by default
return ""
end
--[=[
return the label of the given entity or property id
parameters (frame):
- 1 (opt, string): entity id, or entity id of current page
returns: (string) label text, error text or empty string
]=]
p.labelOf = function( frame )
local id = frame.args[ 1 ]
-- mw.wikibase.getLabel() would support omitting the id, but we want
-- to display an error message if the page isn't connected.
id = Wd.getConnectedEntityIdIfInexisting( id )
if not id then
return Wd.printError( "entity-not-found" )
end
return mw.wikibase.getLabel( id ) or ""
end
--[=[
return description of a Wikidata entity in the given language or the default language of this Wikipedia site
parameters (frame):
- 1 (opt, string): language code
- 2 (opt, string): entity id
- noFallback (opt, string): suppress language fallbacks
returns: (string) description text or empty string
]=]
p.descriptionIn = function( frame )
local langcode = frame.args[ 1 ]
local id = frame.args[ 2 ]
local noFallback = frame.args[ "noFallback" ]
if noFallback then
-- get description in language without fallbacks
-- id is required mw.wikibase.getDescriptionByLang(), so get the connected entity
-- if not given or invalid
id = Wd.getConnectedEntityIdIfInexisting( id )
if not id then
return ""
end
return mw.wikibase.getDescriptionByLang( id, langcode or Wd.wiki.langcode ) or ""
elseif not langcode then
-- no langcode given, return description in wiki language with fallbacks
return mw.wikibase.getDescription( id ) or ""
else
-- get description in language with fallbacks
local entity = mw.wikibase.getEntity( id )
if entity then
return entity:getDescription( langcode ) or ""
end
end
-- return empty string by default
return ""
end
--[=[
return the Wikipedia page name of the given entity on the given site.
parameters (frame):
- 1 (opt, string): entity id, or entity id of current page
- 2 (opt, string): site name, or current site
returns: (string) page name, error text or empty string
]=]
p.sitelinkOf = function( frame )
local id = Wd.getFrameArgument( frame, 1 )
local site = Wd.getFrameArgument( frame, 2 )
-- Wd.getSitelink() doesn't support omitting the id parameter, so
-- get the current page's entity id manually and fail if not found.
id = Wd.getConnectedEntityIdIfInexisting( id )
if not id then
return Wd.printError( "entity-not-found" )
end
-- get the sitelink
local success, result = Wd.getSitelink( id, nil, site )
if not success then
return Wd.printError( Wd.parseError( result ) )
else
return result or ""
end
end
--[=[
return the number of sitelinks in the given entity
parameters (frame):
- 1 (opt, string): sitelink filter for projects
- 2 (opt, string): entity id, or entity id of current page
returns: (number) sitelink count
]=]
p.sitelinkCount = function( frame )
local filter = frame.args[ 1 ]
local id = frame.args[ 2 ]
if filter then
filter = "^.*" .. filter .. "$"
else
filter = false
end
local count = 0
local entity = mw.wikibase.getEntity( id )
if entity and entity.sitelinks then
if filter == false then
count = Wd.getTableSize( entity.sitelinks )
else
for project, _ in pairs( entity.sitelinks ) do
if string.find( project, filter ) then
count = count + 1
end
end
end
end
return count
end
--[=[
return the badges of the given entity for the given site
parameters (frame):
- 1 (opt, string): site id, or current site
- 2 (opt, string): entity id, or entity id of current page
returns: (string) badges
]=]
p.badges = function( frame )
local site = frame.args[ 1 ]
local id = frame.args[ 2 ]
-- entity is required for mw.wikibase.getBadges()
id = Wd.getConnectedEntityIdIfInexisting( id )
if not id then
return Wd.printError( "entity-not-found" )
end
-- get badges, site is allowed to be nil
badges = mw.wikibase.getBadges( id, site )
if type( badges ) == "table" then
return table.concat( badges, "/" )
end
-- return empty string by default
return ""
end
--[=[
==============================================================================
DEBUGGING
==============================================================================
]=]
--[=[
call this in cases of script errors within a function:
instead of {{#invoke:Wikidata|<method>|...}} call {{#invoke:Wikidata|debug|<method>|...}}
parameters (frame):
- (...) call parameters of function to debug
returns: (...) return value of function to debug
]=]
p.debug = function( frame )
-- get function name
local func = frame.args[ 1 ]
if not func then
return Wd.printError( "invalid-parameters" )
end
-- create new parameter set, where the first parameter with the function name is removed
local newargs = {}
for key, val in pairs( frame.args ) do
if type( key ) == "number" then
-- skip first unnamed param
if key > 1 then
newargs[ key - 1 ] = val
end
else
newargs[ key ] = val
end
end
-- replace frame args and call the given function
frame.args = newargs
local status, result = pcall( p[ func ], frame )
-- output return value or state
if status then
return result
else
return mw.ustring.format( "<span class=\"error\">%s</span>", result )
end
end
--[=[
print the content of an entity
parameters (frame):
- 1 (opt, string): entity id, or entity id of current page
returns: (string) entity json content
]=]
p.printEntity = function( frame )
local id = Wd.getConnectedEntityIdIfInexisting( frame.args[ 1 ] );
if id then
-- get entity and dump it
local entity = mw.wikibase.getEntity( id )
if entity then
return mw.ustring.format( "<pre>%s</pre>", mw.dumpObject( entity ) )
end
end
-- return empty string by default
return ""
end
--[=[
==============================================================================
COORDINATE TEMPLATE HELPERS
==============================================================================
]=]
-- formfill Template:Coordinate (NS, EW, name from WikidataEntity) and expand it
-- füllt Vorlage:Coordinate (NS, EW, name mit Wikidata-Werten) + expandiert sie
-- 1st frame.arg .. Q prefixed entity id (mandatory)
-- named frame.arg "type", "region", "text" .. see doc of 'Coordinate' template
p.ffCoordinate = function(frame)
local f = frame
local id = f.args[1] or f.args.Q
local name = f.args.name or p.labelIn{ args = { nil, id, id = id }}
local coord = mw.text.split(p.claim{ args = { "P625", id, id = id }}, "/")
coord[1] = tonumber(coord[1])
coord[2] = tonumber(coord[2])
local t, r = f.args.type, f.args.region
if not t
then t = p.claim{ args = { "P31", id, id = id, parameter = "numeric-id", default = "" }}
if t and t ~= "" then
t = "Q" .. t
t = t:gsub("Q.*", {
Q8502 = "mountain",
Q54050 = "landmark"
})
end
if not t or t and t:find("Q", 1, true)
then t="" -- no default, let Coordinate warn about unset type= param
end
end
if not r
then r = p.claim{ args = { "P17", id, id = id, parameter = "numeric-id", default = "" }}
if r and r ~= "" then
r = "Q" .. r
r = p.claim{ args = { "P297", r, id = r }}
else
r="" -- no default, let Coordinate warn about unset region= param
end
end
return ('<span data-sort-value="%010.6f"></span>'):format((f.args.sortkey
or "EW"):find("EW", 1, true) and coord[2]+360.0 or coord[1]+180.0
) .. f:expandTemplate{ title = "Coordinate", args = {
NS = coord[1], EW = coord[2], type = t, region = r,
text = f.args.text or (f.args.maplink and "ICON0" or "/"),
name = name, simple = f.args.simple
}} .. (not f.args.maplink and "" or (" " ..
--f:callParserFunction{ name="#statements", args={ "P625", from = id } }
f:callParserFunction{ name="#tag:maplink", args={ "",
class = "no-icon", text = f.args.mlname and name,
zoom = 12, latitude = coord[1], longitude = coord[2]
}}
))
end
p.ffCoordinateAndLatLonMaplink = function(frame)
frame.args.maplink = 1
--frame.args.mlname = nil
return p.ffCoordinate(frame)
end
p.ffCoordinateAndMaplink = function(frame)
frame.args.maplink = 1
frame.args.mlname = 1
return p.ffCoordinate(frame)
end
return p