Moodul:Wikidata2
Ilme
- Selle mooduli dokumentatsioon on liidetud lehelt Moodul:Wikidata2/doc (redigeeri | ajalugu).
This module gives format to the data extracted from Wikidata
Simple functions
claim
: It gives the value or values of a qualifier, in the format we ask for. By default, it uses the item (Qnnnn) of the article it is transcluded.
Other functions:
getParentValues
: it gives the values and labels of a propiety, recursively, up to the limit we say.getTAValue
: it gives the value of TA98 (first edition of Terminologia Anatomica, 1998) in the property P1323. There're not extra parameters for this function, but it gives a list with the correct external link format.ViewSomething
: we can see anything inside the data, including labels, descriptions, references or interwiki links.years_old
: it gives the age of a person. If the person is dead it gives the difference between P569 and P570. If there's not a P570 property, it gives the difference between P569 and the current date. According to the precision of the data, it could give two different numbers. If one of the dates is given in century format, there won't be an answer.- The syntax is these:
{{#invoke:Wikidata |years_old|formatting=''pattern''|item=}}
.item
andformatting
are not mandatory. And example would beformatting = ($1 years)
- The syntax is these:
- Full syntax
{{#invoke:Wikidata|claim|property= |qualifier= |value= |list= |tablesort= |formatting= |separator= |conjunction= |item= |showerrors= |default= }}
- If we want it in a table format:
{{#invoke:Wikidata|claim|property= |qualifier= |qualifier2= |...|qualifier9= |tablesort= |rowformat= |colformat0= | colformat1 = |...|rowsubformat1= |...|rowsubformat9= |formatting=table |separator= |conjunction= |item= |showerrors= |default= }}
- Functions for the claim function
property=
(mandatory) The property we want to get, "P" followed by the number. For the full list of properties in Wikidata, see List of properties. You can write it with a little p, but is not recommended. You can even use the label of the property, for example property=country would be the same as property=P17.qualifier=
(optional) The proprety of the qualifier, a "P" followed by a number. It would give the link to the qualifier item. You can write it with a little p, but is not recommended.value=
(optional) It inserts a preferent value to override the Wikidata item. You can use it inside a template, with a {{{parameter|}}} value.list=
(optional). By default list=true, and it gives all the claims of the quliafier (see separator and conjunction).list=false
It gives a unique value. It would give the highest ranked one from preferent/normal/deprecated or the oldest one if all are of the same rank.list=firstrank
It gives a list of all the items of the highest level. Is the same as list=true if all the elements are of the same level.tablesort=0
(optional) it orders the list from bottom to top. By default the order would be according to the level and age of the claims.
formatting=
(optional) The format we want to give to the claim:- Format of the element (entity):
formatting=raw
It gives the Qnnnn value of the element.formatting=label
It gives the label of the element.formatting=sitelink
It gives the link of the Wikipedia link for that element.formatting=internallink
Internal link if possible. This can be a sitelink or a label. If there's not a link but there's a label, it will give a red link to that label. If there's not a label, it will give the English label by default.formatting=pattern
A pattern we give, using $1 as the element to change.
- We can insert templates and parser functions, but we need to use special templates:{{((}}Name of the template{{!}}Parameter{{!}}Parameter{{))}}
- The default formatting will be a link.
- Text format (strings):
formatting=weblink
External link format [http://example.com example.com]formatting=pattern
A pattern we give, using $1 as the element to change. Example:formatting=[http://whc.unesco.org/en/list/$1 $1]
. We can use it in the cases where Wikidata creates external links, and the correct pattern will be in the Talk of the Propiety.
- Number format (quantity):
formatting=unit
The number and the unitformatting=unitcode
The number and the abbr. of the unit. It needs ca:Module:Wikidata/Units.
- Language format (monolingualtext):
formatting=language
It gives the language of the parameter we want to get, for the items that are formatted as monolingual text, i.e. P1448.formatting=text
a special version of the format monolingual text, it only gets the text. If we don't use formatting we would get the text and the language code as in<span lang="en">United...</span>
.formatting=eu
or another language. It gives a unique value, in this case the monolingual text value for the Basque language. This can be used with P2521.
- Format for coordinates (globecoordinate) --> but for better uses see ca:Template:Cal coor:
formatting=latitude
It gives the latitude in decimal format.formatting=longitude
It gives the longitude in decimal formatformatting=dimension
It gives the dimension of the coordinates, in meters, so we can use with the dim parameter of the maps.formatting=globe
(by default) it gives the globe parameter for the coordinate template. If we use an external object, you could use ca:Mòdul:Mapa cos celeste/dades
- Time format (time):
formatting= format #time
it works with any #time format. For example formatting=d-m-Y (17-11-2024), formatting=[[j xg]] (17 novembri), formatting=Y (2024). By default uses "j F Y" (17 november 2024).
- Table format for properties and qualifiers
formatting=table
It gives a list of properties, in a table format, with its qualifiers. It can have up to 9 qualifiers, and every group will be inserted in a row. It uses the separator and conjunction separators as parameters. You can use a lot of different options:qualifier2 ... qualifier9=
if you need more than onequalifier
rowformat=
Formats every row of properties and qualifiers. We use $0 to mark the property, and from $1 to $9 to mark the qualifiers. By default it givesrowformat=$0 ($1, ... $9)
. We can use list items as * and #, and also templates with the syntax {{((}}, {{!}} and {{))}}.rowsubformat1 ... rowsubformat9=
you can define the subformat of every item from $1 to $9. It would be very useful if any qualifier is not present in the item. For example, if you have "rowformat=$0 $1" and "rowsubformat1= or $1" would give "$0 or $1" if both are present and "$0" if there is not "$1".colformat0 ... colformat9=
It gives format to every column, using 0 for the property and 1-9 for the qualifiers. We can use any parameter defined informatting
. So we can make extra operations with every column.tablesort=
With values from 0 to 9, it orders the table according to that property or qualifier. By default the table is ordered according to the level or age of the Wikidata items in $0.sorting=-1
it gives just the opposite sorting for the ordering. By default is from bottom to top.
- Format of the element (entity):
separator=
(optional) for using both in lists and tables. By default, is a comma (, ). In tables, by default, is a line break: <br />.conjunction=
(optional) it inserts a conjunction in the last elements of a list. By default is and . In tables is a line break: <br />.case=
(optional) not in the Catalan Module. You can see it in the Basque module. You can give declension to words, items...item=
(optional) it works with an article that is not the current. We have to insert it using Qnnnn. Use it moderately, please, as the amount of resources are finite.itemgender=
(optional) It gives the gender of the item, using P21.showerrors=
(optional) It shows the error messages.default=
(optional) The message to give if there's an error. It doesn't show if showerrors is on.
- Parameters of getParentValues function
item=
(optional) it allows inserting an item (Qnnnnn). This random approach to Wikidata is not recommended, as is quite resource-consuming.property=
(optional) The property we want to search for parents. By default is P131.label=
(optional) The label of the property. By default is P31.valuetext=
(optional) Value of the link of the property. By default is formatting=internallink.upto=
(optional) Where we want to stop the tree. It will give properties till is reached.- upto=label: a label that determines the stop signal. For example "upto=state".
- upto=number: the number of items we want to get. By default is 10, but normally we wouldn't find so much.
labelshow=
(optional) A filter for the kind of labels we want to show, with a slash to divide them. For example: "labelshow=municipality/region"sorting=
(optional) The order of the list. It does only have one possible value, "sorting=-1", in order to show just the opposite order.rowformat=
(optional) the way in which we show the $0 label and $1 values. By default, "rowformat=$0 = $1", but we can use others,, for example rowformat=<tr><td>'''$0'''</td><td>$1</td>separator=
(optional) The separator of every value couple. By default is <br />. We don't need conjunctions in this function.
Note: Labels can be changed so they fit better in the infoboxes. For example, "Municipality of Brazil" can be changed to "Municipality". We can do this using ca:Module:Wikidata/labels, making every change needed in our language.
- Examples
- Simple use:
- {{#invoke:Wikidata | claim |property=P20}} in the article Jean-François Champollion
- Property P20 showing the element.
- Without link:
- {{#invoke:Wikidata | claim | property=P20 | formatting=label}}
- Default value
- {{#invoke:Wikidata | claim | property=P20 | value={{{death place|}}} }} if we define {{{death place|}}} in the infobox, it will appear, if not, Paris should be rendered.
- Date format
- {{#invoke:Wikidata | claim | property=P569}} gives the birth date in the correct format
- Values given with a pattern:
- {{#invoke:Wikidata | claim | property=P214 | formatting=[http://viaf.org/viaf/$1 $1]}}
- Unique value:
- {{#invoke:Wikidata | claim | property=P18 | list=false | formatting=[[File:$1|thumb|upright=.5|Example image]]}}
- It will give only the first defined image.
- Formatting of external link
- {{#invoke:Wikidata | claim | property=P856 | formatting=weblink}} in the Berlin article
- Value list
- List with format
- Mall:P {{#invoke:Wikidata | claim | property=P527 | separator=<br /> | conjunction=<br />}} in the article Basque Country
- Coordinates:
- {{#invoke:Wikidata | claim | property=P625 | formatting=latitude}}
- {{#invoke:Wikidata | claim | property=P625 | formatting=longitude}}
-- version 20191023 from master @cawiki
local p = {}
-----------------------------------------------------------------------------
-- internationalisation at [[Module:Wikidata/i18n]]
local i18n = {
["errors"] = {
["property-not-found"] = "Property not found.",
["qualifier-not-found"] = "Qualifier not found.",
['not-from-content-page'] = "Do not invoke from content page. Use a template or use a module subpage like /sandbox for testing."
},
["datetime"] =
{
-- $1 is a placeholder for the actual number
[0] = "$1 billion years", -- precision: billion years
[1] = "$100 million years", -- precision: hundred million years
[2] = "$10 million years", -- precision: ten million years
[3] = "$1 million years", -- precision: million years
[4] = "$100000 years", -- precision: hundred thousand years; thousand separators added afterwards
[5] = "$10000 years", -- precision: ten thousand years; thousand separators added afterwards
[6] = "$1 millennium", -- precision: millennium
[7] = "$1 century", -- precision: century
[8] = "$1s", -- precision: decade
-- the following use the format of #time parser function
[9] = "Y", -- precision: year,
[10] = "F Y", -- precision: month
[11] = "F j, Y", -- precision: day
[12] = "F j, Y ga", -- precision: hour
[13] = "F j, Y g:ia", -- precision: minute
[14] = "F j, Y g:i:sa", -- precision: second
["beforenow"] = "$1 BCE", -- how to format negative numbers for precisions 0 to 5
["afternow"] = "$1 CE", -- how to format positive numbers for precisions 0 to 5
["bc"] = '$1 "BCE"', -- how print negative years
["ad"] = "$1", -- how print positive years
["bc-addon"] = " BC", -- suffix for negative dates
["ad-addon"] = "" -- suffix for 1st century AD dates
},
["monolingualtext"] = '<span lang="%language">%text</span>',
["cite"] = { -- Cite web parameters
["url"] = "url",
["title"] = "title",
["website"] = "website",
["access-date"] = "access-date",
["archive-url"] = "archive-url",
["archive-date"]= "archive-date",
["author"] = "author",
["publisher"] = "publisher",
["quote"] = "quote",
["language"] = "language",
["date"] = "date",
["pages"] = "pages"
}
}
local cases = {} -- functions for local grammatical cases defined at [[Module:Wikidata/i18n]]
----------------------------------------------------------------------------
-- module local functions
local wiki =
{
langcode = mw.language.getContentLanguage().code,
module_title = mw.wikibase.getSitelink('Q12069631'), -- title of this module for access to its /subpages
}
-- Credit to http://stackoverflow.com/a/1283608/2644759
-- cc-by-sa 3.0
local function tableMerge(t1, t2)
for k,v in pairs(t2) do
if type(v) == "table" then
if type(t1[k] or false) == "table" then
tableMerge(t1[k] or {}, t2[k] or {})
else
t1[k] = v
end
else
t1[k] = v
end
end
return t1
end
local function loadI18n()
local exist, res = pcall(require, wiki.module_title .. "/i18n")
if exist and next(res) ~= nil then
tableMerge(i18n, res.i18n)
cases = res.cases
end
end
loadI18n()
local function case(word, localcase, lang)
if word == nil or word == '' or cases[localcase] == nil then
return word
end
return cases[localcase](word, lang)
end
local function findLang(langcode)
if langcode == nil or langcode == "" or mw.language.isKnownLanguageTag(langcode) == false then
local myframe = mw.getCurrentFrame()
langcode = myframe.args.lang
if langcode == nil or langcode == "" or mw.language.isKnownLanguageTag(langcode) == false then
langcode = myframe:getParent().args.lang
if langcode == nil or langcode == "" or mw.language.isKnownLanguageTag(langcode) == false then
if not mw.title.getCurrentTitle().isContentPage then
langcode = myframe:preprocess( '{{int:lang}}' )
end
if langcode == nil or langcode == "" or mw.language.isKnownLanguageTag(langcode) == false then
langcode = wiki.langcode
end
end
end
end
local languages = mw.language.getFallbacksFor(langcode)
table.insert(languages, 1, langcode)
return languages
end
-- Helper function to obtain the default language code
function p.lang(frame)
return findLang(frame.args[1])[1]
end
-- mw.wikibase.getLabelWithLang or getLabelByLang with a table of languages
local function getLabelByLangs(id, languages)
local label
local lang = languages[1]
if lang == wiki.langcode then
-- using getLabelWithLang when possible instead of getLabelByLang
label, lang = mw.wikibase.getLabelWithLang(id)
else
for _, l in ipairs(languages) do
label = mw.wikibase.getLabelByLang(id, l)
lang = l
if label then
break
end
end
end
return label, lang
end
-- Is gender femenine? true or false
local function feminineGender(id)
local claims = mw.wikibase.getBestStatements(id or mw.wikibase.getEntityIdForCurrentPage(),'P21')
if #claims == 0 then
return false
elseif claims[1].mainsnak.datavalue == nil then -- novalue or somevalue
return false
else
local genderId = claims[1].mainsnak.datavalue.value.id
if genderId == "Q6581072" or genderId == "Q1052281" or genderId == "Q43445" then -- female, transgender female, female organism
return true
end
end
return false
end
-- Fetch female form of label
local function feminineForm(id, lang)
local feminine_claims = mw.wikibase.getBestStatements(id, 'P2521') -- female form of label
for _, feminine_claim in ipairs(feminine_claims) do
if feminine_claim.mainsnak.datavalue.value.language == lang then
return feminine_claim.mainsnak.datavalue.value.text
end
end
end
-- Fetch unit symbol
local function unitSymbol(id, lang)
local claims = findClaims(id, 'P5061')
local langclaims = {}
if claims then
for _, snak in ipairs(claims) do
if snak.mainsnak and snak.mainsnak.datavalue and snak.mainsnak.datavalue.value and
not langclaims[snak.mainsnak.datavalue.value.language] -- just the first one by language
then
langclaims[snak.mainsnak.datavalue.value.language] = snak.mainsnak.datavalue.value.text
end
end
for _, l in ipairs(lang) do
if langclaims[l] then
return langclaims[l]
end
end
end
return langclaims["mul"] -- last try
end
-- Add a small pencil as icon for edit on Wikidata
local function addEditIcon(id, lang, uselang, icon)
if icon and lang ~= uselang then
return " [[File:Arbcom ru editing.svg|12px|" .. mw.message.new('Translate-taction-translate'):inLanguage(uselang):plain() .. "|link=d:" .. id .. "]]"
end
return ''
end
-- Escape URL escapes to avoid Lua captures
local function urlEscapes(text)
return mw.ustring.gsub(text, "(%%%d)", "%%%1")
end
local function expandBraces(text, formatting)
if text == nil or formatting == nil then return text end
-- only expand braces if provided in argument, not included in value as in Q1164668
if mw.ustring.find(formatting, '{{', 1, true) == nil then return text end
if type(text) ~= "string" then
text = tostring(text)
end
for braces in mw.ustring.gmatch(text, "{{(.-)}}") do
local parts = mw.text.split(braces, "|")
local title_part = parts[1]
local parameters = {}
for i = 2, #parts do
local subparts = mw.ustring.find(parts[i], "=")
if subparts then
parameters[mw.ustring.sub(parts[i], 1, subparts-1)] = mw.ustring.sub(parts[i], subparts+1, -1)
else
table.insert(parameters, parts[i])
end
end
local braces_expanded
if mw.ustring.find(title_part, ":")
and mw.text.split(title_part, ":")[1] ~= mw.site.namespaces[10].name -- not a prefix Template:
then
braces_expanded = mw.getCurrentFrame():callParserFunction{name=title_part, args=parameters}
else
braces_expanded = mw.getCurrentFrame():expandTemplate{title=title_part, args=parameters}
end
braces = mw.ustring.gsub(braces, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- escape magic characters
braces_expanded = urlEscapes(braces_expanded)
text = mw.ustring.gsub(text, "{{" .. braces .. "}}", braces_expanded)
end
return text
end
local function resolveEntityId(id) -- pending phab:T157868
if not id or not mw.wikibase.isValidEntityId(id) then return id end
-- if no label in local language nor its fallbacks, maybe it is a redirect
-- not using mw.title.new(id).isRedirect as it is expensive
if mw.wikibase.getLabel(id) == nil then
local entity = mw.wikibase.getEntity(id) -- expensive function
if not entity then return nil end
if id ~= entity.id then
-- Qid redirected to be fixed
-- see [[Special:WhatLinksHere/Template:Track/wikidata/redirect]]
require(wiki.module_title .. '/debug').track('redirect')
require(wiki.module_title .. '/debug').track('redirect/' .. id)
else
-- no redirect and no label, fix it to avoid expensive functions
require(wiki.module_title .. '/debug').track('label')
require(wiki.module_title .. '/debug').track('label/' .. id)
end
return entity.id
end
return id
end
local function printDatatypeMath(data)
return mw.getCurrentFrame():callParserFunction('#tag:math', data)
end
local function printDatavalueString(data, parameters)
if parameters.formatting == 'weblink' then
return '[' .. data .. ' ' .. mw.text.split(data, '//' )[2] .. ']'
elseif mw.ustring.find((parameters.formatting or ''), '$1', 1, true) then -- formatting = a pattern
return expandBraces(mw.ustring.gsub(parameters.formatting, '$1', data), parameters.formatting)
elseif parameters.case then
return case(data, parameters.case, parameters.lang[1])
else
return data
end
end
local function printDatavalueUrl(data, parameters)
return printDatavalueString(urlEscapes(data), parameters)
end
local function printDatavalueCoordinate(data, parameter)
local globes = {['Q3134']='callisto',['Q596']='ceres',['Q15040']='dione',['Q2']='earth',['Q3303']='enceladus',
['Q3143']='europa',['Q17975']='phoebe',['Q3169']='ganymede',['Q3123']='io',['Q17958']='iapetus',
['Q308']='mercury',['Q15034']='mimas',['Q405']='moon',['Q15050']='rhea',['Q15047']='tethys',
['Q111']='mars',['Q2565']='titan',['Q3359']='triton',['Q313']='venus',['Q3030']='vesta'}
if parameter and mw.ustring.find(parameter, '$lat', 1, true) and mw.ustring.find(parameter, '$lon', 1, true) then
local ret = mw.ustring.gsub(mw.ustring.gsub(parameter, '$lat', data.latitude), '$lon', data.longitude)
if mw.ustring.find(parameter, '$globe', 1, true) then
local myglobe = 'earth'
if data.globe and data.globe ~= '' then
local globenum = mw.text.split(data.globe, 'entity/')[2] -- http://www.wikidata.org/wiki/Q2
myglobe = globes[globenum] or 'earth'
end
ret = mw.ustring.gsub(ret, '$globe', myglobe)
end
return expandBraces(ret, parameter)
elseif parameter == 'latitude' then
return data.latitude
elseif parameter == 'longitude' then
return data.longitude
elseif parameter == 'dimension' then
return data.dimension
else --default formatting='globe'
if data.globe == '' or data.globe == nil or data.globe == 'http://www.wikidata.org/entity/Q2' then
return 'earth'
else
local globenum = mw.text.split(data.globe, 'entity/')[2]
return globes[globenum] or globenum
end
end
end
local function roundPrecision(in_num, out_num)
-- rounds out_num with default precision of in_num
-- first, count digits after decimal mark, handling cases like '12.345e6'
local exponent, prec
local integer, dot, decimals, expstr = in_num:match('^(%d*)(%.?)(%d*)(.*)')
local e = expstr:sub(1, 1)
if e == 'e' or e == 'E' then
exponent = tonumber(expstr:sub(2))
end
if dot == '' then
prec = -integer:match('0*$'):len()
else
prec = #decimals
end
if exponent then
-- So '1230' and '1.23e3' both give prec = -1, and '0.00123' and '1.23e-3' give 5.
prec = prec - exponent
end
-- significant figures
local in_bracket = 10^-prec -- -1 -> 10, 5 -> 0.00001
local out_bracket = in_bracket * out_num / in_num
out_bracket = 10^math.floor(math.log10(out_bracket)+.5) -- 1230 -> 1000, 0.00123 -> 0.001
-- round it (credit to Luc Bloom from http://lua-users.org/wiki/SimpleRound)
return math.floor(out_num/out_bracket + (out_num >=0 and 1 or -1) * 0.5) * out_bracket
end
local function printDatavalueQuantity(data, parameters)
local amount = data.amount
amount = mw.ustring.gsub(amount, "%+", "")
local sortkey = string.format("%09d", amount)
local suffix = ""
if string.sub(parameters.formatting or '', 1, 4) == "unit" or parameters.convert then
-- example "unit": "http://www.wikidata.org/entity/Q174728"
local unitID = data.unit
unitID = mw.ustring.sub(unitID, mw.ustring.find(unitID, "Q"), -1)
if string.sub(unitID, 1, 1) == "Q" then
if parameters.convert then
-- convert units
local factor, conv_unit
local conversion = mw.wikibase.getAllStatements(unitID, 'P2442') -- conversion to standard unit
table.insert(conversion, mw.wikibase.getBestStatements(unitID, 'P2370')[1]) -- conversion to SI unit
local conversion_string = mw.wikibase.getAllStatements(unitID, 'P370') -- Sandbox-String, i.e. Q42289
for _, conv in ipairs(conversion_string) do
table.insert(conversion, conv)
end
for _, conv in ipairs(conversion) do
if conv.mainsnak.snaktype == 'value' then -- no somevalue nor novalue
if conv.mainsnak.datatype == 'string' then
conv_unit = conv.qualifiers['P2210'][1].datavalue.value.id
if conv_unit == parameters.convert then
factor = conv.mainsnak.datavalue.value
local amount_f = mw.getCurrentFrame():callParserFunction('#expr', mw.ustring.gsub(factor, "$1", amount))
amount = roundPrecision(amount, tonumber(amount_f))
unitId = conv_unit
break
end
else
conv_unit = conv.mainsnak.datavalue.value.unit
if conv_unit == "http://www.wikidata.org/entity/" .. parameters.convert then
factor = tonumber(conv.mainsnak.datavalue.value.amount)
amount = roundPrecision(amount, amount * factor)
unitID = mw.ustring.sub(conv_unit, mw.ustring.find(conv_unit, "Q"), -1)
break
end
end
end
end
end
if parameters.formatting == "unitcode" then
-- get unit symbol
local unit_symbol = ''
if parameters.lang[1] == wiki.langcode and pcall(require, wiki.module_title .. "/Units") then
unit_symbol = require(wiki.module_title .. "/Units").getUnit(amount, '', unitID, true, '')
end
if unit_symbol == '' then
unit_symbol = unitSymbol(unitID, parameters.lang)
end
if unit_symbol then
suffix = " " .. unit_symbol
end
end
if suffix == '' then
-- get unit name
local unit_label, lang = getLabelByLangs(unitID, parameters.lang)
if lang == wiki.langcode and pcall(require, wiki.module_title .. "/Units") then
suffix = " " .. require(wiki.module_title .. "/Units").getUnit(amount, unit_label, unitID, false, '')
else
suffix = " " .. (unit_label or unitID) .. addEditIcon(unitID, lang, parameters.lang[1], parameters.editicon)
end
end
end
end
amount = mw.language.new(parameters.lang[1]):formatNum(tonumber(amount))
return amount .. suffix, sortkey
end
local function printDatavalueTime(data, parameters)
-- Dates and times are stored in ISO 8601 format
local timestamp = data.time
local sortkey = timestamp
local addon = ""
local calendar_add = ""
-- check for negative date, ex. "-0027-01-16T00:00:00Z"
if string.sub(timestamp, 1, 1) == '-' then
timestamp = '+' .. string.sub(timestamp, 2)
addon = i18n.datetime["bc-addon"]
elseif string.sub(timestamp, 2, 3) == '00' then
addon = i18n.datetime["ad-addon"]
else
-- calendar model
local calendar_model = {["Q12138"] = "gregorian", ["Q1985727"] = "gregorian", ["Q11184"] = "julian", ["Q1985786"] = "julian"}
local calendar_id = mw.text.split(data.calendarmodel, 'entity/')[2]
if (timestamp < "+1582-10-15T00:00:00Z" and calendar_model[calendar_id] == "gregorian")
or (timestamp > "+1582-10-04T00:00:00Z" and calendar_model[calendar_id] == "julian")
then
calendar_add = " <sup>(" .. mw.message.new('Wikibase-time-calendar-' .. calendar_model[calendar_id]):inLanguage(parameters.lang[1]):plain() .. ")</sup>"
end
end
local function d(f, t)
local ts = t or timestamp
local form = type(f) == "function" and f(ts) or f -- function in i18n.datetime[precision]
return mw.language.new(parameters.lang[1]):formatDate(form, ts) .. addon .. calendar_add
end
local precision = data.precision or 11
local intyear = tonumber(mw.ustring.match(timestamp, "^\+?%d+"))
local ret = ""
if precision <= 5 then -- precision is 10000 years or more
local factor = 10 ^ ((5 - precision) + 4)
local y2 = math.ceil(math.abs(intyear) / factor)
local relative = mw.ustring.gsub(i18n.datetime[precision], "$1", tostring(y2))
if addon == i18n.datetime["bc-addon"] then
-- negative date
ret = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative)
else
ret = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative)
end
local ret_number = string.match(ret, "%d+")
if ret_number ~= nil then
ret = mw.ustring.gsub(ret, ret_number, mw.language.new(parameters.lang[1]):formatNum(tonumber(ret_number)))
end
elseif precision == 6 then -- millennia
local card = math.floor((intyear - 1) / 1000) + 1
if mw.ustring.find(i18n.datetime[6], "$1") then
ret = mw.ustring.gsub(i18n.datetime[6], "$1", tostring(card)) .. addon .. calendar_add
else
ret = d(i18n.datetime[6], string.format("%04d", tostring(card)))
end
elseif precision == 7 then -- centuries
local card = math.floor((math.abs(intyear) - 1) / 100) + 1
if mw.ustring.find(i18n.datetime[7], "$1") then
ret = mw.ustring.gsub(i18n.datetime[7], "$1", tostring(card)) .. addon .. calendar_add
else
ret = d(i18n.datetime[7], string.format("%04d", tostring(card)))
end
elseif precision == 8 then -- decades
local card = math.floor(math.abs(intyear) / 10) * 10
ret = mw.ustring.gsub(i18n.datetime[8], "$1", tostring(card)) .. addon .. calendar_add
elseif parameters.formatting == 'Y' or precision == 9 then -- precision is year
ret = tostring(intyear) .. addon .. calendar_add
elseif precision == 10 then -- month
timestamp = timestamp .. " + 1 day" -- formatDate yyyy-mm-00 returns the previous month
ret, _ = string.gsub(d(i18n.datetime[10]), " 0+", " ") -- supress leading zeros in year
elseif parameters.formatting then
ret, _ = string.gsub(d(parameters.formatting), "([ %[])0+", "%1") -- supress leading zeros in year optionally linked
else
ret, _ = string.gsub(d(i18n.datetime[11]), " 0+", " ")
end
return ret, sortkey
end
local function printDatavalueEntity(data, parameters)
local entityId = data['id']
local entity_page = 'Special:EntityPage/' .. entityId
if parameters.formatting == 'raw' then
if data['entity-type'] == 'item' then
entityId = resolveEntityId(entityId)
end
return entityId, entityId
end
local label, lang = getLabelByLangs(entityId, parameters.lang)
local sitelink = mw.wikibase.getSitelink(entityId)
local parameter = parameters.formatting
local labelcase = label or sitelink
if parameters.gender == 'feminineform' and lang ~= nil then -- case gender and item is female
labelcase = feminineForm(entityId, lang) or labelcase
end
if parameters.case then
labelcase = case(labelcase, parameters.case, lang)
end
local ret1, ret2
if parameter == 'label' then
ret1 = (labelcase or entityId)
ret2 = labelcase or entityId
elseif parameter == 'sitelink' then
ret1 = (sitelink or 'wikidata:' .. entity_page)
ret2 = sitelink or entityId
elseif mw.ustring.find((parameter or ''), '$1', 1, true) then -- formatting = a pattern
ret1 = mw.ustring.gsub(parameter, '$1', labelcase or entityId)
ret1 = expandBraces(ret1, parameter)
ret2 = labelcase or entityId
else
if parameter == "ucfirst" or parameter == "ucinternallink" then
labelcase = labelcase and mw.language.new(lang):ucfirst(labelcase)
-- only first of a list, reset formatting for next ones
if parameter == "ucinterlanllink" then
parameters.formatting = 'internallink'
else
parameters.formatting = nil -- default format
end
end
if sitelink then
ret1 = '[[' .. sitelink .. '|' .. labelcase .. ']]'
ret2 = labelcase
elseif label and (parameter == 'internallink' or parameter == 'ucinternallink') then
ret1 = '[[' .. label .. '|' .. labelcase .. ']]'
ret2 = labelcase
else
ret1 = '[[wikidata:' .. entity_page .. '|' .. (labelcase or entityId) .. ']]'
ret2 = labelcase or entityId
end
end
return ret1 .. addEditIcon(entity_page, lang, parameters.lang[1], parameters.editicon), ret2
end
local function printDatavalueMonolingualText(data, parameters)
-- data fields: language [string], text [string]
if parameters.list == "lang" and data["language"] ~= parameters.lang[1] then
return
elseif parameters.formatting == "language" or parameters.formatting == "text" then
return data[parameters.formatting]
end
local result = data["text"]
if data["language"] ~= wiki.langcode then
result = mw.ustring.gsub(mw.ustring.gsub(i18n.monolingualtext, "%%language", data["language"]), "%%text", data["text"])
end
if mw.ustring.find((parameters.formatting or ''), '$', 1, true) then
-- output format defined with $text, $language
result = mw.ustring.gsub(parameters.formatting, '$text', result)
result = mw.ustring.gsub(result, '$language', data["language"])
end
return result
end
local function printError(key)
return '<span class="error">' .. i18n.errors[key] .. '</span>'
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.
local function orderedpairs(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
function findClaims(entityId, property)
if not property or not entityId then return end
if not mw.ustring.match(property, "^P%d+$") then
-- get property id for the given label
property = mw.wikibase.resolvePropertyId(property)
if not property then return end
end
local claims = mw.wikibase.getAllStatements(entityId, property)
if #claims == 0 then
claims = nil
end
return claims
end
local function getSnakValue(snak, parameters)
if snak.snaktype == 'value' then
-- call the respective snak parser
if snak.datatype == 'math' then
return printDatatypeMath(snak.datavalue.value)
elseif snak.datatype == "url" then
return printDatavalueUrl(snak.datavalue.value, parameters)
elseif snak.datavalue.type == "string" then
return printDatavalueString(snak.datavalue.value, parameters)
elseif snak.datavalue.type == "globecoordinate" then
return printDatavalueCoordinate(snak.datavalue.value, parameters.formatting)
elseif snak.datavalue.type == "quantity" then
return printDatavalueQuantity(snak.datavalue.value, parameters)
elseif snak.datavalue.type == "time" then
return printDatavalueTime(snak.datavalue.value, parameters)
elseif snak.datavalue.type == 'wikibase-entityid' then
return printDatavalueEntity(snak.datavalue.value, parameters)
elseif snak.datavalue.type == 'monolingualtext' then
return printDatavalueMonolingualText(snak.datavalue.value, parameters)
end
elseif snak.snaktype == 'novalue' then
return mw.message.new('Wikibase-snakview-snaktypeselector-novalue'):inLanguage(parameters.lang[1]):plain()
elseif snak.snaktype == 'somevalue' then
return mw.message.new('Wikibase-snakview-snaktypeselector-somevalue'):inLanguage(parameters.lang[1]):plain()
end
return mw.wikibase.renderSnak(snak)
end
local function getQualifierSnak(claim, qualifierId, parameters)
-- 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
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
if qualifier[1].datatype == "monolingualtext" then
-- iterate over monolingualtext qualifiers to get local language
for idx in pairs(qualifier) do
if qualifier[idx].datavalue.value and qualifier[idx].datavalue.value.language == parameters.lang[1] then
return qualifier[idx]
end
end
elseif parameters.list then
return qualifier
else
return qualifier[1]
end
end
end
return nil, printError("qualifier-not-found")
else
-- otherwise return the main snak
return claim.mainsnak
end
end
function getValueOfClaim(claim, qualifierId, parameters)
local error
local snak
snak, error = getQualifierSnak(claim, qualifierId, parameters)
if not snak then
return nil, nil, error
elseif snak[1] then -- a multi qualifier
local result = {}
local sortkey = {}
for idx in pairs(snak) do
result[#result + 1], sortkey[#sortkey + 1] = getSnakValue(snak[idx], parameters)
end
return mw.text.listToText(result, parameters.qseparator, parameters.qconjunction), sortkey[1]
else -- a property or a qualifier
return getSnakValue(snak, parameters)
end
end
local function getValueOfParentClaim(claim, qualifierId, parameters)
local qids = mw.text.split(qualifierId, '/', true)
local valueraw, parent_claims, value, sortkey
if qids[1] == parameters.property then
valueraw, _, _ = getValueOfClaim(claim, nil, {["formatting"]="raw", ["lang"]=parameters.lang})
else
valueraw, _, _ = getValueOfClaim(claim, qids[1], {["formatting"]="raw", ["lang"]=parameters.lang})
end
if string.sub(valueraw or '', 1, 1) == "Q" then -- protection for 'no value'
parent_claims = mw.wikibase.getBestStatements(valueraw, qids[2])
if parent_claims[1] ~= nil then
value, sortkey, _ = getValueOfClaim(parent_claims[1], nil, parameters)
-- raw parent value needed for while/black lists, lang for avoiding an error on types other than entity
valueraw, _, _ = getValueOfClaim(parent_claims[1], nil, {["formatting"]="raw", ["lang"]=parameters.lang})
end
end
return value, sortkey, valueraw
end
local function getReferences(claim)
local refaliases = {
citeWeb = "Q5637226",
author = "P50",
publisher = "P123",
importedFrom = "P143",
statedIn = "P248",
pages = "P304",
publicationDate = "P577",
startTime = "P580",
endTime = "P582",
chapter = "P792",
retrieved = "P813",
referenceURL = "P854",
archiveURL = "P1065",
title = "P1476",
quote = "P1683",
shortName = "P1813",
language = "P2439",
archiveDate = "P2960"
}
local result = ""
-- traverse through all references
for ref in pairs(claim.references or {}) do
local refparts
local refs = {}
-- traverse through all parts of the current reference
for snakkey, snakval in pairs(claim.references[ref].snaks or {}) do
if snakkey ~= refaliases.importedFrom then -- "imported from" is not a proper reference
for snakidx = 1, #snakval do
if snakidx > 1 then refparts = refparts .. ", " end
refparts = refparts or '' .. getSnakValue(snakval[snakidx], {lang={wiki.langcode}})
end
refs[snakkey] = refparts
refparts = nil
end
end
-- get title of general template for citing web references
local template = mw.wikibase.getSitelink(refaliases.citeWeb) or ""
template = mw.text.split(template, ":")[2] -- split off namespace from front
-- (1) if both "reference URL" and "title" are present, then use the general template for citing web references
if refs[refaliases.referenceURL] and (refs[refaliases.title] or refs[refaliases.statedIn]) and template then
local citeParams = {}
citeParams[i18n['cite']['url']] = refs[refaliases.referenceURL]
citeParams[i18n['cite']['title']] = refs[refaliases.title] or refs[refaliases.statedIn]:match("^%[%[.-|(.-)%]%]")
citeParams[i18n['cite']['website']] = refs[refaliases.statedIn]
citeParams[i18n['cite']['language']] = refs[refaliases.language]
citeParams[i18n['cite']['date']] = refs[refaliases.publicationDate]
citeParams[i18n['cite']['access-date']] = refs[refaliases.retrieved]
citeParams[i18n['cite']['archive-url']] = refs[refaliases.archiveURL]
citeParams[i18n['cite']['archive-date']] = refs[refaliases.archiveDate]
citeParams[i18n['cite']['publisher']] = refs[refaliases.publisher]
citeParams[i18n['cite']['quote']] = refs[refaliases.quote]
citeParams[i18n['cite']['pages']] = refs[refaliases.pages]
citeParams[i18n['cite']['author']] = refs[refaliases.author]
refparts = mw.getCurrentFrame():expandTemplate{title=template, args=citeParams}
else
-- raw ouput
for k, v in orderedpairs(refs or {}, claim.references[ref]["snaks-order"]) do
if k and v then
if refparts then refparts = refparts .. ", " else refparts = "" end
refparts = refparts .. tostring(mw.wikibase.getLabel(k)) .. ": "
refparts = refparts .. v
end
end
end
if refparts then result = result .. mw.getCurrentFrame():extensionTag("ref", refparts) end
end
return result
end
-- Move to Module:Wikibase?
function p.getSiteLink(frame)
require(wiki.module_title .. '/debug').track('getSiteLink')
local id = frame.args[1]; if id == '' then id = nil end
return mw.wikibase.getSitelink(id or mw.wikibase.getEntityIdForCurrentPage(), frame.args[2])
end
-- Set whitelist or blacklist values
local function setWhiteOrBlackList(type_list, num_qual, args)
local listed = false
local list = {}
for i = 0, num_qual do
if args[type_list .. i] and args[type_list .. i] ~= '' then
listed = true
list[tostring(i)] = {}
local values = mw.text.split(args[type_list .. i], "/", true)
for _, v in ipairs(values) do
list[tostring(i)][v] = true
list[tostring(i)][resolveEntityId(v)] = true
end
end
end
return list, listed
end
local function tableParameters(args, parameters, column, gender)
local column_params = mw.clone(parameters)
column_params.formatting = args["colformat"..column]; if column_params.formatting == "" then column_params.formatting = nil end
column_params.convert = args["convert"..column]
if args["case"..column] then
if args["case"..column] == "gender" then
column_params.gender = gender
else
column_params.case = args["case"..column]
end
end
return column_params
end
function p.claim(frame)
if mw.title.new(frame:getParent():getTitle()).isContentPage then
if not mw.title.new(frame:getTitle()).isSubpage then
-- invoked from a content page and not invoking a module subpage
return printError("not-from-content-page")
end
end
return p._main(frame.args, frame:getParent().args)
end
-- Entry point from other modules or debugging, with a list of arguments.
-- From other modules use: require("Module:Wikidata")._main({item="Q...", property="P...", ...})
-- On debug console use: =p._main({item="Q...", lang="xx", property="P...", ...})
function p._main(args, pargs)
--If a value is already set, use it
if args.value and args.value ~= '' then
return args.value
end
-- arguments
local id = args.item or (pargs and pargs.item)
if id == nil or id == "" then
id = mw.wikibase.getEntityIdForCurrentPage()
end
local property = string.upper(args["property"] or "")
local qualifierId = {}
qualifierId[1] = args["qualifier"] and string.upper(args["qualifier"]) or nil
local i = 2
while args["qualifier" .. i] do
qualifierId[i] = string.upper(args["qualifier" .. i])
i = i + 1
end
local formatting = args["formatting"]; if formatting == "" then formatting = nil end
local convert = args["convert"]; if convert == "" then convert = nil end
if convert and string.sub(convert, 1, 1) ~= "Q" then convert = nil end
local case = args.case
local list = args["list"] or true; if (list == "false" or list == "no") then list = false end
local sorting_col = args.tablesort
local sorting_up = (args.sorting or "") ~= "-1"
local separator = args.separator
local conjunction = args.conjunction or args.separator
local rowformat = args.rowformat
local references = args["references"]
local showerrors = args["showerrors"]
local default = args["default"]
local editicon = not (args.editicon == "false" or args.editicon == "no")
local parameters = {["property"] = property, ["formatting"] = formatting, ["convert"] = convert,
["list"] = list, ["case"] = case, ["editicon"] = editicon,
["separator"] = separator, ["conjunction"] = conjunction, ["qseparator"] = separator, ["qconjunction"] = conjunction}
-- defaults for table
local preformat, postformat = "", ""
local whitelisted, blacklisted = false, false
local whitelist, blacklist = {}, {}
if parameters.formatting == "table" then
parameters.separator = parameters.separator or "<br />"
parameters.conjunction = parameters.conjunction or "<br />"
parameters.qseparator = ", "
parameters.qconjunction = ", "
if not rowformat then
rowformat = "$0 ($1"
i = 2
while qualifierId[i] do
rowformat = rowformat .. ", $" .. i
i = i + 1
end
rowformat = rowformat .. ")"
elseif mw.ustring.find(rowformat, "^[*#]") then
parameters.separator = "</li><li>"
parameters.conjunction = "</li><li>"
if mw.ustring.match(rowformat, "^[*#]") == "*" then
preformat = "<ul><li>"
postformat = "</li></ul>"
else
preformat = "<ol><li>"
postformat = "</li></ol>"
end
rowformat = mw.ustring.gsub(rowformat, "^[*#] ?", "")
end
-- set whitelist and blacklist values
whitelist, whitelisted = setWhiteOrBlackList("whitelist", #qualifierId, args)
blacklist, blacklisted = setWhiteOrBlackList("blacklist", #qualifierId, args)
end
if default then showerrors = nil end
-- fetch property
local claims = findClaims(id, property)
if not claims or not claims[1] then
if showerrors then return printError("property-not-found") else return default end
end
-- find language and defaults
parameters.lang = findLang(args.lang)
-- set feminine case if gender is requested
local itemgender = args["itemgender"]
local idgender
if itemgender then
if string.match(itemgender, "^P%d+$") then
local snak = mw.wikibase.getBestStatements(id, itemgender)[1]
if snak and snak.mainsnak and snak.mainsnak.datavalue and snak.mainsnak.datavalue.value then
idgender = snak.mainsnak.datavalue.value.id
end
elseif string.match(itemgender, "^Q%d+$") then
idgender = itemgender
end
end
local gender_case
local gender_requested = false
if parameters.case == "gender" or idgender then
gender_requested = true
elseif parameters.formatting == "table" then
for i=0, #qualifierId do
if args["case" .. i] and args["case" .. i] == "gender" then
gender_requested = true
break
end
end
end
if gender_requested then
if feminineGender(idgender or id) then
gender_case = "feminineform"
if parameters.case == "gender" or idgender then
parameters.gender = gender_case
end
end
end
-- get initial sort indices
local sortindices = {}
for idx in pairs(claims) do
sortindices[#sortindices + 1] = idx
end
-- sort by claim rank
local 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
table.sort(sortindices, comparator)
local result
local error
if parameters.list or parameters.formatting == "table" then
-- convert LF to line feed, <br /> may not work on some cases
parameters.separator = parameters.separator == "LF" and "\010" or parameters.separator
parameters.conjunction = parameters.conjunction == "LF" and "\010" or parameters.conjunction
-- i18n separators
parameters.separator = parameters.separator or mw.message.new('Comma-separator'):inLanguage(parameters.lang[1]):plain()
parameters.conjunction = parameters.conjunction or (mw.message.new('And'):inLanguage(parameters.lang[1]):plain() .. mw.message.new('Word-separator'):inLanguage(parameters.lang[1]):plain())
-- iterate over all elements and return their value (if existing)
local value, valueq
local sortkey, sortkeyq
local values = {}
local sortkeys = {}
local refs = {}
local firstrank = parameters.list == "firstrank" and claims[sortindices[1]].rank or ''
local rowlist = {} -- rows to list with whitelist or blacklist
for idx in pairs(claims) do
local claim = claims[sortindices[idx]]
local reference={}
if not whitelisted then rowlist[idx] = true end
if firstrank ~= '' and firstrank ~= claim.rank then
break
end
if parameters.formatting == "table" then
local params = tableParameters(args, parameters, "0", gender_case)
value, sortkey, error = getValueOfClaim(claim, nil, params)
if value then
values[#values + 1] = {}
sortkeys[#sortkeys + 1] = {}
refs[#refs + 1] = {}
if whitelist["0"] or blacklist["0"] then
local valueraw, _, _ = getValueOfClaim(claim, nil, {["formatting"]="raw", ["lang"]=params.lang})
if whitelist["0"] and whitelist["0"][valueraw or ""] then
rowlist[#values] = true
elseif blacklist["0"] and blacklist["0"][valueraw or ""] then
rowlist[#values] = false
end
end
for i, qual in ipairs(qualifierId) do
local j = tostring(i)
params = tableParameters(args, parameters, j, gender_case)
local valueq, sortkeyq, valueraw
if qual == property then -- hack for getting the property with another formatting, i.e. colformat1=raw
valueq, sortkeyq, _ = getValueOfClaim(claim, nil, params)
else
for q in mw.text.gsplit(qual, '%s*OR%s*') do
if string.find(q, ".+/.+") then
valueq, sortkeyq, valueraw = getValueOfParentClaim(claim, q, params)
elseif string.find(q, "^/.+") then
local claim2 = findClaims(id, string.sub(q, 2))
if claim2 then
valueq, sortkeyq, _ = getValueOfClaim(claim2[1], nil, params)
end
else
valueq, sortkeyq, _ = getValueOfClaim(claim, q, params)
end
if valueq then break end
end
end
values[#values]["col" .. j] = valueq
sortkeys[#sortkeys]["col" .. j] = sortkeyq or valueq
if whitelist[j] or blacklist[j] then
valueq = valueraw or getValueOfClaim(claim, qual, {["formatting"]="raw", ["lang"]=params.lang})
if whitelist[j] and whitelist[j][valueq or ""] then
rowlist[#values] = true
elseif blacklist[j] and blacklist[j][valueq or ""] then
rowlist[#values] = false
end
end
end
end
else
value, sortkey, error = getValueOfClaim(claim, qualifierId[1], parameters)
values[#values + 1] = {}
sortkeys[#sortkeys + 1] = {}
refs[#refs + 1] = {}
end
if not value and showerrors then value = error end
if value then
if references and claim.references then reference=claim.references end
refs[#refs]["col0"] =reference
values[#values]["col0"] = value
sortkeys[#sortkeys]["col0"] = sortkey or value
end
end
-- sort and format results
sortindices = {}
for idx in pairs(values) do
sortindices[#sortindices + 1] = idx
end
if sorting_col then
local sorting_table = mw.text.split(sorting_col, '/', true)
local comparator = function(a, b)
local valuea, valueb
local i = 1
while valuea == valueb and i <= #sorting_table do
valuea = sortkeys[a]["col" .. sorting_table[i]] or ''
valueb = sortkeys[b]["col" .. sorting_table[i]] or ''
i = i + 1
end
if sorting_up then
return valueb > valuea
end
return valueb < valuea
end
table.sort(sortindices, comparator)
end
result = {}
for idx in pairs(values) do
local valuerow = values[sortindices[idx]]
local reference = getReferences({["references"] = refs[sortindices[idx]]["col0"]})
value = valuerow["col0"]
if parameters.formatting == "table" then
if not rowlist[sortindices[idx]] then
value = nil
else
value = mw.ustring.gsub(rowformat .. "$", "$0", value) -- fake end character added for easy gsub
value = mw.ustring.gsub(value, "$R0", reference) -- add reference
local rowformatting = rowformat .. "$"
for i, _ in ipairs(qualifierId) do
local valueq = valuerow["col" .. i]
if args["rowsubformat" .. i] and valueq then
-- add fake end character $
-- gsub $i not followed by a number so $1 doesn't match $10, $11...
-- remove fake end character
valueq = mw.ustring.gsub(args["rowsubformat" .. i] .. "$", "$" .. i .. "(%D)", valueq .. "%1")
valueq = string.sub(valueq, 1, -2)
rowformatting = mw.ustring.gsub(rowformatting, "$" .. i .. "(%D)", args["rowsubformat" .. i] .. "%1")
end
valueq = valueq and urlEscapes(valueq) or ''
value = mw.ustring.gsub(value, "$" .. i .. "(%D)", valueq .. "%1")
end
value = string.sub(value, 1, -2) -- remove fake end character
value = expandBraces(value, rowformatting)
end
else
value = expandBraces(value, parameters.formatting)
end
result[#result + 1] = value
if not parameters.list then
break
end
end
result = preformat .. mw.text.listToText(result, parameters.separator, parameters.conjunction) .. postformat
else
-- return first element
local claim = claims[sortindices[1]]
result, _, error = getValueOfClaim(claim, qualifierId[1], parameters)
if result and references then result = result .. getReferences(claim) end
end
if result then return result else
if showerrors then return error else return default end
end
end
-- This is used to get the TA98 (Terminologia Anatomica first edition 1998) values like 'A01.1.00.005' (property P1323)
-- which are then linked to http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/01.1.00.005%20Entity%20TA98%20EN.htm
-- uses the newer mw.wikibase calls instead of directly using the snaks
-- formatPropertyValues returns a table with the P1323 values concatenated with ", " so we have to split them out into a table in order to construct the return string
p.getTAValue = function(frame)
local ent = mw.wikibase.getEntity()
local props = ent:formatPropertyValues('P1323')
local out = {}
local t = {}
for k, v in pairs(props) do
if k == 'value' then
t = mw.text.split( v, ", ")
for k2, v2 in pairs(t) do
out[#out + 1] = "[http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/" .. string.sub(v2, 2) .. "%20Entity%20TA98%20EN.htm " .. v2 .. "]"
end
end
end
local ret = table.concat(out, "<br> ")
if #ret == 0 then
ret = "Invalid TA"
end
return ret
end
-- debugging functions, see module ../debug.
function p.ViewSomething(frame)
return require(wiki.module_title .. "/debug").ViewSomething(frame)
end
function p.Dump(frame)
return require(wiki.module_title .. "/debug").Dump(frame)
end
function p.getEntityFromTree(frame)
return require(wiki.module_title .. "/debug").getEntityFromTree(frame)
end
-- local functions for getParentValues
local function uc_first(word)
return mw.ustring.upper(mw.ustring.sub(word, 1, 1)) .. mw.ustring.sub(word, 2)
end
local function getPropertyValue(id, property, parameter, langs, editicon, case)
local snaks = mw.wikibase.getBestStatements(id, property)
local mysnak
if snaks and snaks[1] and snaks[1].mainsnak then
mysnak = snaks[1].mainsnak
else
return
end
local entityId
local result = '-' -- default for 'no value'
if mysnak.datavalue then
entityId = "Q" .. tostring(mysnak.datavalue.value['numeric-id'])
result, _ = getSnakValue(mysnak, {formatting=parameter, lang=langs, editicon=editicon, case=case})
end
return entityId, result
end
local function contains(tab, val)
for index, value in ipairs(tab) do
if value == val then
return true
end
end
return false
end
local function getParentObjects(id,
formatting,
languages,
propertySupString,
propertyLabel,
propertyLink,
labelShow,
editicon,
upto,
upto_linkId,
last_only,
grammatical_case,
include_self)
if (upto_linkId == nil) then upto_linkId = "" end
local upto_link_ids = mw.text.split(upto_linkId, '[/%s]+')
local propertySups = mw.text.split(propertySupString, '[/%s]+')
local lastlabel = uc_first(upto or '')
local maxloop = tonumber(upto) or (lastlabel == '' and 10 or 50)
local labelFilter = {}
if labelShow then
for i, v in ipairs(mw.text.split(labelShow, "/")) do
labelFilter[uc_first(v)] = true
end
end
local label_self
_, label_self = getPropertyValue(id, propertyLabel, "label", languages)
local result = {}
local label, link, linktext
for iter = 1, maxloop do
local label, link
for _, propertySup in pairs(propertySups) do
_id, _link = getPropertyValue(id, propertySup, formatting, languages, editicon, grammatical_case)
if _id and _link then id = _id link = _link break end
end
if not id or not link then break end
if propertyLink then
_, linktext = getPropertyValue(id, propertyLink, "label", languages)
if linktext then
link = mw.ustring.gsub(link, "%[%[(.*)%|.+%]%]", "[[%1|" .. linktext .. "]]")
end
end
_, label = getPropertyValue(id, propertyLabel, "label", languages)
if label then
label = case(label, "infoboxlabel", languages[1])
end
if labelShow == nil or labelFilter[label] then
result[#result + 1] = {label, link}
if label then
labelFilter[label] = nil -- only first label found
end
end
if not tonumber(upto) and label == lastlabel then
break
end
if contains(upto_link_ids, id) then
break
end
end
if last_only then
result = {result[#result]}
end
if include_self then table.insert(result, 1, {label_self, mw.title.getCurrentTitle().text}) end
return result
end
local function parentObjectsToString(result,
rowformat,
separator,
cascade,
sorting)
local ret = {}
local first = 1
local last = #result
local iter = 1
if sorting == "-1" then first = #result; last = 1; iter = -1 end
for i = first, last, iter do
local rowtext = mw.ustring.gsub(rowformat, "$[01]", {["$0"] = result[i][1], ["$1"] = result[i][2]})
ret[#ret +1] = expandBraces(rowtext, rowformat)
end
if cascade then
local prefix = ""
for i = 1, #ret do
ret[i] = prefix .. "• " .. ret[i]
prefix = prefix .. " "
end
end
return mw.text.listToText(ret, separator, separator)
end
-- Returns pairs of instance label and property value fetching a recursive tree
function p.getParentValues(frame)
local args = frame.args
local id = args.item or frame:getParent().args.item
if id == "" or id == nil then
id = mw.wikibase.getEntityIdForCurrentPage()
if id == nil then return end
end
local languages = findLang(args.lang)
local propertySup = args["property"]; if (propertySup == nil or propertySup == "") then propertySup = "P131" end --administrative entity
local propertyLabel = args["label"]; if (propertyLabel == nil or propertyLabel == "") then propertyLabel = "P31" end --instance
local propertyLink = args["valuetext"]; if propertyLink == "" then propertyLink = nil end --internallink
local upto = args["upto"]; if upto == "" then upto = nil end
local last_only = (args.last_only == "true" or args.last_only == "yes")
local labelShow = args["labelshow"]; if labelShow == "" then labelShow = nil end
local editicon = not (args.editicon == "false" or args.editicon == "no")
local include_self = (args.include_self == "true" or args.include_self == "yes")
local case = args["case"]; if case == "" then case = nil end
if args.uptolabelid and args.uptolabelid ~= "" then
upto = mw.wikibase.getLabel(args.uptolabelid)
end
if args.showlabelid and args.showlabelid ~= "" then
local showLabelList = {} for substring in mw.text.gsplit(args.showlabelid, '[/%s]+') do table.insert(showLabelList, mw.wikibase.getLabel(substring)) end
if #showLabelList > 0 then
labelShow = table.concat(showLabelList,"/")
end
end
local result = getParentObjects(id,
args.formatting,
languages,
propertySup,
propertyLabel,
propertyLink,
labelShow,
editicon,
upto,
args.uptolinkid,
last_only,
case,
include_self)
local rowformat = args["rowformat"]; if (rowformat == nil or rowformat == "") then rowformat = "$0 = $1" end
local separator = args["separator"]; if (separator == nil or separator == "") then separator = "<br />" end
local sorting = args["sorting"]; if sorting == "" then sorting = nil end
local cascade = (args.cascade == "true" or args.cascade == "yes")
return parentObjectsToString(result,
rowformat,
separator,
cascade,
sorting)
end
function p.linkWithParentLabel(frame)
local args = {}
for k, v in pairs(frame.args) do -- metatable
args[k] = v
end
-- get internal link of property/qualifier
args.list = "true"
args.formatting = "internallink"
args.separator = "/·/"
local pargs = frame:getParent().args
local link_list = p._main(args, pargs)
if link_list == nil or link_list == "" then
return
end
local link_table = mw.text.split(link_list, "/·/", true)
-- get id value of property/qualifier
args.formatting = "raw"
local items_list = p._main(args, pargs)
local items_table = mw.text.split(items_list, "/·/", true)
-- get label of parent property
args.property = args.parent
args.qualifier = nil
local parent_claims = findClaims(items_table[1], args.parent)
if parent_claims and parent_claims[1].mainsnak.datatype == 'monolingualtext' then
args.formatting = nil
args.list = 'lang'
else
args.formatting = "label"
args.list = "false"
end
for i, v in ipairs(items_table) do
args.item = v
local link_label = p._main(args, pargs)
if link_label and link_label ~= '' then
link_table[i] = mw.ustring.gsub(link_table[i] or '', "%[%[(.*)%|.+%]%]", "[[%1|" .. link_label .. "]]")
end
end
return mw.text.listToText(link_table)
end
function p.years_old(frame)
require(wiki.module_title .. '/debug').track('years_old')
return p.yearsOld(frame)
end
function p.yearsOld(frame)
local args = frame.args
local id = args.item
if id == '' or id == nil then
id = mw.wikibase.getEntityIdForCurrentPage()
end
local lang = mw.language.new('en')
local function getBestValue(id, prop)
local snak = mw.wikibase.getBestStatements(id, prop)[1]
if snak and snak.mainsnak and snak.mainsnak.datavalue and snak.mainsnak.datavalue.value then
return snak.mainsnak.datavalue.value
end
end
local birth = getBestValue(id, 'P569')
if type(birth) ~= 'table' or birth.time == nil or birth.precision == nil or birth.precision < 8 then
return
end
local death = getBestValue(id, 'P570')
if type(death) ~= 'table' or death.time == nil or death.precision == nil then
death = {['time'] = lang:formatDate('c'), ['precision'] = 11} -- current date
elseif death.precision < 8 then
return
end
local dates = {}
dates[1] = {['min'] = {}, ['max'] = {}, ['precision'] = birth.precision}
dates[1].min.year = tonumber(mw.ustring.match(birth.time, "^[+-]?%d+"))
dates[1].min.month = tonumber(mw.ustring.match(birth.time, "\-(%d%d)\-"))
dates[1].min.day = tonumber(mw.ustring.match(birth.time, "\-(%d%d)T"))
dates[1].max = mw.clone(dates[1].min)
dates[2] = {['min'] = {}, ['max'] = {}, ['precision'] = death.precision}
dates[2].min.year = tonumber(mw.ustring.match(death.time, "^[+-]?%d+"))
dates[2].min.month = tonumber(mw.ustring.match(death.time, "\-(%d%d)\-"))
dates[2].min.day = tonumber(mw.ustring.match(death.time, "\-(%d%d)T"))
dates[2].max = mw.clone(dates[2].min)
for i, d in ipairs(dates) do
if d.precision == 10 then -- month
d.min.day = 1
local timestamp = string.format("%04d", tostring(math.abs(d.max.year)))
.. string.format("%02d", tostring(d.max.month))
.. "01"
d.max.day = tonumber(lang:formatDate("j", timestamp .. " + 1 month - 1 day"))
elseif d.precision < 10 then -- year or decade
d.min.day = 1
d.min.month = 1
d.max.day = 31
d.max.month = 12
if d.precision == 8 then -- decade
d.max.year = d.max.year + 9
end
end
end
local function age(d1, d2)
local years = d2.year - d1.year
if d2.month < d1.month or (d2.month == d1.month and d2.day < d1.day) then
years = years - 1
end
if d2.year > 0 and d1.year < 0 then
years = years - 1 -- no year 0
end
return years
end
local old_min = age(dates[1].max, dates[2].min)
local old_max = age(dates[1].min, dates[2].max)
local old = old_min == old_max and old_min or old_min .. "/" .. old_max
if args.formatting then
old = expandBraces(mw.ustring.gsub(args.formatting, '$1', old), args.formatting)
end
return old
end
-- Gets a label in a given language (content language by default) or its fallbacks, optionnally linked.
function p.getLabel(frame)
local args = frame.args or frame -- via invoke or require
local id = mw.text.trim(args[1] or "")
if not id or id == "" then return end
local editicon = not (args.editicon == "false" or args.editicon == "no")
local pencil = ''
local label, lang
if args.label then
label = args.label
else
local languages = findLang(args.lang)
-- exceptions or labels fixed
local exist, labels = pcall(require, wiki.module_title .. "/labels" .. (languages[1] == wiki.langcode and '' or '/' .. languages[1]))
if exist and next(labels.infoboxLabelsFromId) ~= nil then
label = labels.infoboxLabelsFromId[id]
end
if label == nil then
id = resolveEntityId(id)
label, lang = getLabelByLangs(id, languages)
if label then
if args.itemgender and feminineGender(args.itemgender) then
label = feminineForm(id, lang) or label
end
label = mw.language.new(lang):ucfirst(mw.text.nowiki(label)) -- sanitize
end
pencil = addEditIcon('Special:EntityPage/' .. id, lang, languages[1], editicon)
end
end
local linked = args.linked
if linked and linked ~= "" and linked ~= "no" then
local article = mw.wikibase.getSitelink(id) or ("d:" .. id)
return "[[" .. article .. "|" .. (label or id) .. "]]" .. pencil
else
return (label or id) .. pencil
end
end
return p