Modul:Wikidata/Places
Görünüş
Bu modulun sənədləşdirmə səhifəsi Modul:Wikidata/Places/doc səhifəsində yaradıla bilər
local categorizeByPlace = true;
local WDS = require( 'Module:WikidataSelectors' );
local Flags = require( 'Module:Wikidata/Flags' );
local PlaceData = require( 'Module:Wikidata/places/config' );
local Qualifiers = require( 'Module:Wikidata/qualifiers' );
local function valuesToSet( values )
local result = {};
for key, value in pairs( values or {} ) do
if type( key ) == 'number' then
result[ value ] = true;
elseif value then
result[ key ] = true;
end
end
return result;
end
local ignoredPlaceTypes = valuesToSet( PlaceData.ignoredPlaceTypes );
local unignoredPlaceTypes = valuesToSet( PlaceData.unignoredPlaceTypes );
local unions = valuesToSet( PlaceData.unionTypes );
local countries = valuesToSet( PlaceData.countryTypes );
local constituentCountryTypes = valuesToSet( PlaceData.constituentCountryTypes );
local showCurrentLabels = valuesToSet( PlaceData.showCurrentLabels );
local skippedPlaceTypes = valuesToSet(
PlaceData.skippedPlaceTypes
);
local redundantParentSuffixes = PlaceData.redundantParentSuffixes or {};
local parentChainRules = {};
local adjacentParentRules = {};
local placeReplacementTargets = {};
for _, configuredRule in ipairs( PlaceData.parentChainRules or {} ) do
local patternLength = #configuredRule - 1;
local removePosition = tonumber( configuredRule[ #configuredRule ] );
if patternLength >= 2 and removePosition and
removePosition == math.floor( removePosition ) and
removePosition >= 1 and removePosition <= patternLength
then
local pattern = {};
for i = 1, patternLength do
pattern[ i ] = configuredRule[ i ];
end
local rule = {
pattern = pattern,
remove = removePosition,
};
if patternLength == 2 then
table.insert( adjacentParentRules, rule );
else
table.insert( parentChainRules, rule );
end
end
end
for _, replacement in ipairs( PlaceData.placeReplacements or {} ) do
if replacement.target then
for _, source in ipairs( replacement.sources or {} ) do
placeReplacementTargets[ source ] = replacement.target;
end
end
end
local function getCanonicalEntityId( entityId )
if not entityId then
return entityId;
end
local seen = {};
for _ = 1, 12 do
local replacement = placeReplacementTargets[ entityId ];
if not replacement or replacement == entityId or seen[ replacement ] then
return entityId;
end
seen[ entityId ] = true;
entityId = replacement;
end
return entityId;
end
local function cloneTable( source )
local result = {};
for key, value in pairs( source or {} ) do
result[ key ] = value;
end
return result;
end
local function canonicalizeEntitySnak( snak )
local value = snak and snak.datavalue and snak.datavalue.value;
local entityId = value and value.id;
if not entityId then
return snak;
end
local canonicalId = getCanonicalEntityId( entityId );
if canonicalId == entityId then
return snak;
end
local result = cloneTable( snak );
result.datavalue = cloneTable( snak.datavalue );
result.datavalue.value = cloneTable( value );
result.datavalue.value.id = canonicalId;
local numericId = tonumber( mw.ustring.match( canonicalId, '^Q(%d+)$' ) );
if numericId then
result.datavalue.value[ 'numeric-id' ] = numericId;
end
return result;
end
local p = {
config = {
hideSameLabels = false,
hidePartOfLabels = false,
hideUnitsForCapitals = true,
reverseOrder = false,
catAmbiguousGeoChains = '[[Kateqoriya:Vikipediya:Coğrafi zənciri olan səhifələr]]',
catLoopInGeoChains = '[[Kateqoriya:Vikipediya:Təkrarlanan coğrafi zənciri olan səhifələr]]',
catWikibaseError = '[[Kateqoriya:Vikipediya:Vikiverilənlər istifadə edən skript xətalı səhifələr]]'
}
};
local function min( prev, next )
if prev == nil then
return next;
elseif prev > next then
return next;
else
return prev;
end
end
local function max( prev, next )
if prev == nil then
return next;
elseif prev < next then
return next;
else
return prev;
end
end
local function getTimeBoundariesFromProperty( context, propertyId, ignoreUnknown )
local dateClaims = WDS.filter( context.entity.claims, propertyId );
if not dateClaims or #dateClaims == 0 then
return nil;
end
local left = nil;
local right = nil;
for _, claim in pairs( dateClaims ) do
local boundaries = claim.mainsnak and context.parseTimeBoundariesFromSnak( claim.mainsnak );
if boundaries then
left = min( left, boundaries[ 1 ] );
right = max( right, boundaries[ 2 ] );
elseif not ignoreUnknown then
return nil;
end
end
if not left or not right then return nil; end
return { left, right };
end
local function getTimeBoundariesFromProperties( context, propertyIds )
for _, propertyId in ipairs( propertyIds ) do
local result = getTimeBoundariesFromProperty( context, propertyId );
if result then
return result;
end
end
return nil;
end
local function getLifetimeBoundaries( context )
local claims = context.entity.claims;
local cached = context.placeLifetime;
if cached and cached.birthClaims == claims.P569 and
cached.deathClaims == claims.P570
then
return cached.boundaries;
end
local birth = getTimeBoundariesFromProperty( context, 'P569', true );
local death = getTimeBoundariesFromProperty( context, 'P570', true );
local lifetime = birth and {
birth[ 1 ], death and death[ 1 ] or os.time() * 1000
} or nil;
context.placeLifetime = {
birthClaims = claims.P569,
deathClaims = claims.P570,
boundaries = lifetime,
};
return lifetime;
end
local getTimeBoundariesFromQualifiers = Qualifiers.getTimeBoundariesFromQualifier;
local function selectTemporalEntitySnaks( context, claims, boundaries, pointInTime )
local dated = {};
local datedIds = {};
local undatedPreferred = {};
local undatedPreferredIds = {};
local undatedNormal = {};
local undatedNormalIds = {};
local function insertUnique( target, ids, snak, entityId )
if not ids[ entityId ] then
ids[ entityId ] = true;
snak = cloneTable( snak );
snak.placeClaims = claims;
table.insert( target, snak );
end
end
for _, claim in pairs( claims or {} ) do
if claim.rank ~= 'deprecated' then
local snak = canonicalizeEntitySnak( claim.mainsnak );
local entityId = snak and snak.snaktype == 'value' and
snak.datavalue and snak.datavalue.value and
snak.datavalue.value.id;
if entityId then
local hasStart = claim.qualifiers and
claim.qualifiers.P580 and
claim.qualifiers.P580[ 1 ] ~= nil;
local hasEnd = claim.qualifiers and
claim.qualifiers.P582 and
claim.qualifiers.P582[ 1 ] ~= nil;
if hasStart or hasEnd then
local startBoundaries = getTimeBoundariesFromQualifiers(
context, claim, 'P580'
);
local endBoundaries = getTimeBoundariesFromQualifiers(
context, claim, 'P582'
);
local matches = boundaries ~= nil and
( not hasStart or startBoundaries ~= nil ) and
( not hasEnd or endBoundaries ~= nil ) and
( startBoundaries == nil or
startBoundaries[ pointInTime and 1 or 2 ] <= boundaries[ 1 ] ) and
( endBoundaries == nil or
endBoundaries[ 1 ] >= boundaries[ 2 ] );
if matches then
insertUnique( dated, datedIds, snak, entityId );
end
elseif claim.rank == 'preferred' then
insertUnique(
undatedPreferred,
undatedPreferredIds,
snak,
entityId
);
else
insertUnique(
undatedNormal,
undatedNormalIds,
snak,
entityId
);
end
end
end
end
if #dated > 0 then
return dated;
end
if #undatedPreferred == 1 then
return undatedPreferred;
end
if #undatedPreferred > 1 then
return {};
end
if #undatedNormal == 1 then
return undatedNormal;
end
return {};
end
local function getParentsInBoundariesSnakImpl( context, entityId, boundaries, propertyIds, selectors )
local results = {};
local resultEntityIds = {};
entityId = getCanonicalEntityId( entityId );
local function insertResult( snak )
snak = canonicalizeEntitySnak( snak );
local resultId = snak and snak.datavalue and
snak.datavalue.value and snak.datavalue.value.id;
if resultId then
if resultEntityIds[ resultId ] then
return;
end
resultEntityIds[ resultId ] = true;
end
table.insert( results, snak );
end
if not propertyIds or #propertyIds == 0 then
return results;
end
for _, propertyId in ipairs( propertyIds ) do
if (not string.match( propertyId, '^P%d+$' )) then error('Incorrect propertyId: ' .. propertyId); end
local selector;
if (selectors ~= nil) then
selector = selectors[propertyId] or propertyId;
else
selector = propertyId;
end
local entityClaims = {};
entityClaims[propertyId] = mw.wikibase.getAllStatements( entityId, propertyId );
local filteredClaims = WDS.filter( entityClaims, selector .. '[rank:preferred, rank:normal]' );
if filteredClaims then
if propertyId == 'P131' then
for _, snak in ipairs( selectTemporalEntitySnaks(
context, filteredClaims, boundaries
) ) do
insertResult( snak );
end
else
for _, claim in pairs( filteredClaims ) do
if not boundaries or not propertyIds or #propertyIds == 0 then
insertResult( claim.mainsnak );
else
local startBoundaries = getTimeBoundariesFromQualifiers( context, claim, 'P580' );
local endBoundaries = getTimeBoundariesFromQualifiers( context, claim, 'P582' );
if ( startBoundaries == nil or startBoundaries[2] <= boundaries[1] ) and
( endBoundaries == nil or endBoundaries[1] >= boundaries[2] )
then
insertResult( claim.mainsnak );
end
end
end
end
end
if #results > 0 then
break;
end
end
return results;
end
local function getCountriesInBoundariesSnak( context, entityId, boundaries )
entityId = getCanonicalEntityId( entityId );
local entityClaims = {
P17 = mw.wikibase.getAllStatements( entityId, 'P17' )
};
local filteredClaims = WDS.filter(
entityClaims,
'P17[rank:preferred, rank:normal]'
);
return selectTemporalEntitySnaks( context, filteredClaims, boundaries );
end
local function getParentsInBoundariesSnak( context, entityId, boundaries )
if not entityId then error('entityId must be specified'); end
if type(entityId) ~= 'string' then error('entityId must be string'); end
if not boundaries then error('boundaries must be specified'); end
if type(boundaries) ~= 'table' then error('boundaries must be table'); end
entityId = getCanonicalEntityId( entityId );
local results = getParentsInBoundariesSnakImpl( context, entityId, boundaries, {'P131'} )
local isCountryResult = false;
if not results or #results == 0 then
results = getCountriesInBoundariesSnak( context, entityId, boundaries )
isCountryResult = true;
elseif #results > 1 then
local countryResults = getCountriesInBoundariesSnak( context, entityId, boundaries )
if countryResults and #countryResults == 1 then
results = countryResults;
isCountryResult = true;
end
end
for r, result in pairs( results ) do
if result.snaktype ~= 'value' then
return nil;
end
local resultId = result.datavalue.value.id;
if resultId == entityId then
return nil;
end
end
return results, isCountryResult;
end
local function isSkipTopLevel( entity )
local isCountry = false;
local isUnion = false;
if entity and
entity.claims and
entity.claims.P31
then
for c, claim in pairs( entity.claims.P31 ) do
if claim and
claim.mainsnak and
claim.mainsnak.datavalue and
claim.mainsnak.datavalue.value and
claim.mainsnak.datavalue.value.id
then
local typeId = claim.mainsnak.datavalue.value.id;
isCountry = isCountry or countries[ typeId ];
isUnion = isUnion or unions[ typeId ];
end
end
end
return isUnion and not isCountry;
end
local function isPartOfNext( prevLabel, nextLabel )
return ( mw.ustring.len( prevLabel ) > mw.ustring.len( nextLabel ) )
and ( mw.ustring.sub( prevLabel, mw.ustring.len( prevLabel ) - mw.ustring.len( nextLabel ) + 1 ) == nextLabel );
end
local function isIgnoredPlace( entityId, instanceOf )
entityId = getCanonicalEntityId( entityId );
local hasIgnoredType = false;
instanceOf = instanceOf or mw.wikibase.getAllStatements( entityId, 'P31' ) or {};
for _, statement in ipairs( instanceOf ) do
local snak = statement.mainsnak;
local typeId = snak and
snak.datavalue and
snak.datavalue.value and
snak.datavalue.value.id;
if typeId then
if unignoredPlaceTypes[ typeId ] then
return false;
elseif ignoredPlaceTypes[ typeId ] then
hasIgnoredType = true;
end
end
end
return hasIgnoredType;
end
local function matchesParentChainValue( entityId, expected )
if type( expected ) == 'table' then
for _, expectedId in ipairs( expected ) do
if entityId == expectedId then
return true;
end
end
return false;
end
return entityId == expected;
end
local function matchesParentChainPattern( parentEntityIds, pattern, startIndex )
for offset, expected in ipairs( pattern ) do
if not matchesParentChainValue(
parentEntityIds[ startIndex + offset - 1 ], expected
) then
return false;
end
end
return true;
end
local function applyParentChainRule( parentSnaks, parentEntityIds, rule )
local lastStart = #parentEntityIds - #rule.pattern + 1;
if lastStart < 1 then
return nil;
end
for startIndex = 1, lastStart do
if matchesParentChainPattern( parentEntityIds, rule.pattern, startIndex ) then
local removeIndex = startIndex + rule.remove - 1;
table.remove( parentSnaks, removeIndex );
table.remove( parentEntityIds, removeIndex );
return removeIndex;
end
end
return nil;
end
local function getAdjacentParentRemoval( previousEntityId, nextEntityId )
for _, rule in ipairs( adjacentParentRules ) do
if matchesParentChainValue( previousEntityId, rule.pattern[ 1 ] ) and
matchesParentChainValue( nextEntityId, rule.pattern[ 2 ] )
then
return rule.remove;
end
end
return nil;
end
local function applyParentChainRules( parentSnaks, parentEntityIds )
local baseRemoved = false;
for _, rule in ipairs( parentChainRules ) do
local removeIndex = applyParentChainRule( parentSnaks, parentEntityIds, rule );
baseRemoved = baseRemoved or removeIndex == 1;
end
local index = 2;
while index <= #parentEntityIds do
local removePosition = getAdjacentParentRemoval(
parentEntityIds[ index - 1 ], parentEntityIds[ index ]
);
if removePosition then
local removeIndex = index + removePosition - 2;
table.remove( parentSnaks, removeIndex );
table.remove( parentEntityIds, removeIndex );
baseRemoved = baseRemoved or removeIndex == 1;
index = math.max( 2, index - 1 );
else
index = index + 1;
end
end
return baseRemoved;
end
function p.formatPlaceWithQualifiers( context, options, statement )
local property = mw.ustring.upper( options.property );
local skipAdministrativeUnits = options.skipAdministrativeUnits == true or
options.skipAdministrativeUnits == 'true';
local actualDateBoundariesProperties = nil;
if property == 'P19' then actualDateBoundariesProperties = {'P569','P570'}; end
if property == 'P20' then actualDateBoundariesProperties = {'P570','P569'}; end
if property == 'P119' then actualDateBoundariesProperties = {'P570','P569'}; end
if property == 'P159' then actualDateBoundariesProperties = {'P576'}; end
local boundaries = nil;
if actualDateBoundariesProperties ~= nil then
boundaries = getTimeBoundariesFromProperties( context, actualDateBoundariesProperties );
if (boundaries == nil) and (property == 'P159') then
boundaries = {os.time() * 1000, os.time() * 1000};
end
end
local lifetime = ( property == 'P19' or property == 'P20' or property == 'P119' ) and
getLifetimeBoundaries( context ) or nil;
local placeLabelCache = {};
local placeNameInfo = {};
local chainSnaks = {};
local countryNameIds = {};
local placeTypeCache = {};
local function getPlaceTypes( entityId )
if not placeTypeCache[ entityId ] then
placeTypeCache[ entityId ] = mw.wikibase.getAllStatements( entityId, 'P31' ) or {};
end
return placeTypeCache[ entityId ];
end
local maxGeoChainDepth = 12;
local countryResolutionStepsRemaining = maxGeoChainDepth * 2;
local ignoredPlaceCache = {};
local function isIgnoredPlaceCached( entityId )
entityId = getCanonicalEntityId( entityId );
if not entityId then
return false;
end
local cached = ignoredPlaceCache[ entityId ];
if cached ~= nil then
return cached;
end
local ignored = isIgnoredPlace( entityId, getPlaceTypes( entityId ) );
ignoredPlaceCache[ entityId ] = ignored;
return ignored;
end
local azLabelCache = {};
local function getAzLabel( entityId )
entityId = getCanonicalEntityId( entityId );
if not entityId then
return nil;
end
local cached = azLabelCache[ entityId ];
if cached == false then
return nil;
elseif cached ~= nil then
return cached;
end
local label = mw.wikibase.getLabelByLang( entityId, 'az' );
azLabelCache[ entityId ] = label or false;
return label;
end
local countryEntityCache = {};
local function isConstituentCountry( entityId )
for _, claim in ipairs( getPlaceTypes( entityId ) ) do
local snak = claim.mainsnak;
local typeId = snak and snak.datavalue and
snak.datavalue.value and snak.datavalue.value.id;
if claim.rank ~= 'deprecated' and typeId and constituentCountryTypes[ typeId ] then
return true;
end
end
return false;
end
local function isCountryEntityCached( entityId )
entityId = getCanonicalEntityId( entityId );
local cached = countryEntityCache[ entityId ];
if cached ~= nil then
return cached;
end
local isCountry = false;
for _, statement in ipairs(
getPlaceTypes( entityId )
) do
local snak = statement.mainsnak;
local typeId = snak and snak.datavalue and
snak.datavalue.value and snak.datavalue.value.id;
if typeId and countries[ typeId ] then
isCountry = true;
break;
end
end
countryEntityCache[ entityId ] = isCountry;
return isCountry;
end
local skippedPlaceTypeCache = {};
local function isSkippedPlaceTypeCached( entityId )
entityId = getCanonicalEntityId( entityId );
local cached = skippedPlaceTypeCache[ entityId ];
if cached ~= nil then
return cached;
end
local skipped = false;
for _, statement in ipairs(
getPlaceTypes( entityId )
) do
local value = statement.mainsnak and
statement.mainsnak.datavalue and
statement.mainsnak.datavalue.value;
if value and skippedPlaceTypes[ value.id ] then
skipped = true;
break;
end
end
skippedPlaceTypeCache[ entityId ] = skipped;
return skipped;
end
local historicalCountryCache = {};
local function getPlaceLabel( entityId, keepHistorical )
entityId = getCanonicalEntityId( entityId );
if not entityId then return nil; end
if showCurrentLabels[ entityId ] then return getAzLabel( entityId ); end
if keepHistorical then countryNameIds[ entityId ] = true; end
if lifetime then
if historicalCountryCache[ entityId ] == nil then
local isCountry = false;
for _, claim in ipairs( getPlaceTypes( entityId ) ) do
local snak = claim.mainsnak;
local typeId = snak and snak.datavalue and
snak.datavalue.value and snak.datavalue.value.id;
if claim.rank ~= 'deprecated' and typeId and
( countries[ typeId ] or unions[ typeId ] or skippedPlaceTypes[ typeId ] )
then
isCountry = true;
break;
end
end
historicalCountryCache[ entityId ] = isCountry;
end
keepHistorical = keepHistorical or countryNameIds[ entityId ] or
historicalCountryCache[ entityId ];
end
local key = entityId .. ( keepHistorical and ':historical' or '' );
if placeLabelCache[ key ] ~= nil then
return placeLabelCache[ key ] or nil;
end
local label, nameInfo = getLabel(
context, entityId, boundaries, not keepHistorical and lifetime or nil
);
if label and nameInfo then
placeNameInfo[ entityId ] = placeNameInfo[ entityId ] or {};
placeNameInfo[ entityId ][ label ] = nameInfo;
end
placeLabelCache[ key ] = label or false;
return label;
end
local function findIgnoredPlaceFallbacks(
startEntityId, firstUsableOnly, expectedCountryId
)
startEntityId = getCanonicalEntityId( startEntityId );
local currentEntityId = startEntityId;
local seen = { [ startEntityId ] = true };
local fallbacks = {};
for _ = 1, maxGeoChainDepth do
local parentSnaks = getParentsInBoundariesSnakImpl(
context, currentEntityId, boundaries, { 'P131' }
);
local isCountryFallback = false;
if not parentSnaks or #parentSnaks == 0 then
parentSnaks = getCountriesInBoundariesSnak(
context, currentEntityId, boundaries
);
isCountryFallback = true;
elseif #parentSnaks > 1 then
local countrySnaks = getCountriesInBoundariesSnak(
context, currentEntityId, boundaries
);
if countrySnaks and #countrySnaks == 1 then
parentSnaks = countrySnaks;
isCountryFallback = true;
end
end
if isCountryFallback and expectedCountryId and
#parentSnaks > 1
then
for _, countrySnak in ipairs( parentSnaks ) do
if countrySnak.datavalue.value.id == expectedCountryId then
parentSnaks = { countrySnak };
break;
end
end
end
if not parentSnaks or #parentSnaks == 0 then
return fallbacks;
elseif #parentSnaks > 1 then
return fallbacks, 'ambiguous';
end
local parentSnak = parentSnaks[ 1 ];
local parentId = parentSnak and
parentSnak.snaktype == 'value' and
parentSnak.datavalue and
parentSnak.datavalue.value and
parentSnak.datavalue.value.id;
if not parentId then
return fallbacks;
elseif seen[ parentId ] then
if isCountryFallback and
( parentId == expectedCountryId or not isConstituentCountry( parentId ) )
then
return fallbacks, nil, {
snak = parentSnak,
entityId = parentId,
label = getPlaceLabel( parentId, true ),
};
end
return fallbacks, 'loop';
end
seen[ parentId ] = true;
local label = getPlaceLabel(
parentId, isCountryFallback or parentId == expectedCountryId
);
if label and label ~= '' then
if not isIgnoredPlaceCached( parentId ) then
table.insert( fallbacks, {
snak = parentSnak,
entityId = parentId,
label = label,
} );
if firstUsableOnly then
return fallbacks;
end
end
end
if parentId == expectedCountryId or
isCountryFallback and not isConstituentCountry( parentId )
then
return fallbacks, nil, {
snak = parentSnak,
entityId = parentId,
label = label,
};
end
currentEntityId = parentId;
end
return fallbacks;
end
local countryResolutionCache = {};
local function resolveCountryFromPlace( startEntityId, startSnak )
startEntityId = getCanonicalEntityId( startEntityId );
local cached = countryResolutionCache[ startEntityId ];
if cached then
return cached.country, cached.status;
end
local currentEntityId = startEntityId;
local currentSnak = canonicalizeEntitySnak( startSnak );
local seen = {};
local path = {};
local country = nil;
local status = nil;
local completed = false;
for _ = 1, maxGeoChainDepth do
local currentCached = countryResolutionCache[ currentEntityId ];
if currentCached then
country = currentCached.country;
status = currentCached.status;
completed = true;
break;
end
if seen[ currentEntityId ] then
status = 'loop';
completed = true;
break;
end
if countryResolutionStepsRemaining == 0 then
status = 'limit';
break;
end
countryResolutionStepsRemaining = countryResolutionStepsRemaining - 1;
seen[ currentEntityId ] = true;
table.insert( path, currentEntityId );
local countrySnaks = getCountriesInBoundariesSnak(
context, currentEntityId, boundaries
);
if #countrySnaks == 1 then
local countrySnak = countrySnaks[ 1 ];
local countryId = countrySnak.datavalue.value.id;
country = {
snak = countrySnak,
entityId = countryId,
label = getPlaceLabel( countryId, true ),
};
completed = true;
break;
elseif #countrySnaks > 1 then
status = 'ambiguous';
completed = true;
break;
end
local parentSnaks = getParentsInBoundariesSnakImpl(
context, currentEntityId, boundaries, { 'P131' }
);
if #parentSnaks == 0 then
if isCountryEntityCached( currentEntityId ) then
country = {
snak = currentSnak,
entityId = currentEntityId,
label = getPlaceLabel( currentEntityId, true ),
};
end
completed = true;
break;
elseif #parentSnaks > 1 then
status = 'ambiguous';
completed = true;
break;
end
local parentSnak = parentSnaks[ 1 ];
local parentId = parentSnak and
parentSnak.snaktype == 'value' and
parentSnak.datavalue and
parentSnak.datavalue.value and
parentSnak.datavalue.value.id;
if not parentId then
completed = true;
break;
end
currentSnak = parentSnak;
currentEntityId = parentId;
end
local resolution = {
country = country,
status = status,
};
if completed then
for _, pathEntityId in ipairs( path ) do
countryResolutionCache[ pathEntityId ] = resolution;
end
end
return country, status;
end
local entriesToLookupCategory = {};
local mainSnak = canonicalizeEntitySnak( statement.mainsnak );
table.insert( chainSnaks, mainSnak );
local mainEntityId = mainSnak and
mainSnak.datavalue and
mainSnak.datavalue.value and
mainSnak.datavalue.value.id;
local mainAzLabel = mainEntityId and getAzLabel( mainEntityId );
local hasMainAzLabel = mainAzLabel and mainAzLabel ~= '';
local ignoredBasePlace = mainEntityId and hasMainAzLabel and
isIgnoredPlaceCached( mainEntityId ) or false;
local burialMainOnly = property == 'P119' and hasMainAzLabel and
not ignoredBasePlace or false;
local suppressBasePlace = mainEntityId and
( not hasMainAzLabel or ignoredBasePlace ) or false;
local circumstances = context.getSourcingCircumstances( statement );
local result = '';
local fallbackMaintenanceCategory = '';
local baseResult = '';
local baseDisplayLabel = mainAzLabel;
if not suppressBasePlace then
baseDisplayLabel = getPlaceLabel( mainEntityId ) or mainAzLabel;
end
if suppressBasePlace then
baseResult = '';
else
local baseOptions = context.cloneOptions( options );
baseOptions[ 'text' ] = baseDisplayLabel;
baseResult = context.formatSnak( baseOptions, mainSnak, circumstances );
if not baseResult then
return nil;
end
end
local lastDisplayedPlaceLabel = suppressBasePlace and nil or baseDisplayLabel;
local displayedPlaceLabels = {};
local displayedPlaceEntityIds = {};
local displayedPlaceEntries = {};
local baseRemovedByRule = false;
if not suppressBasePlace then
if baseDisplayLabel and baseDisplayLabel ~= '' then
displayedPlaceLabels[ baseDisplayLabel ] = true;
end
if mainEntityId then
displayedPlaceEntityIds[ mainEntityId ] = true;
end
table.insert( displayedPlaceEntries, {
label = baseDisplayLabel,
entityId = mainEntityId,
snak = mainSnak,
rendered = baseResult,
isBase = true,
categoryCountBefore = 0,
} );
end
local function registerPlaceLabel( label, snak )
if not label or label == '' then
return true;
end
local entityId = snak and snak.datavalue and
snak.datavalue.value and snak.datavalue.value.id;
entityId = getCanonicalEntityId( entityId );
if displayedPlaceLabels[ label ] or
( entityId and displayedPlaceEntityIds[ entityId ] )
then
return false;
end
if lastDisplayedPlaceLabel then
for _, suffix in ipairs( redundantParentSuffixes ) do
if label == lastDisplayedPlaceLabel .. ' ' .. suffix then
return false;
end
end
end
lastDisplayedPlaceLabel = label;
displayedPlaceLabels[ label ] = true;
if entityId then
displayedPlaceEntityIds[ entityId ] = true;
end
return true;
end
insertFromSnak( mainSnak, entriesToLookupCategory )
local function removeLastDisplayedPlace()
local previous = table.remove( displayedPlaceEntries );
if not previous then
return false;
end
if previous.label then
displayedPlaceLabels[ previous.label ] = nil;
end
if previous.entityId then
displayedPlaceEntityIds[ previous.entityId ] = nil;
end
while #entriesToLookupCategory > previous.categoryCountBefore do
table.remove( entriesToLookupCategory );
end
if previous.isBase then
baseResult = '';
baseRemovedByRule = true;
else
result = previous.resultBefore or '';
end
local newPrevious = displayedPlaceEntries[ #displayedPlaceEntries ];
lastDisplayedPlaceLabel = newPrevious and newPrevious.label or nil;
return true;
end
local function appendFormattedPlace( snak, label, skipAdjacentRules )
if not snak or not label or label == '' then
return false;
end
local entityId = snak.datavalue and snak.datavalue.value and
snak.datavalue.value.id;
entityId = getCanonicalEntityId( entityId );
if entityId and ( isIgnoredPlaceCached( entityId ) or
skipAdministrativeUnits and isSkippedPlaceTypeCached( entityId ) )
then
return false;
end
local parentOptions = context.cloneOptions( options );
parentOptions[ 'text' ] = label;
local link = context.formatSnak( parentOptions, snak );
if link == nil then
return false;
end
if displayedPlaceLabels[ label ] or
( entityId and displayedPlaceEntityIds[ entityId ] )
then
return false;
end
while entityId and not skipAdjacentRules do
local previous = displayedPlaceEntries[ #displayedPlaceEntries ];
if not previous or not previous.entityId then
break;
end
local removePosition = getAdjacentParentRemoval(
previous.entityId, entityId
);
if removePosition == 2 then
return false;
elseif removePosition == 1 then
removeLastDisplayedPlace();
else
break;
end
end
if not registerPlaceLabel( label, snak ) then
return false;
end
local resultBefore = result;
local categoryCountBefore = #entriesToLookupCategory;
if p.config.reverseOrder then
result = link .. ', ' .. result;
else
result = result .. ', ' .. link;
end
insertFromSnak( snak, entriesToLookupCategory );
table.insert( displayedPlaceEntries, {
label = label,
entityId = entityId,
snak = snak,
rendered = link,
resultBefore = resultBefore,
categoryCountBefore = categoryCountBefore,
} );
table.insert( chainSnaks, snak );
return true;
end
local function appendFallbacks( fallbacks, startEntityId )
local chain = {};
local chainEntityIds = {};
local startEntry = nil;
if startEntityId then
startEntry = {};
table.insert( chain, startEntry );
table.insert( chainEntityIds, getCanonicalEntityId( startEntityId ) );
end
for _, fallback in ipairs( fallbacks or {} ) do
table.insert( chainSnaks, fallback.snak );
table.insert( chain, fallback );
table.insert( chainEntityIds, fallback.entityId );
end
if startEntry and applyParentChainRules( chain, chainEntityIds ) then
removeLastDisplayedPlace();
elseif not startEntry then
applyParentChainRules( chain, chainEntityIds );
end
local appended = false;
for _, fallback in ipairs( chain ) do
if fallback ~= startEntry and
appendFormattedPlace( fallback.snak, fallback.label )
then
appended = true;
end
end
return appended;
end
local p131Qualifiers = {};
local p131QualifierIds = {};
local p17Qualifiers = {};
local p17QualifierIds = {};
if statement.qualifiers then
for _, qualifier in ipairs( statement.qualifiers.P131 or {} ) do
qualifier = canonicalizeEntitySnak( qualifier );
local qualifierId = qualifier and qualifier.datavalue and
qualifier.datavalue.value and qualifier.datavalue.value.id;
if qualifierId and not p131QualifierIds[ qualifierId ] then
p131QualifierIds[ qualifierId ] = true;
if not burialMainOnly then table.insert( chainSnaks, qualifier ); end
table.insert( p131Qualifiers, qualifier );
end
end
for _, qualifier in ipairs( statement.qualifiers.P17 or {} ) do
qualifier = canonicalizeEntitySnak( qualifier );
local qualifierId = qualifier and qualifier.datavalue and
qualifier.datavalue.value and qualifier.datavalue.value.id;
if qualifierId and not p17QualifierIds[ qualifierId ] then
p17QualifierIds[ qualifierId ] = true;
if not burialMainOnly then table.insert( chainSnaks, qualifier ); end
countryNameIds[ qualifierId ] = true;
table.insert( p17Qualifiers, qualifier );
end
end
end
local rawP131QualifierCount = #p131Qualifiers;
local rawDirectP131EntityId = rawP131QualifierCount == 1 and
p131Qualifiers[ 1 ].datavalue.value.id or nil;
local hasDirectP17Qualifier = #p17Qualifiers > 0;
local directP17EntityId = #p17Qualifiers == 1 and
p17Qualifiers[ 1 ].datavalue.value.id or nil;
local isBirthOrDeathPlace = property == 'P19' or property == 'P20';
local hasAdditionalQualifiers = false;
if property ~= 'P119' and not burialMainOnly then
for _, qualifier in ipairs( p131Qualifiers ) do
local qualifierEntityId = qualifier.datavalue.value.id;
local qualifierLabel = getPlaceLabel( qualifierEntityId );
if qualifierLabel and qualifierLabel ~= '' and
appendFormattedPlace( qualifier, qualifierLabel, true )
then
hasAdditionalQualifiers = true;
end
end
end
local function applyFallbackStatus( fallbackStatus )
if fallbackStatus == 'loop' and p.config and p.config.catLoopInGeoChains then
fallbackMaintenanceCategory = p.config.catLoopInGeoChains;
elseif fallbackStatus == 'ambiguous' and p.config and p.config.catAmbiguousGeoChains then
fallbackMaintenanceCategory = p.config.catAmbiguousGeoChains;
end
end
local function appendMatchingCountryChain( startEntityId )
if not directP17EntityId then
return;
end
local fallbacks, fallbackStatus, country = findIgnoredPlaceFallbacks(
startEntityId, false, directP17EntityId
);
if country and country.entityId == directP17EntityId then
appendFallbacks( fallbacks, startEntityId );
end
applyFallbackStatus( fallbackStatus );
end
if mainSnak and
mainSnak.datavalue and
mainSnak.datavalue.value and
mainSnak.datavalue.value.id
then
local entityId = mainSnak.datavalue.value.id;
local parentSnaks = { mainSnak };
local parentEntityIds = { entityId };
if property == 'P119' then
if not burialMainOnly then
baseResult = '';
local burialResolved = false;
local fallbackStatus = nil;
if rawP131QualifierCount == 1 then
local qualifier = p131Qualifiers[ 1 ];
local qualifierId = qualifier.datavalue.value.id;
local qualifierLabel = getPlaceLabel( qualifierId );
if qualifierLabel and qualifierLabel ~= '' and
not isIgnoredPlaceCached( qualifierId )
then
burialResolved = appendFormattedPlace( qualifier, qualifierLabel );
end
if not burialResolved then
local fallbacks;
fallbacks, fallbackStatus =
findIgnoredPlaceFallbacks( qualifierId, true );
burialResolved = appendFallbacks( fallbacks );
end
else
local fallbacks;
fallbacks, fallbackStatus =
findIgnoredPlaceFallbacks( entityId, true );
burialResolved = appendFallbacks( fallbacks );
end
if not burialResolved then
for _, qualifier in ipairs( p17Qualifiers ) do
local qualifierId = qualifier.datavalue.value.id;
local qualifierLabel = getPlaceLabel( qualifierId, true );
if appendFormattedPlace( qualifier, qualifierLabel ) then
burialResolved = true;
break;
end
end
end
applyFallbackStatus( fallbackStatus );
end
elseif isBirthOrDeathPlace and rawP131QualifierCount > 0 and
not hasDirectP17Qualifier
then
local sharedCountry = nil;
local countriesAgree = true;
for _, qualifier in ipairs( p131Qualifiers ) do
local country, fallbackStatus = resolveCountryFromPlace(
qualifier.datavalue.value.id, qualifier
);
applyFallbackStatus( fallbackStatus );
if not country then
countriesAgree = false;
elseif sharedCountry and
sharedCountry.entityId ~= country.entityId
then
countriesAgree = false;
elseif not sharedCountry then
sharedCountry = country;
end
end
if countriesAgree and sharedCountry then
table.insert( chainSnaks, sharedCountry.snak );
end
if not skipAdministrativeUnits and countriesAgree and sharedCountry and
sharedCountry.label and sharedCountry.label ~= ''
then
appendFormattedPlace(
sharedCountry.snak, sharedCountry.label, true
);
end
if countriesAgree and sharedCountry and isConstituentCountry( sharedCountry.entityId ) then
local fallbacks, fallbackStatus = findIgnoredPlaceFallbacks( sharedCountry.entityId, false );
appendFallbacks( fallbacks );
applyFallbackStatus( fallbackStatus );
end
elseif isBirthOrDeathPlace and rawP131QualifierCount == 0 and
hasDirectP17Qualifier and suppressBasePlace
then
baseResult = '';
appendMatchingCountryChain( entityId );
elseif suppressBasePlace then
baseResult = '';
local fallbackStartEntityId = nil;
if not hasDirectP17Qualifier then
if rawP131QualifierCount == 1 then
fallbackStartEntityId = rawDirectP131EntityId;
elseif rawP131QualifierCount == 0 then
fallbackStartEntityId = entityId;
end
end
if fallbackStartEntityId then
local fallbacks, fallbackStatus =
findIgnoredPlaceFallbacks( fallbackStartEntityId, false );
appendFallbacks( fallbacks, fallbackStartEntityId );
applyFallbackStatus( fallbackStatus );
end
elseif actualDateBoundariesProperties ~= nil and
not hasAdditionalQualifiers and
not ( isBirthOrDeathPlace and hasDirectP17Qualifier and
( rawP131QualifierCount > 0 or not directP17EntityId ) )
then
if boundaries then
local entityOptions = context.cloneOptions( options );
entityOptions['text'] = baseDisplayLabel;
baseResult = context.formatSnak( entityOptions, mainSnak, circumstances );
local parentId = entityId;
local parentDepth = 0;
local terminalEntityId = entityId;
while parentId ~= nil and parentDepth < maxGeoChainDepth do
parentDepth = parentDepth + 1;
local newParentSnaks, isCountryResult =
getParentsInBoundariesSnak( context, parentId, boundaries );
if not newParentSnaks or #newParentSnaks == 0 then
parentId = nil;
elseif #newParentSnaks == 1 then
local parentSnak = newParentSnaks[ 1 ];
parentId = parentSnak.datavalue.value.id;
local hasLoop = false
for _, parentEntityId in pairs(parentEntityIds) do
if parentEntityId == parentId then
hasLoop = true
end
end
if hasLoop then
if p.config and p.config.catLoopInGeoChains then
result = result .. p.config.catLoopInGeoChains;
end
break
end
table.insert( parentSnaks, parentSnak );
table.insert( parentEntityIds, parentId );
terminalEntityId = parentId;
if isCountryResult or parentId == directP17EntityId then
countryNameIds[ parentId ] = true;
if parentId == directP17EntityId or not isConstituentCountry( parentId ) then
parentId = nil;
end
end
else
parentId = nil;
if p.config and p.config.catAmbiguousGeoChains then
result = result .. p.config.catAmbiguousGeoChains;
end
end
end
if isBirthOrDeathPlace and rawP131QualifierCount == 0 and
hasDirectP17Qualifier and
terminalEntityId ~= directP17EntityId
then
parentSnaks = { mainSnak };
parentEntityIds = { entityId };
end
for _, parentSnak in ipairs( parentSnaks ) do
table.insert( chainSnaks, parentSnak );
end
local firstParentIndex = 2;
if applyParentChainRules( parentSnaks, parentEntityIds ) then
removeLastDisplayedPlace();
firstParentIndex = 1;
end
do
local capitalOf = {};
if skipAdministrativeUnits then
for _, placeId in ipairs( parentEntityIds ) do
capitalOf[ placeId ] = getParentsInBoundariesSnakImpl(
context, placeId, boundaries, { 'P1376' }
);
end
end
local checkAdministrativeType = false;
local removeFirstParent = skipAdministrativeUnits and
#parentSnaks == 2;
local i = #parentSnaks;
while i > 1 do
if skipAdministrativeUnits and checkAdministrativeType and
parentEntityIds[ i ]
then
if isSkippedPlaceTypeCached( parentEntityIds[ i ] ) then
for _, capitalSnaks in pairs( capitalOf ) do
for _, capitalSnak in pairs( capitalSnaks ) do
if capitalSnak.datavalue and
capitalSnak.datavalue.value.id == parentEntityIds[ i ]
then
removeFirstParent = true;
end
end
end
table.remove( parentSnaks, i );
table.remove( parentEntityIds, i );
end
end
if skipAdministrativeUnits then
checkAdministrativeType = true;
end
local prevEntityId = parentEntityIds[ i - 1 ];
local prevLabel = getPlaceLabel( prevEntityId ) or '';
local nextEntityId = parentEntityIds[ i ];
local nextLabel = getPlaceLabel( nextEntityId ) or '';
if p.config and p.config.hideSameLabels == true and prevLabel == nextLabel then
table.remove( parentSnaks, i );
table.remove( parentEntityIds, i );
elseif p.config and p.config.hidePartOfLabels == true and isPartOfNext( prevLabel, ' ' .. nextLabel ) then
table.remove( parentSnaks, i - 1 );
table.remove( parentEntityIds, i - 1 );
elseif skipAdministrativeUnits and p.config and
p.config.hideUnitsForCapitals == true
then
local capitalId = nil;
for placeId, capitalSnaks in pairs( capitalOf ) do
for _, capitalSnak in pairs( capitalSnaks ) do
if capitalSnak.datavalue and
capitalSnak.datavalue.value.id == parentEntityIds[ i ]
then
capitalId = placeId;
end
end
end
if capitalId or i ~= #parentSnaks then
table.remove( parentSnaks, 1 );
table.remove( parentEntityIds, 1 );
end
end
i = i - 1;
if skipAdministrativeUnits and i == 2 then
table.remove( parentSnaks, i + 1 );
table.remove( parentEntityIds, i + 1 );
end
if skipAdministrativeUnits and i == 1 and removeFirstParent then
table.remove( parentSnaks, 1 );
table.remove( parentEntityIds, 1 );
end
end
end
if isSkipTopLevel( parentEntityIds[ #parentEntityIds ] ) then
table.remove( parentSnaks, #parentEntityIds );
table.remove( parentEntityIds, #parentEntityIds );
end
if not hasAdditionalQualifiers then
for i = firstParentIndex, #parentSnaks, 1 do
local parentSnak = parentSnaks[ i ];
local parentId = parentEntityIds[ i ];
local parentLabel = getPlaceLabel( parentId );
if parentLabel and parentLabel ~= '' then
appendFormattedPlace( parentSnak, parentLabel );
end
end
end
elseif isBirthOrDeathPlace and rawP131QualifierCount == 0 and
hasDirectP17Qualifier
then
appendMatchingCountryChain( entityId );
end
end
end
if property ~= 'P119' then
for _, qualifier in ipairs( p17Qualifiers ) do
local qualifierId = qualifier.datavalue.value.id;
local qualifierLabel = getPlaceLabel( qualifierId, true );
if qualifierLabel and qualifierLabel ~= '' then
appendFormattedPlace( qualifier, qualifierLabel, true );
end
end
end
if lifetime then
local candidates = {};
local chainEnd = lifetime[ 2 ];
for _, entry in ipairs( displayedPlaceEntries ) do
local info = placeNameInfo[ entry.entityId ];
info = info and info[ entry.label ];
if info and info.currentStart then
table.insert( candidates, { entry = entry, info = info } );
end
end
if #candidates > 0 then
local relations = {};
local renameClaims = {};
local dissolutionDates = {};
local function getRenameClaims( entityId, propertyId )
local key = entityId .. ':' .. propertyId;
if not renameClaims[ key ] then
renameClaims[ key ] = mw.wikibase.getAllStatements( entityId, propertyId ) or {};
end
return renameClaims[ key ];
end
local function getDissolution( entityId )
if dissolutionDates[ entityId ] == nil then
local ending;
for _, claim in ipairs( getRenameClaims( entityId, 'P576' ) ) do
local dates = claim.rank ~= 'deprecated' and claim.mainsnak and
context.parseTimeBoundariesFromSnak( claim.mainsnak );
if dates then ending = min( ending, dates[ 1 ] ); end
end
dissolutionDates[ entityId ] = ending or false;
end
return dissolutionDates[ entityId ] or nil;
end
local countryAnchor, constituentAnchor;
for _, snak in ipairs( chainSnaks ) do
if snak.placeClaims then table.insert( relations, snak ); end
local entityId = snak.datavalue and snak.datavalue.value and snak.datavalue.value.id;
if entityId then
local ending = getDissolution( entityId );
if ending then chainEnd = min( chainEnd, ending ); end
local constituent = isConstituentCountry( entityId );
if ( not countryAnchor or constituent and not constituentAnchor ) and
( constituent or countryNameIds[ entityId ] or isCountryEntityCached( entityId ) )
then
countryAnchor, constituentAnchor = entityId, constituent;
end
end
end
local countryBonds = {};
local function matchesCountryAtDate( entityId, date )
local key = entityId .. ':' .. tostring( date );
if countryBonds[ key ] ~= nil then return countryBonds[ key ]; end
local seen = {};
local current = entityId;
local matches = false;
for _ = 1, maxGeoChainDepth do
if seen[ current ] then break; end
seen[ current ] = true;
local ending = getDissolution( current );
if ending and date > ending then break; end
if current == countryAnchor then matches = true; break; end
local parents = selectTemporalEntitySnaks(
context, getRenameClaims( current, 'P131' ), { date, date }, true
);
if #parents ~= 1 then
parents = selectTemporalEntitySnaks(
context, getRenameClaims( current, 'P17' ), { date, date }, true
);
end
if #parents ~= 1 then break; end
current = parents[ 1 ].datavalue.value.id;
end
countryBonds[ key ] = matches;
return matches;
end
for propertyId, qualifiers in pairs( { P131 = p131Qualifiers, P17 = p17Qualifiers } ) do
if #qualifiers > 0 and mainEntityId and not burialMainOnly then
local parents;
if propertyId == 'P17' then
parents = getCountriesInBoundariesSnak( context, mainEntityId, boundaries );
else
parents = getParentsInBoundariesSnakImpl(
context, mainEntityId, boundaries, { propertyId }
);
end
for _, parent in ipairs( parents ) do
local parentId = parent.datavalue.value.id;
if ( propertyId == 'P131' and p131QualifierIds[ parentId ] or
propertyId == 'P17' and p17QualifierIds[ parentId ] )
then
table.insert( relations, parent );
end
end
end
end
local relationDates = {};
for _, candidate in ipairs( candidates ) do
local date = candidate.info.currentStart;
candidate.rejected = date > chainEnd;
for _, relation in ipairs( relations ) do
if candidate.rejected then break; end
local claims = relation.placeClaims;
relationDates[ claims ] = relationDates[ claims ] or {};
local matchingIds = relationDates[ claims ][ date ];
if not matchingIds then
matchingIds = {};
for _, parent in ipairs( selectTemporalEntitySnaks(
context, claims, { date, date }, true
) ) do
matchingIds[ parent.datavalue.value.id ] = true;
end
relationDates[ claims ][ date ] = matchingIds;
end
candidate.rejected = not matchingIds[ relation.datavalue.value.id ];
end
if candidate.rejected and countryAnchor and date <= lifetime[ 2 ] and
matchesCountryAtDate( candidate.entry.entityId, date )
then
candidate.rejected = false;
end
end
for _, candidate in ipairs( candidates ) do
if candidate.rejected then
local entry = candidate.entry;
local labelOptions = context.cloneOptions( options );
labelOptions.text = candidate.info.originalLabel;
local rendered = context.formatSnak(
labelOptions, entry.snak, entry.isBase and circumstances or nil
);
if rendered then
if entry.isBase then
baseResult = rendered;
else
local first, last = string.find( result, entry.rendered, 1, true );
if first then
result = string.sub( result, 1, first - 1 ) .. rendered .. string.sub( result, last + 1 );
end
end
entry.label = candidate.info.originalLabel;
entry.rendered = rendered;
end
end
end
end
end
if options[ 'thisLocationOnly' ] and not suppressBasePlace and
not baseRemovedByRule
then
result = baseResult;
elseif p.config.reverseOrder then
if baseResult == '' then
result = string.gsub( result, ',%s*$', '', 1 );
end
result = result .. baseResult;
else
if baseResult == '' then
result = string.gsub( result, '^,%s*', '', 1 );
end
result = baseResult .. result;
end
local displayResult = result;
if property == 'P119' and displayResult == '' then
return nil;
end
local displayParts = {};
for _, entry in ipairs( displayedPlaceEntries ) do
if entry.entityId and entry.rendered and entry.rendered ~= '' then
table.insert( displayParts, {
entityId = entry.entityId,
text = entry.rendered,
} );
end
end
local canonicalDirectP17EntityId = getCanonicalEntityId( directP17EntityId );
local lastDisplayPart = displayParts[ #displayParts ];
local displayInfo = {
parts = displayParts,
explicitTerminalCountry = canonicalDirectP17EntityId ~= nil and
lastDisplayPart ~= nil and
lastDisplayPart.entityId == canonicalDirectP17EntityId,
};
if options.references then
result = result .. context.formatRefs( options, statement );
end
result = result .. fallbackMaintenanceCategory;
if categorizeByPlace then
if property == 'P19' then result = result .. getCategory( 'P1464', entriesToLookupCategory ); end
if property == 'P20' then result = result .. getCategory( 'P1465', entriesToLookupCategory ); end
if property == 'P119' then result = result .. getCategory( 'P1791', entriesToLookupCategory ); end
end
return result, displayResult, displayInfo;
end
function p.formatPlaceProperty( context, options )
return require( 'Module:Wikidata/Places/property' ).formatPlaceProperty(
context, options
);
end
function insertFromSnak( snak, result )
if not categorizeByPlace then
return;
end
if snak and
snak.datavalue and
snak.datavalue.type == 'wikibase-entityid' and
snak.datavalue.value and
snak.datavalue.value[ 'entity-type' ] == 'item'
then
table.insert( result, snak.datavalue.value.id );
end
end
function getCategory( propertyId, entriesToLookupCategoryFor )
if mw.title.getCurrentTitle().namespace == 0 then
for _, placeId in pairs( entriesToLookupCategoryFor ) do
local claims = mw.wikibase.getBestStatements(placeId, propertyId);
if claims then
for _, claim in pairs( claims ) do
if claim.mainsnak and
claim.mainsnak and
claim.mainsnak.datavalue and
claim.mainsnak.datavalue.type == 'wikibase-entityid'
then
local catEntityId = claim.mainsnak.datavalue.value.id;
local catSitelink = mw.wikibase.getSitelink(catEntityId);
if (catSitelink) then
return '[[' .. catSitelink .. ']]';
end
end
end
end
end
end
return '';
end
local historicNamesProperties = { 'P1813', 'P1448' };
function getLabel( context, entityId, boundaries, lifetime )
if not entityId then
return nil;
end
if (type(entityId) ~= 'string') then error('incorrect type of entityId argument'); end;
entityId = getCanonicalEntityId( entityId );
local currentLabel = mw.wikibase.getLabelByLang( entityId, 'az' );
if showCurrentLabels[ entityId ] or not boundaries then return currentLabel; end
local label = nil;
local labelEnd = nil;
local currentStart = nil;
local lastOldNameEnd = nil;
local today = os.time() * 1000;
for _, propertyId in ipairs( historicNamesProperties ) do
local propertyLabel = nil;
local propertyLabelEnd = nil;
local preferredLabel = nil;
local preferredLabelEnd = nil;
for _, claim in ipairs( mw.wikibase.getAllStatements( entityId, propertyId ) or {} ) do
local snak = claim.mainsnak;
local value = snak and snak.snaktype == 'value' and
snak.datavalue and snak.datavalue.value;
local hasStart = claim.qualifiers and claim.qualifiers.P580 and
claim.qualifiers.P580[ 1 ];
local hasEnd = claim.qualifiers and claim.qualifiers.P582 and
claim.qualifiers.P582[ 1 ];
if claim.rank ~= 'deprecated' and type( value ) == 'table' and
value.language == 'az' and value.text and ( hasStart or hasEnd )
then
local startBoundaries = getTimeBoundariesFromQualifiers( context, claim, 'P580' );
local endBoundaries = getTimeBoundariesFromQualifiers( context, claim, 'P582' );
if ( not hasStart or startBoundaries ) and ( not hasEnd or endBoundaries ) then
if ( not startBoundaries or startBoundaries[ 1 ] <= boundaries[ 1 ] ) and
( not endBoundaries or endBoundaries[ 1 ] >= boundaries[ 1 ] )
then
if not propertyLabel then
propertyLabel = value.text;
propertyLabelEnd = endBoundaries and endBoundaries[ 1 ];
end
if claim.rank == 'preferred' and not preferredLabel then
preferredLabel = value.text;
preferredLabelEnd = endBoundaries and endBoundaries[ 1 ];
end
end
if lifetime and value.text == currentLabel and startBoundaries and
startBoundaries[ 1 ] <= today and
( not endBoundaries or endBoundaries[ 2 ] >= today )
then
currentStart = max( currentStart, startBoundaries[ 1 ] );
elseif lifetime and value.text ~= currentLabel and endBoundaries and
endBoundaries[ 1 ] <= today
then
lastOldNameEnd = max( lastOldNameEnd, endBoundaries[ 1 ] );
end
end
end
end
if not label then
label = preferredLabel or propertyLabel;
if preferredLabel then
labelEnd = preferredLabelEnd;
else
labelEnd = propertyLabelEnd;
end
end
if label and not lifetime then return label, { endTime = labelEnd }; end
end
currentStart = currentStart or lastOldNameEnd;
if lifetime and currentLabel and currentStart and
lifetime[ 1 ] <= currentStart and currentStart <= lifetime[ 2 ]
then
return currentLabel, {
currentStart = label and label ~= currentLabel and currentStart or nil,
originalLabel = label or currentLabel,
originalEnd = labelEnd,
};
end
return label or currentLabel, { endTime = labelEnd };
end
p.getLabel = getLabel;
local function calculateEndDateTimestamp( context, options, statement )
if not context then error( 'context not specified' ) end;
if not options then error( 'options not specified' ) end;
if not options.entity then error( 'options.entity missing' ) end;
if not statement then error( 'statement not specified' ) end;
if statement.qualifiers and statement.qualifiers.P582 then
for i, qualifier in ipairs( statement.qualifiers.P582 ) do
local parsedTime = context.parseTimeFromSnak( qualifier );
if parsedTime then
return parsedTime;
end
end
end
for h, propertyId in pairs( { "P570", "P577", "P576" } ) do
local dateClaims = context.selectClaims( options, propertyId );
if dateClaims then
for i, statement in ipairs( dateClaims ) do
local parsedTime = context.parseTimeFromSnak( statement.mainsnak );
if parsedTime then
return parsedTime;
end
end
end
end
return os.time() * 1000;
end
local function deleteTwinAncestors( countryEntityId, propertyId, entityId )
local badTwins
if ( countryEntityId == 'Q174193' ) then
badTwins = {'Q145'}
elseif ( countryEntityId == 'Q161885' ) then
badTwins = {'Q174193'}
elseif ( countryEntityId == 'Q43287' ) then
badTwins = {'Q41304', 'Q7318', 'Q2415901', 'Q183'}
elseif ( countryEntityId == 'Q41304' ) then
badTwins = {'Q7318', 'Q2415901', 'Q183'}
elseif ( countryEntityId == 'Q7318' ) then
badTwins = {'Q2415901', 'Q183'}
elseif ( countryEntityId == 'Q2415901' ) then
badTwins = {'Q183'}
elseif ( countryEntityId == 'Q696908' ) then
badTwins = {'Q207272', 'Q211274', 'Q36'}
elseif ( countryEntityId == 'Q207272' ) then
badTwins = {'Q211274', 'Q36'}
elseif ( countryEntityId == 'Q211274' ) then
badTwins = {'Q36'}
elseif ( countryEntityId == 'Q203493' ) then
badTwins = {'Q842794', 'Q218'}
elseif ( countryEntityId == 'Q842794' ) then
badTwins = {'Q218'}
elseif ( countryEntityId == 'Q838261' ) then
badTwins = {'Q37024'}
else
return true;
end
local listforcheck
if propertyId == 'P1532' then
listforcheck = mw.wikibase.getAllStatements( entityId, propertyId );
else
listforcheck = mw.wikibase.getBestStatements( entityId, propertyId );
end
for _, claim in pairs( listforcheck ) do
if ( claim and claim.mainsnak
and claim.mainsnak.datavalue
and claim.mainsnak.datavalue.value
and claim.mainsnak.datavalue.value.id ) then
local actualId = claim.mainsnak.datavalue.value.id;
for index, value in ipairs(badTwins) do
if ( value == actualId ) then
return false;
end
end
end
end
return true;
end
function p.formatCountryClaimWithFlag( context, options, statement )
if not context then error('context not specified') end;
if not options then error('options not specified') end;
if not options.entity then error('options.entity is missing') end;
if not statement then error('statement not specified') end;
if not statement.mainsnak or
not statement.mainsnak.datavalue or
not statement.mainsnak.datavalue.value or
not statement.mainsnak.datavalue.value.id
then
local result = context.formatStatementDefault( context, options, statement );
if not result then
return '';
end
return '<span class="country-name">' .. result .. '</span>';
end
local countryEntityId = statement.mainsnak.datavalue.value.id;
local endDateTimestamp = calculateEndDateTimestamp( context, options, statement );
local boundaries = getTimeBoundariesFromProperties( context, {'P570', 'P577', 'P571'} );
if deleteTwinAncestors( countryEntityId, string.upper(options.property), options.entity.id ) then
local countryOptions = context.cloneOptions( options );
if not countryOptions['text'] or countryOptions['text'] == '' then
countryOptions['text'] = getLabel( context, countryEntityId, boundaries );
end
local flag = Flags.getFlag( context, countryEntityId, endDateTimestamp );
if context.formatStatementDefault( context, countryOptions, statement ) ~= nil then
if flag then
return flag .. ' <span class="country-name">' ..
context.formatStatementDefault( context, countryOptions, statement ) ..
'</span>';
end
end
if context.formatStatementDefault( context, countryOptions, statement ) ~= nil then
return '<span class="country-name">' ..
context.formatStatementDefault( context, countryOptions, statement ) ..
'</span>';
else
return nil;
end
end
end
return p;