local categorizeByPlace = true; local WDS = require( 'Module:WikidataSelectors' ); local Flags = require( 'Module:Wikidata/Flags' ); 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 ) 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 .. '[rankreferred, 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, {'P131'} ) -- located in if not results or #results 0 then results getParentsInBoundariesSnakImpl( context, entityId, boundaries, {'P17'} ) -- 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 = { Q1140229 = true, Q3623811 = true, Q4120211 = true } local countries = { Q6256 = true, Q7275 = true, Q3024240 = true, Q3624078 = true } 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 --Property19, Property20, Property119 function p.formatPlaceWithQualifiers( context, options, statement ) local property = mw.ustring.upper( options.property ); 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 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.P131 then for i, qualifier in ipairs( statement.qualifiers.P131 ) 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 if link ~= nil then result = result .. ', ' .. link; end end insertFromSnak( qualifier, entriesToLookupCategory ) hasAdditionalQualifiers = true; end end end --country if statement.qualifiers.P17 then for i, qualifier in ipairs( statement.qualifiers.P17 ) 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 if link ~= nil then result = result .. ', ' .. link; end 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 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 ); 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" local simularCountries { ['Q41304'] 'Q1206012', -- Weimar Republic / German Reich ['Q7318'] 'Q1206012', -- Weimar Republic / Nazi Germany ['Q35'] 'Q756617', -- Denmark / Danish Realm ['Q55'] 'Q29999', -- Netherlands / Kingdom of the Netherlands ['Q32081'] 'Q865', -- Taiwan Province / Taiwan } if (#parentSnaks > 1) then for smallerCountryId, largerCountryId in pairs( simularCountries ) do if parentSnaks[ #parentSnaks ].datavalue.value.id largerCountryId and parentSnaks[ #parentSnaks - 1 ].datavalue.value.id smallerCountryId then table.remove( parentSnaks, #parentSnaks ); table.remove( parentEntityIds, #parentEntityIds ); end end end -- optimization for capital regions if (#parentSnaks > 3) then if parentSnaks[ #parentSnaks - 2 ].datavalue.value.id 'Q23939248' --Greater London, Greater London and parentSnaks[ #parentSnaks - 3 ].datavalue.value.id 'Q23306' then table.remove( parentSnaks, #parentSnaks - 2 ); table.remove( parentEntityIds, #parentEntityIds - 2 ); end end if (#parentSnaks > 2) then if parentSnaks[ #parentSnaks - 1 ].datavalue.value.id 'Q240' --Brussels-Capital, Brussels and parentSnaks[ #parentSnaks - 2 ].datavalue.value.id 'Q90870' then table.remove( parentSnaks, #parentSnaks - 2 ); table.remove( parentEntityIds, #parentEntityIds - 2 ); end end if (#parentSnaks > 2) then if parentSnaks[ #parentSnaks ].datavalue.value.id 'Q34266' and parentSnaks[ #parentSnaks - 1 ].datavalue.value.id 'Q2424466' -- Qafqaz canişinliyi görünməsin then table.remove( parentSnaks, #parentSnaks - 1 ); table.remove( parentEntityIds, #parentEntityIds - 1 ); end end if (#parentSnaks > 2) then if parentSnaks[ #parentSnaks - 1 ].datavalue.value.id 'Q763603' and parentSnaks[ #parentSnaks - 2 ].datavalue.value.id 'Q4075927' and parentSnaks[ #parentSnaks - 3 ].datavalue.value.id 'Q9248' -- Bakı, Bakı qəzası, Bakı quberniyası ardıcıllığında qəza görünməsin then table.remove( parentSnaks, #parentSnaks - 2 ); table.remove( parentEntityIds, #parentEntityIds - 2 ); end end if (#parentSnaks > 2) then if parentSnaks[ #parentSnaks - 1 ].datavalue.value.id 'Q596063' and parentSnaks[ #parentSnaks - 2 ].datavalue.value.id 'Q8051535' and parentSnaks[ #parentSnaks - 3 ].datavalue.value.id 'Q131290' -- Yelizavetpol then table.remove( parentSnaks, #parentSnaks - 2 ); table.remove( parentEntityIds, #parentEntityIds - 2 ); end end if (#parentSnaks > 2) then if parentSnaks[ #parentSnaks - 1 ].datavalue.value.id 'Q1773517' and parentSnaks[ #parentSnaks - 2 ].datavalue.value.id 'Q12864947' and parentSnaks[ #parentSnaks - 3 ].datavalue.value.id 'Q994' -- Tiflis then table.remove( parentSnaks, #parentSnaks - 2 ); table.remove( parentEntityIds, #parentEntityIds - 2 ); end end --if (#parentSnaks > 3) then --if parentSnaks[ #parentSnaks - 3 ].datavalue.value.text nil --then --table.remove( parentSnaks, #parentSnaks - 3 ); --table.remove( parentEntityIds, #parentEntityIds - 3 ); --end --end -- do not output (maternity) hospitals, houses and streets local ignoredTypes { 'Q3947', -- house 'Q16917', -- hospital 'Q34442', -- road 'Q79007', -- street 'Q174782', -- square 'Q958822', -- maternity hospital 'Q2087181', -- historic house museum 'Q11303', -- skyscraper 'Q751876', -- chateu 'Q23413', -- castle 'Q15071808', -- selsovet 'Q27002', -- selsovet } -- unignored types local unignoredTypes { 'Q879050', -- manor house 'Q1343246', -- English country house } if (#parentSnaks > 1) then local p31 mw.wikibase.getAllStatements( parentEntityIds[ 1 ], 'P31' ); local doignore true; for _, iOf in ipairs( p31 ) do for _, unignoredTypeId in ipairs( unignoredTypes ) do if ( iOf.mainsnak.datavalue.value.id unignoredTypeId ) then doignore = false; unignoredTypes = {}; ignoredTypes = {}; break; end end end if (doignore) then for _, iOf in ipairs( p31 ) do for _, ignoredTypeId in ipairs( ignoredTypes ) do if ( iOf.mainsnak.datavalue.value.id ignoredTypeId ) then baseResult ; unignoredTypes {}; ignoredTypes {}; break; end end 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; 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 ], 'P31'); for _, iOf in pairs (p31) 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 if result and context.formatSnak( parentOptions, parentSnak ) ~= nil then result = result .. comma .. context.formatSnak( parentOptions, parentSnak ); end 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 '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; 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 = { 'P1813', 'P1448', 'P1705' }; local langCode = mw.language.getContentLanguage:getCode; local historicNamesPropertySelectors = { P1813 = 'P1813[language:' .. langCode .. ']', P1448 = 'P1448[language:' .. langCode .. ']', P1705 = 'P1705[language:' .. langCode .. ']' }; -- 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.getLabelByLang( entityId, 'tr' ) 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( { "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 -- TODO: check other "end" properties -- no death day return os.time * 1000; end local function deleteTwinAncestors( countryEntityId, propertyId, entityId ) --do not display countries which have twin ancestors local badTwins if ( countryEntityId 'Q174193' ) then --Great Britain and Ireland badTwins {'Q145'} --Great Brirani & NI elseif ( countryEntityId 'Q161885' ) then --Great Britain badTwins = {'Q174193'} --Great Britain and Ireland elseif ( countryEntityId 'Q43287' ) then --German Impire badTwins {'Q41304', 'Q7318', 'Q2415901', 'Q183'} --Weimar Republic or Nazi Germany or Allied-occupied Germany or Germany elseif ( countryEntityId 'Q41304' ) then --Weimar Republic badTwins = {'Q7318', 'Q2415901', 'Q183'} --Nazi Germany or Allied-occupied Germany or Germany elseif ( countryEntityId 'Q7318' ) then --Nazi Germany badTwins {'Q2415901', 'Q183'} --Allied-occupied Germany or Germany elseif ( countryEntityId 'Q2415901' ) then --Allied-occupied Germany badTwins = {'Q183'} --Germany elseif ( countryEntityId 'Q696908' ) then --Kingdom of Poland badTwins {'Q207272', 'Q211274', 'Q36'} --Second Polish Republic or Polish People's Republic or Poland elseif ( countryEntityId 'Q207272' ) then --Second Polish Republic badTwins = {'Q211274', 'Q36'} --Polish People's Republic or Poland elseif ( countryEntityId 'Q211274' ) then --Polish People's Republic badTwins {'Q36'} --Poland elseif ( countryEntityId 'Q203493' ) then --Kingdom of Romania badTwins = {'Q842794', 'Q218'} --Socialist Republic of Romania or Romania elseif ( countryEntityId 'Q842794' ) then --Socialist Republic of Romania badTwins {'Q218'} --Romania elseif ( countryEntityId 'Q838261' ) then --FR of Yugoslavia badTwins = {'Q37024'} --Serbia & Montenegro 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 ; 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 .. ; end end if context.formatStatementDefault( context, countryOptions, statement ) ~= nil then return ; else return nil; end end end return p;