Modulo:Wikidata

Dokumentado Dokumentado


Ŝablona programadoDiskutojLuaTestojSubpaĝoj
ModuloEsperantoEnglishDeutsch

Modulo:Dokumentado


Se vi havas demandon pri ĉi tiu Lua-modulo, tiam vi povas demandi en la diskutejo pri Lua-moduloj. La Intervikiaj ligiloj estu metataj al Vikidatumoj. (Vidu Helpopaĝon pri tio.)
-------------------------------------------------------------------------------- module local variables and functionslocal wiki ={langcode = mw.language.getContentLanguage().code}-- internationalisationlocal i18n ={["errors"] ={["property-not-found"] = "Eco ne trovita.",["entity-not-found"] = "Vikidatumoj-ero ne trovita.",["unknown-claim-type"] = "Nekonata tipo de deklaro.",["unknown-entity-type"] = "Nekonata tipo de ento.",["qualifier-not-found"] = "Kvalifiko ne trovita.",["site-not-found"] = "Vikimedia projekto ne trovita.",["invalid-parameters"] = "Nevalidaj parametroj.",["image-not-found"] = "Bildo ne difinita en P18 de Vikidatumoj"  },["datetime"] ={-- $1 is a placeholder for the actual number[0] = "$1 mrd. da jaroj",-- precision: billion years[1] = "$100 mio. da jaroj",-- precision: hundred million years[2] = "$10 mio. da jaroj",-- precision: ten million years[3] = "$1 mio. da jaroj",-- precision: million years[4] = "$100 000 jaroj",-- precision: hundred thousand years[5] = "$10 000 jaroj",-- precision: ten thousand years[6] = "$1-a jarmilo",-- precision: millennium[7] = "$1-a jarcento",-- precision: century[8] = "$1-aj jaroj",-- precision: decade-- the following use the format of #time parser function[9]  = "Y",-- precision: year,[10] = "F Y",-- precision: month[11] = 'j"-a de" F Y',-- precision: day[12] = 'j"-a de" F Y, G "horo"',-- precision: hour[13] = 'j"-a de" F Y G:i',-- precision: minute[14] = 'j"-a de" F Y G:i:s',-- precision: second["beforenow"] = '$1 "a.K.E."',-- how to format negative numbers for precisions 0 to 5["afternow"] = '$1 "K.E."',-- how to format positive numbers for precisions 0 to 5["bc"] = '$1 "a.K.E."',-- how print negative years["ad"] = "$1"-- how print positive years},["monolingualtext"] = '<span lang="%language">%text</span>',["FETCH_WIKIDATA"] = "PETI_VIKIDATUMOJN"}--important propertieslocal 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 p = {}local function printError(code)return '<span class="error">' .. (i18n.errors[code] or code) .. '</span>'end-- exported version of the functionfunction p.printError(code)return printError(code)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 functionlocal i = 0return function()i = i + 1if order[i] thenreturn order[i], array[order[i]]endendendfunction 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 sitelocal entity = mw.wikibase.getEntity(id)if entity and entity.descriptions thenlocal desc = entity.descriptions[langcode or wiki.langcode]if desc then return desc.value endendendfunction 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 sitelocal entity = mw.wikibase.getEntity(id)if entity and entity.labels thenlocal label = entity.labels[langcode or wiki.langcode]if label then return label.value endendendlocal 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 thenif parameter == "globe" then data.globe = mw.ustring.match(data.globe, "Q%d+") end -- extract entity id from the globe URIreturn data[parameter]elsereturn data.latitude .. "/" .. data.longitude -- combine latitude and longitude, which can be decomposed using the #titleparts wiki functionendendlocal function printDatavalueQuantity(data, parameter)-- data fields: amount [number], unit [string], upperBound [number], lowerBound [number]if not parameter or parameter == "amount" thenreturn tonumber(data.amount)elseif parameter == "unit" thenreturn mw.ustring.match(data.unit, "Q%d+")elsereturn data[parameter]endend-- precision: 0 - billion years, 1 - hundred million years, ..., 6 - millennia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - secondlocal function normalizeDate(date)date = mw.text.trim(date, "+")-- extract yearlocal yearstr = mw.ustring.match(date, "^\-?%d+")local year = tonumber(yearstr)-- remove leading zeros of yearreturn year .. mw.ustring.sub(date, #yearstr + 1), yearendlocal function formatDate(date, precision, timezone, formatstr)precision = precision or 11local 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 moreif precision <= 5 thenlocal 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 thenrelative = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative)elserelative = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative)endreturn relativeend-- precision is decades, centuries and millennialocal eraif precision == 6 then era = mw.ustring.gsub(i18n.datetime[6], "$1", tostring(math.floor((math.abs(year) - 1) / 1000) + 1)) endif precision == 7 then era = mw.ustring.gsub(i18n.datetime[7], "$1", tostring(math.floor((math.abs(year) - 1) / 100) + 1)) endif precision == 8 then era = mw.ustring.gsub(i18n.datetime[8], "$1", tostring(math.floor(math.abs(year) / 10) * 10)) endif era thenif 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) endreturn eraend-- precision is yearif precision == 9 thenreturn yearend-- precision is less than yearsif precision > 9 then--[[ the following code replaces the UTC suffix with the given negated timezone to convert the global time to the given local timetimezone = tonumber(timezone)if timezone and timezone ~= 0 thentimezone = -timezonetimezone = string.format("%.2d%.2d", timezone / 60, timezone % 60)if timezone[1] ~= '-' then timezone = "+" .. timezone enddate = mw.text.trim(date, "Z") .. " " .. timezoneend]]--if formatstr thenfor i=(precision+1), 14 dofor _, ch in pairs(formatchar[i]) doif formatstr:find(ch) thenformatstr = i18n.datetime[precision]endendendelseformatstr = i18n.datetime[precision]endif year == 0 then formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], "")elseif year < 0 then-- Mediawiki formatDate doesn't support negative yearsdate = 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" thenformatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], mw.ustring.gsub(i18n.datetime.ad, "$1", i18n.datetime[9]))endreturn mw.language.new(wiki.langcode):formatDate(formatstr, date)endendlocal 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 thenpara, formatstr = parameter:match("([^:]+):([^:]+)")if parameter == "calendarmodel" thendata.calendarmodel = string.match(data.calendarmodel, "Q%d+") -- extract entity id from the calendar model URIelseif para and para == "time" thenreturn formatDate(data.time, data.precision, data.timezone,formatstr)elseif parameter == "time" thendata.time = normalizeDate(data.time)endreturn data[parameter]elsereturn formatDate(data.time, data.precision, data.timezone)endendlocal function printDatavalueEntity(data, parameter)-- data fields: entity-type [string], numeric-id [int, Wikidata id]local idif 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")endif parameter thenif parameter == "link" thenlocal linkTarget = mw.wikibase.getSitelink(id)local linkName = mw.wikibase.getLabel(id)if linkTarget thenlocal link = linkTarget-- if there is a local Wikipedia article linking to it, use the label or the article titleif linkName and (linkName ~= linkTarget) then link = link .. "|" .. linkName endreturn "[[" .. link .. "]]"else-- if there is no local Wikipedia article output the label or link to the Wikidata object to input a proper labelif linkName then return linkName else return "[[:d:" .. id .. "|" .. id .. "]]" endendelsereturn data[parameter]endelsereturn mw.wikibase.label(id) or idendendlocal function printDatavalueMonolingualText(data, parameter)-- data fields: language [string], text [string]if parameter thenreturn data[parameter]elselocal result = mw.ustring.gsub(mw.ustring.gsub(i18n.monolingualtext, "%%language", data["language"]), "%%text", data["text"])return resultendendlocal function getSnakValue(snak, parameter)-- snaks have three types: "novalue" for null/nil, "somevalue" for not null/not nil, or "value" for actual dataif snak.snaktype == "value" then-- call the respective snak parserif snak.datavalue.type == "string" then return snak.datavalue.valueelseif 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)endendreturn mw.wikibase.renderSnak(snak)endlocal 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 snaksif qualifierId then-- search the attribute snak with the given qualifier as keyif claim.qualifiers thenlocal qualifier = claim.qualifiers[qualifierId]if qualifier then return qualifier[1] endendreturn nil, printError("qualifier-not-found")else-- otherwise return the main snakreturn claim.mainsnakendendlocal 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 endreturn resultendfunction julianDay(dateObject)local year = dateObject.yearlocal month = dateObject.month or 0local day = dateObject.day or 0if month == 0 then month = 1 endif day == 0 then day = 1 endif month <= 2 thenyear = year - 1month = month + 12endlocal time = ((((dateObject.sec or 0) / 60 + (dateObject.min or 0) + (dateObject.timezone or 0)) / 60) + (dateObject.hour or 0)) / 24local bif dateObject.julian then b = 0 elselocal century = math.floor(year / 100)b = 2 - century + math.floor(century / 4)endreturn math.floor(365.25 * (year + 4716)) + math.floor(30.6001 * (month + 1)) + day + time + b - 1524.5endfunction getQualifierSortValue(claim, qualifierId)local snak = getQualifierSnak(claim, qualifierId)if snak and snak.snaktype == "value" thenif snak.datavalue.type == "time" thenreturn julianDay(datavalueTimeToDateObject(snak.datavalue.value))elsereturn getSnakValue(snak)endendendlocal function getValueOfClaim(claim, qualifierId, parameter)local errorlocal snaksnak, error = getQualifierSnak(claim, qualifierId)if snak thenreturn getSnakValue(snak, parameter)elsereturn nil, errorendendfunction formatReference(ref)-- "imported from"-references are mostly useless, skipp themif ref["P143"] then-- do nothingelselocal formatedRefif type(ref["P854"]) == "string" and type(ref["P1476"]) == "string" thenformatedRef = "[" .. ref["P854"] .. " " .. ref["P1476"] .. "], "ref["P854"] = nilref["P1476"] = nilendfor prop, value in pairs(ref) doif type(value) == "table" thenfor _, subvalue in pairs(value) doif formatedRef then formatedRef = formatedRef .. ", " else formatedRef = "" endformatedRef = formatedRef .. tostring(mw.wikibase.label(prop))  .. ": " .. subvalueendelseif formatedRef then formatedRef = formatedRef .. ", " else formatedRef = "" endformatedRef = formatedRef .. tostring(mw.wikibase.label(prop))  .. ": " .. valueendendreturn formatedRefendendlocal function getReferences(frame, claim)local result = ""-- traverse through all referencesfor ref in pairs(claim.references or {}) dolocal refTable = {}for snakkey, snakval in orderedpairs(claim.references[ref].snaks or {}, claim.references[ref]["snaks-order"]) doif #snakval == 1 thenrefTable[snakkey] = getSnakValue(snakval[1])else--multival = {}for snakidx = 1, #snakval dotable.insert(multival, getSnakValue(snakval[snakidx]))endrefTable[snakkey] = multivalendendformatedRef = formatReference(refTable)if formatedRef then if frame.args['references'] == "raw" thenif ref == 1 thenresult = result .. formatedRefelse-- se estas pli ol unu referenco, disigaj signoj estas metataj, kiuj poste en la vokanta modulo estu iel traktataj-- tio necesas, ĉar frame:extensionTag ial ne funkcius ĉi tie!result = result .. "///" .. formatedRefendelseresult = result .. frame:extensionTag("ref", formatedRef) endendendreturn resultendlocal function hasqualifier(claim, qualifierproperty)local invertif string.sub(qualifierproperty, 1, 1) == "!" then invert = true else invert = false endif not claim.qualifiers and not invert then return false endif not claim.qualifiers and invert then return true endif qualifierproperty == '' then return true endif not invert and not claim.qualifiers[qualifierproperty] then return false endif invert and claim.qualifiers[string.sub(qualifierproperty, 2)] then return false endreturn trueendlocal function hassource(claim, sourceproperty)if not claim.references then return false endif sourceproperty == '' then return true endif string.sub(sourceproperty,1,1) ~= "!" thenfor _, source in pairs(claim.references) doif source.snaks[sourceproperty] then return true endendreturn falseelsefor _, source in pairs(claim.references) dofor key in pairs(source.snaks) doif key ~= string.sub(sourceproperty,2) then return true endendendreturn falseendendfunction atdate(claim, mydate)local refdateif not mydate or mydate == "" thenrefdate = os.date("!*t")elseif string.match(mydate, "^%d+$") thenrefdate = { year = tonumber(mydate) }elserefdate = datavalueTimeToDateObject({ time = mw.language.getContentLanguage():formatDate("+Y-m-d\\TH:i:s\\Z", mydate) })endendlocal refjd = julianDay(refdate)local mindate = getQualifierSortValue(claim, propertyId["starttime"])local maxdate = getQualifierSortValue(claim, propertyId["endtime"])if mindate and mindate > refjd then return false endif maxdate and maxdate < refjd then return false endreturn trueend--returns a table of claims excluding claims not passed the filtersfunction filterClaims(frame, claims)local function filter(condition, filterfunction)if not frame.args[condition] thenreturnendlocal newclaims = {}for i, claim in pairs(claims) doif filterfunction(claim, frame.args[condition]) thentable.insert(newclaims, claim)endendclaims = newclaimsendfilter('hasqualifier', hasqualifier)filter('hassource', hassource)filter('atdate', atdate)return claimsendfunction 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 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 entitylocal entity = mw.wikibase.getEntity(id)if not entity thenif showerrors then return printError("entity-not-found") else return default endend-- fetch the first claim of satisfying the given propertylocal claimsif entity.claims then claims = entity.claims[mw.wikibase.resolvePropertyId(property)] endif not claims or not claims[1] thenif showerrors then return printError("property-not-found") else return default endend--filter claimsclaims = filterClaims(frame, claims)if not claims[1] then return default end-- get initial sort indiceslocal sortindices = {}for idx in pairs(claims) dosortindices[#sortindices + 1] = idxendlocal comparatorif sort then-- sort by time qualifiercomparator = function(a, b)local timea = getQualifierSortValue(claims[a], sort) or ''local timeb = getQualifierSortValue(claims[b], sort) or ''if type(timea) ~= type(timeb) and not (tonumber(timea) and tonumber(timeb)) thenif tonumber(timea) then return trueelseif tonumber(timeb) then return falseelseif tostring(timea) and tostring(timeb) thenif inverse then return tostring(timea) > tostring(timeb) else return tostring(timea) < tostring(timeb) endelse return false end -- different types, neither numbers nor strings, no chance to compare => random result to avoid script errorelseif tonumber(timea) and tonumber(timeb) thentimea = tonumber(timea)timeb = tonumber(timeb)endif inverse thenreturn timea > timebelsereturn timea < timebendendelseif sortInItem then-- fill table sortkeyslocal sortkeys = {}local snakSinglelocal sortkeyValueIdlocal claimContainingValuefor idx, claim in pairs(claims) dosnakSingle = getQualifierSnak(claim)sortkeyValueId = "Q" .. getSnakValue(snakSingle, "numeric-id")claimContainingValue = mw.wikibase.getEntity(sortkeyValueId).claims[mw.wikibase.resolvePropertyId(sortInItem)]if claimContainingValue thensortkeys[#sortkeys + 1] = getValueOfClaim(claimContainingValue[1])elsesortkeys[#sortkeys + 1] = ""endendcomparator = function(a, b)if inverse thenreturn sortkeys[a] > sortkeys [b]elsereturn sortkeys[a] < sortkeys [b]endendelse-- sort by claim rankcomparator = 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 < rankbendendtable.sort(sortindices, comparator)local resultlocal errorif list thenlist = string.gsub(list, "\\n", "\n") -- if a newline is provided (whose backslash will be escaped) unescape itlocal value-- iterate over all elements and return their value (if existing)result = {}for idx in pairs(claims) dolocal claim = claims[sortindices[idx]]value, error =  getValueOfClaim(claim, qualifierId, parameter)if not value and value ~= 0 and showerrors then value = error endif not value and value ~= 0 and includeempty then value = "" endif value and references then value = value .. getReferences(frame, claim) endresult[#result + 1] = valueendresult = table.concat(result, list)else-- return first elementlocal claim = claims[sortindices[1]]if language and claim.mainsnak.datatype == "monolingualtext" then-- iterate over claims to find adequate languagefor idx, claim in pairs(claims) doif claim.mainsnak.datavalue.value.language == language thenresult, error = getValueOfClaim(claim, qualifierId, parameter)breakendendelseresult, error = getValueOfClaim(claim, qualifierId, parameter)endif result and references == "raw" thenlocal ref = getReferences(frame, claim)return result, refendif result and references then result = result .. getReferences(frame, claim) endendif result then return result elseif showerrors then return error else return default endendendfunction 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 endend-- for use in templates onlyfunction p.pageId(frame)local id = frame.args[1]return p._pageId(id)end-- for use in modules onlyfunction p._pageId(id)local entity = mw.wikibase.getEntity(id)if not entity then return nil else return entity.id endendfunction 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 usedif not id thenlocal entity = mw.wikibase.getEntity()if not entity then return printError("entity-not-found") endid = entity.idendreturn mw.wikibase.label(id)endfunction 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 usedif not id thenlocal entity = mw.wikibase.getEntity()if not entity then return printError("entity-not-found") endid = entity.idendreturn mw.wikibase.sitelink(id)endfunction p.badges(frame)local site = frame.args[1]local id = frame.args[2]if not site then return printError("site-not-found") endlocal entity = mw.wikibase.getEntity(id)if not entity then return printError("entity-not-found") endlocal badges = entity.sitelinks[site].badgesif badges thenlocal resultfor idx = 1, #badges doif result then result = result .. "/" .. badges[idx] else result = badges[idx] endendreturn resultendendfunction p.sitelinkCount(frame)local filter = "^.*" .. (frame.args[1] or "") .. "$"local id = frame.args[2]local entity = mw.wikibase.getEntity(id)local count = 0if entity and entity.sitelinks thenfor project, _ in pairs(entity.sitelinks) doif string.find(project, filter) then count = count + 1 endendendreturn countend-- 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 removedlocal newargs = {}for key, val in pairs(frame.args) doif type(key) == "number" thenif key > 1 then newargs[key - 1] = val endelsenewargs[key] = valendendframe.args = newargslocal status, result = pcall(p[func], frame)if status then return result else return '<span class="error">' .. result .. '</span>' endelsereturn printError("invalid-parameters")endendfunction 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>" endendreturn p