Module:Wikidata-n
Uit EverybodyWiki Bios & Wiki
local p = {}
local formatEntityId, formatSnak;
local WDS = require( 'Module:WikidataSelectors' );
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 )
mw.log( 'Get time boundaries for ' .. propertyId .. '...');
local dateClaims = WDS.filter( context.entity.claims, propertyId );
if ( not dateClaims or #dateClaims == 0 ) then return nil; end
mw.log( 'Get time boundaries for ' .. propertyId .. '... Got ' .. #dateClaims .. ' date claim(s)');
-- only support exact date so far, but need improvment
local left = nil;
local right = nil;
if ( not left or not right ) then return nil; end
mw.log( 'Time boundaries for ' .. propertyId .. ' are ' .. left .. ' and ' .. right );
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, entity, boundaries, propertyIds )
local results = {};
if entity.claims then
for _, propertyId in ipairs( propertyIds ) do
local filteredClaims = WDS.filter( entity.claims, propertyId .. '[rank:preferred, rank:normal]' );
if filteredClaims then
for _, claim in pairs( filteredClaims ) do
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
if #results > 0 then
break;
end
end
end
return results;
end
local function getParentsInBoundariesSnak( context, entity, boundaries )
if ( not entity ) then error('entity must be specified'); end
if ( type(entity) ~= 'table' ) then error('entity must be table'); 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, entity, boundaries, {'P131'} )
if not results or #results == 0 then
results = getParentsInBoundariesSnakImpl( context, entity, boundaries, {'P17'} )
end
for r, result in pairs( results ) do
local resultId = 'Q' .. result.datavalue.value['numeric-id'];
if ( resultId == entity.id ) then
return nil;
end
end
return results;
end
local unions = {
Q1140229 = true, -- political union
Q3623811 = true, -- Экономический союз
Q4120211 = true -- региональная организация
}
local countries = {
Q6256 = true, -- страна
Q7275 = 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['numeric-id'] ) then
local typeId = 'Q' .. claim.mainsnak.datavalue.value['numeric-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
-- append entity id from snak to result
function insertFromSnak( snak, result )
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, 'Q' .. snak.datavalue.value['numeric-id'] );
end
end
-- get current of historic name of place
function getLabel( context, entity, boundaries )
if not entity then
return nil;
end
local lang = mw.language.getContentLanguage();
local langCode = lang:getCode();
-- name from label
-- TODO: lang:getFallbackLanguages()
local label = nil;
if entity.labels then
if entity.labels[langCode] and entity.labels[langCode].value then
label = entity.labels[langCode].value;
elseif entity.labels.en and entity.labels.en.value then
label = entity.labels.en.value;
end
end
-- name from properties
local results = getParentsInBoundariesSnakImpl( context, entity, boundaries, {
'P1813[language:' .. langCode .. ']',
'P1448[language:' .. langCode .. ']',
'P1705[language:' .. langCode .. ']'
} );
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
return label;
end
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
-- TODO: check other "end" properties
-- no death day
return os.time() * 1000;
end
function getFlag( context, countryEntityId, actualDate )
local countryEntity = mw.wikibase.getEntity( countryEntityId );
if ( not countryEntity or not countryEntity.claims or not countryEntity.claims.P41 ) then
return nil;
end
local countryFlags = {};
local flagImageStatements = countryEntity.claims.P41;
for _, flagImageStatement in pairs( countryEntity.claims.P41 ) do
if ( flagImageStatement.rank ~= 'deprecated' ) then
local flagImage;
if ( flagImageStatement and flagImageStatement.mainsnak and flagImageStatement.mainsnak.datavalue and flagImageStatement.mainsnak.datavalue.value ) then
flagImage = flagImageStatement.mainsnak.datavalue.value;
end
local flagStartTime = -9223372036854775808;
if ( flagImage ) then
countryFlags[ flagStartTime ] = flagImage;
end
end
end
local goodFlag = nil;
if ( countryFlags ) then
local ordered_dates = {}
for flagBeginDate in pairs(countryFlags) do
table.insert(ordered_dates, flagBeginDate)
end
table.sort(ordered_dates)
for i = 1, #ordered_dates do
local flagBeginDate, flag = ordered_dates[i], countryFlags[ ordered_dates[i] ];
if ( actualDate >= flagBeginDate ) then
goodFlag = flag;
end
end
end
if ( goodFlag ) then
return '[[File:' .. goodFlag .. '|20x15px|border]]';
end
return nil;
end
function 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;
local countryEntityId = nil;
local countryEntity = nil;
if ( statement.mainsnak and statement.mainsnak.datavalue and statement.mainsnak.datavalue.value and statement.mainsnak.datavalue.value["numeric-id"] ) then
countryEntityId = 'Q' .. statement.mainsnak.datavalue.value["numeric-id"];
countryEntity = mw.wikibase.getEntity( countryEntityId );
end
if not countryEntity then
return '<span class="country-name">' .. formatSnak(context, options, statement.mainsnak ) .. '</span>';
end
local endDateTimestamp = calculateEndDateTimestamp( context, options, statement );
local boundaries = getTimeBoundariesFromProperties( context, {'P570', 'P577', 'P571'} );
local countryOptions = mw.clone( options );
if not countryOptions['text'] or countryOptions['text'] == '' then
countryOptions['text'] = getLabel( context, countryEntity, boundaries );
end
local flag = getFlag( context, countryEntityId, endDateTimestamp );
if ( flag ) then
return flag .. ' <span class="country-name">' .. formatSnak(context, countryOptions, statement.mainsnak ) .. '</span>';
end
return '<span class="country-name">' .. formatSnak(context, countryOptions, statement.mainsnak ) .. '</span>';
end
local function getEntityIdFromValue( value )
return 'Q' .. value['numeric-id']
end
local function selectClaims(entity, propertySelector )
local WDS = require('Module:WikidataSelectors')
result = WDS.filter(entity.claims, propertySelector)
if ( not result or #result == 0 ) then
return nil;
end
return result;
end
function formatSnak( context, args, snak)
local hash = '';
local mainSnakClass = '';
if ( snak.hash ) then
hash = ' data-wikidata-hash="' .. snak.hash .. '"';
else
mainSnakClass = ' wikidata-main-snak';
end
local before = '<span class="wikidata-snak ' .. mainSnakClass .. '"' .. hash .. '>'
local after = '</span>'
if snak.snaktype == 'somevalue' then
return before .. after;
elseif snak.snaktype == 'novalue' then
return before .. after;
elseif snak.snaktype == 'value' then
return before .. formatDatavalue( context, args, snak.datavalue, snak.datatype ) .. after;
end
end
--[[
Функция для оформления значений (value)
Подробнее о значениях см. d:Wikidata:Glossary/ru
Принимает: объект-значение и таблицу параметров,
Возвращает: строку оформленного текста
]]
function formatDatavalue( context, options, datavalue, datatype )
if ( not context ) then error( 'context not specified' ); end;
if ( not options ) then error( 'options not specified' ); end;
if ( not datavalue ) then error( 'datavalue not specified' ); end;
if ( not datavalue.value ) then error( 'datavalue.value is missng' ); end;
-- проверка на указание специализированных обработчиков в параметрах,
-- переданных при вызове
if datavalue.type == 'wikibase-entityid' then
-- идентификатор сущности
context.formatValueDefault = function( context, options, value ) return formatEntityId( getEntityIdFromValue( value ), options ) end;
end
local functionToCall = context.formatValueDefault;
return functionToCall( context, options, datavalue.value );
end
--[[
Функция для оформления идентификатора сущности
Принимает: строку индентификатора (типа Q42) и таблицу параметров,
Возвращает: строку оформленного текста
]]
function formatEntityId( entityId, options )
local label = nil;
label = mw.wikibase.label( entityId );
local link = mw.wikibase.sitelink( entityId )
if link then
if label then
return '[[' .. link .. '|' .. label .. ']]'
else
return '[[' .. link .. ']]'
end
end
if label then
-- TODO: перенести до проверки на существование статьи
local sup = '';
if ( not options.format or options.format ~= 'text' )
and entityId ~= 'Q6581072' and entityId ~= 'Q6581097' -- TODO: переписать на format=text
then
local lang = mw.language.getContentLanguage()
sup = '<sup class="plainlinks noprint">[//www.wikidata.org/wiki/' .. entityId .. '?uselang=' .. lang:getCode() .. ' [d]]</sup>'
end
-- одноимённая статья уже существует - выводится текст и ссылка на ВД
return '<span class="iw" data-title="' .. label .. '">' .. label
.. sup
.. '</span>'
end
return '';
end
function p.formatProperty( frame )
local args = frame.args
local entity = mw.wikibase.getEntityObject();
args.entity = entity;
-- create context
local context = {
entity = mw.wikibase.getEntityObject()}
local claims = selectClaims(entity, args.property );
local formattedClaims = {}
for i, claim in ipairs(claims) do
local formattedStatement = formatCountryClaimWithFlag( context, args, claim )
formattedStatement = '<span class="wikidata-claim" data-wikidata-claim-id="' .. claim.id .. '">' .. formattedStatement .. '</span>'
table.insert( formattedClaims, formattedStatement )
end
-- создание текстовой строки со списком оформленых заявлений из таблицы
local out = mw.text.listToText( formattedClaims, '<br />', '<br />' )
if out ~= '' then
if args.before then
out = args.before .. out
end
if args.after then
out = out .. args.after
end
end
return out
end
return p