Module:LocationAndCountry

Από τη Βικιπαίδεια, την ελεύθερη εγκυκλοπαίδεια
Documentation icon Τεκμηρίωση module[δημιουργία]
-- 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('Module:Arguments').getArgs
local wikidata = require('Module:Wikidata')
local StringUtils = require('Module:StringUtils')
local GregorianDate = require('Module:GregorianDate')
local Date = require('Module:Date')
local TableTools = require('Module:TableTools')

local appendToString = StringUtils._appendToString
local emptyToNil = StringUtils._emptyToNil

local ESCALATABLE_LOCATION_ENTITIES = { -- if this is one of the types of the location from Wikidata, then we skip and escalate it to the administrative unit
	'Q16917', --hospital
	'Q1059324', -- university hospital
	'Q702842', --municipal arrondissement (France)
	'Q15921300', --sector of Bucharest
}
local ADMIN_UNIT_ATTRACTOR_ENTITIES = { -- if this is one of the types of the location from Wikidata, then we also pull in the administrative unit
	'Q532', -- village
	'Q659103', -- commune of Romania
}
local p = {}

local function extractGregorianDateFromSnak(snak, hms)
	local afterDate = wikidata.extractDateFromClaim(snak)
	local afterDateGregorian = GregorianDate.convertToGregorianIfInInterval(afterDate)
	afterDateGregorian.hour = hms[1]
	afterDateGregorian.min = hms[2]
	afterDateGregorian.sec = hms[3]
	after = tonumber(mw.language.getContentLanguage():formatDate('U', os.date('%d %B %Y %H:%M:%S', os.time(afterDateGregorian)), nil))
	return after
end

local function getShortOrOfficialNameRetrieverClosure(ts)
	return function(object)
		if not object then return nil end -- just to be safe: this callback should only be called with existing objects
		local correspondingShortNameClaim = wikidata.findClaimForTimestamp(object.id, 'P1813', ts)
		if correspondingShortNameClaim then return correspondingShortNameClaim.mainsnak.datavalue.value.text end
		local correspondingOfficialNameClaim = wikidata.findClaimForTimestamp(object.id, 'P1448', ts)
		if ts and correspondingOfficialNameClaim then return correspondingOfficialNameClaim.mainsnak.datavalue.value.text end
	end
end

local function getOfficialNameRetrieverClosure(ts)
	return function(object)
		if not object then return nil end -- just to be safe: this callback should only be called with existing objects
		local correspondingOfficialNameClaim = wikidata.findClaimForTimestamp(object.id, 'P1448', ts)
		if ts and correspondingOfficialNameClaim then return correspondingOfficialNameClaim.mainsnak.datavalue.value.text end
	end
end

local function getShortNameRetrieverClosure(ts)
	return function(object)
		if not object then return nil end -- just to be safe: this callback should only be called with existing objects
		local correspondingShortNameClaim = wikidata.findClaimForTimestamp(object.id, 'P1813', ts)
		if correspondingShortNameClaim then return correspondingShortNameClaim.mainsnak.datavalue.value.text end
	end
end

local function getAdminUnitForTimestamp(locationId, ts)
	local unitClaim = wikidata.findClaimForTimestamp(locationId, 'P131', ts)
	if unitClaim and unitClaim.mainsnak and unitClaim.mainsnak.snaktype == 'value' then
		return unitClaim.mainsnak.datavalue.value.id
	else
		local unitId = wikidata.loadOneValueInChain({locationId, 'P131', 'raw'})
		if unitId and mw.ustring.len(unitId) > 0 then
			return unitId
		end
	end
	return nil
end

p.displayFromParams = function(param, entity, timestamp, maxvalues, separator)
	if param == nil then return '' end
	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
	
	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 and 0 < #wdDates then
				local wdDateIdx = 1
				while wdDateIdx <= #wdDates and (wdDates[wdDateIdx].claim.type ~= 'statement' or wdDates[wdDateIdx].claim.mainsnak.snaktype ~= 'value') do
					wdDateIdx = wdDateIdx + 1
				end
				if wdDateIdx <= #wdDates and (wdDates[wdDateIdx].claim.type == 'statement' and wdDates[wdDateIdx].claim.mainsnak.snaktype == 'value') then
					wdDates[wdDateIdx] = GregorianDate.convertToGregorianIfInInterval(wdDates[wdDateIdx])
					ts = wdDates[wdDateIdx]
				end
			end
		end
		if ts == nil and type(timestamp) == 'string' then
			ts = DateUtils.parseYear(timestamp)
		elseif ts == nil and type(timestamp) == 'table' and timestamp.year then
			ts = timestamp
		end
	end

	
	local valueList = {}
	local valueCount = 0
	if claims and 0 < #claims then
		for claimIdx, actualClaim in pairs(claims) do
			valueCount = valueCount + 1
			local locationEntitiesIds = {}
			local locationNames = {}
			local skipActualLocation = false
			if actualClaim.mainsnak and actualClaim.mainsnak.snaktype == 'value' and actualClaim.mainsnak.datavalue.type == 'wikibase-entityid' then
				local locationEntityId = actualClaim.mainsnak.datavalue.value.id
				
				local seekAdminUnit
				repeat
					seekAdminUnit = false
					repeat
						skipActualLocation = false
						--what is this location?
						local locationInstanceOfClaims = mw.wikibase.getBestStatements(locationEntityId, 'P31')
						if locationInstanceOfClaims then for _,eachInstOfClaim in ipairs(locationInstanceOfClaims) do
							if eachInstOfClaim.mainsnak and eachInstOfClaim.mainsnak.snaktype == 'value' and eachInstOfClaim.mainsnak.datavalue.type == 'wikibase-entityid' then
								mw.logObject(eachInstOfClaim.mainsnak.datavalue.value.id, '[LocationAndCountry] location is a')
								if TableTools.contains(ESCALATABLE_LOCATION_ENTITIES, eachInstOfClaim.mainsnak.datavalue.value.id) then --escalatable?
									skipActualLocation = true
								end
								if TableTools.contains(ADMIN_UNIT_ATTRACTOR_ENTITIES, eachInstOfClaim.mainsnak.datavalue.value.id) then --do we also pull the admin unit?
									seekAdminUnit = true
								end
							end
						end end -- end for end if
							
						if skipActualLocation then
							locationEntityId = getAdminUnitForTimestamp(locationEntityId, ts)
						else
							table.insert(locationEntitiesIds, locationEntityId)
							locationNames[locationEntityId] = wikidata.findLinkToItemWithCallback(locationEntityId, false, getOfficialNameRetrieverClosure(ts))
						end
					until not skipActualLocation
				
					--attempt to also load administrative unit, but only if present as a qualifier
					local unitQualifier = actualClaim.qualifiers and actualClaim.qualifiers['P131'] and actualClaim.qualifiers['P131'][1]
					if unitQualifier and unitQualifier.snaktype == 'value' then
						table.insert(locationEntitiesIds, unitQualifier.datavalue.value.id)
						locationNames[unitQualifier.datavalue.value.id] = wikidata.findLinkToItemWithCallback(unitQualifier.datavalue.value.id, false, getShortOrOfficialNameRetrieverClosure(ts))
						seekAdminUnit = false
					end
					--... or something happened that we have to look for it
					if seekAdminUnit then
						local adminUnitId = getAdminUnitForTimestamp(locationEntityId, ts)
						if adminUnitId then
							table.insert(locationEntitiesIds, adminUnitId)
							locationEntityId = adminUnitId
						else
							seekAdminUnit = false
						end
					end
				until not seekAdminUnit
				
				-- attempt to identify country in the qualifier first, but if it's not, go to the entity
				local countryQualifier = actualClaim.qualifiers and actualClaim.qualifiers['P17'] and actualClaim.qualifiers['P17'][1]
				local countryId = nil

				if countryQualifier and countryQualifier.snaktype == 'value' then
					table.insert(locationEntitiesIds, countryQualifier.datavalue.value.id)
					locationNames[countryQualifier.datavalue.value.id] = wikidata.findLinkToItemWithCallback(countryQualifier.datavalue.value.id, false, getShortOrOfficialNameRetrieverClosure(ts))
				else
					local countryClaim = nil
					countryClaim = wikidata.findClaimForTimestamp(locationEntityId, 'P17', ts)
					if countryClaim and countryClaim.mainsnak and countryClaim.mainsnak.snaktype == 'value' then
						countryId = countryClaim.mainsnak.datavalue.value.id
						table.insert(locationEntitiesIds, countryId)
					elseif not countryClaim then
						countryId = wikidata.loadOneValueInChain({locationEntityId, 'P17', 'raw'})
						if countryId and mw.ustring.len(countryId) > 0 then
							table.insert(locationEntitiesIds, countryId)
						end
					end
				end
				locationEntitiesIds = TableTools.removeDuplicates(locationEntitiesIds)
				local locationNamesList = {}
				for _,eachLocationId in ipairs(locationEntitiesIds) do
					if not locationNames[eachLocationId] then
						locationNames[eachLocationId] = wikidata.findLinkToItemWithCallback(eachLocationId, false, getShortNameRetrieverClosure(ts))
					end
					table.insert(locationNamesList, locationNames[eachLocationId])
				end
				if 0 < #locationNamesList then
					table.insert(
						valueList,
						appendToString({
							emptyToNil({table.concat(locationNamesList, ', ')}),
							wikidata.outputReferences(actualClaim)
						})
					)
				end
			end
		end	
	end
	if #valueList == 0 then return '' end
	if separator == nil then separator = '; ' end
	if maxvalues > #valueList then maxvalues = #valueList end
	return table.concat(valueList, separator, 1, maxvalues)
end

p.displayFromArgs = function(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 p.displayFromParams(param, entity, timestamp, maxvalues, separator)
end

p.displayFromFrame = function(frame)
	local args = getArgs(frame, { frameOnly = true })
	return p.displayFromArgs(args)
end

p.displayFromParentFrame = function(frame)
	local args = getArgs(frame, { parentOnly = true})
	return p.displayFromArgs(args)
end

return p