-------------------------------------------------------------------------------- module local variables and functionslocalwiki={langcode=mw.language.getContentLanguage().code}-- internationalisationlocali18n={["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 propertieslocalpropertyId={["starttime"]="P580",["endtime"]="P582"}localformatchar={[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}localp={}localfunctionprintError(code)return'<span class="error">'..(i18n.errors[code]orcode)..'</span>'end-- exported version of the functionfunctionp.printError(code)returnprintError(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.localfunctionorderedpairs(array,order)ifnotorderthenreturnpairs(array)end-- return iterator functionlocali=0returnfunction()i=i+1iforder[i]thenreturnorder[i],array[order[i]]endendendfunctionp.descriptionIn(frame)locallangcode=frame.args[1]localid=frame.args[2]-- return description of a Wikidata entity in the given language or the default language of this Wikipedia sitelocalentity=mw.wikibase.getEntity(id)ifentityandentity.descriptionsthenlocaldesc=entity.descriptions[langcodeorwiki.langcode]ifdescthenreturndesc.valueendendendfunctionp.labelIn(frame)locallangcode=frame.args[1]localid=frame.args[2]-- return label of a Wikidata entity in the given language or the default language of this Wikipedia sitelocalentity=mw.wikibase.getEntity(id)ifentityandentity.labelsthenlocallabel=entity.labels[langcodeorwiki.langcode]iflabelthenreturnlabel.valueendendendlocalfunctionprintDatavalueCoordinate(data,parameter)-- data fields: latitude [double], longitude [double], altitude [double], precision [double], globe [wikidata URI, usually http://www.wikidata.org/entity/Q2 [earth]]ifparameterthenifparameter=="globe"thendata.globe=mw.ustring.match(data.globe,"Q%d+")end-- extract entity id from the globe URIreturndata[parameter]elsereturndata.latitude.."/"..data.longitude-- combine latitude and longitude, which can be decomposed using the #titleparts wiki functionendendlocalfunctionprintDatavalueQuantity(data,parameter)-- data fields: amount [number], unit [string], upperBound [number], lowerBound [number]ifnotparameterorparameter=="amount"thenreturntonumber(data.amount)elseifparameter=="unit"thenreturnmw.ustring.match(data.unit,"Q%d+")elsereturndata[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 - secondlocalfunctionnormalizeDate(date)date=mw.text.trim(date,"+")-- extract yearlocalyearstr=mw.ustring.match(date,"^\-?%d+")localyear=tonumber(yearstr)-- remove leading zeros of yearreturnyear..mw.ustring.sub(date,#yearstr+1),yearendlocalfunctionformatDate(date,precision,timezone,formatstr)precision=precisionor11localdate,year=normalizeDate(date)date=string.gsub(date,"-00%f[%D]","-01")ifyear==0andprecision<=9thenreturn""end-- precision is 10000 years or moreifprecision<=5thenlocalfactor=10^((5-precision)+4)localy2=math.ceil(math.abs(year)/factor)localrelative=mw.ustring.gsub(i18n.datetime[precision],"$1",tostring(y2))ifyear<0thenrelative=mw.ustring.gsub(i18n.datetime.beforenow,"$1",relative)elserelative=mw.ustring.gsub(i18n.datetime.afternow,"$1",relative)endreturnrelativeend-- precision is decades, centuries and millennialocaleraifprecision==6thenera=mw.ustring.gsub(i18n.datetime[6],"$1",tostring(math.floor((math.abs(year)-1)/1000)+1))endifprecision==7thenera=mw.ustring.gsub(i18n.datetime[7],"$1",tostring(math.floor((math.abs(year)-1)/100)+1))endifprecision==8thenera=mw.ustring.gsub(i18n.datetime[8],"$1",tostring(math.floor(math.abs(year)/10)*10))endiferathenifyear<0thenera=mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.bc,'"',""),"$1",era)elseifyear>0thenera=mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.ad,'"',""),"$1",era)endreturneraend-- precision is yearifprecision==9thenreturnyearend-- precision is less than yearsifprecision>9then--[[ 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]]--ifformatstrthenfori=(precision+1),14dofor_,chinpairs(formatchar[i])doifformatstr:find(ch)thenformatstr=i18n.datetime[precision]endendendelseformatstr=i18n.datetime[precision]endifyear==0thenformatstr=mw.ustring.gsub(formatstr,i18n.datetime[9],"")elseifyear<0then-- 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]))elseifyear>0andi18n.datetime.ad~="$1"thenformatstr=mw.ustring.gsub(formatstr,i18n.datetime[9],mw.ustring.gsub(i18n.datetime.ad,"$1",i18n.datetime[9]))endreturnmw.language.new(wiki.langcode):formatDate(formatstr,date)endendlocalfunctionprintDatavalueTime(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]ifparameterthenpara,formatstr=parameter:match("([^:]+):([^:]+)")ifparameter=="calendarmodel"thendata.calendarmodel=string.match(data.calendarmodel,"Q%d+")-- extract entity id from the calendar model URIelseifparaandpara=="time"thenreturnformatDate(data.time,data.precision,data.timezone,formatstr)elseifparameter=="time"thendata.time=normalizeDate(data.time)endreturndata[parameter]elsereturnformatDate(data.time,data.precision,data.timezone)endendlocalfunctionprintDatavalueEntity(data,parameter)-- data fields: entity-type [string], numeric-id [int, Wikidata id]localidifdata["entity-type"]=="item"thenid="Q"..data["numeric-id"]elseifdata["entity-type"]=="property"thenid="P"..data["numeric-id"]elsereturnprintError("unknown-entity-type")endifparameterthenifparameter=="link"thenlocallinkTarget=mw.wikibase.getSitelink(id)locallinkName=mw.wikibase.getLabel(id)iflinkTargetthenlocallink=linkTarget-- if there is a local Wikipedia article linking to it, use the label or the article titleiflinkNameand(linkName~=linkTarget)thenlink=link.."|"..linkNameendreturn"[["..link.."]]"else-- if there is no local Wikipedia article output the label or link to the Wikidata object to input a proper labeliflinkNamethenreturnlinkNameelsereturn"[[:d:"..id.."|"..id.."]]"endendelsereturndata[parameter]endelsereturnmw.wikibase.label(id)oridendendlocalfunctionprintDatavalueMonolingualText(data,parameter)-- data fields: language [string], text [string]ifparameterthenreturndata[parameter]elselocalresult=mw.ustring.gsub(mw.ustring.gsub(i18n.monolingualtext,"%%language",data["language"]),"%%text",data["text"])returnresultendendlocalfunctiongetSnakValue(snak,parameter)-- snaks have three types: "novalue" for null/nil, "somevalue" for not null/not nil, or "value" for actual dataifsnak.snaktype=="value"then-- call the respective snak parserifsnak.datavalue.type=="string"thenreturnsnak.datavalue.valueelseifsnak.datavalue.type=="globecoordinate"thenreturnprintDatavalueCoordinate(snak.datavalue.value,parameter)elseifsnak.datavalue.type=="quantity"thenreturnprintDatavalueQuantity(snak.datavalue.value,parameter)elseifsnak.datavalue.type=="time"thenreturnprintDatavalueTime(snak.datavalue.value,parameter)elseifsnak.datavalue.type=="wikibase-entityid"thenreturnprintDatavalueEntity(snak.datavalue.value,parameter)elseifsnak.datavalue.type=="monolingualtext"thenreturnprintDatavalueMonolingualText(snak.datavalue.value,parameter)endendreturnmw.wikibase.renderSnak(snak)endlocalfunctiongetQualifierSnak(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 snaksifqualifierIdthen-- search the attribute snak with the given qualifier as keyifclaim.qualifiersthenlocalqualifier=claim.qualifiers[qualifierId]ifqualifierthenreturnqualifier[1]endendreturnnil,printError("qualifier-not-found")else-- otherwise return the main snakreturnclaim.mainsnakendendlocalfunctiondatavalueTimeToDateObject(data)localsign,year,month,day,hour,minute,second=string.match(data.time,"(.)(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)Z")localresult={year=tonumber(year),month=tonumber(month),day=tonumber(day),hour=tonumber(hour),min=tonumber(minute),sec=tonumber(second),timezone=data.timezone,julian=data.calendarmodelandstring.match(data.calendarmodel,"Q11184$")}ifsign=="-"thenresult.year=-result.yearendreturnresultendfunctionjulianDay(dateObject)localyear=dateObject.yearlocalmonth=dateObject.monthor0localday=dateObject.dayor0ifmonth==0thenmonth=1endifday==0thenday=1endifmonth<=2thenyear=year-1month=month+12endlocaltime=((((dateObject.secor0)/60+(dateObject.minor0)+(dateObject.timezoneor0))/60)+(dateObject.houror0))/24localbifdateObject.julianthenb=0elselocalcentury=math.floor(year/100)b=2-century+math.floor(century/4)endreturnmath.floor(365.25*(year+4716))+math.floor(30.6001*(month+1))+day+time+b-1524.5endfunctiongetQualifierSortValue(claim,qualifierId)localsnak=getQualifierSnak(claim,qualifierId)ifsnakandsnak.snaktype=="value"thenifsnak.datavalue.type=="time"thenreturnjulianDay(datavalueTimeToDateObject(snak.datavalue.value))elsereturngetSnakValue(snak)endendendlocalfunctiongetValueOfClaim(claim,qualifierId,parameter)localerrorlocalsnaksnak,error=getQualifierSnak(claim,qualifierId)ifsnakthenreturngetSnakValue(snak,parameter)elsereturnnil,errorendendfunctionformatReference(ref)-- "imported from"-references are mostly useless, skipp themifref["P143"]then-- do nothingelselocalformatedRefiftype(ref["P854"])=="string"andtype(ref["P1476"])=="string"thenformatedRef="["..ref["P854"].." "..ref["P1476"].."], "ref["P854"]=nilref["P1476"]=nilendforprop,valueinpairs(ref)doiftype(value)=="table"thenfor_,subvalueinpairs(value)doifformatedRefthenformatedRef=formatedRef..", "elseformatedRef=""endformatedRef=formatedRef..tostring(mw.wikibase.label(prop))..": "..subvalueendelseifformatedRefthenformatedRef=formatedRef..", "elseformatedRef=""endformatedRef=formatedRef..tostring(mw.wikibase.label(prop))..": "..valueendendreturnformatedRefendendlocalfunctiongetReferences(frame,claim)localresult=""-- traverse through all referencesforrefinpairs(claim.referencesor{})dolocalrefTable={}forsnakkey,snakvalinorderedpairs(claim.references[ref].snaksor{},claim.references[ref]["snaks-order"])doif#snakval==1thenrefTable[snakkey]=getSnakValue(snakval[1])else--multival={}forsnakidx=1,#snakvaldotable.insert(multival,getSnakValue(snakval[snakidx]))endrefTable[snakkey]=multivalendendformatedRef=formatReference(refTable)ifformatedRefthenifframe.args['references']=="raw"thenifref==1thenresult=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)endendendreturnresultendlocalfunctionhasqualifier(claim,qualifierproperty)localinvertifstring.sub(qualifierproperty,1,1)=="!"theninvert=trueelseinvert=falseendifnotclaim.qualifiersandnotinvertthenreturnfalseendifnotclaim.qualifiersandinvertthenreturntrueendifqualifierproperty==''thenreturntrueendifnotinvertandnotclaim.qualifiers[qualifierproperty]thenreturnfalseendifinvertandclaim.qualifiers[string.sub(qualifierproperty,2)]thenreturnfalseendreturntrueendlocalfunctionhassource(claim,sourceproperty)ifnotclaim.referencesthenreturnfalseendifsourceproperty==''thenreturntrueendifstring.sub(sourceproperty,1,1)~="!"thenfor_,sourceinpairs(claim.references)doifsource.snaks[sourceproperty]thenreturntrueendendreturnfalseelsefor_,sourceinpairs(claim.references)doforkeyinpairs(source.snaks)doifkey~=string.sub(sourceproperty,2)thenreturntrueendendendreturnfalseendendfunctionatdate(claim,mydate)localrefdateifnotmydateormydate==""thenrefdate=os.date("!*t")elseifstring.match(mydate,"^%d+$")thenrefdate={year=tonumber(mydate)}elserefdate=datavalueTimeToDateObject({time=mw.language.getContentLanguage():formatDate("+Y-m-d\\TH:i:s\\Z",mydate)})endendlocalrefjd=julianDay(refdate)localmindate=getQualifierSortValue(claim,propertyId["starttime"])localmaxdate=getQualifierSortValue(claim,propertyId["endtime"])ifmindateandmindate>refjdthenreturnfalseendifmaxdateandmaxdate<refjdthenreturnfalseendreturntrueend--returns a table of claims excluding claims not passed the filtersfunctionfilterClaims(frame,claims)localfunctionfilter(condition,filterfunction)ifnotframe.args[condition]thenreturnendlocalnewclaims={}fori,claiminpairs(claims)doiffilterfunction(claim,frame.args[condition])thentable.insert(newclaims,claim)endendclaims=newclaimsendfilter('hasqualifier',hasqualifier)filter('hassource',hassource)filter('atdate',atdate)returnclaimsendfunctionp.claim(frame)localproperty=frame.args[1]or""localid=frame.args["id"]localqualifierId=frame.args["qualifier"]localparameter=frame.args["parameter"]locallanguage=frame.args["language"]locallist=frame.args["list"]localincludeempty=frame.args["includeempty"]localreferences=frame.args["references"]localsort=frame.args["sort"]localsortInItem=frame.args["sortInItem"]localinverse=frame.args["inverse"]localshowerrors=frame.args["showerrors"]localdefault=frame.args["default"]ifdefaultthenshowerrors=nilend-- get wikidata entitylocalentity=mw.wikibase.getEntity(id)ifnotentitythenifshowerrorsthenreturnprintError("entity-not-found")elsereturndefaultendend-- fetch the first claim of satisfying the given propertylocalclaimsifentity.claimsthenclaims=entity.claims[mw.wikibase.resolvePropertyId(property)]endifnotclaimsornotclaims[1]thenifshowerrorsthenreturnprintError("property-not-found")elsereturndefaultendend--filter claimsclaims=filterClaims(frame,claims)ifnotclaims[1]thenreturndefaultend-- get initial sort indiceslocalsortindices={}foridxinpairs(claims)dosortindices[#sortindices+1]=idxendlocalcomparatorifsortthen-- sort by time qualifiercomparator=function(a,b)localtimea=getQualifierSortValue(claims[a],sort)or''localtimeb=getQualifierSortValue(claims[b],sort)or''iftype(timea)~=type(timeb)andnot(tonumber(timea)andtonumber(timeb))theniftonumber(timea)thenreturntrueelseiftonumber(timeb)thenreturnfalseelseiftostring(timea)andtostring(timeb)thenifinversethenreturntostring(timea)>tostring(timeb)elsereturntostring(timea)<tostring(timeb)endelsereturnfalseend-- different types, neither numbers nor strings, no chance to compare => random result to avoid script errorelseiftonumber(timea)andtonumber(timeb)thentimea=tonumber(timea)timeb=tonumber(timeb)endifinversethenreturntimea>timebelsereturntimea<timebendendelseifsortInItemthen-- fill table sortkeyslocalsortkeys={}localsnakSinglelocalsortkeyValueIdlocalclaimContainingValueforidx,claiminpairs(claims)dosnakSingle=getQualifierSnak(claim)sortkeyValueId="Q"..getSnakValue(snakSingle,"numeric-id")claimContainingValue=mw.wikibase.getEntity(sortkeyValueId).claims[mw.wikibase.resolvePropertyId(sortInItem)]ifclaimContainingValuethensortkeys[#sortkeys+1]=getValueOfClaim(claimContainingValue[1])elsesortkeys[#sortkeys+1]=""endendcomparator=function(a,b)ifinversethenreturnsortkeys[a]>sortkeys[b]elsereturnsortkeys[a]<sortkeys[b]endendelse-- sort by claim rankcomparator=function(a,b)localrankmap={deprecated=2,normal=1,preferred=0}localranka=rankmap[claims[a].rankor"normal"]..string.format("%08d",a)localrankb=rankmap[claims[b].rankor"normal"]..string.format("%08d",b)returnranka<rankbendendtable.sort(sortindices,comparator)localresultlocalerroriflistthenlist=string.gsub(list,"\\n","\n")-- if a newline is provided (whose backslash will be escaped) unescape itlocalvalue-- iterate over all elements and return their value (if existing)result={}foridxinpairs(claims)dolocalclaim=claims[sortindices[idx]]value,error=getValueOfClaim(claim,qualifierId,parameter)ifnotvalueandvalue~=0andshowerrorsthenvalue=errorendifnotvalueandvalue~=0andincludeemptythenvalue=""endifvalueandreferencesthenvalue=value..getReferences(frame,claim)endresult[#result+1]=valueendresult=table.concat(result,list)else-- return first elementlocalclaim=claims[sortindices[1]]iflanguageandclaim.mainsnak.datatype=="monolingualtext"then-- iterate over claims to find adequate languageforidx,claiminpairs(claims)doifclaim.mainsnak.datavalue.value.language==languagethenresult,error=getValueOfClaim(claim,qualifierId,parameter)breakendendelseresult,error=getValueOfClaim(claim,qualifierId,parameter)endifresultandreferences=="raw"thenlocalref=getReferences(frame,claim)returnresult,refendifresultandreferencesthenresult=result..getReferences(frame,claim)endendifresultthenreturnresultelseifshowerrorsthenreturnerrorelsereturndefaultendendendfunctionp.getValue(frame)localparam=frame.args[2]ifparam=="FETCH_WIKIDATA"orparam==i18n["FETCH_WIKIDATA"]thenreturnp.claim(frame)elsereturnparamendend-- for use in templates onlyfunctionp.pageId(frame)localid=frame.args[1]returnp._pageId(id)end-- for use in modules onlyfunctionp._pageId(id)localentity=mw.wikibase.getEntity(id)ifnotentitythenreturnnilelsereturnentity.idendendfunctionp.labelOf(frame)localid=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 usedifnotidthenlocalentity=mw.wikibase.getEntity()ifnotentitythenreturnprintError("entity-not-found")endid=entity.idendreturnmw.wikibase.label(id)endfunctionp.sitelinkOf(frame)localid=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 usedifnotidthenlocalentity=mw.wikibase.getEntity()ifnotentitythenreturnprintError("entity-not-found")endid=entity.idendreturnmw.wikibase.sitelink(id)endfunctionp.badges(frame)localsite=frame.args[1]localid=frame.args[2]ifnotsitethenreturnprintError("site-not-found")endlocalentity=mw.wikibase.getEntity(id)ifnotentitythenreturnprintError("entity-not-found")endlocalbadges=entity.sitelinks[site].badgesifbadgesthenlocalresultforidx=1,#badgesdoifresultthenresult=result.."/"..badges[idx]elseresult=badges[idx]endendreturnresultendendfunctionp.sitelinkCount(frame)localfilter="^.*"..(frame.args[1]or"").."$"localid=frame.args[2]localentity=mw.wikibase.getEntity(id)localcount=0ifentityandentity.sitelinksthenforproject,_inpairs(entity.sitelinks)doifstring.find(project,filter)thencount=count+1endendendreturncountend-- call this in cases of script errors within a function instead of {{#invoke:Wikidata|<method>|...}} call {{#invoke:Wikidata|debug|<method>|...}}functionp.debug(frame)localfunc=frame.args[1]iffuncthen-- create new parameter set, where the first parameter with the function name is removedlocalnewargs={}forkey,valinpairs(frame.args)doiftype(key)=="number"thenifkey>1thennewargs[key-1]=valendelsenewargs[key]=valendendframe.args=newargslocalstatus,result=pcall(p[func],frame)ifstatusthenreturnresultelsereturn'<span class="error">'..result..'</span>'endelsereturnprintError("invalid-parameters")endendfunctionp.printEntity(frame)localid=frame.args[1]localentity=mw.wikibase.getEntity(id)ifentitythenreturn"<pre>"..mw.text.jsonEncode(entity,mw.text.JSON_PRETTY).."</pre>"endendreturnp