Модуль:Wikidata: различия между версиями

Материал из Энциклопедия автомобильных номеров мира
Перейти к навигации Перейти к поиску
/>Vlsergey
(Новая страница: «Test»)
/>Vlsergey
(Новая страница: «Test»)
Строка 18: Строка 18:
}
}


local moduleSources = require('Module:Wikidata/Sources')
local moduleSources = require('Module:Sources')


local p = {}
local p = {}
Строка 54: Строка 54:
     return '<span class="error">' .. i18n.errors[key] .. '</span>'
     return '<span class="error">' .. i18n.errors[key] .. '</span>'
end
end


function formatStatements( options )
function formatStatements( options )
    if not options.property then
        return formatError( 'property-param-not-provided' )
    end
     --Get entity
     --Get entity
     local entity = getEntityFromId( options.entityId )
     local entity = getEntityFromId( options.entityId )
Строка 86: Строка 81:
             local formattedStatement = p.formatStatement( statement, options )
             local formattedStatement = p.formatStatement( statement, options )
             if (formattedStatement) then
             if (formattedStatement) then
                 if ( not options.plain ) then
                 formattedStatement = '<span class="wikidata-claim" data-wikidata-claim-id="' .. statement.id .. '">' .. formattedStatement .. '</span>'
                    formattedStatement = '<span class="wikidata-claim" data-wikidata-claim-id="' .. statement.id .. '">' .. formattedStatement .. '</span>'
                end
                 table.insert( formattedStatements, formattedStatement )
                 table.insert( formattedStatements, formattedStatement )
             end
             end
Строка 126: Строка 119:


function formatStatement( statement, options, formatSnak, formatRefs )
function formatStatement( statement, options, formatSnak, formatRefs )
if ( options.plain ) then
return formatSnak( statement.mainsnak, options );
end
if ( options.references ) then
if ( options.references ) then
     return formatSnak( statement.mainsnak, options ) .. formatRefs( statement );
     return formatSnak( statement.mainsnak, options ) .. formatRefs( statement );
Строка 147: Строка 137:
local before = '<span class="wikidata-snak ' .. mainSnakClass .. '"' .. hash .. '>'
local before = '<span class="wikidata-snak ' .. mainSnakClass .. '"' .. hash .. '>'
local after = '</span>'
local after = '</span>'
 
if options.plain then
before = ''
after = ''
end
     if snak.snaktype == 'somevalue' then
     if snak.snaktype == 'somevalue' then
         return before .. (options['somevalue'] or i18n['somevalue']) .. after;
         return before .. (options['somevalue'] or i18n['somevalue']) .. after;
Строка 165: Строка 150:


function formatGlobeCoordinate( value, options )
function formatGlobeCoordinate( value, options )
    if ( options.plain ) then
        return value['latitude'] .. ';' .. value['longitude']
    end
     if options['subvalue'] == 'latitude' then
     if options['subvalue'] == 'latitude' then
         return value['latitude']
         return value['latitude']
Строка 237: Строка 217:
         return formatGlobeCoordinate( datavalue.value, options )  
         return formatGlobeCoordinate( datavalue.value, options )  
     elseif datavalue.type == 'quantity' then
     elseif datavalue.type == 'quantity' then
         return string.gsub(datavalue.value['amount'], '^%+', '')
         local value = string.gsub(datavalue.value['amount'], '^%+', '')
        local lang = mw.language.new( 'ru' )
        return lang:formatNum( tonumber( value ) )
     else
     else
         return formatError( 'unknown-datavalue-type' )
         return formatError( 'unknown-datavalue-type' )
Строка 247: Строка 229:
     if ( options.text and options.text ~= '' ) then
     if ( options.text and options.text ~= '' ) then
         label = options.text
         label = options.text
    end
    if ( options.plain ) then
        if ( label ) then
            return label
        else
            return entityId
        end
     end
     end


Строка 275: Строка 249:


function p.formatStatements( frame )
function p.formatStatements( frame )
g_frame = frame
     local args = frame.args
     local args = frame.args


Строка 283: Строка 256:
     end
     end


     frame.args.plain = toBoolean( frame.args.plain, false );
     local plain = toBoolean( frame.args.plain, false );
     frame.args.references = toBoolean( frame.args.references, true );
     frame.args.references = toBoolean( frame.args.references, true );
    if not frame.args.property then
        return formatError( 'property-param-not-provided' )
    end
    if ( plain ) then
    return frame:callParserFunction( '#property', frame.args.property );
    end
g_frame = frame
     return formatStatements( frame.args )
     return formatStatements( frame.args )
end
end
local haveReferences = false;


function p.formatRefs( statement )
function p.formatRefs( statement )
local result = '';
local result = '';
if ( statement.references ) then
if ( statement.references ) then
haveReferences = true;
for _, reference in pairs( statement.references ) do
for _, reference in pairs( statement.references ) do
result = result .. formatRef(reference);
result = result .. moduleSources.renderReference( g_frame, reference );
end
end
end
end
return result
return result
end
function formatRef( reference )
if ( not reference.snaks ) then
return ''
end
if ( reference.snaks.p248 ) then
local entityId = "Q" .. reference.snaks.p248[0].datavalue.value["numeric-id"];
local renderFunction = moduleSources['src' .. entityId]
if ( renderFunction ) then
local rendered = moduleSources['src' .. entityId]( reference );
return g_frame:extensionTag( 'ref', rendered.text, {name = rendered.code} ) .. '[[К:ЭАНМ:Статьи с источниками из Викиданных]]';
end
end
local rendered = moduleSources.srcDefault( reference );
if ( rendered ) then
if ( string.len( rendered.text ) ~= 0 ) then
if ( rendered.code ) then
return g_frame:extensionTag( 'ref', rendered.text, {name = rendered.code} ) .. '[[К:ЭАНМ:Статьи с источниками из Викиданных]][[К:ЭАНМ:Статьи с неоформленными источниками из Викиданных]]';
else
return g_frame:extensionTag( 'ref', rendered.text ) .. '[[К:ЭАНМ:Статьи с источниками из Викиданных]]';
end
end
end
return '';
end
function p.formatReferences( )
if ( haveReferences ) then
return g_frame:extensionTag( 'references' );
end
return '';
end
end


return p
return p

Версия от 20:25, 12 августа 2014

Используется в {{Wikidata}} (см. описания параметров там же). Настраивается при помощи Модуль:Wikidata/config.

Прежде чем вносить какие-либо изменения в данный модуль, просьба оттестировать их в /песочнице. Обратите внимание, что не всё корректно работает в песочнице.

Общие сведения

Функции данного модуля не предназначены для прямого вызова из шаблонов карточек или других модулей, не являющихся функциями расширения данного. Для вызова из шаблонов карточек используйте шаблон {{wikidata}} или один из специализированных шаблонов для свойств. Для вызова функций Викиданных предназначенных для отображения чаще всего достаточно вызова frame:expandTemplate{} с вызовом шаблона, ответственного за отрисовку свойства. С другой стороны, вызов определённых функций модуля (в основном это касается getEntityObject()) может в будущем стать предпочтительным. Данный Lua-функционал в любом случае стоит рассматривать как unstable с точки зрения сохранения совместимости на уровне кода (вместе с соответствующими функциями API для Wikibase Client).

Далее описывается внутренняя документация. Названия функций и параметров могут изменяться. При их изменении автор изменений обязан обновить шаблон {{wikidata}} и специализированные шаблоны свойств. Изменения в других местах, если кто-то всё-таки вызывает функции модуля напрямую, остаются на совести автора «костыля». Итак, при вызове шаблона {{wikidata}} или специализированного шаблона свойства управление отдаётся на функцию formatStatements, которая принимает frame. Из frame достаются следующие опции, которые так или иначе передаются в остальные функции:

  • plain — булевый переключатель (по умолчанию false). Если true, результат совпадает с обычным вызовом {{#property:pNNN}} (по факту им и будет являться)
  • references — булевый переключатель (по умолчанию true). Если true, после вывода значения параметра дополнительно выводит ссылки на источники, указанные в Викиданных. Для вывода используется Модуль:Sources. Обычно отключается для тех свойств, которые являются «самоописываемыми», например, внешними идентификаторами или ссылками (когда такая ссылка является доказательством своей актуальности), например, идентификаторы IMDb.
  • value — значение, которое надо выводить вместо значений из Викиданных (используется, если что-то задано уже в карточке в виде т. н. локального свойства)

По умолчанию модуль поддерживает вывод следующих значений без дополнительных настроек:

  • географические координаты (coordinates)
  • количественные значения (quantity)
  • моноязычный текст (monolingualtext)
  • строки (string)
  • даты (time)

Остальные типы данных требуют указания функции форматирования значения.

Кастомизация

Поддерживаются три типа параметров-функций, которые дополнительно указывают, как надо форматировать значения:

  • property-module, property-function — название модуля и функции модуля, которые отвечают за форматирование вывода массива значений свойства (statements, claims) с учётом квалификаторов, ссылок и прочего. Например, оформляет множество выводов в таблицу или график. Характерные примеры:
    Спецификация функции: function p.…( context, options ), поведение по умолчанию: Модуль:Wikidata#formatPropertyDefault.
  • claim-module, claim-function — название модуля и функции модуля, которые отвечают за форматирование вывода значения свойства (statement, claim) с учётом квалификаторов, ссылок и прочего. Может, например, дополнительно к основному значению (main snak) вывести значения квалификаторов. Характерные примеры:
    Спецификация функции: function p.…( context, statement )
  • value-module, value-function — название модуля и функции модуля, которые отвечают за форматирование значения (snak, snak data value), в зависимости от контекста, как значений свойства, так и значений квалификатора (если вызывается из claim-module/claim-function). Необходимо для изменения отображения свойства, например, генерации викиссылки вместо простой строки или даже вставки изображения вместо отображения имени файла изображения (так как ссылки на изображения хранятся как строки). Характерные примеры:
    Спецификация функции: function p.…( value, options )

Заготовки функций

Context API

Переменные

  • entity
  • frame

Методы

  • cloneOptions( options )
  • getSourcingCircumstances( statement )
  • formatProperty( options )
  • formatPropertyDefault( context, options )
  • formatSnak( options, snak, circumstances )
  • formatStatement( options, statement )
  • formatStatementDefault( context, options, statement )
  • formatRefs( options, statement )
  • formatValueDefault( context, options, value )
  • parseTimeBoundariesFromSnak( snak )
  • parseTimeFromSnak( snak )
  • selectClaims( options, propertyId )
  • wrapSnak( value, hash, attributes )
  • wrapStatement( value, propertyId, claimId, attributes )
  • wrapQualifier( value, qualifierId, attributes )

Функции для форматирования

property-function

claim-function

value-function

См. также


local i18n = {
    ["errors"] = {
        ["property-param-not-provided"] = "Не дан параметр свойства",
        ["entity-not-found"] = "Сущность не найдена.",
        ["unknown-claim-type"] = "Неизвестный тип заявления.",
        ["unknown-snak-type"] = "Неизвестный тип снэка.",
        ["unknown-datavalue-type"] = "Неизвестный тип значения данных.",
        ["unknown-entity-type"] = "Неизвестный тип сущности.",
        ["unknown-claim-module"] = "Вы должны установить и claim-module, и claim-function.",
        ["unknown-value-module"] = "Вы должны установить и value-module, и value-function.",
        ["claim-module-not-found"] = "Модуль, на который указывает утверждение, не найден.",
        ["claim-function-not-found"] = "Функция, на которую указывает утверждение, не найдена.",
        ["value-module-not-found"] = "Модуль, на который указывает значение, не найден.",
        ["value-function-not-found"] = "Функция, на которую указывает значение, не найдена."
    },
    ["somevalue"] = "",
    ["novalue"] = ""
}

local moduleSources = require('Module:Sources')

local p = {}

function toBoolean( valueToParse, defaultValue )
    if ( valueToParse ) then
        if valueToParse == '' or valueToParse == 'false' or valueToParse == '0' then
            return false
        end
        return true
    end
    return defaultValue;
end

function getEntityFromId( id )
    if id then
        return mw.wikibase.getEntity( id )
    end
    return mw.wikibase.getEntity()
end

function getEntityIdFromValue( value )
    local prefix = ''
    if value['entity-type'] == 'item' then
        prefix = 'q'
    elseif value['entity-type'] == 'property' then
        prefix = 'p'
    else
        return formatError( 'unknown-entity-type' )
    end
    return prefix .. value['numeric-id']
end

function formatError( key )
    return '<span class="error">' .. i18n.errors[key] .. '</span>'
end

function formatStatements( options )
    --Get entity
    local entity = getEntityFromId( options.entityId )
    if not entity then
        return -- formatError( 'entity-not-found' )
    end

    if (entity.claims == nil) or (not entity.claims[string.lower(options.property)]) then
        return '' --TODO error?
    end

    --Find if any statements are prefered
    local rank = 'normal'
    for i, statement in pairs( entity.claims[string.lower(options.property)] ) do
        if (statement.rank == 'preferred') then
            rank = 'preferred'
            break
        end
    end

    --Format statement and concat them cleanly
    local formattedStatements = {}
    for i, statement in pairs( entity.claims[string.lower(options.property)] ) do
        if (statement.rank == rank) then
            local formattedStatement = p.formatStatement( statement, options )
            if (formattedStatement) then
                formattedStatement = '<span class="wikidata-claim" data-wikidata-claim-id="' .. statement.id .. '">' .. formattedStatement .. '</span>'
                table.insert( formattedStatements, formattedStatement )
            end
        end
    end
    return mw.text.listToText( formattedStatements, options.separator, options.conjunction )
end

function p.formatStatement( statement, options )
    if not statement.type or statement.type ~= 'statement' then
        return formatError( 'unknown-claim-type' )
    end

    local claim

    --Use the customize handler if provided
    if options['claim-module'] or options['claim-function'] then
        if not options['claim-module'] or not options['claim-function'] then
            return formatError( 'unknown-claim-module' )
        end
        local formatter = require ('Module:' .. options['claim-module'])
        if formatter == nil then
            return formatError( 'claim-module-not-found' )
        end
        local fun = formatter[options['claim-function']]
        if fun == nil then
            return formatError( 'claim-function-not-found' )
        end
        claim = fun( statement, options, formatSnak, p.formatRefs )
    else
        --Default formatter
        claim = formatStatement( statement, options, formatSnak, p.formatRefs )
    end

    return claim
end

function formatStatement( statement, options, formatSnak, formatRefs )
	if ( options.references ) then
    	return formatSnak( statement.mainsnak, options ) .. formatRefs( statement );
    else
    	return formatSnak( statement.mainsnak, options );
    end
end

function formatSnak( snak, options )
	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 .. (options['somevalue'] or i18n['somevalue']) .. after;
    elseif snak.snaktype == 'novalue' then
        return before .. (options['novalue'] or i18n['novalue']) .. after;
    elseif snak.snaktype == 'value' then
        return before .. formatDatavalue( snak.datavalue, options ) .. after;
    else
        return before .. formatError( 'unknown-snak-type' ) .. after;
    end
end

function formatGlobeCoordinate( value, options )
    if options['subvalue'] == 'latitude' then
        return value['latitude']
    elseif options['subvalue'] == 'longitude' then
        return value['longitude']
    else
        local eps = 0.0000001 -- < 1/360000
        local globe = '' -- TODO
        local lat = {}
        lat['abs'] = math.abs(value['latitude'])
        lat['ns'] = value['latitude'] >= 0 and 'N' or 'S'
        lat['d'] = math.floor(lat['abs'] + eps)
        lat['m'] = math.floor((lat['abs'] - lat['d']) * 60 + eps)
        lat['s'] = math.max(0, ((lat['abs'] - lat['d']) * 60 - lat['m']) * 60)
        local lon = {}
        lon['abs'] = math.abs(value['longitude'])
        lon['ew'] = value['longitude'] >= 0 and 'E' or 'W'
        lon['d'] = math.floor(lon['abs'] + eps)
        lon['m'] = math.floor((lon['abs'] - lon['d']) * 60 + eps)
        lon['s'] = math.max(0, ((lon['abs'] - lon['d']) * 60 - lon['m']) * 60)
        local coord = '{{coord'
        if (value['precision'] == nil) or (value['precision'] < 1/60) then -- по умолчанию с точностью до секунды
            coord = coord .. '|' .. lat['d'] .. '|' .. lat['m'] .. '|' .. lat['s'] .. '|' .. lat['ns']
            coord = coord .. '|' .. lon['d'] .. '|' .. lon['m'] .. '|' .. lon['s'] .. '|' .. lon['ew']
        elseif value['precision'] < 1 then
            coord = coord .. '|' .. lat['d'] .. '|' .. lat['m'] .. '|' .. lat['ns']
            coord = coord .. '|' .. lon['d'] .. '|' .. lon['m'] .. '|' .. lon['ew']
        else
            coord = coord .. '|' .. lat['d'] .. '|' .. lat['ns']
            coord = coord .. '|' .. lon['d'] .. '|' .. lon['ew']
        end
        coord = coord .. '|globe:' .. globe
        if options['display'] then
            coord = coord .. '|display=' .. options.display
        else
            coord = coord .. '|display=title'
        end
        coord = coord .. '}}'

        return g_frame:preprocess(coord)
    end
end

function formatDatavalue( datavalue, options )
    --Use the customize handler if provided
    if options['value-module'] or options['value-function'] then
        if not options['value-module'] or not options['value-function'] then
            return formatError( 'unknown-value-module' )
        end
        local formatter = require ('Module:' .. options['value-module'])
        if formatter == nil then
            return formatError( 'value-module-not-found' )
        end
        local fun = formatter[options['value-function']]
        if fun == nil then
            return formatError( 'value-function-not-found' )
        end
        return fun( datavalue.value, options )
    end

    --Default formatters
    if datavalue.type == 'wikibase-entityid' then
        return formatEntityId( getEntityIdFromValue( datavalue.value ), options )
    elseif datavalue.type == 'string' then
        return datavalue.value --TODO ids + media
    elseif datavalue.type == 'globecoordinate' then
        return formatGlobeCoordinate( datavalue.value, options ) 
    elseif datavalue.type == 'quantity' then
        local value = string.gsub(datavalue.value['amount'], '^%+', '')
        local lang = mw.language.new( 'ru' )
        return lang:formatNum( tonumber( value ) )
    else
        return formatError( 'unknown-datavalue-type' )
    end
end

function formatEntityId( entityId, options )
    local label = mw.wikibase.label( entityId )
    if ( options.text and options.text ~= '' ) then
        label = options.text
    end

    local link = mw.wikibase.sitelink( entityId )
    if link then
        if label then
            return '[[' .. link .. '|' .. label .. ']]'
        else
            return '[[' .. link .. ']]'
        end
    end

    if label then
        return label
    end

    -- not good, but better than nothing
    return '[[d:' .. entityId .. '|' .. entityId .. ']]<span style="border-bottom: 1px dotted; cursor: help; white-space: nowrap" title="На Викиданных нет русской подписи к элементу. Вы можете помочь, указав русский вариант подписи.">?</span>[[Категория:ЭАНМ:Статьи со ссылками на элементы Викиданных без русской подписи]]';
end

function p.formatStatements( frame )
    local args = frame.args

    --If a value if already set, use it
    if args.value and args.value ~= '' then
        return args.value
    end

    local plain = toBoolean( frame.args.plain, false );
    frame.args.references = toBoolean( frame.args.references, true );

    if not frame.args.property then
        return formatError( 'property-param-not-provided' )
    end

    if ( plain ) then
    	return frame:callParserFunction( '#property', frame.args.property );
    end

	g_frame = frame
    return formatStatements( frame.args )
end

function p.formatRefs( statement )
	local result = '';
	if ( statement.references ) then
		for _, reference in pairs( statement.references ) do
			result = result .. moduleSources.renderReference( g_frame, reference );
		end
	end
	return result
end

return p