KKeine Bearbeitungszusammenfassung |
K (1 Version importiert: Wikipedia) |
||
(42 dazwischenliegende Versionen von 4 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
-- module local variables |
-- module local variables |
||
− | local wiki = |
+ | local wiki = |
{ |
{ |
||
langcode = mw.language.getContentLanguage().code |
langcode = mw.language.getContentLanguage().code |
||
Zeile 6: | Zeile 6: | ||
-- internationalisation |
-- internationalisation |
||
− | local i18n = |
+ | local i18n = |
+ | { |
||
− | ["errors"] = { |
||
+ | ["errors"] = |
||
− | ["property-not-found"] = "Eigenschaft nicht gefunden.", |
||
+ | { |
||
− | ["entity-not-found"] = "Entity nicht gefunden.", |
||
− | + | ["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-datavalue-type"] = "Unbekannter Datentyp.", |
||
− | + | ["unknown-claim-type"] = "Unbekannter Aussagentyp.", |
|
+ | ["unknown-entity-type"] = "Unbekannter Entity-Typ.", |
||
− | ["qualifier-not-found"] = "Qualifikator nicht gefunden." |
||
+ | ["qualifier-not-found"] = "Qualifikator nicht gefunden.", |
||
− | }, |
||
+ | ["site-not-found"] = "Wikimedia-Projekt nicht gefunden.", |
||
− | ["somevalue"] = "''unbekannter Wert''", |
||
− | + | ["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"] = |
["datetime"] = |
||
{ |
{ |
||
Zeile 31: | Zeile 39: | ||
[8] = "$1er", -- precision: decade |
[8] = "$1er", -- precision: decade |
||
-- the following use the format of #time parser function |
-- the following use the format of #time parser function |
||
− | [9] = "Y", -- precision: year, |
+ | [9] = "Y", -- precision: year, |
[10] = "F Y", -- precision: month |
[10] = "F Y", -- precision: month |
||
[11] = "j. F Y", -- precision: day |
[11] = "j. F Y", -- precision: day |
||
Zeile 41: | Zeile 49: | ||
["bc"] = '$1 "v.Chr."', -- how print negative years |
["bc"] = '$1 "v.Chr."', -- how print negative years |
||
["ad"] = "$1" -- how print positive years |
["ad"] = "$1" -- how print positive years |
||
− | } |
+ | }, |
+ | ["monolingualtext"] = '<span lang="%language">%text</span>', |
||
+ | ["FETCH_WIKIDATA"] = "ABFRAGE_WIKIDATA" |
||
} |
} |
||
+ | --important properties |
||
− | local p = { } |
||
+ | local propertyId = |
||
+ | { |
||
+ | ["starttime"] = "P580", |
||
+ | ["endtime"] = "P582" |
||
+ | } |
||
+ | |||
+ | local 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 |
||
+ | } |
||
local function printError(code) |
local function printError(code) |
||
− | return '<span class="error">' .. i18n.errors[code] .. '</span>' |
+ | return '<span class="error">' .. (i18n.errors[code] or code) .. '</span>' |
end |
end |
||
Zeile 55: | Zeile 79: | ||
local function orderedpairs(array, order) |
local function orderedpairs(array, order) |
||
if not order then return pairs(array) end |
if not order then return pairs(array) end |
||
+ | |||
− | |||
-- return iterator function |
-- return iterator function |
||
− | + | local i = 0 |
|
− | + | return function() |
|
− | + | i = i + 1 |
|
− | + | if order[i] then |
|
− | + | return order[i], array[order[i]] |
|
− | + | end |
|
− | + | end |
|
end |
end |
||
+ | -- Function to check whether a certain item is a parent of a given item. |
||
− | function p.descriptionIn(frame) |
||
+ | -- If pExitItem is reached without finding the searched parent item, the search stops. |
||
− | local langcode = frame.args[1] |
||
+ | -- A parent is connected via P31 or P279. |
||
− | -- return description of a Wikidata entity in the given language or the default language of this Wikipedia site |
||
+ | -- Attention: very intensive function, use carefully! |
||
− | return mw.wikibase.getEntityObject().descriptions[langcode or wiki.langcode].value |
||
+ | local function isParent(pItem, pParent, pExitItem, pMaxDepth, pDepth) |
||
− | end |
||
+ | if not pDepth then pDepth = 0 end |
||
+ | if type(pItem) == "number" then pItem = "Q" .. pItem end |
||
− | function p.labelIn(frame) |
||
+ | |||
− | local langcode = frame.args[1] |
||
+ | local entity = mw.wikibase.getEntity(pItem) |
||
− | -- return label of a Wikidata entity in the given language or the default language of this Wikipedia site |
||
+ | if not entity then return false end |
||
− | return mw.wikibase.getEntityObject().labels[langcode or wiki.langcode].value |
||
+ | |||
+ | local claims31 |
||
+ | local claims279 |
||
+ | if entity.claims then |
||
+ | claims31 = entity.claims[mw.wikibase.resolvePropertyId('P31')] |
||
+ | claims279 = entity.claims[mw.wikibase.resolvePropertyId('P279')] |
||
+ | else |
||
+ | return false |
||
+ | end |
||
+ | if not claims31 and not claims279 then return false end |
||
+ | |||
+ | local parentIds = {} |
||
+ | if claims31 and #claims31 > 0 then |
||
+ | for i, v in ipairs(claims31) do parentIds[#parentIds+1] = getSnakValue(v.mainsnak, "numeric-id") end |
||
+ | end |
||
+ | if claims279 and #claims279 > 0 then |
||
+ | for i, v in ipairs(claims279) do parentIds[#parentIds+1] = getSnakValue(v.mainsnak, "numeric-id") end |
||
+ | end |
||
+ | |||
+ | -- check if searched parent or exit item is reached or do recursive call |
||
+ | if not parentIds[1] or #parentIds == 0 then return false end |
||
+ | local itemString = "" |
||
+ | local result = nil |
||
+ | for i, v in ipairs(parentIds) do |
||
+ | if not v then return false end |
||
+ | itemString = "Q" .. v |
||
+ | |||
+ | if itemString == pParent then |
||
+ | -- successful! |
||
+ | return true |
||
+ | elseif itemString == pExitItem or itemString == "Q35120" then |
||
+ | -- exit if either "exit item" or node item (Q35120) is reached |
||
+ | return false |
||
+ | else |
||
+ | if pDepth+1 < pMaxDepth then |
||
+ | result = isParent(itemString, pParent, pExitItem, pMaxDepth, pDepth+1) |
||
+ | else return false end |
||
+ | |||
+ | if result == true then return result end |
||
+ | end |
||
+ | end |
||
+ | do return false end |
||
end |
end |
||
Zeile 90: | Zeile 157: | ||
local function printDatavalueQuantity(data, parameter) |
local function printDatavalueQuantity(data, parameter) |
||
-- data fields: amount [number], unit [string], upperBound [number], lowerBound [number] |
-- data fields: amount [number], unit [string], upperBound [number], lowerBound [number] |
||
− | if parameter then |
+ | if not parameter or parameter == "amount" then |
− | return data[paramater] |
||
− | else |
||
return tonumber(data.amount) |
return tonumber(data.amount) |
||
+ | elseif parameter == "unit" then |
||
+ | return mw.ustring.match(data.unit, "Q%d+") |
||
+ | else |
||
+ | return data[parameter] |
||
end |
end |
||
end |
end |
||
+ | local function normalizeDate(date) |
||
− | -- 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 |
||
− | function normalizeDate(date) |
||
date = mw.text.trim(date, "+") |
date = mw.text.trim(date, "+") |
||
-- extract year |
-- extract year |
||
Zeile 107: | Zeile 175: | ||
end |
end |
||
+ | -- 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 |
||
− | function formatDate(date, precision, timezone) |
||
+ | function formatDate(date, precision, timezone, formatstr) |
||
precision = precision or 11 |
precision = precision or 11 |
||
− | date, year = |
+ | date, year = normalizeDate(date) |
+ | date = string.gsub(date, "-00%f[%D]", "-01") |
||
if year == 0 and precision <= 9 then return "" end |
if year == 0 and precision <= 9 then return "" end |
||
+ | |||
− | |||
− | + | -- precision is 10000 years or more |
|
if precision <= 5 then |
if precision <= 5 then |
||
local factor = 10 ^ ((5 - precision) + 4) |
local factor = 10 ^ ((5 - precision) + 4) |
||
Zeile 121: | Zeile 191: | ||
else |
else |
||
relative = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative) |
relative = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative) |
||
− | end |
+ | end |
return relative |
return relative |
||
end |
end |
||
+ | |||
− | |||
− | + | -- precision is decades, centuries and millenia |
|
local era |
local era |
||
if precision == 6 then era = mw.ustring.gsub(i18n.datetime[6], "$1", tostring(math.floor((math.abs(year) - 1) / 1000) + 1)) end |
if precision == 6 then era = mw.ustring.gsub(i18n.datetime[6], "$1", tostring(math.floor((math.abs(year) - 1) / 1000) + 1)) end |
||
Zeile 135: | Zeile 205: | ||
return era |
return era |
||
end |
end |
||
+ | |||
− | |||
-- precision is years or less |
-- precision is years or less |
||
if precision >= 9 then |
if precision >= 9 then |
||
Zeile 147: | Zeile 217: | ||
end |
end |
||
]]-- |
]]-- |
||
+ | if formatstr then |
||
− | |||
− | + | for i=(precision+1), 14 do |
|
+ | for _, ch in pairs(formatchar[i]) do |
||
+ | if formatstr:find(ch) then |
||
+ | formatstr = i18n.datetime[precision] |
||
+ | end |
||
+ | end |
||
+ | end |
||
+ | else |
||
+ | formatstr = i18n.datetime[precision] |
||
+ | end |
||
if year == 0 then formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], "") |
if year == 0 then formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], "") |
||
elseif year < 0 then |
elseif year < 0 then |
||
Zeile 166: | Zeile 245: | ||
-- 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] |
-- 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] |
||
if parameter then |
if parameter then |
||
+ | para, formatstr = parameter:match("([^:]+):([^:]+)") |
||
− | if parameter == "calendarmodel" then data.calendarmodel = mw.ustring.match(data.calendarmodel, "Q%d+") -- extract entity id from the calendar model URI |
||
− | + | if parameter == "calendarmodel" then |
|
+ | data.calendarmodel = string.match(data.calendarmodel, "Q%d+") -- extract entity id from the calendar model URI |
||
+ | elseif para and para == "time" then |
||
+ | return formatDate(data.time, data.precision, data.timezone,formatstr) |
||
+ | elseif parameter == "time" then |
||
+ | data.time = normalizeDate(data.time) |
||
+ | end |
||
return data[parameter] |
return data[parameter] |
||
else |
else |
||
Zeile 176: | Zeile 261: | ||
local function printDatavalueEntity(data, parameter) |
local function printDatavalueEntity(data, parameter) |
||
-- data fields: entity-type [string], numeric-id [int, Wikidata id] |
-- data fields: entity-type [string], numeric-id [int, Wikidata id] |
||
+ | local id |
||
+ | |||
+ | if data["entity-type"] == "item" then id = "Q" .. data["numeric-id"] |
||
+ | elseif data["entity-type"] == "property" then id = "P" .. data["numeric-id"] |
||
+ | else return printError("unknown-entity-type") |
||
+ | end |
||
+ | |||
if parameter then |
if parameter then |
||
− | + | if parameter == "link" then |
|
+ | local linkTarget = mw.wikibase.sitelink(id) |
||
+ | local linkName = mw.wikibase.label(id) |
||
+ | if linkTarget then |
||
+ | local link = linkTarget |
||
+ | -- if there is a local Wikipedia article linking to it, use the label or the article title |
||
+ | 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 |
else |
||
− | + | return mw.wikibase.label(id) or id |
|
end |
end |
||
end |
end |
||
− | function |
+ | local function printDatavalueMonolingualText(data, parameter) |
+ | -- data fields: language [string], text [string] |
||
− | if not property or not entity or not entity.claims then return end |
||
+ | if parameter then |
||
− | |||
+ | return data[parameter] |
||
− | if mw.ustring.match(property, "^P%d+$") then |
||
− | -- if the property is given by an id (P..) access the claim list by this id |
||
− | return entity.claims[property] |
||
else |
else |
||
+ | local result = mw.ustring.gsub(mw.ustring.gsub(i18n.monolingualtext, "%%language", data["language"]), "%%text", data["text"]) |
||
− | -- otherwise, iterate over all properties, fetch their labels and compare this to the given property name |
||
+ | return result |
||
− | for k, v in pairs(entity.claims) do |
||
− | if mw.wikibase.label(k) == property then return v end |
||
− | end |
||
− | return |
||
end |
end |
||
end |
end |
||
Zeile 200: | Zeile 301: | ||
function getSnakValue(snak, parameter) |
function getSnakValue(snak, parameter) |
||
-- snaks have three types: "novalue" for null/nil, "somevalue" for not null/not nil, or "value" for actual data |
-- snaks have three types: "novalue" for null/nil, "somevalue" for not null/not nil, or "value" for actual data |
||
− | if snak.snaktype == " |
+ | if snak.snaktype == "value" then |
+ | -- call the respective snak parser |
||
− | elseif snak.snaktype == "somevalue" then return i18n["somevalue"] |
||
− | + | if snak.datavalue.type == "string" then return snak.datavalue.value |
|
+ | elseif snak.datavalue.type == "globecoordinate" then return printDatavalueCoordinate(snak.datavalue.value, parameter) |
||
− | end |
||
+ | elseif snak.datavalue.type == "quantity" then return printDatavalueQuantity(snak.datavalue.value, parameter) |
||
− | |||
+ | elseif snak.datavalue.type == "time" then return printDatavalueTime(snak.datavalue.value, parameter) |
||
− | -- call the respective snak parser |
||
− | + | elseif snak.datavalue.type == "wikibase-entityid" then return printDatavalueEntity(snak.datavalue.value, parameter) |
|
− | + | elseif snak.datavalue.type == "monolingualtext" then return printDatavalueMonolingualText(snak.datavalue.value, parameter) |
|
+ | end |
||
− | elseif snak.datavalue.type == "quantity" then return printDatavalueQuantity(snak.datavalue.value, parameter) |
||
− | elseif snak.datavalue.type == "time" then return printDatavalueTime(snak.datavalue.value, parameter) |
||
− | elseif snak.datavalue.type == "wikibase-entityid" then return printDatavalueEntity(snak.datavalue.value, parameter) |
||
− | else return nil, printError("unknown-datavalue-type") |
||
end |
end |
||
+ | return mw.wikibase.renderSnak(snak) |
||
end |
end |
||
Zeile 221: | Zeile 320: | ||
if qualifierId then |
if qualifierId then |
||
-- search the attribute snak with the given qualifier as key |
-- search the attribute snak with the given qualifier as key |
||
− | if claim.qualifiers then |
+ | if claim and claim.qualifiers then |
local qualifier = claim.qualifiers[qualifierId] |
local qualifier = claim.qualifiers[qualifierId] |
||
if qualifier then return qualifier[1] end |
if qualifier then return qualifier[1] end |
||
Zeile 229: | Zeile 328: | ||
-- otherwise return the main snak |
-- otherwise return the main snak |
||
return claim.mainsnak |
return claim.mainsnak |
||
+ | end |
||
+ | end |
||
+ | |||
+ | local function datavalueTimeToDateObject(data) |
||
+ | local sign, year, month, day, hour, minute, second = string.match(data.time, "(.)(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)Z") |
||
+ | 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$") |
||
+ | } |
||
+ | if sign == "-" then result.year = -result.year end |
||
+ | return result |
||
+ | end |
||
+ | |||
+ | function julianDay(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 |
||
+ | |||
+ | function getQualifierSortValue(claim, qualifierId) |
||
+ | local snak = getQualifierSnak(claim, qualifierId) |
||
+ | if snak and snak.snaktype == "value" then |
||
+ | if snak.datavalue.type == "time" then |
||
+ | return julianDay(datavalueTimeToDateObject(snak.datavalue.value)) |
||
+ | else |
||
+ | return getSnakValue(snak) |
||
+ | end |
||
end |
end |
||
end |
end |
||
Zeile 243: | Zeile 393: | ||
end |
end |
||
+ | |||
− | function getReferences(claim) |
||
+ | function formatReference(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, 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 |
||
+ | refFormatted, f = Zitation.format() |
||
+ | return refFormatted, f |
||
+ | end |
||
+ | |||
+ | function getReferences(frame, claim) |
||
local result = "" |
local result = "" |
||
-- traverse through all references |
-- traverse through all references |
||
for ref in pairs(claim.references or {}) do |
for ref in pairs(claim.references or {}) do |
||
− | local |
+ | local refTable = {} |
− | -- traverse through all parts of the current reference |
||
for snakkey, snakval in orderedpairs(claim.references[ref].snaks or {}, claim.references[ref]["snaks-order"]) do |
for snakkey, snakval in orderedpairs(claim.references[ref].snaks or {}, claim.references[ref]["snaks-order"]) do |
||
+ | if #snakval == 1 then |
||
− | if refparts then refparts = refparts .. ", " else refparts = "" end |
||
+ | refTable[snakkey] = getSnakValue(snakval[1]) |
||
− | -- output the label of the property of the reference part, e.g. "imported from" for P143 |
||
+ | else |
||
− | refparts = refparts .. tostring(mw.wikibase.label(snakkey)) .. ": " |
||
+ | -- |
||
− | -- output all values of this reference part, e.g. "German Wikipedia" and "English Wikipedia" if the referenced claim was imported from both sites |
||
+ | multival = {} |
||
− | for snakidx = 1, #snakval do |
||
− | + | for snakidx = 1, #snakval do |
|
− | + | table.insert(multival, getSnakValue(snakval[snakidx])) |
|
+ | end |
||
+ | refTable[snakkey] = multival |
||
end |
end |
||
end |
end |
||
+ | local formattedRef, f = formatReference(refTable) |
||
− | if refparts then result = result .. "<ref>" .. refparts .. "</ref>" end |
||
+ | -- log errors that occur during formatting |
||
+ | if f then |
||
+ | mw.log(f) |
||
+ | end |
||
+ | if formattedRef and formattedRef ~= "" then |
||
+ | local hash = mw.hash.hashValue('fnv164', formattedRef) |
||
+ | result = result .. frame:extensionTag("ref", formattedRef, { name = '_' .. hash }) |
||
+ | end |
||
end |
end |
||
return result |
return result |
||
+ | end |
||
+ | |||
+ | local function hasqualifier(claim, qualifierproperty) |
||
+ | local invert |
||
+ | if string.sub(qualifierproperty, 1, 1) == "!" then invert = true else invert = false end |
||
+ | if not claim.qualifiers and not invert then return false end |
||
+ | if not claim.qualifiers and invert then return true end |
||
+ | if qualifierproperty == '' then return true end |
||
+ | if not invert and not claim.qualifiers[qualifierproperty] then return false end |
||
+ | if invert and claim.qualifiers[string.sub(qualifierproperty, 2)] then return false end |
||
+ | return true |
||
+ | end |
||
+ | |||
+ | local function qualifierhasvalue(claim, property, value) |
||
+ | -- TODO: not yet documented! |
||
+ | if not claim.qualifiers then return false end |
||
+ | if not claim.qualifiers[property] then return false end |
||
+ | for key, snak in pairs(claim.qualifiers[property]) do |
||
+ | if snak.snaktype == "value" then |
||
+ | if snak.datavalue.type == "wikibase-entityid" then |
||
+ | if snak.datavalue.value.id == value then |
||
+ | return true |
||
+ | end |
||
+ | --TODO: elseif other types |
||
+ | end |
||
+ | end |
||
+ | end |
||
+ | return false |
||
+ | end |
||
+ | |||
+ | local function hassource(claim, sourceproperty) |
||
+ | if not claim.references then return false end |
||
+ | if sourceproperty == '' then return true end |
||
+ | if string.sub(sourceproperty,1,1) ~= "!" then |
||
+ | for _, source in pairs(claim.references) do |
||
+ | if source.snaks[sourceproperty] then return true end |
||
+ | end |
||
+ | return false |
||
+ | else |
||
+ | for _, source in pairs(claim.references) do |
||
+ | for key in pairs(source.snaks) do |
||
+ | if key ~= string.sub(sourceproperty,2) then return true end |
||
+ | end |
||
+ | end |
||
+ | return false |
||
+ | end |
||
+ | end |
||
+ | |||
+ | function atdate(claim, mydate) |
||
+ | local refdate |
||
+ | if not mydate or mydate == "" then |
||
+ | refdate = os.date("!*t") |
||
+ | else |
||
+ | if string.match(mydate, "^%d+$") then |
||
+ | refdate = { year = tonumber(mydate) } |
||
+ | else |
||
+ | refdate = datavalueTimeToDateObject({ time = mw.language.getContentLanguage():formatDate("+Y-m-d\\TH:i:s\\Z", mydate) }) |
||
+ | end |
||
+ | end |
||
+ | local refjd = julianDay(refdate) |
||
+ | |||
+ | local mindate = getQualifierSortValue(claim, propertyId["starttime"]) |
||
+ | local maxdate = getQualifierSortValue(claim, propertyId["endtime"]) |
||
+ | |||
+ | if mindate and mindate > refjd then return false end |
||
+ | if maxdate and maxdate < refjd then return false end |
||
+ | |||
+ | return true |
||
+ | end |
||
+ | |||
+ | local function notdeprecated(claim, sourceproperty) |
||
+ | return not (claim.rank == "deprecated") |
||
+ | end |
||
+ | |||
+ | --returns a table of claims excluding claims not passed the filters |
||
+ | function filterClaims(frame, claims) |
||
+ | local function filter(condition, filterfunction) |
||
+ | if not frame.args[condition] then |
||
+ | return |
||
+ | end |
||
+ | local newclaims = {} |
||
+ | for i, claim in pairs(claims) do |
||
+ | if filterfunction(claim, frame.args[condition]) then |
||
+ | table.insert(newclaims, claim) |
||
+ | end |
||
+ | end |
||
+ | claims = newclaims |
||
+ | end |
||
+ | |||
+ | filter('hasqualifier', hasqualifier) |
||
+ | filter('hassource', hassource) |
||
+ | filter('atdate', atdate) |
||
+ | if not frame.args.includedeprecated then |
||
+ | frame.args.notdeprecated = true |
||
+ | filter('notdeprecated', notdeprecated) |
||
+ | end |
||
+ | |||
+ | -- use additional unnamed parameters as qualifier conditions (in pairs) |
||
+ | -- not yet documented! |
||
+ | -- TODO: not sure if this is good approach. Maybe use named parameter that has pairs split by semicolon |
||
+ | for key, val 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 = {} |
||
+ | for i, claim in pairs(claims) do |
||
+ | if qualifierhasvalue(claim, frame.args[key - 1], frame.args[key]) then |
||
+ | table.insert(newclaims, claim) |
||
+ | end |
||
+ | end |
||
+ | claims = newclaims |
||
+ | end |
||
+ | end |
||
+ | |||
+ | return claims |
||
+ | end |
||
+ | |||
+ | local p = {} |
||
+ | |||
+ | function p.isSubclass(frame) |
||
+ | if not frame.args["parent"] then return "" end |
||
+ | |||
+ | local maxDepth |
||
+ | maxDepth = frame.args["maxDepth"] or 5 |
||
+ | if not type(maxDepth) == "number" then maxDepth = 5 end |
||
+ | |||
+ | local result |
||
+ | result = isParent(frame.args["id"], frame.args["parent"], frame.args["exitItem"], maxDepth) |
||
+ | |||
+ | 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 |
||
+ | |||
+ | function p.descriptionIn(frame) |
||
+ | local langcode = frame.args[1] |
||
+ | local id = frame.args[2] |
||
+ | -- return description of a Wikidata entity in the given language or the default language of this Wikipedia site |
||
+ | local entity = mw.wikibase.getEntity(id) |
||
+ | if entity and entity.descriptions then |
||
+ | local desc = entity.descriptions[langcode or wiki.langcode] |
||
+ | if desc then return desc.value end |
||
+ | else |
||
+ | return ""; |
||
+ | end |
||
+ | end |
||
+ | |||
+ | function p.labelIn(frame) |
||
+ | local langcode = frame.args[1] |
||
+ | local id = frame.args[2] |
||
+ | -- return label of a Wikidata entity in the given language or the default language of this Wikipedia site |
||
+ | local entity = mw.wikibase.getEntity(id) |
||
+ | if entity and entity.labels then |
||
+ | local label = entity.labels[langcode or wiki.langcode] |
||
+ | if label then return label.value end |
||
+ | else |
||
+ | return ""; |
||
+ | end |
||
end |
end |
||
function p.claim(frame) |
function p.claim(frame) |
||
local property = frame.args[1] or "" |
local property = frame.args[1] or "" |
||
+ | local id = frame.args["id"] |
||
local qualifierId = frame.args["qualifier"] |
local qualifierId = frame.args["qualifier"] |
||
local parameter = frame.args["parameter"] |
local parameter = frame.args["parameter"] |
||
+ | local language = frame.args["language"] |
||
local list = frame.args["list"] |
local list = frame.args["list"] |
||
+ | local includeempty = frame.args["includeempty"] |
||
+ | local listMaxItems = tonumber(frame.args["listMaxItems"]) or 0 |
||
local references = frame.args["references"] |
local references = frame.args["references"] |
||
+ | local sort = frame.args["sort"] |
||
+ | local sortInItem = frame.args["sortInItem"] |
||
+ | local inverse = frame.args["inverse"] |
||
local showerrors = frame.args["showerrors"] |
local showerrors = frame.args["showerrors"] |
||
+ | local default = frame.args["default"] |
||
+ | if default then showerrors = nil end |
||
-- get wikidata entity |
-- get wikidata entity |
||
+ | if id then |
||
− | local entity = mw.wikibase.getEntityObject() |
||
+ | if not mw.wikibase.isValidEntityId(id) then |
||
+ | if showerrors then |
||
+ | return printError("entity-not-valid") |
||
+ | else |
||
+ | local temp = mw.title.new(i18n["maintenance-pages"]["entity-not-valid"], "Modul").exists |
||
+ | return default |
||
+ | end |
||
+ | elseif not mw.wikibase.entityExists(id) then |
||
+ | if showerrors then |
||
+ | return printError("entity-not-found") |
||
+ | else |
||
+ | local temp = mw.title.new(i18n["maintenance-pages"]["entity-not-found"], "Modul").exists |
||
+ | return default |
||
+ | end |
||
+ | end |
||
+ | end |
||
+ | |||
+ | local entity = mw.wikibase.getEntity(id) |
||
if not entity then |
if not entity then |
||
− | if showerrors then return printError("entity-not-found") else return end |
+ | if showerrors then return printError("entity-not-found") else return default end |
end |
end |
||
+ | |||
+ | -- check if property exists |
||
+ | local realProp = mw.wikibase.resolvePropertyId(property) |
||
+ | if not realProp then |
||
+ | local temp = mw.title.new(i18n["maintenance-pages"]["property-not-existing"], "Modul").exists |
||
+ | end |
||
+ | |||
-- fetch the first claim of satisfying the given property |
-- fetch the first claim of satisfying the given property |
||
− | local claims |
+ | local claims |
+ | if entity.claims then claims = entity.claims[realProp] end |
||
if not claims or not claims[1] then |
if not claims or not claims[1] then |
||
− | if showerrors then return printError("property-not-found") else return end |
+ | if showerrors then return printError("property-not-found") else return default end |
end |
end |
||
+ | |||
− | |||
+ | --filter claims |
||
+ | claims = filterClaims(frame, claims) |
||
+ | if not claims[1] then return default end |
||
+ | |||
-- get initial sort indices |
-- get initial sort indices |
||
local sortindices = {} |
local sortindices = {} |
||
Zeile 288: | Zeile 708: | ||
sortindices[#sortindices + 1] = idx |
sortindices[#sortindices + 1] = idx |
||
end |
end |
||
− | -- sort by claim rank |
||
− | local comparator = function(a, b) |
||
− | --local rankmap = { deprecated = -1, normal = 0, preferred = 1 } -- not needed as the ranks are coincidentally in alphanumeric order |
||
− | local ranka = claims[a].rank or "normal" |
||
− | local rankb = claims[b].rank or "normal" |
||
− | return ranka > rankb |
||
+ | local comparator |
||
− | --return mw.wikibase.label("Q" .. .mainsnak.datavalue.value["numeric-id"]) < mw.wikibase.label("Q" .. claims[b].mainsnak.datavalue.value["numeric-id"]) |
||
+ | if sort then |
||
+ | comparator = function(a, b) --comparator function for sorting statements based on qualifier value |
||
+ | -- load qualifier values |
||
+ | local QualifierSortValueA = getQualifierSortValue(claims[a], sort) |
||
+ | local QualifierSortValueB = getQualifierSortValue(claims[b], sort) |
||
+ | |||
+ | -- if either of the two statements does not have this qualifer, always sort it to the end |
||
+ | if not QualifierSortValueB then |
||
+ | if not QualifierSortValueA then |
||
+ | -- if neither of the two statements has this qualifier, arbitrarily but consistently return a < b |
||
+ | return a < b |
||
+ | else |
||
+ | return true |
||
+ | end |
||
+ | elseif not QualifierSortValueA then return false end |
||
+ | |||
+ | if type(QualifierSortValueA) ~= type(QualifierSortValueB) and not (tonumber(QualifierSortValueA) and tonumber(QualifierSortValueB)) then |
||
+ | if tonumber(QualifierSortValueA) then return true |
||
+ | elseif tonumber(QualifierSortValueB) then return false |
||
+ | elseif tostring(QualifierSortValueA) and tostring(QualifierSortValueB) then |
||
+ | if inverse then return tostring(QualifierSortValueA) > tostring(QualifierSortValueB) else return tostring(QualifierSortValueA) < tostring(QualifierSortValueB) end |
||
+ | else return false end -- different types, neither numbers nor strings, no chance to compare => random result to avoid script error |
||
+ | elseif tonumber(QualifierSortValueA) and tonumber(QualifierSortValueB) then |
||
+ | QualifierSortValueA = tonumber(QualifierSortValueA) |
||
+ | QualifierSortValueB = tonumber(QualifierSortValueB) |
||
+ | end |
||
+ | if inverse then |
||
+ | return QualifierSortValueA > QualifierSortValueB |
||
+ | else |
||
+ | return QualifierSortValueA < QualifierSortValueB |
||
+ | end |
||
+ | end |
||
+ | elseif sortInItem then |
||
+ | -- fill table sortkeys |
||
+ | local sortkeys = {} |
||
+ | local snakSingle |
||
+ | local sortkeyValueId |
||
+ | local claimContainingValue |
||
+ | for idx, claim in pairs(claims) do |
||
+ | snakSingle = getQualifierSnak(claim) |
||
+ | sortkeyValueId = "Q" .. getSnakValue(snakSingle, "numeric-id") |
||
+ | claimContainingValue = mw.wikibase.getEntity(sortkeyValueId).claims[mw.wikibase.resolvePropertyId(sortInItem)] |
||
+ | if claimContainingValue then |
||
+ | sortkeys[#sortkeys + 1] = getValueOfClaim(claimContainingValue[1]) |
||
+ | else |
||
+ | sortkeys[#sortkeys + 1] = "" |
||
+ | end |
||
+ | end |
||
+ | 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 |
end |
||
table.sort(sortindices, comparator) |
table.sort(sortindices, comparator) |
||
Zeile 302: | Zeile 779: | ||
local error |
local error |
||
if list then |
if list then |
||
+ | list = string.gsub(list, "\\n", "\n") -- if a newline is provided (whose backslash will be escaped) unescape it |
||
local value |
local value |
||
-- iterate over all elements and return their value (if existing) |
-- iterate over all elements and return their value (if existing) |
||
+ | result = {} |
||
for idx in pairs(claims) do |
for idx in pairs(claims) do |
||
local claim = claims[sortindices[idx]] |
local claim = claims[sortindices[idx]] |
||
value, error = getValueOfClaim(claim, qualifierId, parameter) |
value, error = getValueOfClaim(claim, qualifierId, parameter) |
||
− | if not value and showerrors then value = error end |
+ | if not value and value ~= 0 and showerrors then value = error end |
+ | if not value and value ~= 0 and includeempty then value = "" end |
||
− | if result then |
||
− | + | if value and references then value = value .. getReferences(frame, claim) end |
|
+ | result[#result + 1] = value |
||
− | else |
||
+ | end |
||
− | result = value |
||
+ | if listMaxItems and listMaxItems > 0 then |
||
− | end |
||
+ | result = table.concat(result, list, 1, math.min(table.getn(result), listMaxItems)) |
||
− | if value and references then result = result .. getReferences(claim) end |
||
+ | else |
||
+ | result = table.concat(result, list) |
||
end |
end |
||
else |
else |
||
− | -- return first element |
+ | -- return first element |
+ | |||
local claim = claims[sortindices[1]] |
local claim = claims[sortindices[1]] |
||
+ | if language == "Q" then |
||
− | result, error = getValueOfClaim(claim, qualifierId, parameter) |
||
− | + | result, error = "Q" .. getSnakValue(getQualifierSnak(claim), "numeric-id") |
|
+ | elseif 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 = getValueOfClaim(claim, qualifierId, parameter) |
||
+ | break |
||
+ | end |
||
+ | end |
||
+ | else |
||
+ | result, error = getValueOfClaim(claim, qualifierId, parameter) |
||
+ | end |
||
+ | if references == "only" then |
||
+ | result = getReferences(frame, claim) |
||
+ | elseif result and references then |
||
+ | result = result .. getReferences(frame, claim) |
||
+ | end |
||
end |
end |
||
+ | |||
− | |||
− | if |
+ | if result then return result else |
+ | if showerrors then return error else return default end |
||
+ | end |
||
+ | end |
||
+ | |||
+ | function p.getValue(frame) |
||
+ | local param = frame.args[2] |
||
+ | if param == "FETCH_WIKIDATA" or param == i18n["FETCH_WIKIDATA"] then return p.claim(frame) else return param end |
||
+ | end |
||
+ | |||
+ | function p.pageId(frame) |
||
+ | local id = frame.args[1] |
||
+ | local entity = mw.wikibase.getEntity(id) |
||
+ | if not entity then return "" else return entity.id end |
||
end |
end |
||
Zeile 330: | Zeile 841: | ||
-- if no id is given, the one from the entity associated with the calling Wikipedia article is used |
-- if no id is given, the one from the entity associated with the calling Wikipedia article is used |
||
if not id then |
if not id then |
||
− | local entity = mw.wikibase. |
+ | local entity = mw.wikibase.getEntity() |
if not entity then return printError("entity-not-found") end |
if not entity then return printError("entity-not-found") end |
||
id = entity.id |
id = entity.id |
||
Zeile 342: | Zeile 853: | ||
-- if no id is given, the one from the entity associated with the calling Wikipedia article is used |
-- if no id is given, the one from the entity associated with the calling Wikipedia article is used |
||
if not id then |
if not id then |
||
− | local entity = mw.wikibase. |
+ | local entity = mw.wikibase.getEntity() |
if not entity then return printError("entity-not-found") end |
if not entity then return printError("entity-not-found") end |
||
id = entity.id |
id = entity.id |
||
end |
end |
||
return mw.wikibase.sitelink(id) |
return mw.wikibase.sitelink(id) |
||
+ | end |
||
+ | |||
+ | function p.badges(frame) |
||
+ | local site = frame.args[1] |
||
+ | local id = frame.args[2] |
||
+ | if not site then return printError("site-not-found") end |
||
+ | local entity = mw.wikibase.getEntity(id) |
||
+ | if not entity then return printError("entity-not-found") end |
||
+ | local badges = entity.sitelinks[site].badges |
||
+ | if badges then |
||
+ | local result |
||
+ | for idx = 1, #badges do |
||
+ | if result then result = result .. "/" .. badges[idx] else result = badges[idx] end |
||
+ | end |
||
+ | return result |
||
+ | end |
||
+ | end |
||
+ | |||
+ | function p.sitelinkCount(frame) |
||
+ | local filter = "^.*" .. (frame.args[1] or "") .. "$" |
||
+ | local id = frame.args[2] |
||
+ | |||
+ | local entity = mw.wikibase.getEntity(id) |
||
+ | local count = 0 |
||
+ | if entity and entity.sitelinks then |
||
+ | for project, _ in pairs(entity.sitelinks) do |
||
+ | if string.find(project, filter) then count = count + 1 end |
||
+ | end |
||
+ | end |
||
+ | return count |
||
end |
end |
||
Zeile 353: | Zeile 894: | ||
local func = frame.args[1] |
local func = frame.args[1] |
||
if func then |
if func then |
||
+ | -- create new parameter set, where the first parameter with the function name is removed |
||
− | table.remove(frame.args, 1) |
||
+ | local newargs = {} |
||
+ | for key, val in pairs(frame.args) do |
||
+ | if type(key) == "number" then |
||
+ | if key > 1 then newargs[key - 1] = val end |
||
+ | else |
||
+ | newargs[key] = val |
||
+ | end |
||
+ | end |
||
+ | frame.args = newargs |
||
local status, result = pcall(p[func], frame) |
local status, result = pcall(p[func], frame) |
||
+ | -- if status then return tostring(result) or "" else return '<span class="error">' .. result .. '</span>' end -- revert |
||
if status then return result else return '<span class="error">' .. result .. '</span>' end |
if status then return result else return '<span class="error">' .. result .. '</span>' end |
||
+ | else |
||
+ | return printError("invalid-parameters") |
||
end |
end |
||
end |
end |
||
− | function |
+ | function p.printEntity(frame) |
+ | local id = frame.args[1] |
||
− | level = tonumber(level) or 0 |
||
− | local |
+ | local entity = mw.wikibase.getEntity(id) |
+ | if entity then return "<pre>" .. mw.text.jsonEncode(entity, mw.text.JSON_PRETTY) .. "</pre>" end |
||
− | local prefix = "" |
||
+ | end |
||
− | for idx = 1, level do prefix = prefix .. " " end |
||
+ | |||
+ | -- 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 |
||
+ | function p.ffCoordinate(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 }}, '/') |
||
− | if type(data) == "table" then |
||
+ | coord[1] = tonumber(coord[1]) |
||
− | for key, val in pairs(data) do |
||
+ | coord[2] = tonumber(coord[2]) |
||
− | result = result .. prefix .. key .. ": " |
||
+ | |||
− | if type(val) == "table" then result = result .. "\n" .. printTable(val, level + 1) else result = result .. tostring(val) .. "\n" end |
||
+ | local t, r = f.args.type, f.args.region |
||
− | end |
||
+ | if not t |
||
− | else |
||
+ | then t = p.claim{ args = { "P31", id, id = id, language = "Q" }} |
||
− | result = prefix .. tostring(data) |
||
+ | t = t and t:gsub("Q.*", { |
||
+ | Q8502 = "mountain", |
||
+ | Q54050 = "landmark" |
||
+ | }) |
||
+ | if not t or t and t:find("Q", 1, true) |
||
+ | then t="" -- no default, let Coordinate warn about unset type= param |
||
+ | end |
||
end |
end |
||
+ | if not r |
||
− | if level == 0 then result = "<pre>" .. mw.text.encode(result) .. "</pre>" end |
||
+ | then r = p.claim{ args = { "P17", id, id = id, language = "Q" }} |
||
− | return result |
||
+ | r = r and p.claim{ args = { "P297", r, id = r }} |
||
+ | if not r |
||
+ | then 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 |
end |
||
− | function p. |
+ | function p.ffCoordinateAndLatLonMaplink(frame) |
+ | frame.args.maplink = 1 |
||
− | local entity = mw.wikibase.getEntityObject() |
||
+ | --frame.args.mlname = nil |
||
− | return printTable(entity) |
||
+ | return p.ffCoordinate(frame) |
||
end |
end |
||
+ | |||
+ | function p.ffCoordinateAndMaplink(frame) |
||
+ | frame.args.maplink = 1 |
||
+ | frame.args.mlname = 1 |
||
+ | return p.ffCoordinate(frame) |
||
+ | end |
||
+ | |||
return p |
return p |
Version vom 15. November 2020, 17:08 Uhr
Die Dokumentation für dieses Modul kann unter Modul:Wikidata/Doku erstellt werden
-- module local variables
local wiki =
{
langcode = mw.language.getContentLanguage().code
}
-- internationalisation
local 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"] =
{
-- $1 is a placeholder for the actual number
[0] = "$1 Mrd. Jahren", -- precision: billion years
[1] = "$100 Mio. Jahren", -- precision: hundred million years
[2] = "$10 Mio. Jahren", -- precision: ten million years
[3] = "$1 Mio. Jahren", -- precision: million years
[4] = "$100.000 Jahren", -- precision: hundred thousand years
[5] = "$10.000 Jahren", -- precision: ten thousand years
[6] = "$1. Jahrtausend", -- precision: millenium
[7] = "$1. Jahrhundert", -- precision: century
[8] = "$1er", -- 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 $1", -- how to format negative numbers for precisions 0 to 5
["afternow"] = "in $1", -- how to format positive numbers for precisions 0 to 5
["bc"] = '$1 "v.Chr."', -- how print negative years
["ad"] = "$1" -- how print positive years
},
["monolingualtext"] = '<span lang="%language">%text</span>',
["FETCH_WIKIDATA"] = "ABFRAGE_WIKIDATA"
}
--important properties
local propertyId =
{
["starttime"] = "P580",
["endtime"] = "P582"
}
local 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
}
local function printError(code)
return '<span class="error">' .. (i18n.errors[code] or code) .. '</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 to check whether a certain item is a parent of a given item.
-- If pExitItem is reached without finding the searched parent item, the search stops.
-- A parent is connected via P31 or P279.
-- Attention: very intensive function, use carefully!
local function isParent(pItem, pParent, pExitItem, pMaxDepth, pDepth)
if not pDepth then pDepth = 0 end
if type(pItem) == "number" then pItem = "Q" .. pItem end
local entity = mw.wikibase.getEntity(pItem)
if not entity then return false end
local claims31
local claims279
if entity.claims then
claims31 = entity.claims[mw.wikibase.resolvePropertyId('P31')]
claims279 = entity.claims[mw.wikibase.resolvePropertyId('P279')]
else
return false
end
if not claims31 and not claims279 then return false end
local parentIds = {}
if claims31 and #claims31 > 0 then
for i, v in ipairs(claims31) do parentIds[#parentIds+1] = getSnakValue(v.mainsnak, "numeric-id") end
end
if claims279 and #claims279 > 0 then
for i, v in ipairs(claims279) do parentIds[#parentIds+1] = getSnakValue(v.mainsnak, "numeric-id") end
end
-- check if searched parent or exit item is reached or do recursive call
if not parentIds[1] or #parentIds == 0 then return false end
local itemString = ""
local result = nil
for i, v in ipairs(parentIds) do
if not v then return false end
itemString = "Q" .. v
if itemString == pParent then
-- successful!
return true
elseif itemString == pExitItem or itemString == "Q35120" then
-- exit if either "exit item" or node item (Q35120) is reached
return false
else
if pDepth+1 < pMaxDepth then
result = isParent(itemString, pParent, pExitItem, pMaxDepth, pDepth+1)
else return false end
if result == true then return result end
end
end
do return false end
end
local function printDatavalueCoordinate(data, parameter)
-- data fields: latitude [double], longitude [double], altitude [double], precision [double], globe [wikidata URI, usually http://www.wikidata.org/entity/Q2 [earth]]
if parameter then
if parameter == "globe" then data.globe = mw.ustring.match(data.globe, "Q%d+") end -- extract entity id from the globe URI
return data[parameter]
else
return data.latitude .. "/" .. data.longitude -- combine latitude and longitude, which can be decomposed using the #titleparts wiki function
end
end
local function printDatavalueQuantity(data, parameter)
-- data fields: amount [number], unit [string], upperBound [number], lowerBound [number]
if not parameter or parameter == "amount" then
return tonumber(data.amount)
elseif parameter == "unit" then
return mw.ustring.match(data.unit, "Q%d+")
else
return data[parameter]
end
end
local function normalizeDate(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
-- 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
function formatDate(date, precision, timezone, formatstr)
precision = precision or 11
date, year = normalizeDate(date)
date = string.gsub(date, "-00%f[%D]", "-01")
if year == 0 and precision <= 9 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.gsub(i18n.datetime[precision], "$1", tostring(y2))
if year < 0 then
relative = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative)
else
relative = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative)
end
return relative
end
-- precision is decades, centuries and millenia
local era
if precision == 6 then era = mw.ustring.gsub(i18n.datetime[6], "$1", tostring(math.floor((math.abs(year) - 1) / 1000) + 1)) end
if precision == 7 then era = mw.ustring.gsub(i18n.datetime[7], "$1", tostring(math.floor((math.abs(year) - 1) / 100) + 1)) end
if precision == 8 then era = mw.ustring.gsub(i18n.datetime[8], "$1", tostring(math.floor(math.abs(year) / 10) * 10)) end
if era then
if year < 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.bc, '"', ""), "$1", era)
elseif year > 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.ad, '"', ""), "$1", era) end
return era
end
-- precision is years or less
if precision >= 9 then
--[[ the following code replaces the UTC suffix with the given negated timezone to convert the global time to the given local time
timezone = tonumber(timezone)
if timezone and timezone ~= 0 then
timezone = -timezone
timezone = string.format("%.2d%.2d", timezone / 60, timezone % 60)
if timezone[1] ~= '-' then timezone = "+" .. timezone end
date = mw.text.trim(date, "Z") .. " " .. timezone
end
]]--
if formatstr then
for i=(precision+1), 14 do
for _, ch in pairs(formatchar[i]) do
if formatstr:find(ch) then
formatstr = i18n.datetime[precision]
end
end
end
else
formatstr = i18n.datetime[precision]
end
if year == 0 then formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], "")
elseif year < 0 then
-- Mediawiki formatDate doesn't support negative years
date = mw.ustring.sub(date, 2)
formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], mw.ustring.gsub(i18n.datetime.bc, "$1", i18n.datetime[9]))
elseif year > 0 and i18n.datetime.ad ~= "$1" then
formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], mw.ustring.gsub(i18n.datetime.ad, "$1", i18n.datetime[9]))
end
return mw.language.new(wiki.langcode):formatDate(formatstr, date)
end
end
local function printDatavalueTime(data, parameter)
-- data fields: 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]
if parameter then
para, formatstr = parameter:match("([^:]+):([^:]+)")
if parameter == "calendarmodel" then
data.calendarmodel = string.match(data.calendarmodel, "Q%d+") -- extract entity id from the calendar model URI
elseif para and para == "time" then
return formatDate(data.time, data.precision, data.timezone,formatstr)
elseif parameter == "time" then
data.time = normalizeDate(data.time)
end
return data[parameter]
else
return formatDate(data.time, data.precision, data.timezone)
end
end
local function printDatavalueEntity(data, parameter)
-- data fields: entity-type [string], numeric-id [int, Wikidata id]
local id
if data["entity-type"] == "item" then id = "Q" .. data["numeric-id"]
elseif data["entity-type"] == "property" then id = "P" .. data["numeric-id"]
else return printError("unknown-entity-type")
end
if parameter then
if parameter == "link" then
local linkTarget = mw.wikibase.sitelink(id)
local linkName = mw.wikibase.label(id)
if linkTarget then
local link = linkTarget
-- if there is a local Wikipedia article linking to it, use the label or the article title
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.label(id) or id
end
end
local function printDatavalueMonolingualText(data, parameter)
-- data fields: language [string], text [string]
if parameter then
return data[parameter]
else
local result = mw.ustring.gsub(mw.ustring.gsub(i18n.monolingualtext, "%%language", data["language"]), "%%text", data["text"])
return result
end
end
function getSnakValue(snak, parameter)
-- snaks have three types: "novalue" for null/nil, "somevalue" for not null/not nil, or "value" for actual data
if snak.snaktype == "value" then
-- call the respective snak parser
if snak.datavalue.type == "string" then return snak.datavalue.value
elseif snak.datavalue.type == "globecoordinate" then return printDatavalueCoordinate(snak.datavalue.value, parameter)
elseif snak.datavalue.type == "quantity" then return printDatavalueQuantity(snak.datavalue.value, parameter)
elseif snak.datavalue.type == "time" then return printDatavalueTime(snak.datavalue.value, parameter)
elseif snak.datavalue.type == "wikibase-entityid" then return printDatavalueEntity(snak.datavalue.value, parameter)
elseif snak.datavalue.type == "monolingualtext" then return printDatavalueMonolingualText(snak.datavalue.value, parameter)
end
end
return mw.wikibase.renderSnak(snak)
end
function getQualifierSnak(claim, qualifierId)
-- 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 and claim.qualifiers then
local qualifier = claim.qualifiers[qualifierId]
if qualifier then return qualifier[1] end
end
return nil, printError("qualifier-not-found")
else
-- otherwise return the main snak
return claim.mainsnak
end
end
local function datavalueTimeToDateObject(data)
local sign, year, month, day, hour, minute, second = string.match(data.time, "(.)(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)Z")
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$")
}
if sign == "-" then result.year = -result.year end
return result
end
function julianDay(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
function getQualifierSortValue(claim, qualifierId)
local snak = getQualifierSnak(claim, qualifierId)
if snak and snak.snaktype == "value" then
if snak.datavalue.type == "time" then
return julianDay(datavalueTimeToDateObject(snak.datavalue.value))
else
return getSnakValue(snak)
end
end
end
function getValueOfClaim(claim, qualifierId, parameter)
local error
local snak
snak, error = getQualifierSnak(claim, qualifierId)
if snak then
return getSnakValue(snak, parameter)
else
return nil, error
end
end
function formatReference(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, 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
refFormatted, f = Zitation.format()
return refFormatted, f
end
function getReferences(frame, claim)
local result = ""
-- traverse through all references
for ref in pairs(claim.references or {}) do
local refTable = {}
for snakkey, snakval in orderedpairs(claim.references[ref].snaks or {}, claim.references[ref]["snaks-order"]) do
if #snakval == 1 then
refTable[snakkey] = getSnakValue(snakval[1])
else
--
multival = {}
for snakidx = 1, #snakval do
table.insert(multival, getSnakValue(snakval[snakidx]))
end
refTable[snakkey] = multival
end
end
local formattedRef, f = formatReference(refTable)
-- log errors that occur during formatting
if f then
mw.log(f)
end
if formattedRef and formattedRef ~= "" then
local hash = mw.hash.hashValue('fnv164', formattedRef)
result = result .. frame:extensionTag("ref", formattedRef, { name = '_' .. hash })
end
end
return result
end
local function hasqualifier(claim, qualifierproperty)
local invert
if string.sub(qualifierproperty, 1, 1) == "!" then invert = true else invert = false end
if not claim.qualifiers and not invert then return false end
if not claim.qualifiers and invert then return true end
if qualifierproperty == '' then return true end
if not invert and not claim.qualifiers[qualifierproperty] then return false end
if invert and claim.qualifiers[string.sub(qualifierproperty, 2)] then return false end
return true
end
local function qualifierhasvalue(claim, property, value)
-- TODO: not yet documented!
if not claim.qualifiers then return false end
if not claim.qualifiers[property] then return false end
for key, snak in pairs(claim.qualifiers[property]) do
if snak.snaktype == "value" then
if snak.datavalue.type == "wikibase-entityid" then
if snak.datavalue.value.id == value then
return true
end
--TODO: elseif other types
end
end
end
return false
end
local function hassource(claim, sourceproperty)
if not claim.references then return false end
if sourceproperty == '' then return true end
if string.sub(sourceproperty,1,1) ~= "!" then
for _, source in pairs(claim.references) do
if source.snaks[sourceproperty] then return true end
end
return false
else
for _, source in pairs(claim.references) do
for key in pairs(source.snaks) do
if key ~= string.sub(sourceproperty,2) then return true end
end
end
return false
end
end
function atdate(claim, mydate)
local refdate
if not mydate or mydate == "" then
refdate = os.date("!*t")
else
if string.match(mydate, "^%d+$") then
refdate = { year = tonumber(mydate) }
else
refdate = datavalueTimeToDateObject({ time = mw.language.getContentLanguage():formatDate("+Y-m-d\\TH:i:s\\Z", mydate) })
end
end
local refjd = julianDay(refdate)
local mindate = getQualifierSortValue(claim, propertyId["starttime"])
local maxdate = getQualifierSortValue(claim, propertyId["endtime"])
if mindate and mindate > refjd then return false end
if maxdate and maxdate < refjd then return false end
return true
end
local function notdeprecated(claim, sourceproperty)
return not (claim.rank == "deprecated")
end
--returns a table of claims excluding claims not passed the filters
function filterClaims(frame, claims)
local function filter(condition, filterfunction)
if not frame.args[condition] then
return
end
local newclaims = {}
for i, claim in pairs(claims) do
if filterfunction(claim, frame.args[condition]) then
table.insert(newclaims, claim)
end
end
claims = newclaims
end
filter('hasqualifier', hasqualifier)
filter('hassource', hassource)
filter('atdate', atdate)
if not frame.args.includedeprecated then
frame.args.notdeprecated = true
filter('notdeprecated', notdeprecated)
end
-- use additional unnamed parameters as qualifier conditions (in pairs)
-- not yet documented!
-- TODO: not sure if this is good approach. Maybe use named parameter that has pairs split by semicolon
for key, val 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 = {}
for i, claim in pairs(claims) do
if qualifierhasvalue(claim, frame.args[key - 1], frame.args[key]) then
table.insert(newclaims, claim)
end
end
claims = newclaims
end
end
return claims
end
local p = {}
function p.isSubclass(frame)
if not frame.args["parent"] then return "" end
local maxDepth
maxDepth = frame.args["maxDepth"] or 5
if not type(maxDepth) == "number" then maxDepth = 5 end
local result
result = isParent(frame.args["id"], frame.args["parent"], frame.args["exitItem"], maxDepth)
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
function p.descriptionIn(frame)
local langcode = frame.args[1]
local id = frame.args[2]
-- return description of a Wikidata entity in the given language or the default language of this Wikipedia site
local entity = mw.wikibase.getEntity(id)
if entity and entity.descriptions then
local desc = entity.descriptions[langcode or wiki.langcode]
if desc then return desc.value end
else
return "";
end
end
function p.labelIn(frame)
local langcode = frame.args[1]
local id = frame.args[2]
-- return label of a Wikidata entity in the given language or the default language of this Wikipedia site
local entity = mw.wikibase.getEntity(id)
if entity and entity.labels then
local label = entity.labels[langcode or wiki.langcode]
if label then return label.value end
else
return "";
end
end
function p.claim(frame)
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 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 sortInItem = frame.args["sortInItem"]
local inverse = frame.args["inverse"]
local showerrors = frame.args["showerrors"]
local default = frame.args["default"]
if default then showerrors = nil end
-- get wikidata entity
if id then
if not mw.wikibase.isValidEntityId(id) then
if showerrors then
return printError("entity-not-valid")
else
local temp = mw.title.new(i18n["maintenance-pages"]["entity-not-valid"], "Modul").exists
return default
end
elseif not mw.wikibase.entityExists(id) then
if showerrors then
return printError("entity-not-found")
else
local temp = mw.title.new(i18n["maintenance-pages"]["entity-not-found"], "Modul").exists
return default
end
end
end
local entity = mw.wikibase.getEntity(id)
if not entity then
if showerrors then return printError("entity-not-found") else return default end
end
-- check if property exists
local realProp = mw.wikibase.resolvePropertyId(property)
if not realProp then
local temp = mw.title.new(i18n["maintenance-pages"]["property-not-existing"], "Modul").exists
end
-- fetch the first claim of satisfying the given property
local claims
if entity.claims then claims = entity.claims[realProp] end
if not claims or not claims[1] then
if showerrors then return printError("property-not-found") else return default end
end
--filter claims
claims = filterClaims(frame, claims)
if not claims[1] then return default end
-- get initial sort indices
local sortindices = {}
for idx in pairs(claims) do
sortindices[#sortindices + 1] = idx
end
local comparator
if sort then
comparator = function(a, b) --comparator function for sorting statements based on qualifier value
-- load qualifier values
local QualifierSortValueA = getQualifierSortValue(claims[a], sort)
local QualifierSortValueB = getQualifierSortValue(claims[b], sort)
-- if either of the two statements does not have this qualifer, always sort it to the end
if not QualifierSortValueB then
if not QualifierSortValueA then
-- if neither of the two statements has this qualifier, arbitrarily but consistently return a < b
return a < b
else
return true
end
elseif not QualifierSortValueA then return false end
if type(QualifierSortValueA) ~= type(QualifierSortValueB) and not (tonumber(QualifierSortValueA) and tonumber(QualifierSortValueB)) then
if tonumber(QualifierSortValueA) then return true
elseif tonumber(QualifierSortValueB) then return false
elseif tostring(QualifierSortValueA) and tostring(QualifierSortValueB) then
if inverse then return tostring(QualifierSortValueA) > tostring(QualifierSortValueB) else return tostring(QualifierSortValueA) < tostring(QualifierSortValueB) end
else return false end -- different types, neither numbers nor strings, no chance to compare => random result to avoid script error
elseif tonumber(QualifierSortValueA) and tonumber(QualifierSortValueB) then
QualifierSortValueA = tonumber(QualifierSortValueA)
QualifierSortValueB = tonumber(QualifierSortValueB)
end
if inverse then
return QualifierSortValueA > QualifierSortValueB
else
return QualifierSortValueA < QualifierSortValueB
end
end
elseif sortInItem then
-- fill table sortkeys
local sortkeys = {}
local snakSingle
local sortkeyValueId
local claimContainingValue
for idx, claim in pairs(claims) do
snakSingle = getQualifierSnak(claim)
sortkeyValueId = "Q" .. getSnakValue(snakSingle, "numeric-id")
claimContainingValue = mw.wikibase.getEntity(sortkeyValueId).claims[mw.wikibase.resolvePropertyId(sortInItem)]
if claimContainingValue then
sortkeys[#sortkeys + 1] = getValueOfClaim(claimContainingValue[1])
else
sortkeys[#sortkeys + 1] = ""
end
end
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
table.sort(sortindices, comparator)
local result
local error
if list then
list = string.gsub(list, "\\n", "\n") -- if a newline is provided (whose backslash will be escaped) unescape it
local value
-- iterate over all elements and return their value (if existing)
result = {}
for idx in pairs(claims) do
local claim = claims[sortindices[idx]]
value, error = getValueOfClaim(claim, qualifierId, parameter)
if not value and value ~= 0 and showerrors then value = error end
if not value and value ~= 0 and includeempty then value = "" end
if value and references then value = value .. getReferences(frame, claim) end
result[#result + 1] = value
end
if listMaxItems and listMaxItems > 0 then
result = table.concat(result, list, 1, math.min(table.getn(result), listMaxItems))
else
result = table.concat(result, list)
end
else
-- return first element
local claim = claims[sortindices[1]]
if language == "Q" then
result, error = "Q" .. getSnakValue(getQualifierSnak(claim), "numeric-id")
elseif 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 = getValueOfClaim(claim, qualifierId, parameter)
break
end
end
else
result, error = getValueOfClaim(claim, qualifierId, parameter)
end
if references == "only" then
result = getReferences(frame, claim)
elseif result and references then
result = result .. getReferences(frame, claim)
end
end
if result then return result else
if showerrors then return error else return default end
end
end
function p.getValue(frame)
local param = frame.args[2]
if param == "FETCH_WIKIDATA" or param == i18n["FETCH_WIKIDATA"] then return p.claim(frame) else return param end
end
function p.pageId(frame)
local id = frame.args[1]
local entity = mw.wikibase.getEntity(id)
if not entity then return "" else return entity.id end
end
function p.labelOf(frame)
local id = frame.args[1]
-- returns the label of the given entity/property id
-- if no id is given, the one from the entity associated with the calling Wikipedia article is used
if not id then
local entity = mw.wikibase.getEntity()
if not entity then return printError("entity-not-found") end
id = entity.id
end
return mw.wikibase.label(id)
end
function p.sitelinkOf(frame)
local id = frame.args[1]
-- returns the Wikipedia article name of the given entity
-- if no id is given, the one from the entity associated with the calling Wikipedia article is used
if not id then
local entity = mw.wikibase.getEntity()
if not entity then return printError("entity-not-found") end
id = entity.id
end
return mw.wikibase.sitelink(id)
end
function p.badges(frame)
local site = frame.args[1]
local id = frame.args[2]
if not site then return printError("site-not-found") end
local entity = mw.wikibase.getEntity(id)
if not entity then return printError("entity-not-found") end
local badges = entity.sitelinks[site].badges
if badges then
local result
for idx = 1, #badges do
if result then result = result .. "/" .. badges[idx] else result = badges[idx] end
end
return result
end
end
function p.sitelinkCount(frame)
local filter = "^.*" .. (frame.args[1] or "") .. "$"
local id = frame.args[2]
local entity = mw.wikibase.getEntity(id)
local count = 0
if entity and entity.sitelinks then
for project, _ in pairs(entity.sitelinks) do
if string.find(project, filter) then count = count + 1 end
end
end
return count
end
-- call this in cases of script errors within a function instead of {{#invoke:Wikidata|<method>|...}} call {{#invoke:Wikidata|debug|<method>|...}}
function p.debug(frame)
local func = frame.args[1]
if func then
-- 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
if key > 1 then newargs[key - 1] = val end
else
newargs[key] = val
end
end
frame.args = newargs
local status, result = pcall(p[func], frame)
-- if status then return tostring(result) or "" else return '<span class="error">' .. result .. '</span>' end -- revert
if status then return result else return '<span class="error">' .. result .. '</span>' end
else
return printError("invalid-parameters")
end
end
function p.printEntity(frame)
local id = frame.args[1]
local entity = mw.wikibase.getEntity(id)
if entity then return "<pre>" .. mw.text.jsonEncode(entity, mw.text.JSON_PRETTY) .. "</pre>" end
end
-- 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
function p.ffCoordinate(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, language = "Q" }}
t = t and t:gsub("Q.*", {
Q8502 = "mountain",
Q54050 = "landmark"
})
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, language = "Q" }}
r = r and p.claim{ args = { "P297", r, id = r }}
if not r
then 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
function p.ffCoordinateAndLatLonMaplink(frame)
frame.args.maplink = 1
--frame.args.mlname = nil
return p.ffCoordinate(frame)
end
function p.ffCoordinateAndMaplink(frame)
frame.args.maplink = 1
frame.args.mlname = 1
return p.ffCoordinate(frame)
end
return p