Module:Pornbasedata/Places
Jump to navigation
Jump to search
Documentation for this module may be created at Module:Pornbasedata/Places/doc
local categorizeByPlace = true;
local WDS = require( 'Module:PornBaseDataSelectors' );
local Flags = require( 'Module:Pornbasedata/Flags' );
local p = {
config = {
hideSameLabels = false,
hidePartOfLabels = false,
hideUnitsForCapitals = true,
reverseOrder = false,
catAmbiguousGeoChains = '[[Category:Pages with ambiguous geo-chains]]',
catLoopInGeoChains = '[[Category:Pages with loop geo-chains]]',
catWikibaseError = '[[Category:Script error pages using PornBaseData]]'
}
};
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 )
local dateClaims = WDS.filter( context.entity.claims, propertyId );
if not dateClaims or #dateClaims == 0 then
return nil;
end
-- only support exact date so far, but need improvment
local left = nil;
local right = nil;
for _, claim in pairs( dateClaims ) do
if not claim.mainsnak then return nil; end
local boundaries = context.parseTimeBoundariesFromSnak( claim.mainsnak );
if not boundaries then return nil; end
left = min( left, boundaries[ 1 ] );
right = max( right, boundaries[ 2 ] );
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 getTimeBoundariesFromQualifiers( context, statement, qualifierId )
-- only support exact date so far, but need improvment
local left = nil;
local right = nil;
if statement.qualifiers and statement.qualifiers[qualifierId] then
for _, qualifier in pairs( statement.qualifiers[qualifierId] ) do
local boundaries = context.parseTimeBoundariesFromSnak( qualifier );
if not boundaries then return nil; end
left = min( left, boundaries[1] );
right = max( right, boundaries[2] );
end
end
if not left or not right then
return nil;
end
return { left, right };
end
local function getParentsInBoundariesSnakImpl( context, entityId, boundaries, propertyIds, selectors )
local results = {};
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
for _, claim in pairs( filteredClaims ) do
if not boundaries or not propertyIds or #propertyIds == 0 then
table.insert( results, 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
table.insert( results, claim.mainsnak );
end
end
end
end
if #results > 0 then
break;
end
end
return results;
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
local results = getParentsInBoundariesSnakImpl( context, entityId, boundaries, {'P49'} ) -- located in
if not results or #results == 0 then
results = getParentsInBoundariesSnakImpl( context, entityId, boundaries, {'P40'} ) -- country
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;
end
local unions = {
Q3127 = true, -- political union
Q3128 = true, -- Экономический союз
Q3129 = true -- региональная организация
}
local countries = {
Q210 = true, -- страна
Q3130 = true, -- государство
Q3124 = true, -- историческое государство
Q1041 = true -- суверенное государство
}
local function isSkipTopLevel( entity )
local isCountry = false;
local isUnion = false;
if entity and
entity.claims and
entity.claims.P1
then
for c, claim in pairs( entity.claims.P1 ) 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
--Property:P20, Property:P23, Property:P26
function p.formatPlaceWithQualifiers( context, options, statement )
local property = mw.ustring.upper( options.property );
local actualDateBoundariesProperties = nil;
if property == 'P20' then actualDateBoundariesProperties = {'P9','P247'}; end
if property == 'P23' then actualDateBoundariesProperties = {'P247','P9'}; end
if property == 'P26' then actualDateBoundariesProperties = {'P247','P9'}; end
if property == 'P273' then actualDateBoundariesProperties = {'P572'}; end
local boundaries = nil;
if actualDateBoundariesProperties ~= nil then
boundaries = getTimeBoundariesFromProperties( context, actualDateBoundariesProperties );
if (boundaries == nil) and (property == 'P273') then
boundaries = {os.time() * 1000, os.time() * 1000};
end
end
local entriesToLookupCategory = {};
local circumstances = context.getSourcingCircumstances( statement );
local result = '';
local baseResult = context.formatSnak( options, statement.mainsnak, circumstances );
if not baseResult then
return nil;
end
insertFromSnak( statement.mainsnak, entriesToLookupCategory )
local hasAdditionalQualifiers = false;
if statement.qualifiers then
--parent divisions
if statement.qualifiers.P49 then
for i, qualifier in ipairs( statement.qualifiers.P49 ) do
if qualifier.datavalue then
local parentOptions = context.cloneOptions( options );
local qualifierEntityId = qualifier.datavalue.value.id;
parentOptions['text'] = getLabel( context, qualifierEntityId, boundaries );
local link = context.formatSnak( parentOptions, qualifier );
if p.config.reverseOrder then
result = link .. ', ' .. result;
else
result = result .. ', ' .. link;
end
insertFromSnak( qualifier, entriesToLookupCategory )
hasAdditionalQualifiers = true;
end
end
end
--country
if statement.qualifiers.P40 then
for i, qualifier in ipairs( statement.qualifiers.P40 ) do
if qualifier.datavalue then
local parentOptions = context.cloneOptions( options );
local qualifierEntityId = qualifier.datavalue.value.id;
parentOptions[ 'text' ] = getLabel( context, qualifierEntityId, boundaries );
local link = context.formatSnak( parentOptions, qualifier );
if p.config.reverseOrder then
result = link .. ', ' .. result;
else
result = result .. ', ' .. link;
end
insertFromSnak( qualifier, entriesToLookupCategory )
hasAdditionalQualifiers = true;
end
end
end
end
if statement.mainsnak and
statement.mainsnak.datavalue and
statement.mainsnak.datavalue.value and
statement.mainsnak.datavalue.value.id
then
local entityId = statement.mainsnak.datavalue.value.id;
local parentSnaks = { statement.mainsnak };
local parentEntityIds = { entityId };
if actualDateBoundariesProperties ~= nil then
local filterCapitalOf = {
[ entityId ] = getParentsInBoundariesSnakImpl( context, entityId, boundaries, {'P70'} )
};
if boundaries then
local entityOptions = context.cloneOptions( options );
entityOptions['text'] = getLabel( context, entityId, boundaries );
baseResult = context.formatSnak( entityOptions, statement.mainsnak, circumstances );
local parentId = entityId;
while parentId ~= nil do
-- get parent
local newParentSnaks = 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 -- while parentId ~= nil do
end
table.insert( parentSnaks, parentSnak );
table.insert( parentEntityIds, parentId );
filterCapitalOf[ parentId ] = getParentsInBoundariesSnakImpl( context, parentId, boundaries, { 'P70' } );
else
parentId = nil;
if p.config and p.config.catAmbiguousGeoChains then
result = result .. p.config.catAmbiguousGeoChains;
end
end
end
if not hasAdditionalQualifiers then
for i = 2, #parentSnaks, 1 do
local parentSnak = parentSnaks[ i ];
insertFromSnak( parentSnak, entriesToLookupCategory )
end
end
--do not output similar countries like "Denmark, the Kingdom of Denmark"
if (#parentSnaks > 1) then
if ((parentSnaks[ #parentSnaks ].datavalue.value['numeric-id'] == 756617) and (parentSnaks[ #parentSnaks-1 ].datavalue.value['numeric-id'] == 35)) or ((parentSnaks[ #parentSnaks ].datavalue.value['numeric-id'] == 29999) and (parentSnaks[ #parentSnaks-1 ].datavalue.value['numeric-id'] == 55)) or ((parentSnaks[ #parentSnaks ].datavalue.value['numeric-id'] == 865) and (parentSnaks[ #parentSnaks-1 ].datavalue.value['numeric-id'] == 32081)) then
table.remove( parentSnaks, #parentSnaks );
table.remove( parentEntityIds, #parentEntityIds );
end
end
--do not output (maternity) hospitals and houses
if (#parentSnaks > 1) then
local p31 = mw.wikibase.getAllStatements(parentEntityIds[ 1 ], 'P1');
for _, iOf in pairs (p1) do
if ((iOf.mainsnak.datavalue.value['numeric-id'] == 3947) or (iOf.mainsnak.datavalue.value['numeric-id'] == 16917) or (iOf.mainsnak.datavalue.value['numeric-id'] == 958822) or (iOf.mainsnak.datavalue.value['numeric-id'] == 2087181)) then
baseResult = '';
end
end
end
do
local capofstate = false;
local i = #parentSnaks;
while i > 1 do
local prevEntityId = parentEntityIds[ i - 1 ];
-- TODO: use English labels, if there is no current language labels
local prevLabel = getLabel( context, prevEntityId, boundaries ) or '';
local nextEntityId = parentEntityIds[ i ];
local nextLabel = getLabel( context, nextEntityId, boundaries ) or '';
if p.config and p.config.hideSameLabels == true and prevLabel == nextLabel then
-- do not output same label twice (NY, NY, USA)
table.remove( parentSnaks, i );
table.remove( parentEntityIds, i );
elseif p.config and p.config.hidePartOfLabels == true and isPartOfNext( prevLabel, ' ' .. nextLabel ) then
-- do not output same label if it's part of previos
table.remove( parentSnaks, i - 1 );
table.remove( parentEntityIds, i - 1 );
elseif p.config and p.config.hideUnitsForCapitals == true then
-- do not ouput items whose capital is the first item
local capitalId = nil;
for _capitalId, capitalSnaks in pairs( filterCapitalOf ) do
if #capitalSnaks > 0 then
for __, capitalSnak in pairs( capitalSnaks ) do
if parentSnaks[ i ].datavalue.value.id == capitalSnak.datavalue.value.id then
capitalId = _capitalId;
if (i == #parentSnaks) then
capofstate = true;
end
break;
end
end
end
end
if capitalId ~= nil then
if i == #parentSnaks then
i = i - 1;
end
-- always ouput constituent countries like England or Russian SFSR
if (i == (#parentSnaks-1)) and (capofstate == false) then
local p31 = mw.wikibase.getAllStatements(parentEntityIds[ i ], 'P1');
for _, iOf in pairs (p1) do
if (iOf.mainsnak.datavalue.value['numeric-id'] == 236036) or (iOf.mainsnak.datavalue.value['numeric-id'] == 3336843) or (iOf.mainsnak.datavalue.value['numeric-id'] == 12959600) or (iOf.mainsnak.datavalue.value['numeric-id'] == 56219758) or (iOf.mainsnak.datavalue.value['numeric-id'] == 15304003) or (iOf.mainsnak.datavalue.value['numeric-id'] == 66724388) then
i = i - 1;
end
end
end
while i > 1 and parentEntityIds[ i ] ~= capitalId do
table.remove( parentSnaks, i );
table.remove( parentEntityIds, i );
i = i - 1;
end
end
end
i = i - 1;
end
end
if isSkipTopLevel( parentEntityIds[ #parentEntityIds ] ) then
table.remove( parentSnaks, #parentEntityIds );
table.remove( parentEntityIds, #parentEntityIds );
end
if not hasAdditionalQualifiers then
for i = 2, #parentSnaks, 1 do
local parentSnak = parentSnaks[ i ];
local parentOptions = context.cloneOptions( options );
parentOptions['text'] = getLabel( context, parentEntityIds[ i ], boundaries );
local comma;
if ((baseResult == '') and (i == 2)) then
comma = '';
else
comma = ', ';
end
if p.config.reverseOrder then
result = context.formatSnak( parentOptions, parentSnak ) .. comma .. result;
else
result = result .. comma .. context.formatSnak( parentOptions, parentSnak );
end
end
end
end
end
end
if options[ 'thisLocationOnly' ] then
result = baseResult;
elseif p.config.reverseOrder then
result = result .. baseResult;
else
result = baseResult .. result;
end
if options.references then
result = result .. context.formatRefs( options, statement );
end
if categorizeByPlace then
if property == 'P20' then result = result .. getCategory( 'P22', entriesToLookupCategory ); end
if property == 'P23' then result = result .. getCategory( 'P65', entriesToLookupCategory ); end
if property == 'P26' then result = result .. getCategory( 'P95', entriesToLookupCategory ); end
end
return result;
end
-- append entity id from snak to result
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 = { 'P7', 'P203', 'P77', 'P79' };
local langCode = mw.language.getContentLanguage():getCode();
local historicNamesPropertySelectors = {
P7 = 'P7[language:' .. langCode .. '][!P25,P25:Q105690470]',
P203 = 'P203[language:' .. langCode .. '][!P25,P25:Q105690470]',
P77 = 'P77[language:' .. langCode .. '][!P25,P25:Q105690470]',
P79 = 'P79[language:' .. langCode .. '][!P25,P25:Q105690470]'
};
-- get current of historic name of place
function getLabel( context, entityId, boundaries )
if not entityId then
return nil;
end
if (type(entityId) ~= 'string') then error('incorrect type of entityId argument'); end;
local label = nil;
-- name from properties
local results = getParentsInBoundariesSnakImpl( context, entityId, boundaries,
historicNamesProperties, historicNamesPropertySelectors);
for r, result in pairs( results ) do
if result.datavalue and
result.datavalue.value and
result.datavalue.value.text
then
label = result.datavalue.value.text;
break;
end
end
-- name from label
if label == nil then
label = mw.wikibase.getLabel( entityId );
end
return label;
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
-- check death day... do we have it at all?
for h, propertyId in pairs( { "P247", "P19", "P572" } ) 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
-- TODO: check other "end" properties
-- no death day
return os.time() * 1000;
end
local function deleteTwinAncestors( countryEntityId, propertyId ) --do not display countries which have twin ancestors
local badTwins
if ( countryEntityId == 'Q3123' ) then --Great Britain and Ireland
badTwins = {'Q380'} --Great Brirani & NI
elseif ( countryEntityId == 'Q3125' ) then --FR of Yugoslavia
badTwins = {'Q3126'} --Serbia & Montenegro
else
return true;
end
local listforcheck
if propertyId == 'P299' then
listforcheck = mw.wikibase.getAllStatements( mw.wikibase.getEntityIdForCurrentPage(), propertyId );
else
listforcheck = mw.wikibase.getBestStatements( mw.wikibase.getEntityIdForCurrentPage(), 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, {'P247', 'P19', 'P85'} );
if deleteTwinAncestors( countryEntityId, string.upper(options.property) ) 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 flag then
return flag .. ' <span class="country-name">' ..
context.formatStatementDefault( context, countryOptions, statement ) ..
'</span>';
end
return '<span class="country-name">' ..
context.formatStatementDefault( context, countryOptions, statement ) ..
'</span>';
else
return nil;
end
end
return p;