Modul:LocationAndCountry
Aspect
Implementează formatul {{Loc, Țară}}, dar poate fi apelat prin intermediul funcției displayFromParams și din alte module pentru a afișa țara atașată de un loc la un anumit moment de timp.
-- will display a wikidata property representing a location, followed by a comma and the name of the country, both with wikilinks
-- the first argument specifies the parameter to be displayed
-- the second argument specifies the entity ID
-- the third argument specifies a timestamp showing the moment in time for which the country is to be identified
-- the fourth argument specifies the maximum number of values to be processed (default 1)
-- the fifth argument specifies the separator to use when displaying multiple values
local getArgs = require('Modul:Arguments').getArgs
local Wikidata = require('Modul:Wikidata')
local lacData = mw.loadData('Modul:LocationAndCountry/data')
local TableTools = require('Modul:TableTools')
local GregorianDate = require('Modul:GregorianDate')
local DateUtils = require('Modul:DateUtils')
local Set = require('Modul:Set')
local illWd = require('Modul:Ill-wd').fromArgs
local Transliteration = require('Modul:Transliteration')
local getValueByTimestamp = require('Modul:TimestampedTable').getValueByTimestamp
local p = {}
local function getClaimsForParam(entity, param)
local claims = nil
local workingEntityId = nil
if type(entity) == 'table' then
workingEntityId = entity.id
claims = entity:getBestStatements(param)
else
workingEntityId = entity
if not workingEntityId then workingEntityId = mw.wikibase.getEntityIdForCurrentPage() end
if type(entity) == 'number' then workingEntityId = 'Q' .. tostring(entity) end
if workingEntityId == nil then return '' end
claims = mw.wikibase.getBestStatements(workingEntityId, param)
end
return claims, workingEntityId
end
local function getTimestamp(timestamp, workingEntityId)
local ts = nil
if timestamp then
if type(timestamp) == 'string' and mw.ustring.gmatch(timestamp, 'P%d+') then
local wdDates = Wikidata.findDateValues(timestamp, workingEntityId)
if wdDates then for _,eachWdDate in ipairs(wdDates) do
if Wikidata.isClaimTrue(eachWdDate.claim) and Wikidata.hasValueSnak(eachWdDate.claim) then
ts = GregorianDate.convertToGregorianIfInInterval(eachWdDate)
break
end
end end
end
if ts == nil and type(timestamp) == 'string' and mw.ustring.match(timestamp, '^%d+') then
ts = DateUtils.parseYear(timestamp)
elseif ts == nil and type(timestamp) == 'table' and timestamp.year then
ts = timestamp
end
end
return ts
end
local function getUatsAndCountryFromQuals(claim)
if not Wikidata.isClaimTrue(claim) or not claim.qualifiers then return nil end
local country = nil
local uats = {}
if claim.qualifiers['P17'] then for _,eachCountryQual in ipairs(claim.qualifiers['P17']) do
if Wikidata.isValueSnak(eachCountryQual) then
country = eachCountryQual.datavalue.value.id
break
end
end end
if claim.qualifiers['P131'] then for _,eachUatQual in ipairs(claim.qualifiers['P131']) do
if Wikidata.isValueSnak(eachUatQual) then
table.insert(uats, eachUatQual.datavalue.value.id)
end
end end
return uats, country
end
local function getClaimForTimestamp(entityId, propId, ts)
local timestampedClaim = Wikidata.findClaimForTimestamp(entityId, propId, ts)
if timestampedClaim and Wikidata.hasValueSnak(timestampedClaim) then
local retVal = timestampedClaim
local claimTs = nil
if timestampedClaim.qualifiers and timestampedClaim.qualifiers['P582'] then
for _,endTQual in ipairs(timestampedClaim.qualifiers['P582']) do if Wikidata.isValueSnak(endTQual) then
claimTs = GregorianDate.convertToGregorianIfInInterval(DateUtils.extractDateFromWikidataSnak(endTQual))
break
end end
end
return retVal, claimTs
end
return nil, nil
end
local function getCountryIdForTimestamp(entityId, ts)
local countryClaim, retTs = getClaimForTimestamp(entityId, 'P17', ts)
if countryClaim and Wikidata.hasValueSnak(countryClaim) then
return countryClaim.mainsnak.datavalue.value.id, retTs
else
return Wikidata.loadOneValueInChain({entityId, 'P17', 'raw'}), nil
end
end
-- returns 0, 1 or 2 - the number of admin units to be pulled
local function shouldPullUats(entityId, countryId)
-- villages, rural settlements, should get 2 uats
if Wikidata.isA(entityId, lacData.adminUnitExpandableLocationTypes) then
return 2
end
-- locations in federal countries should get at least one UAT regardless of size
if countryId and (Wikidata.isA(countryId, { 'Q22676603', 'Q43702' })
or TableTools.contains(lacData.adminUnitExpandableCountries, countryId)) then
return 1
end
-- only big cities should have just the country
if not Wikidata.isA(entityId, {'Q1549591', 'Q1637706', 'Q200250'}) then
return 1
end
return 0
end
local function recurseUats(entityId, ts)
local crtUatId = entityId
local crtCountryId = Wikidata.loadOneValueInChain({entityId, 'P17', 'raw'})
local timestampedCountryId = getCountryIdForTimestamp(entityId, ts)
local uatList = {}
while crtUatId and crtUatId ~= crtCountryId and crtUatId ~= timestampedCountryId do
local timestampedUatClaim = Wikidata.findClaimForTimestamp(crtUatId, 'P131', ts)
crtUatId = timestampedUatClaim and Wikidata.hasValueSnak(timestampedUatClaim) and timestampedUatClaim.mainsnak.datavalue.value.id or Wikidata.loadOneValueInChain({crtUatId, 'P131', 'raw'})
if (TableTools.contains(uatList, crtUatId)) then
break
end
table.insert(uatList, crtUatId)
end
local uatListCountryId = nil
if #uatList > 0 and (uatList[#uatList] == crtCountryId or uatList[#uatList] == timestampedCountryId or Wikidata.isA(uatList[#uatList], {'Q6256'})) then
uatListCountryId = table.remove(uatList, #uatList)
end
return uatList, uatListCountryId
end
local function shouldSkipLocation(entityId)
return Wikidata.isA(entityId, lacData.escalatableLocationTypes)
end
local function trimUats(uatList)
while #uatList > 0 and (Wikidata.isA(uatList[1], lacData.skippableUatTypes) or TableTools.contains(lacData.skippableUats, uatList[1])) do
table.remove(uatList, 1)
end
while #uatList > 0 and (Wikidata.isA(uatList[#uatList], lacData.skippableUatTypes) or TableTools.contains(lacData.skippableUats, uatList[#uatList])) do
table.remove(uatList, #uatList)
end
return uatList
end
local function resolvePartName(partId, ts, crtCountryId, uats)
if lacData.locationNameOverrides[partId] then
local timestampedLabel = getValueByTimestamp(lacData.locationNameOverrides[partId], ts)
return illWd(partId, timestampedLabel)
end
local offLangCodes = { 'ro' }
local offLangSource = nil
local countryId = getCountryIdForTimestamp(partId, ts)
if TableTools.contains(lacData.invariantLocations, partId) or
(crtCountryId and TableTools.contains(lacData.invariantLocationCountries, crtCountryId))
or TableTools.size(Set.valueIntersection(recurseUats(partId), lacData.invariantLocationUats)) > 0
or Wikidata.isA(partId, lacData.invariantLocationTypes) then
offLangCodes = { 'ro' }
else
-- look for (1) official language of the location
local officialLangAtTsClaim = getClaimForTimestamp(partId, 'P37', ts)
local officialLangClaims = officialLangAtTsClaim and { officialLangAtTsClaim } or Wikidata.findBestClaimsForProperty(partId, 'P37')
-- (2) official language of one of the UATs
if not officialLangClaims or #officialLangClaims == 0 then
for uatIdx,eachUat in ipairs(uats or {}) do
local uatOfficialLangAtTsClaim = getClaimForTimestamp(eachUat, 'P37', ts)
local uatOfficialLangClaims = uatOfficialLangAtTsSnak and {uatOfficialLangAtTsClaim } or Wikidata.findBestClaimsForProperty(eachUat, 'P37')
if not officialLangClaims or #officialLangClaims == 0 then
officialLangClaims = uatOfficialLangClaims
offLangSource = { qId = eachUat, srcType = 'uat', srcIdx = uatIdx }
end
end
else
offLangSource = { qId = partId, srcType = 'part' }
end
-- (3) official language of the country
if not officialLangClaims or #officialLangClaims == 0 then
if not countryId then return illWd(partId) end
officialLangClaims = Wikidata.findBestClaimsForProperty(countryId, 'P37')
offLangSource = { qId = countryId, srcType = 'country' }
end
for _,eachOffLangClaim in ipairs(officialLangClaims) do
if Wikidata.hasValueSnak(eachOffLangClaim) and (not eachOffLangClaim.qualifiers or not eachOffLangClaim.qualifiers['P518']) then
local eachOffLangId = eachOffLangClaim.mainsnak.datavalue.value.id
local writingSystemQueue = Wikidata.getBestEntityIdsList(eachOffLangId, 'P282')
local writingSystem = writingSystemQueue and table.remove(writingSystemQueue, 1)
while writingSystem and writingSystem ~= '' and writingSystem ~= 8229 do
TableTools.appendAll(writingSystemQueue, Wikidata.getBestEntityIdsList(writingSystem, 'P282'))
writingSystem = table.remove(writingSystemQueue, 1)
end
local offLangCode = Wikidata.loadOneValueInChain({eachOffLangId, 'P218'})
if offLangCode and offLangCode ~= '' then
if writingSystem == 8229 or Transliteration.isTransliterationSupported(offLangCode) then
table.insert(offLangCodes, offLangCode)
end
end
end
end
end
if #offLangCodes > 1 and offLangSource then
--search config order
local configOrder = lacData.languageOrder[offLangSource.qId]
if offLangSource.srcType == 'part' or offLangSource.srcType == 'uat' then
local startIdx = offLangSource.srcType == 'uat' and offLangSource.srcIdx or 1
local crtIdx = startIdx
while not configOrder and crtIdx <= #(uats or {}) do
configOrder = configOrder or lacData.languageOrder[uats[crtIdx]]
crtIdx = crtIdx + 1
end
configOrder = configOrder or lacData.languageOrder[countryId]
end
if configOrder then
local sortedOffLangCodes = {}
local unknownOffLangCodes = {}
for _,eachLang in ipairs(configOrder) do
if TableTools.contains(offLangCodes, eachLang) then
table.insert(sortedOffLangCodes, eachLang)
end
end
for _,eachLang in ipairs(offLangCodes) do
if not TableTools.contains(sortedOffLangCodes, eachLang) then
table.insert(sortedOffLangCodes, eachLang)
end
end
offLangCodes = sortedOffLangCodes
end
end
for _,offLangCode in ipairs(offLangCodes) do
local wdPropForName = Wikidata.isA(partId, lacData.shortNameLocationTypes) and 'P1813' or 'P1448'
local officialNameClaim = Wikidata.findClaimForTimestamp(partId, wdPropForName, ts, offLangCode)
if officialNameClaim and Wikidata.isClaimTrue(officialNameClaim) and Wikidata.hasValueSnak(officialNameClaim) then
local nameInOffLang = officialNameClaim.mainsnak.datavalue.value.text
if wdPropForName == 'P1448' and (not officialNameClaim.qualifiers or not officialNameClaim.qualifiers['P582']) then -- if this is a former name, use it
-- if this is the current name, then just get the label from that language
nameInOffLang = mw.wikibase.getLabelByLang(partId, offLangCode)
end
return illWd(partId, Transliteration.isTransliterationSupported(offLangCode) and Transliteration.transliterate(nameInOffLang, offLangCode) or nameInOffLang)
end
end
for _,offLangCode in ipairs(offLangCodes) do
local labelInLang = Wikidata.findLabel(partId, offLangCode)
if Transliteration.isTransliterationSupported(offLangCode) then
labelInLang = Transliteration.transliterate(labelInLang, offLangCode)
end
return illWd(partId, labelInLang)
end
return nil
end
local function resolvePartNames(partIds, ts, crtCountryId, uats)
if not partIds then return nil end
local res = {}
for _,partId in ipairs(partIds) do
table.insert(res, resolvePartName(partId, ts, crtCountryId, uats))
end
return res
end
local function collapseCapitalledUat(locationEntityId, uatEntitiesIds)
if not uatEntitiesIds or #uatEntitiesIds == 0 then return uatEntitiesIds end
local out = {}
local firstUatEntCapital = Wikidata.loadOneValueInChain({uatEntitiesIds[1], 'P36', 'raw'})
if not firstUatEntCapital or firstUatEntCapital ~= locationEntityId then
table.insert(out, uatEntitiesIds[1])
end
for idx = 2,#uatEntitiesIds do table.insert(out, uatEntitiesIds[idx]) end
return out
end
local function displayFromParams(param, entity, timestamp, maxvalues, separator, enableRefs)
if param == nil then return '' end
local claims, workingEntityId = getClaimsForParam(entity, param)
local ts = getTimestamp(timestamp, workingEntityId)
local valueList = {}
local maxvalues = maxvalues or 1
if claims and 0 < #claims then
for _,actualClaim in ipairs(claims) do
local uatEntitiesIds = {}
local countryId = nil
local locationEntityId = nil
if Wikidata.isClaimTrue(actualClaim) and Wikidata.hasValueSnak(actualClaim) then
-- get admin unit(s) and/or country from qualifiers
uatEntitiesIds, countryId = getUatsAndCountryFromQuals(actualClaim)
locationEntityId = actualClaim.mainsnak.datavalue.value.id
local countryByTs, countryTs = getCountryIdForTimestamp(locationEntityId, ts)
countryId = countryId or countryByTs
-- (1) see if admin unit needs to be pulled
local uatsEnabled = shouldPullUats(locationEntityId, countryId)
-- (2) pull all admin units based on timestamp until reaching nothing, self or country
local allUats, uatCountryId = recurseUats(locationEntityId, ts)
if uatsEnabled > 0 then
if uatCountryId and uatCountryId ~= countryId and not countryTs then
countryId = uatCountryId
elseif countryTs and uatCountryId and uatCountryId ~= countryId then
allUats = {}
end
-- (3) if the location itself is to be skipped, keep the first admin unit in the list as location
while #allUats > 0 and shouldSkipLocation(locationEntityId) do
locationEntityId = table.remove(allUats, 1)
end
-- (4) apply rules to eliminate from both ends
local trimmedUats = trimUats(allUats)
-- (5) keep only first and last (or the only one if only one is left)
uatEntitiesIds = #trimmedUats == 0 and {} or #trimmedUats == 1 and trimmedUats or uatsEnabled == 1 and {trimmedUats[#trimmedUats]} or {trimmedUats[1], trimmedUats[#trimmedUats]}
if #uatEntitiesIds > 1 then
uatEntitiesIds = collapseCapitalledUat(locationEntityId, uatEntitiesIds)
end
else
uatEntitiesIds = {}
end
local allPartIds = {}
table.insert(allPartIds, locationEntityId)
for __,eachUat in ipairs(uatEntitiesIds) do table.insert(allPartIds, eachUat) end
local allPartLinks = resolvePartNames(allPartIds, ts, Wikidata.loadOneValueInChain({locationEntityId, 'P17', 'raw'}), allUats)
if not countryId and uatEntitiesIds and #uatEntitiesIds > 0 then
countryId = Wikidata.loadOneValueInChain({uatEntitiesIds[#uatEntitiesIds], 'P17', 'raw'})
end
if countryId and not TableTools.contains(allPartIds, countryId)
and not Wikidata.isA(#uatEntitiesIds > 0 and uatEntitiesIds[#uatEntitiesIds] or locationEntityId, lacData.skipCountryTypes) then
table.insert(allPartLinks, lacData.countryNameOverrides[countryId] and illWd(countryId, lacData.countryNameOverrides[countryId]) or resolvePartName(countryId, ts, countryId))
end
local crtVal = table.concat(allPartLinks, ', ')
if enableRefs then crtVal = crtVal .. Wikidata.outputReferences(actualClaim) end
table.insert(valueList, crtVal)
end
if #valueList >= maxvalues then
break
end
end
end
return table.concat(valueList, separator or '; ')
end
p.displayFromParams = displayFromParams
local function displayFromArgs(args)
local param = nil
local entity = nil
local timestamp = nil
local maxvalues = 1
local separator = '; '
if args[1] or args['param'] then
param = args[1] or args['param']
end
if args[2] or args['entityId'] then
entity = args[2] or args['entityId']
end
if args[3] or args['timestamp'] then
timestamp = args[3] or args['timestamp']
end
if args[4] or args['maxvalues'] then
maxvalues = tonumber(args[4] or args['maxvalues'])
end
if args[5] or args['separator'] then
separator = args[5] or args['separator']
end
return displayFromParams(param, entity, timestamp, maxvalues, separator)
end
p.displayFromArgs = displayFromArgs
p.displayFromFrame = function(frame)
local args = getArgs(frame, { frameOnly = true })
return displayFromArgs(args)
end
p.displayFromParentFrame = function(frame)
local args = getArgs(frame, { parentOnly = true})
return displayFromArgs(args)
end
return p