autoreview, Бюрократы, Проверяющие участников, developer, editor, Администраторы интерфейса, reviewer, Администраторы, translator
2481
правка
/>Vlsergey-at-work (Новая страница: «Test») |
Admin (обсуждение | вклад) м (Замена текста — «Википедия:» на «ЭАНМ:») |
||
(не показано 30 промежуточных версий 8 участников) | |||
Строка 1: | Строка 1: | ||
---@alias args table | |||
---@alias frame { args: args, extensionTag: function, newChild: ( fun( args: args ): frame ) } | |||
---@alias source { publication: source, [string]: any } | |||
---@alias value: string | { id: string } | |||
---@alias snak { datatype: string, snaktype: string, datavalue: { type: string, value: value } } | |||
---@alias snaks table<string, table<number, snak>> | |||
---@alias statement { mainsnak: snak, rank: string, qualifiers: snaks } | |||
---@alias statements table<string, table<number, statement>> | |||
---@alias map { name: string, ids: string[] }[]> | |||
local i18nDefaultLanguage = 'ru' | ---@type table | ||
local p = {} | |||
---@type table<string, string> | |||
local NORMATIVE_DOCUMENTS = { | |||
Q20754888 = 'Закон Российской Федерации', | |||
Q20754884 = 'Закон РСФСР', | |||
Q20873831 = 'Распоряжение Президента Российской Федерации', | |||
Q20873834 = 'Указ исполняющего обязанности Президента Российской Федерации', | |||
Q2061228 = 'Указ Президента Российской Федерации', | |||
} | |||
---@type table<string, string> | |||
local LANG_CACHE = { | |||
Q150 = 'fr', | |||
Q188 = 'de', | |||
Q1321 = 'es', | |||
Q1860 = 'en', | |||
Q652 = 'it', | |||
Q7737 = 'ru', | |||
Q8798 = 'uk', | |||
} | |||
---@type map | |||
local PROPERTY_MAP = { | |||
{ name = 'sourceId', ids = { 'P248', 'P805' } }, | |||
{ name = 'lang', ids = { 'P407', 'P364' } }, | |||
{ name = 'author', ids = { 'P50', 'P2093' } }, | |||
{ name = 'part', ids = { 'P958', 'P1810' } }, | |||
{ name = 'title', ids = { 'P1476' } }, | |||
{ name = 'subtitle', ids = { 'P1680' } }, | |||
{ name = 'url', ids = { 'P953', 'P1065', 'P854', 'P973', 'P2699', 'P888' } }, | |||
{ name = 'editor', ids = { 'P98' } }, | |||
{ name = 'translator', ids = { 'P655' } }, | |||
{ name = 'publication-id', ids = { 'P1433' } }, | |||
{ name = 'edition', ids = { 'P393' } }, | |||
{ name = 'publisher', ids = { 'P123' } }, | |||
{ name = 'place', ids = { 'P291' } }, | |||
{ name = 'volume', ids = { 'P478' } }, | |||
{ name = 'issue', ids = { 'P433' } }, | |||
{ name = 'dateOfCreation', ids = { 'P571' } }, | |||
{ name = 'dateOfPublication', ids = { 'P577' } }, | |||
{ name = 'pages', ids = { 'P304' } }, | |||
{ name = 'numberOfPages', ids = { 'P1104' } }, | |||
{ name = 'tirage', ids = { 'P1092' } }, | |||
{ name = 'isbn', ids = { 'P212', 'P957' } }, | |||
{ name = 'issn', ids = { 'P236' } }, | |||
-- { name = 'accessdate', ids = { 'P813' } }, -- disable, creates duplicate references | |||
{ name = 'docNumber', ids = { 'P1545' } }, | |||
{ name = 'type', ids = { 'P31' } }, | |||
{ name = 'arxiv', ids = { 'P818' } }, | |||
{ name = 'doi', ids = { 'P356' } }, | |||
{ name = 'pmid', ids = { 'P698' } }, | |||
} | |||
-- table.insert( PROPERTY_MAP.url, 'P856' ) -- only as qualifier | |||
---@type map | |||
local PUBLICATION_PROPERTY_MAP = mw.clone( PROPERTY_MAP ) | |||
---@type string[] | |||
local monthGen = { 'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря' } | |||
---@type string | |||
local i18nDefaultLanguage = mw.language.getContentLanguage():getCode() | |||
p.i18nDefaultLanguage = i18nDefaultLanguage | |||
---@type string | |||
local i18nEtAlDefault = ' et al.' | |||
---@type table<string, string> | |||
local i18nEtAl = { | |||
ru = ' и др.', | |||
uk = ' та ін.', | |||
} | |||
---@type table<string, string> | |||
local i18nEditors = { | local i18nEditors = { | ||
fr = '', | |||
de = 'Hrsg.: ', | |||
es = '', | |||
en = '', | |||
it = '', | |||
ru = 'под ред. ', | |||
uk = 'за ред. ', | |||
} | } | ||
local | |||
---@type table<string, string> | |||
local i18nTranslators = { | |||
fr = '', | |||
de = '', | |||
es = '', | |||
en = '', | |||
it = '', | |||
ru = 'пер. ', | |||
uk = 'пер. ', | |||
} | } | ||
---@type table<string, string> | |||
local i18nVolume = { | local i18nVolume = { | ||
de = 'Vol.', | |||
fr = 'Vol.', | |||
es = 'Vol.', | |||
en = 'Vol.', | |||
it = 'Vol.', | |||
ru = 'Т.', | |||
uk = 'Т.', | |||
} | } | ||
---@type table<string, string> | |||
local i18nIssue = { | local i18nIssue = { | ||
en = 'Iss.', | |||
ru = 'вып.', | |||
uk = 'вип.', | |||
} | } | ||
---@type table<string, string> | |||
local i18nPages = { | local i18nPages = { | ||
fr = 'P.', | |||
de = 'S.', | |||
es = 'P.', | |||
en = 'P.', | |||
it = 'P.', | |||
ru = 'С.', | |||
uk = 'С.', | |||
} | } | ||
---@type table<string, string> | |||
local i18nNumberOfPages = { | local i18nNumberOfPages = { | ||
en = 'p.', | |||
ru = 'с.', | |||
} | } | ||
local | ---@type table<string, string> | ||
local i18nTirage = { | |||
en = 'ed. size: %d', | |||
ru = '%d экз.', | |||
} | } | ||
local | ---@param args args | ||
---@return source | |||
local function getFilledArgs( args ) | |||
---@type source | |||
local data = {} | |||
for key, value in pairs( args ) do | |||
if mw.text.trim( value ) ~= '' then | |||
if key == 1 then | |||
key = 'sourceId' | |||
end | |||
data[ key ] = mw.text.trim( value ) | |||
end | |||
end | |||
return data | |||
end | |||
---Returns formatted pair {Family name(s), First name(s)} | |||
---@param fullName string | |||
local | ---@return table<number, string> | ||
local | local function tokenizeName( fullName ) | ||
local | local space = '%s+' -- matches single or more spacing character | ||
local | local name = "(%a[%a%-']*)%.?" -- matches single name, have to start with letter, can contain apostrophe and hyphen, may end with dot | ||
local | local surname = "(%a[%a%-']*)" -- same as name, but can't end with dot | ||
local surnamePrefixes = { 'ван', 'van', 'де', 'de' } | |||
local nm, nm2, srn, srn2, pref | |||
fullName = ' ' .. fullName .. ' ' | |||
fullName = mw.ustring.gsub( fullName, ' оглы ', ' ' ) | |||
fullName = mw.text.trim( fullName ) | |||
-- Surname, Name | |||
local pattern = '^' .. surname .. ',' .. space .. name .. '$' | |||
end | srn, nm = mw.ustring.match( fullName, pattern ) | ||
if srn then | |||
return { | |||
srn, | |||
mw.ustring.sub( nm, 1, 1 ) .. '.' | |||
} | |||
end | |||
-- Surname, Name prefix | |||
for _, surnamePrefix in pairs( surnamePrefixes ) do | |||
pattern = '^' .. surname .. ',' .. space .. name .. space .. '(' .. surnamePrefix .. ')' .. '$' | |||
srn, nm, pref = mw.ustring.match( fullName, pattern ) | |||
if srn then | |||
return { | |||
mw.ustring.sub( pref ) .. ' ' .. srn, | |||
mw.ustring.sub( nm, 1, 1 ) .. '.' } | |||
end | |||
end | |||
-- Surname, Name Name | |||
pattern = '^' .. surname .. ',' .. space .. name .. space .. name .. '$' | |||
srn, nm, nm2 = mw.ustring.match( fullName, pattern ) | |||
if srn then | |||
return { | |||
srn, | |||
mw.ustring.sub( nm, 1, 1 ) .. '. ' .. mw.ustring.sub( nm2, 1, 1 ) .. '.' | |||
} | |||
end | |||
-- Surname Surname, Name | |||
end | pattern = '^' .. surname .. space .. surname .. ',' .. space .. name .. '$' | ||
srn, srn2, nm = mw.ustring.match( fullName, pattern ) | |||
if srn then | |||
return { | |||
srn .. ' ' .. srn2, | |||
mw.ustring.sub( nm, 1, 1 ) .. '.' | |||
} | |||
end | |||
-- Name Name Surname | |||
pattern = '^' .. name .. space .. name .. space .. surname .. '$' | |||
nm, nm2, srn = mw.ustring.match( fullName, pattern ) | |||
if srn then | |||
return { | |||
srn, | |||
mw.ustring.sub( nm, 1, 1 ) .. '. ' .. mw.ustring.sub( nm2, 1, 1 ) .. '.' | |||
} | |||
end | |||
-- Name Name prefix Surname | |||
for _, surnamePrefix in pairs( surnamePrefixes ) do | |||
pattern = '^' .. name .. space .. name .. space .. '(' .. surnamePrefix .. ')' .. space .. surname .. '$' | |||
nm, nm2, pref, srn = mw.ustring.match( fullName, pattern ) | |||
if srn then | |||
return { | |||
mw.ustring.sub( pref ) .. ' ' .. srn, | |||
mw.ustring.sub( nm, 1, 1 ) .. '. ' .. mw.ustring.sub( nm2, 1, 1 ) .. '.' | |||
} | |||
end | |||
end | |||
-- Surname, Name Name prefix | |||
for _, surnamePrefix in pairs( surnamePrefixes ) do | |||
pattern = '^' .. surname .. ',' .. space .. name .. space .. name .. space .. '(' .. surnamePrefix .. ')' .. '$' | |||
srn, nm, nm2, pref = mw.ustring.match( fullName, pattern ) | |||
if srn then | |||
return { | |||
mw.ustring.sub( pref ) .. ' ' .. srn, | |||
mw.ustring.sub( nm, 1, 1 ) .. '. ' .. mw.ustring.sub( nm2, 1, 1 ) .. '.' | |||
} | |||
end | |||
end | |||
-- Name{1,4} Surname | |||
end | for k = 1, 4 do | ||
pattern = '^' .. string.rep( name .. space, k ) .. surname .. '$' | |||
---@type string[] | |||
local matched = { mw.ustring.match( fullName, pattern ) } | |||
if #matched ~= 0 then | |||
for j = 1, k do | |||
matched[ j ] = mw.ustring.sub( matched[ j ], 1, 1 ) | |||
end | |||
return { | |||
matched[ k + 1 ], | |||
table.concat( matched, '. ', 1, k ) .. '.' | |||
} | |||
end | |||
end | |||
-- Surname Name{1,4} | |||
for k = 1, 4 do | |||
pattern = '^' .. surname .. string.rep( space .. name, k ) .. '$' | |||
---@type string[] | |||
local matched = { mw.ustring.match( fullName, pattern ) } | |||
if #matched ~= 0 then | |||
for j = 2, k + 1 do | |||
matched[ j ] = mw.ustring.sub( matched[ j ], 1, 1 ) | |||
end | |||
return { | |||
matched[ 1 ], | |||
table.concat( matched, '. ', 2, k + 1 ) .. '.' | |||
} | |||
end | |||
end | |||
return { fullName } | |||
end | end | ||
function | ---@param fullName string | nil | ||
---@return string | nil | |||
local function personNameToAuthorName( fullName ) | |||
if not fullName then | |||
return nil | |||
end | |||
local tokenized = tokenizeName( fullName ) | |||
if #tokenized == 1 then | |||
return tokenized[ 1 ] | |||
end | |||
return tokenized[ 1 ] .. ' ' .. tokenized[ 2 ] | |||
end | end | ||
---@param fullName string | nil | |||
---@return string | nil | |||
local function personNameToResponsibleName( fullName ) | |||
if not fullName then | |||
return nil | |||
end | |||
local tokenized = tokenizeName( fullName ) | |||
if #tokenized == 1 then | |||
return tokenized[ 1 ] | |||
end | |||
return tokenized[ 2 ] .. ' ' .. tokenized[ 1 ] | |||
end | end | ||
---@alias options { separator: string, conjunction: string, format: ( fun( data: string ): string ), nolinks: boolean, preferids: boolean, short: boolean } | |||
---@type options | |||
local options_commas = { | |||
separator = ', ', | |||
conjunction = ', ', | |||
format = function( data ) return data end, | |||
nolinks = false, | |||
preferids = false, | |||
short = false, | |||
} | |||
---@type options | |||
local options_commas_short = mw.clone( options_commas ) | |||
options_commas_short.short = true | |||
---@type options | |||
local options_commas_it_short = mw.clone( options_commas_short ) | |||
options_commas_it_short.format = function( data ) return "''" .. data .. "''" end | |||
---@type options | |||
local options_commas_nolinks = mw.clone( options_commas ) | |||
options_commas_nolinks.nolinks = true | |||
---@type options | |||
local options_citetypes = { | |||
separator = ' ', | |||
conjunction = ' ', | |||
format = function( data ) return 'citetype_' .. data end, | |||
nolinks = true , | |||
preferids = true, | |||
short = false, | |||
} | |||
---@type options | |||
local options_commas_authors = mw.clone( options_commas ) | |||
options_commas_authors.format = personNameToAuthorName | |||
---@type options | |||
local options_commas_responsible = mw.clone( options_commas ) | |||
options_commas_responsible.format = personNameToResponsibleName | |||
---@type options | |||
local options_ids = { | |||
separator = '; ', | |||
conjunction = '; ', | |||
format = function( id ) return id end, | |||
nolinks = true, | |||
preferids = false, | |||
short = false, | |||
} | |||
---@type options | |||
local options_arxiv = mw.clone( options_ids ) | |||
options_arxiv.format = function( id ) return '[https://arxiv.org/abs/' .. id .. ' arXiv:' .. id .. ']' end | |||
---@type options | |||
local options_doi = mw.clone( options_ids ) | |||
options_doi.format = function( doi ) return '[https://dx.doi.org/' .. doi .. ' doi:' .. doi .. ']' end | |||
---@type options | |||
local options_issn = mw.clone( options_ids ) | |||
options_issn.format = function( issn ) return '[https://www.worldcat.org/issn/' .. issn .. ' ' .. issn .. ']' end | |||
---@type options | |||
local options_pmid = mw.clone( options_ids ) | |||
options_pmid.format = function( pmid ) return '[https://www.ncbi.nlm.nih.gov/pubmed/?term=' .. pmid .. ' PMID:' .. pmid .. ']' end | |||
---@param str string | nil | |||
---@return boolean | |||
local function isEmpty( str ) | |||
return not str or #str == 0 | |||
end | |||
---@param allQualifiers snaks | |||
---@param qualifierPropertyId string | |||
---@return string | nil | |||
local function getSingleStringQualifierValue( allQualifiers, qualifierPropertyId ) | |||
if not allQualifiers or not allQualifiers[ qualifierPropertyId ] then | |||
return nil | |||
end | |||
---@type table<number, snak> | |||
local propertyQualifiers = allQualifiers[ qualifierPropertyId ] | |||
for _, qualifier in pairs( propertyQualifiers ) do | |||
if ( qualifier | |||
and qualifier.datatype == 'string' | |||
and qualifier.datavalue | |||
and qualifier.datavalue.type == 'string' | |||
and qualifier.datavalue.value ~= '' | |||
) then | |||
return qualifier.datavalue.value | |||
end | |||
end | |||
return nil | |||
end | |||
---@param data table | |||
---@param resultProperty string | |||
---@return void | |||
local function appendImpl_toTable( data, resultProperty ) | |||
if not data[ resultProperty ] then | |||
data[ resultProperty ] = {} | |||
elseif ( type( data[ resultProperty ] ) == 'string' or ( type( data[ resultProperty ] ) == 'table' and type( data[ resultProperty ].id ) == 'string' ) ) then | |||
data[ resultProperty ] = { data[ resultProperty ] } | |||
end | |||
end | |||
---@param datavalue table | |||
---@param qualifiers snaks | |||
---@param data table | |||
---@param propertyName string | |||
---@param options table | |||
local function appendImpl( datavalue, qualifiers, data, propertyName, options ) | |||
data[ propertyName ] = data[ propertyName ] or {} | |||
if propertyName == 'issn' then | |||
table.insert( data[ propertyName ], datavalue.value ) | |||
elseif propertyName == 'url' or datavalue.type == 'url' then | |||
local value = datavalue.value | |||
if options.format then | |||
value = options.format( value ) | |||
end | |||
appendImpl_toTable( data, propertyName ) | |||
table.insert( data[ propertyName ], value ) | |||
elseif datavalue.type == 'string' then | |||
local value = getSingleStringQualifierValue( qualifiers, 'P1932' ) | |||
if not value then | |||
value = getSingleStringQualifierValue( qualifiers, 'P1810' ) | |||
end | |||
if not value then | |||
value = datavalue.value | |||
if options.format then | |||
value = options.format( value ) | |||
end | |||
end | |||
appendImpl_toTable(data, propertyName) | |||
local pos = getSingleStringQualifierValue( qualifiers, 'P1545' ) | |||
if pos then | |||
table.insert( data[ propertyName ], tonumber(pos), value ) | |||
else | |||
table.insert( data[ propertyName ], value ) | |||
end | |||
elseif datavalue.type == 'monolingualtext' then | |||
local value = datavalue.value.text | |||
if options.format then | |||
value = options.format( value ) | |||
end | |||
appendImpl_toTable( data, propertyName ) | |||
table.insert( data[ propertyName ], value ) | |||
elseif datavalue.type == 'quantity' then | |||
local value = datavalue.value.amount | |||
if ( mw.ustring.sub( value , 1, 1 ) == '+' ) then | |||
value = mw.ustring.sub( value , 2 ) | |||
end | |||
if options.format then | |||
value = options.format( value ) | |||
end | |||
appendImpl_toTable( data, propertyName ) | |||
table.insert( data[ propertyName ], value ) | |||
elseif datavalue.type == 'wikibase-entityid' then | |||
local pos = getSingleStringQualifierValue( qualifiers, 'P1545' ) | |||
local value = datavalue.value | |||
appendImpl_toTable(data, propertyName) | |||
local label = getSingleStringQualifierValue( qualifiers, 'P1932' ) | |||
if not label then | |||
label = getSingleStringQualifierValue( qualifiers, 'P1810' ) | |||
end | |||
local toInsert = { | |||
id = value.id, | |||
label = label | |||
} | |||
if pos and tonumber( pos ) then | |||
table.insert( data[ propertyName ], tonumber( pos ), toInsert ) | |||
else | |||
table.insert( data[ propertyName ], toInsert ) | |||
end | |||
elseif datavalue.type == 'time' then | |||
local value = datavalue.value | |||
if options.format then | |||
value = options.format( value ) | |||
end | |||
appendImpl_toTable( data, propertyName ) | |||
table.insert( data[ propertyName ], tostring( value.time ) ) | |||
end | |||
end | |||
---@param entityId string | |||
---@param propertyId string | |||
---@return table<number, statement> | |||
local function getAllStatements( entityId, propertyId ) | |||
---@type boolean, table<number, statement> | |||
local wdStatus, statements = pcall( mw.wikibase.getAllStatements, entityId, propertyId ) | |||
if wdStatus and statements then | |||
return statements | |||
end | |||
return {} | |||
end | |||
---@param entityId string | |||
---@param propertyId string | |||
---@return table<number, statement> | |||
local function getBestStatements( entityId, propertyId ) | |||
---@type boolean, table<number, statement> | |||
local wdStatus, statements = pcall( mw.wikibase.getBestStatements, entityId, propertyId ) | |||
if wdStatus and statements then | |||
return statements | |||
end | |||
return {} | |||
end | |||
---@param entityId string | |||
---@param projectToCheck string? | |||
---@return string | nil | |||
local function getSitelink( entityId, projectToCheck ) | |||
---@type boolean, string | |||
local wbStatus, sitelink | |||
if projectToCheck then | |||
wbStatus, sitelink = pcall( mw.wikibase.getSitelink, entityId, projectToCheck ) | |||
else | |||
wbStatus, sitelink = pcall( mw.wikibase.getSitelink, entityId ) | |||
end | |||
if not wbStatus then | |||
return nil | |||
end | |||
return sitelink | |||
end | end | ||
function | ---@param args any[] | ||
---@return any | nil | |||
local function coalesce( args ) | |||
for _, arg in pairs( args ) do | |||
if not isEmpty( arg ) then return arg end | |||
end | |||
return nil | |||
end | end | ||
function | ---@param value any | ||
---@return string | nil | |||
local function getSingle( value ) | |||
if type( value ) == 'string' then | |||
return tostring( value ) | |||
elseif type( value ) == 'table' then | |||
if value.id then | |||
return tostring( value.id ) | |||
end | |||
for _, tableValue in pairs( value ) do | |||
return getSingle( tableValue ) | |||
end | |||
end | |||
return nil | |||
end | |||
---@param langEntityId string | |||
---@return string | nil | |||
local function getLangCode( langEntityId ) | |||
if not langEntityId then | |||
return nil | |||
end | |||
langEntityId = getSingle( langEntityId ) | |||
if not string.match( langEntityId, '^Q%d+$' ) then | |||
return langEntityId | |||
end | |||
end | |||
local cached = LANG_CACHE[ langEntityId ] | |||
if cached then | |||
if cached == '' then | |||
return nil | |||
end | |||
return cached | |||
end | |||
local claims = getBestStatements( langEntityId, 'P424' ) | |||
for _, claim in pairs( claims ) do | |||
if claim | |||
and claim.mainsnak | |||
and claim.mainsnak.datavalue | |||
and claim.mainsnak.datavalue.value | |||
then | |||
LANG_CACHE[ langEntityId ] = claim.mainsnak.datavalue.value | |||
return claim.mainsnak.datavalue.value | |||
end | |||
end | |||
LANG_CACHE[ langEntityId ] = '' | |||
return nil | |||
end | end | ||
function | ---@param entityId string | ||
---@param propertyId string | |||
---@param data source | |||
---@param propertyName string | |||
---@param options table? | |||
---@return void | |||
local function appendEntitySnaks( entityId, propertyId, data, propertyName, options ) | |||
options = options or {} | |||
-- do not populate twice | |||
if data[ propertyName ] and ( propertyName ~= 'author' or data[ propertyId ] ) then | |||
return | |||
end | |||
local statements = getBestStatements( entityId, propertyId ) | |||
if propertyName == 'author' then | |||
data[ propertyId ] = true | |||
end | |||
local lang = getLangCode( data.lang ) or i18nDefaultLanguage | |||
if propertyId == 'P1680' then -- if there is a default language | |||
for _, statement in pairs( statements ) do | |||
if statement and | |||
statement.mainsnak and | |||
statement.mainsnak.datavalue and | |||
statement.mainsnak.datavalue.value and | |||
statement.mainsnak.datavalue.value.language == lang | |||
then | |||
--found default language string | |||
appendImpl( statement.mainsnak.datavalue, statement.qualifiers, data, propertyName, options ) | |||
return | |||
end | |||
end | |||
end | |||
for _, statement in pairs( statements ) do | |||
if statement and statement.mainsnak and statement.mainsnak.datavalue then | |||
appendImpl( statement.mainsnak.datavalue, statement.qualifiers or {}, data, propertyName, options ) | |||
if propertyName == 'publication-id' and statement.qualifiers then | |||
data[ 'publication-qualifiers' ] = statement.qualifiers | |||
end | |||
end | |||
end | |||
end | end | ||
function | ---@param claims table<number, statement> | ||
---@param qualifierPropertyId string | |||
---@param result table | |||
---@param resultPropertyId string | |||
---@param options table | |||
---@return void | |||
local function appendQualifiers( claims, qualifierPropertyId, result, resultPropertyId, options ) | |||
-- do not populate twice | |||
if not claims or result[ resultPropertyId ] then | |||
return | |||
end | |||
for _, claim in pairs( claims ) do | |||
if claim.qualifiers and claim.qualifiers[ qualifierPropertyId ] then | |||
---@type table<number, snak> | |||
local propertyQualifiers = claim.qualifiers[ qualifierPropertyId ] | |||
for _, qualifier in pairs( propertyQualifiers ) do | |||
if qualifier and qualifier.datavalue then | |||
appendImpl( qualifier.datavalue, nil, result, resultPropertyId, options ) | |||
end | |||
end | |||
end | |||
end | |||
end | |||
---@param entityId string | |||
---@param propertyId string | |||
---@param value any | |||
---@return table<number, statement> | |||
local function findClaimsByValue( entityId, propertyId, value ) | |||
local result = {} | |||
local claims = getAllStatements( entityId, propertyId ) | |||
for _, claim in pairs( claims ) do | |||
if ( claim.mainsnak and claim.mainsnak.datavalue ) then | |||
local datavalue = claim.mainsnak.datavalue | |||
if ( datavalue.type == "string" and datavalue.value == value ) or | |||
( datavalue.type == "wikibase-entityid" and | |||
datavalue.value[ "entity-type" ] == "item" and | |||
tostring( datavalue.value.id ) == value ) | |||
then | |||
table.insert( result, claim ) | |||
end | |||
end | |||
end | |||
return result | |||
end | |||
---@param entityId string | |||
---@param typeEntityId string | |||
---@return boolean | |||
local function isInstanceOf( entityId, typeEntityId ) | |||
return findClaimsByValue( entityId, 'P31', typeEntityId )[ 1 ] ~= nil | |||
end | end | ||
function | ---@param entityId string | ||
---@param typeEntityIds string[] | |||
---@return string | |||
---@todo Rewrite | |||
local function getFirstType( entityId, typeEntityIds ) | |||
for _, typeEntityId in pairs( typeEntityIds ) do | |||
if isInstanceOf( entityId, typeEntityId ) then | |||
return typeEntityId | |||
end | |||
end | |||
return nil | |||
end | |||
---@param snaks snaks | |||
---@param data source | |||
---@param map map | |||
---@return void | |||
local function populateDataFromSnaks( snaks, data, map ) | |||
for _, row in ipairs( map ) do | |||
local parameterName, propertyIds = row.name, row.ids | |||
for _, propertyId in pairs( propertyIds ) do | |||
if not data[ parameterName ] and snaks[ propertyId ] then | |||
local options = {} | |||
if propertyId == 'P888' then | |||
options = { format = function( id ) return 'http://www.jstor.org/stable/' .. id end } | |||
end | |||
for _, snak in pairs( snaks[ propertyId ] ) do | |||
if snak and snak.datavalue then | |||
appendImpl( snak.datavalue, {}, data, parameterName, options ) | |||
end | |||
end | |||
end | |||
end | |||
end | |||
end | end | ||
function | ---@param entityId string | nil | ||
---@param data source | |||
---@param map map | |||
---@return void | |||
local function populateDataFromEntity( entityId, data, map ) | |||
if not data.title then | |||
if not isEmpty( entityId ) then | |||
local optionsAsLinks = { format = function( text ) return { id = entityId, label = text } end } | |||
appendEntitySnaks( entityId, 'P1476', data, 'title', optionsAsLinks ) | |||
else | |||
appendEntitySnaks( entityId, 'P1476', data, 'title', {} ) | |||
end | |||
appendEntitySnaks( entityId, 'P1680', data, 'subtitle', {} ) | |||
end | |||
local bookSeriesStatements = getBestStatements( entityId, 'P361' ) | |||
for _, statement in pairs( bookSeriesStatements ) do | |||
if statement and | |||
statement.mainsnak and | |||
statement.mainsnak.datavalue and | |||
statement.mainsnak.datavalue.value and | |||
statement.mainsnak.datavalue.value.id | |||
then | |||
local possibleBookSeriesEntityId = statement.mainsnak.datavalue.value.id | |||
if isInstanceOf( possibleBookSeriesEntityId, 'Q277759' ) then | |||
appendImpl_toTable( data, 'bookSeries' ) | |||
table.insert( data.bookSeries, { id = possibleBookSeriesEntityId } ) | |||
appendQualifiers( { statement }, 'P478', data, 'bookSeriesVolume', {} ) | |||
appendQualifiers( { statement }, 'P433', data, 'bookSeriesIssue', {} ) | |||
end | |||
end | |||
end | |||
for _, row in ipairs( map ) do | |||
local parameterName, propertyIds = row.name, row.ids | |||
for _, propertyId in pairs( propertyIds ) do | |||
local options = {} | |||
if propertyId == 'P888' then | |||
options = { format = function( id ) return 'http://www.jstor.org/stable/' .. id end } | |||
end | |||
appendEntitySnaks( entityId, propertyId, data, parameterName, options ) | |||
end | |||
end | |||
end | end | ||
---@param data source | |||
---@return void | |||
local function expandPublication( data ) | |||
if not data[ 'publication-id' ] then | |||
return | |||
end | |||
local publicationId = getSingle( data[ 'publication-id' ] ) | |||
data.publication = {} | |||
for key, value in pairs( data ) do | |||
if not string.match( key, '^publication-' ) then | |||
data.publication[ key ] = value | |||
end | |||
end | |||
data.publication.sourceId = publicationId | |||
data.publication.title = data[ 'publication-title' ] | |||
data.publication.subtitle = data[ 'publication-subtitle' ] | |||
if data[ 'publication-qualifiers' ] then | |||
populateDataFromSnaks( data[ 'publication-qualifiers' ], data.publication, PUBLICATION_PROPERTY_MAP ) | |||
end | |||
populateDataFromEntity( publicationId, data.publication, PUBLICATION_PROPERTY_MAP ) | |||
if type( data.publication.title ) == 'table' and data.publication.title[ 1 ] then | |||
data.publication.title = data.publication.title[ 1 ] | |||
end | |||
if type( data.publication.subtitle ) == 'table' and data.publication.subtitle[ 1 ] then | |||
data.publication.subtitle = data.publication.subtitle[ 1 ] | |||
end | |||
for key, value in pairs( data.publication ) do | |||
if key ~= 'sourceId' and key ~= 'title' and key ~= 'subtitle' and key ~= 'url' and not data[ key ] then | |||
data[ key ] = value | |||
end | |||
end | |||
end | end | ||
function | ---@param data source | ||
---@return void | |||
local function expandBookSeries( data ) | |||
local bookSeries = data.bookSeries | |||
if not bookSeries then | |||
return | |||
end | |||
-- use only first one | |||
if type( bookSeries ) == 'table' and bookSeries[ 1 ] and bookSeries[ 1 ].id then | |||
data.bookSeries = bookSeries[ 1 ] | |||
bookSeries = data.bookSeries | |||
end | |||
if not bookSeries or not bookSeries.id then | |||
return | |||
end | |||
appendEntitySnaks( bookSeries.id, 'P123', data, 'publisher', {} ) | |||
appendEntitySnaks( bookSeries.id, 'P291', data, 'place', {} ) | |||
appendEntitySnaks( bookSeries.id, 'P236', data, 'issn', {} ) | |||
end | end | ||
function | ---@param entityId string | ||
---@return string | nil | |||
local function getNormativeTitle( entityId ) | |||
local possibleTypeIds = {} | |||
for typeId, _ in pairs( NORMATIVE_DOCUMENTS ) do | |||
table.insert( possibleTypeIds, typeId ) | |||
end | |||
local foundTypeId = getFirstType( entityId, possibleTypeIds ) | |||
if foundTypeId then | |||
return NORMATIVE_DOCUMENTS[ foundTypeId ] | |||
end | |||
return nil | |||
end | |||
---@param urls table<number, string> | string | |||
---@param text string | |||
---@return string | |||
local function wrapInUrl( urls, text ) | |||
local url = getSingle( urls ) | |||
if string.sub( url, 1, 1 ) == ':' then | |||
return '[[' .. url .. '|' .. text .. ']]' | |||
else | |||
return '[' .. url .. ' ' .. text .. ']' | |||
end | |||
end | |||
---@param entityId string | |||
---@param lang string | |||
---@return string | |||
local function getElementLink( entityId, lang ) | |||
local sitelink = getSitelink( entityId, nil ) | |||
if sitelink then | |||
return ':' .. sitelink | |||
end | |||
-- | if lang ~= 'mul' then | ||
-- link to entity in source language | |||
sitelink = getSitelink( entityId, lang .. 'wiki' ) | |||
if sitelink then | |||
return ':' .. lang .. ':' .. sitelink | |||
end | |||
end | |||
return ':d:' .. entityId | |||
end | |||
---@param entityId string | |||
---@param lang string | |||
---@return string | |||
local function getLabel( entityId, lang ) | |||
local wbStatus, label = pcall( mw.wikibase.getLabelByLang, entityId, lang ) | |||
if not wbStatus then | |||
return '' | |||
end | |||
if label and label ~= '' then | |||
return label | |||
end | |||
wbStatus, label = pcall( mw.wikibase.getLabel, entityId ) | |||
if not wbStatus then | |||
return '' | |||
end | |||
return label or '' | |||
end | end | ||
-- | ---@param lang string | ||
function | ---@param entityId string | ||
---@param customTitle string | |||
---@param options table | |||
local function renderLink( lang, entityId, customTitle, options ) | |||
if not entityId then | |||
error( 'entityId is not specified' ) | |||
end | |||
if type( entityId ) ~= 'string' then | |||
error( 'entityId is not string, but ' .. type( entityId ) ) | |||
end | |||
if type( customTitle or '' ) ~= 'string' then | |||
error( 'customTitle is not string, but ' .. type( customTitle ) ) | |||
end | |||
local title = customTitle | |||
-- ISO 4 | |||
if isEmpty( title ) then | |||
local propertyStatements = getBestStatements( entityId, 'P1160' ) | |||
for _, claim in pairs( propertyStatements ) do | |||
if ( claim | |||
and claim.mainsnak | |||
and claim.mainsnak.datavalue | |||
and claim.mainsnak.datavalue.value | |||
and claim.mainsnak.datavalue.value.language == lang | |||
) then | |||
title = claim.mainsnak.datavalue.value.text | |||
-- mw.log( 'Got title of ' .. entityId .. ' from ISO 4 claim: «' .. title .. '»' ) | |||
break | |||
end | |||
end | |||
end | |||
-- official name P1448 | |||
-- short name P1813 | |||
if isEmpty( title ) and options.short then | |||
local propertyStatements = getBestStatements( entityId, 'P1813' ) | |||
for _, claim in pairs( propertyStatements ) do | |||
if ( claim | |||
and claim.mainsnak | |||
and claim.mainsnak.datavalue | |||
and claim.mainsnak.datavalue.value | |||
and claim.mainsnak.datavalue.value.language == lang | |||
) then | |||
title = claim.mainsnak.datavalue.value.text | |||
-- mw.log( 'Got title of ' .. entityId .. ' from short name claim: «' .. title .. '» (' .. lang .. ')' ) | |||
break | |||
end | |||
end | |||
end | |||
-- person name P1559 | |||
-- labels | |||
if isEmpty( title ) then | |||
title = getLabel( entityId, lang ) | |||
-- mw.log( 'Got title of ' .. entityId .. ' from label: «' .. title .. '» (' .. lang .. ')' ) | |||
end | |||
local actualText = title or '\'\'(untranslated)\'\'' | |||
local link = getElementLink( entityId, lang ) | |||
return wrapInUrl( link, actualText ) | |||
end | |||
---@param lang string | |||
---@param value value | |||
---@param options options | |||
---@return string | |||
local function asString( lang, value, options ) | |||
if type( value ) == 'string' then | |||
return options.format( value ) | |||
end | |||
if type( value ) ~= 'table' then | |||
return options.format( '(unknown type)' ) | |||
end | |||
if value.id then | |||
-- this is link | |||
if type( value.label or '' ) ~= 'string' then | |||
mw.logObject( value, 'error value' ) | |||
error( 'label of table value is not string but ' .. type( value.label ) ) | |||
end | |||
local title | |||
if options.preferids then | |||
title = value.id | |||
elseif options.nolinks then | |||
title = value.label or getLabel( value.id, lang ) | |||
else | |||
title = renderLink( lang, value.id, value.label, options ) | |||
end | |||
if title == '' then | |||
title = "''(untranslated title)''" | |||
end | |||
return options.format( title ) | |||
end | |||
local resultList = {} | |||
for _, tableValue in pairs( value ) do | |||
table.insert( resultList, asString( lang, tableValue, options ) ) | |||
end | |||
return mw.text.listToText( resultList, options.separator, options.conjunction ) | |||
end | end | ||
function | ---@param entityId string | ||
---@param data source | |||
---@return source | |||
local function populateSourceDataImpl( entityId, data, map ) | |||
local wsLink = getSitelink( entityId, 'ruwikisource' ) | |||
if wsLink and not mw.ustring.gmatch( wsLink, 'Категория:' ) then | |||
data.url = ":ru:s:" .. wsLink | |||
end | |||
populateDataFromEntity( entityId, data, map ) | |||
local normativeTitle = getNormativeTitle( entityId ) | |||
if normativeTitle then | |||
local y, m, d = mw.ustring.match( getSingle( data.dateOfCreation ) , "(%-?%d+)%-(%d+)%-(%d+)T" ) | |||
y, m, d = tonumber( y ),tonumber( m ), tonumber( d ) | |||
local title = asString( 'ru', data.title, options_commas_nolinks ) | |||
local docNumber = getSingle( data.docNumber ) | |||
data.title = { | |||
normativeTitle .. | |||
" от " .. tostring( d ) .. " " .. monthGen[ m ] .. " " .. tostring( y ) .. " г." .. | |||
( docNumber and ( " № " .. docNumber ) or '' ) .. | |||
' «' .. title.. '»' | |||
} | |||
end | |||
if not data.title then | |||
local lang = getLangCode( data.lang ) or i18nDefaultLanguage | |||
local label = getLabel( entityId, lang ) | |||
if label ~= '' then | |||
data.title = { label } | |||
end | |||
end | |||
return data | |||
end | end | ||
function | ---@param entityId string | ||
---@param propertyId string | |||
---@param data source | |||
---@return void | |||
local function expandSpecialsQualifiers( entityId, propertyId, data ) | |||
local statements = getBestStatements( entityId, propertyId ) | |||
for _, statement in pairs( statements ) do | |||
populateDataFromSnaks( statement.qualifiers or {}, data, PROPERTY_MAP ) | |||
end | |||
end | end | ||
---Expand special types of references when additional data could be found in OTHER entity properties | |||
---@param data source | |||
---@return void | |||
local function expandSpecials( data ) | |||
if not data.entityId then | |||
return | |||
end | |||
if data.sourceId == 'Q36578' then | |||
-- Gemeinsame Normdatei -- specified by P227 | |||
appendEntitySnaks( data.entityId, 'P227', data, 'part', { format = function(gnd ) return 'Record #' .. gnd; end } ) | |||
appendEntitySnaks( data.entityId, 'P227', data, 'url', { format = function(gnd ) return 'http://d-nb.info/gnd/' .. gnd .. '/'; end } ) | |||
data.year = '2012—2016' | |||
expandSpecialsQualifiers( data.entityId, 'P227', data ) | |||
elseif data.sourceId == 'Q15222191' then | |||
-- BNF -- specified by P268 | |||
appendEntitySnaks( data.entityId, 'P268', data, 'part', { format = function(id ) return 'Record #' .. id; end } ) | |||
appendEntitySnaks( data.entityId, 'P268', data, 'url', { format = function(id ) return 'http://catalogue.bnf.fr/ark:/12148/cb' .. id; end } ) | |||
expandSpecialsQualifiers( data.entityId, 'P268', data ) | |||
elseif data.sourceId == 'Q54919' then | |||
-- VIAF -- specified by P214 | |||
appendEntitySnaks( data.entityId, 'P214', data, 'part', { format = function(id ) return 'Record #' .. id; end } ) | |||
appendEntitySnaks( data.entityId, 'P214', data, 'url', { format = function(id ) return 'https://viaf.org/viaf/' .. id; end } ) | |||
expandSpecialsQualifiers( data.entityId, 'P214', data ) | |||
else | |||
-- generic property search | |||
for _, sourceClaim in pairs( getBestStatements( data.sourceId, 'P1687' ) ) do | |||
if sourceClaim.mainsnak.snaktype == 'value' then | |||
local sourcePropertyId = sourceClaim.mainsnak.datavalue.value.id | |||
for _, sourcePropertyClaim in pairs( getBestStatements( sourcePropertyId, 'P1630' ) ) do | |||
if sourcePropertyClaim.mainsnak.snaktype == 'value' then | |||
appendEntitySnaks( data.entityId, sourcePropertyId, data, 'url', { | |||
format = function( id ) | |||
return mw.ustring.gsub( mw.ustring.gsub( sourcePropertyClaim.mainsnak.datavalue.value, '$1', id ), ' ', '%%20' ) | |||
end | |||
} ) | |||
expandSpecialsQualifiers( data.entityId, sourcePropertyId, data ) | |||
break | |||
end | |||
end | |||
end | |||
end | |||
end | |||
-- do we have appropriate record in P1433 ? | |||
local claims = findClaimsByValue( currentEntityId, 'P1343', data.sourceId ) | |||
if claims and #claims ~= 0 then | |||
for _, claim in pairs( claims ) do | |||
populateDataFromSnaks( claim.qualifiers, data, PROPERTY_MAP ) | |||
populateDataFromEntity( data.sourceId, data, PROPERTY_MAP ) | |||
end | |||
end | |||
end | end | ||
---@param text string | |||
---@param tip string | |||
---@return string | |||
local function toTextWithTip( text, tip ) | |||
return '<span title="' .. tip .. '" style="border-bottom: 1px dotted; cursor: help; white-space: nowrap">' .. text .. '</span>' | |||
end | end | ||
function | ---@param lang string | ||
---@param placeId string | |||
---@return string | |||
local function getPlaceName( placeId, lang ) | |||
-- ГОСТ Р 7.0.12—2011 | |||
if lang == 'ru' then | |||
if placeId == 'Q649' then return toTextWithTip( 'М.', 'Москва' ); end | |||
if placeId == 'Q656' then return toTextWithTip( 'СПб.', 'Санкт-Петербург' ); end | |||
if placeId == 'Q891' then return toTextWithTip( 'Н. Новгород', 'Нижний Новгород' ); end | |||
if placeId == 'Q908' then return toTextWithTip( 'Ростов н/Д.', 'Ростов-на-Дону' ); end | |||
end | |||
return nil | |||
end | end | ||
function | ---@param data source | ||
---@param lang string | |||
---@return void | |||
local function preprocessPlace( data, lang ) | |||
if not data.place then | |||
return | |||
end | |||
---@type table<number, string> | |||
local newPlace = {} | |||
for index, place in pairs( data.place ) do | |||
if place.id then | |||
local newPlaceStr = getPlaceName( place.id, lang ) | |||
if newPlaceStr then | |||
newPlace[ index ] = newPlaceStr | |||
else | |||
newPlace[ index ] = getLabel( place.id, lang ) | |||
end | |||
else | |||
newPlace[ index ] = place | |||
end | |||
end | |||
data.place = newPlace | |||
end | end | ||
function | ---@param entityId string | ||
---@param lang string | |||
---@param providedLabel string | nil | |||
---@param options options | |||
---@return string | |||
local function getPersonNameAsLabel( entityId, lang, providedLabel, options ) | |||
-- would custom label provided we don't need to check entity at all | |||
if not isEmpty( providedLabel ) then | |||
return options.format( providedLabel ) | |||
end | |||
if lang == 'mul' then | |||
lang = i18nDefaultLanguage | |||
end | |||
---@type string | nil | |||
local personName = getLabel( entityId, lang ) | |||
if isEmpty( personName ) then | |||
return '\'\'(not translated to ' .. lang .. ')\'\'' | |||
end | |||
if not isInstanceOf( entityId, 'Q5' ) then | |||
return personName | |||
end | |||
return options.format( personName ) | |||
end | |||
---@param entityId string | |||
---@param lang string | |||
---@param customLabel string | nil | |||
---@param options options | |||
---@return string | |||
local function getPersonNameAsWikitext( entityId, lang, customLabel, options ) | |||
local personName = getPersonNameAsLabel( entityId, lang, customLabel, options ) | |||
local link = getElementLink( entityId, lang ) | |||
return wrapInUrl( link, personName ) | |||
end | end | ||
function | ---@param value value | ||
---@param lang string | |||
---@param options options | |||
---@return string | |||
local function getPeopleAsWikitext( value, lang, options ) | |||
if type( value ) == 'string' then | |||
return options.format( value ) | |||
elseif type( value ) == 'table' then | |||
if value.id then | |||
-- this is link | |||
if options.preferids then | |||
return tostring( value.id ) | |||
else | |||
if options.nolinks then | |||
return getPersonNameAsLabel( value.id, lang, value.label, options ) | |||
else | |||
return getPersonNameAsWikitext( value.id, lang, value.label, options ) | |||
end | |||
end | |||
end | |||
local maxAuthors = 10 -- need some restrictions, as some publications have enormous amount of authors (e.g. 115 authors of Q68951544) | |||
local resultList = {} | |||
for _, tableValue in pairs( value ) do | |||
local nextWikitext = getPeopleAsWikitext( tableValue, lang, options ) | |||
if not isEmpty( nextWikitext ) then | |||
table.insert( resultList, nextWikitext ) | |||
if #resultList == maxAuthors + 1 then | |||
-- keep one more to indicate that there are too many | |||
break | |||
end | |||
end | |||
end | |||
local resultWikitext = '' | |||
for i, wikitext in pairs( resultList ) do | |||
if i == maxAuthors + 1 then | |||
resultWikitext = resultWikitext .. ( i18nEtAl[ lang ] or i18nEtAlDefault ) | |||
break | |||
end | |||
if i ~= 1 then | |||
resultWikitext = resultWikitext .. ', ' | |||
end | |||
resultWikitext = resultWikitext .. wikitext | |||
end | |||
return resultWikitext | |||
end | |||
return '' -- options.format( '(unknown type)' ) | |||
end | |||
---@param lang string | |||
---@param data source | |||
---@return string | |||
local function generateAuthorLinks( lang, data ) | |||
local result = '' | |||
if data.author then | |||
result = getPeopleAsWikitext( data.author, lang, options_commas_authors ) | |||
result = '<i class="wef_low_priority_links">' .. result .. '</i> ' | |||
end | |||
return result | |||
end | |||
---@param lang string | |||
---@param data source | |||
---@param conjunction string | |||
---@param propertyName string | |||
---@param urlPropertyName string? | |||
---@return string | |||
local function appendProperty( lang, data, conjunction, propertyName, urlPropertyName ) | |||
if not data[ propertyName ] then | |||
return '' | |||
end | |||
local out | |||
if urlPropertyName and data[ urlPropertyName ] then | |||
out = wrapInUrl( data[ urlPropertyName ], asString( lang, data[ propertyName ], options_commas_nolinks ) ) | |||
else | |||
out = asString( lang, data[ propertyName ], options_commas ) | |||
end | |||
if not out or out == '' then | |||
return '' | |||
end | |||
return conjunction .. out | |||
end | end | ||
function | ---@param lang string | ||
---@param data source | |||
---@return string | |||
local function appendTitle( lang, data ) | |||
local conjunction = '' | |||
local result = '' | |||
if data.part then | |||
result = result .. appendProperty( lang, data, '', 'part', 'parturl' ) | |||
conjunction = ' // ' | |||
end | |||
return result .. appendProperty( lang, data, conjunction, 'title', 'url' ) | |||
end | |||
---@param lang string | |||
---@return string | |||
local function appendLanguage( lang ) | |||
if lang == i18nDefaultLanguage then | |||
return '' | |||
end | |||
---@type { getRefHtml: ( fun( lang: string ): string ), list_ref: ( fun( frame: frame ): string ) } | |||
local langs = require( 'Module:Languages' ) | |||
return langs.list_ref( p.currentFrame:newChild{ args = { lang } } ) | |||
end | end | ||
function | ---@param lang string | ||
---@param data source | |||
---@return string | |||
local function appendSubtitle( lang, data ) | |||
return appendProperty( lang, data, ': ', 'subtitle', nil ) | |||
end | |||
---@param lang string | |||
---@param data source | |||
---@return string | |||
local function appendOriginalTitle( lang, data ) | |||
return appendProperty( lang, data, ' = ', 'originaltitle', nil ) | |||
end | |||
---@param lang string | |||
---@param data source | |||
---@return string | |||
local function appendPublication( lang, data ) | |||
if not data.publication then | |||
return '' | |||
end | |||
local result = ' // ' .. asString( lang, data.publication.title, options_commas_it_short ) | |||
if data.publication.subtitle and data.publication.subtitle ~= '' then | |||
result = result .. ': ' .. asString( lang, data.publication.subtitle, options_commas_it_short ) | |||
end | |||
return result | |||
end | end | ||
---@param lang string | |||
---@param data source | |||
---@return string | |||
local function appendEditor( lang, data ) | |||
if not data.editor and not data.translator then | |||
return '' | |||
end | |||
local result = ' / ' | |||
if data.editor then | |||
local prefix = i18nEditors[ lang ] or i18nEditors[ i18nDefaultLanguage ] | |||
result = result .. prefix .. getPeopleAsWikitext( data.editor, lang, options_commas_responsible ) | |||
if data.translator then | |||
result = result .. ', ' | |||
end | |||
end | |||
if data.translator then | |||
local prefix = i18nTranslators[ lang ] or i18nTranslators[ i18nDefaultLanguage ] | |||
result = result .. prefix .. getPeopleAsWikitext( data.translator, lang, options_commas_responsible ) | |||
end | |||
return result | |||
end | |||
---@param lang string | |||
---@param data source | |||
local function appendEdition( lang, data ) | |||
return appendProperty( lang, data, ' — ', 'edition', nil ) | |||
end | |||
---@param lang string | |||
---@param data source | |||
---@return string | |||
local function appendPublicationData( lang, data ) | |||
if not data.place and not data.publisher and not data.year then | |||
return '' | |||
end | |||
local result = ' — ' | |||
if data.place then | |||
result = result .. asString( lang, data.place, options_commas_short ) | |||
if data.publisher or data.year then | |||
result = result .. ': ' | |||
end | |||
end | |||
if data.publisher then | |||
result = result .. asString( lang, data.publisher, options_commas_short ) | |||
if data.year then | |||
result = result .. ', ' | |||
end | |||
end | |||
if data.year then | |||
result = result .. asString( lang, data.year, options_commas ) | |||
end | |||
result = result .. '.' | |||
return result | |||
end | end | ||
function | ---@param lang string | ||
---@param data source | |||
---@return string | |||
local function appendVolumeAndIssue( lang, data ) | |||
if not data.volume and not data.issue then | |||
return '' | |||
end | |||
local result = ' — ' | |||
local letter_vol = i18nVolume[ lang ] or i18nVolume[ i18nDefaultLanguage ] | |||
local letter_iss = i18nIssue[ lang ] or i18nIssue[ i18nDefaultLanguage ] | |||
if data.volume then | |||
result = result .. appendProperty( lang, data, letter_vol .. ' ', 'volume', nil ) | |||
result = result ..appendProperty( lang, data, ', ' .. letter_iss .. ' ', 'issue', nil ) | |||
else | |||
result = result .. appendProperty( lang, data, letter_iss .. ' ', 'issue', nil ) | |||
end | |||
result = result .. '.' | |||
return result | |||
end | end | ||
function | ---@param lang string | ||
---@param data source | |||
---@return string | |||
local function appendPages( lang, data ) | |||
if not data.pages then | |||
return '' | |||
end | |||
local letter = i18nPages[ lang ] or i18nPages[ i18nDefaultLanguage ] | |||
local strPages = asString( lang, data.pages, options_commas ) | |||
strPages = mw.ustring.gsub( strPages, '[-—]', '—' ) | |||
return ' — ' .. letter .. ' ' .. strPages .. '.' | |||
end | |||
---@param lang string | |||
---@param data source | |||
---@return string | |||
local function appendNumberOfPages( lang, data ) | |||
if not data.numberOfPages then | |||
return '' | |||
end | |||
local letter = i18nNumberOfPages[ lang ] or i18nNumberOfPages[ i18nDefaultLanguage ] | |||
return appendProperty( lang, data, ' — ', 'numberOfPages', nil ) .. ' ' .. letter | |||
end | end | ||
function | ---@param lang string | ||
---@param data source | |||
---@return string | |||
local function appendBookSeries( lang, data ) | |||
if not data.bookSeries then | |||
return '' | |||
end | |||
local result = appendProperty( lang, data, ' — (', 'bookSeries', nil ) | |||
if data.bookSeriesVolume or data.bookSeriesIssue then | |||
result = result .. '; ' | |||
local letter_vol = i18nVolume[ lang ] or i18nVolume[ i18nDefaultLanguage ] | |||
local letter_iss = i18nIssue[ lang ] or i18nIssue[ i18nDefaultLanguage ] | |||
if data.bookSeriesVolume then | |||
result = result .. appendProperty( lang, data, letter_vol .. ' ', 'bookSeriesVolume', nil ) | |||
result = result .. appendProperty( lang, data, ', ' .. letter_iss .. ' ', 'bookSeriesIssue', nil ) | |||
else | |||
result = result .. appendProperty( lang, data, letter_iss .. ' ', 'bookSeriesIssue', nil ) | |||
end | |||
end | |||
result = result .. ')' | |||
return result | |||
end | end | ||
function | ---@param lang string | ||
---@param data source | |||
---@return string | |||
local function appendTirage( lang, data ) | |||
if not data.tirage then | |||
return '' | |||
end | |||
local tirageTemplate = i18nTirage[ lang ] or i18nTirage[ i18nDefaultLanguage ] | |||
---@type options | |||
local optionsTirage = { | |||
separator = '; ', | |||
conjunction = '; ', | |||
format = function( _data ) return tostring( mw.ustring.format( tirageTemplate, _data ) ) end, | |||
short = false, | |||
nolinks = false, | |||
preferids = false, | |||
} | |||
return ' — ' .. asString( lang, data.tirage, optionsTirage ) | |||
end | |||
---@param lang string | |||
---@param value string | nil | |||
---@param options options | |||
---@param prefix string? | |||
---@return string | |||
local function appendIdentifier( lang, value, options, prefix ) | |||
if not value then | |||
return '' | |||
end | |||
return ' — ' .. ( prefix or '' ) .. asString( lang, value, options ) | |||
end | end | ||
function | ---@param result string | ||
---@param lang string | |||
---@param data source | |||
---@return string | |||
local function wrapSourceId( result, lang, data ) | |||
if not data.sourceId then | |||
return result | |||
end | |||
local citeType = data.type and asString( lang, data.type, options_citetypes ) or 'citetype_unknown' | |||
return '<span class="wikidata_cite ' .. citeType .. '" data-entity-id="' .. data.sourceId .. '">' .. result .. '</span>' | |||
end | |||
---@param data source | |||
---@return string | |||
local function appendAccessDate( data ) | |||
if not data.accessdate then | |||
return '' | |||
end | |||
local date = getSingle( data.accessdate ) | |||
local pattern = "(%-?%d+)%-(%d+)%-(%d+)T" | |||
local y, m, d = mw.ustring.match( date, pattern ) | |||
y, m, d = tonumber( y ), tonumber( m ), tonumber( d ) | |||
local date_str = ( d > 0 and ' ' .. tostring( d ) or '' ) | |||
.. ( m > 0 and ' ' .. monthGen[ m ] or '' ) | |||
.. ( y > 0 and ' ' .. tostring( y ) or '' ) | |||
return " <small>Проверено" .. date_str .. ".</small>" | |||
end | |||
---@param data source | |||
---@param lang string | |||
---@return void | |||
local function populateUrl( data, lang ) | |||
if data.sourceId and not data.url then | |||
local sitelink = getSitelink( data.sourceId, lang .. 'wikisource' ) | |||
if sitelink then | |||
data.url = ':' .. lang .. ':s:' .. sitelink | |||
end | |||
end | |||
end | |||
---@param data source | |||
---@return void | |||
local function populateYear( data ) | |||
if not data.year and data.dateOfPublication then | |||
local date = getSingle( data.dateOfPublication ) | |||
data.year = mw.ustring.sub( date, 2, 5 ) | |||
end | |||
if not data.year and data.dateOfCreation then | |||
local date = getSingle( data.dateOfCreation ) | |||
data.year = mw.ustring.sub( date, 2, 5 ) | |||
end | |||
end | |||
---@param data source | |||
---@return void | |||
local function populateTitle( data ) | |||
data.title = data.title or getSingle( data.url ) | |||
end | end | ||
function | ---@param data source | ||
---@return string | |||
local function renderSource( data ) | |||
local lang = getLangCode( data.lang ) or i18nDefaultLanguage | |||
preprocessPlace( data, lang ) | |||
populateUrl( data, lang ) | |||
populateTitle( data ) | |||
if not data.title then | |||
return '' | |||
end | |||
populateYear( data ) | |||
local result = generateAuthorLinks( lang, data ) | |||
result = result .. appendTitle( lang, data ) | |||
result = result .. appendLanguage( lang ) | |||
result = result .. appendSubtitle( lang, data ) | |||
result = result .. appendOriginalTitle( lang, data ) | |||
result = result .. appendPublication( lang, data ) | |||
result = result .. '<span class="wef_low_priority_links">' | |||
result = result .. appendEditor( lang, data ) -- Might take current editor instead of actual. Use with caution | |||
result = result .. appendEdition( lang, data ) | |||
result = result .. appendPublicationData( lang, data ) | |||
result = result .. appendVolumeAndIssue( lang, data ) | |||
result = result .. appendPages( lang, data ) | |||
result = result .. appendNumberOfPages( lang, data ) | |||
result = result .. appendBookSeries( lang, data ) | |||
result = result .. appendTirage( lang, data ) | |||
result = result .. appendIdentifier( lang, data.isbn, options_commas, 'ISBN ' ) | |||
result = result .. appendIdentifier( lang, data.issn, options_issn, 'ISSN ' ) | |||
result = result .. appendIdentifier( lang, data.doi, options_doi, nil ) | |||
result = result .. appendIdentifier( lang, data.pmid, options_pmid, nil ) | |||
result = result .. appendIdentifier( lang, data.arxiv, options_arxiv, nil ) | |||
result = result .. appendAccessDate( data ) | |||
result = result .. '</span>' | |||
return wrapSourceId( result, lang, data ) | |||
end | |||
---@param data source Данные в простом формате, согласованном с модулями формирования библиографического описания | |||
---@param snaks snaks | |||
---@return string | nil | |||
local function renderReferenceImpl( data, snaks ) | |||
-- не показывать источники с "импортировано из" | |||
if snaks.P143 then | |||
return nil | |||
end | |||
-- забрать данные из reference | |||
populateDataFromSnaks( snaks or {}, data, PROPERTY_MAP ) | |||
data.sourceId = getSingle( data.sourceId ) | |||
populateDataFromEntity( data.sourceId, data, PROPERTY_MAP ) | |||
expandSpecials( data ) | |||
populateSourceDataImpl( data.sourceId, data, PROPERTY_MAP ) | |||
expandPublication( data ) | |||
end | expandBookSeries( data ) | ||
if next( data ) == nil then | |||
return nil | |||
end | |||
local rendered = renderSource( data ) | |||
if mw.ustring.len( rendered ) == 0 then | |||
return nil | |||
end | |||
if data.ref then | |||
local anchorValue = 'CITEREF' .. data.ref .. ( coalesce( { data[ 'ref-year' ], data.year } ) or '' ) | |||
rendered = '<span class="citation" id="' .. mw.uri.anchorEncode( anchorValue ) .. '">' .. rendered .. '</span>' | |||
end | |||
return rendered | |||
end | end | ||
---@param frame frame | |||
---@param currentEntityId string | { id: string } | |||
---@param reference table{ snaks: snaks } | |||
---@return string | nil | |||
function p.renderSource( frame, currentEntityId, reference ) | |||
reference = reference or { snaks = {} } | |||
p.currentFrame = frame | |||
local data = getFilledArgs( frame.args or {} ) | |||
populateDataFromSnaks( reference.snaks, data, PROPERTY_MAP ) | |||
data.sourceId = getSingle( data.sourceId ) | |||
if not currentEntityId then | |||
data.entityId = mw.wikibase.getEntityIdForCurrentPage() | |||
elseif type( currentEntityId ) == 'string' then | |||
data.entityId = currentEntityId | |||
elseif type( currentEntityId ) == 'table' and currentEntityId.id then | |||
data.entityId = currentEntityId.id | |||
end | |||
---@type string | |||
local rendered = renderReferenceImpl( data, reference.snaks or {} ) | |||
if not rendered then | |||
return '' | |||
end | |||
return rendered | |||
end | end | ||
---@param frame frame | |||
---@param currentEntityId string | |||
---@param reference table | |||
---@return string | |||
function p.renderReference( frame, currentEntityId, reference ) | |||
local rendered = p.renderSource( frame, currentEntityId, reference ) | |||
if not rendered or rendered == '' then | |||
return '' | |||
end | |||
-- Про выбор алгоритма хеширования см. [[Модуль:Hash]]. Знак подчёркивания в начале позволяет | |||
-- исключить ошибку, когда имя сноски — чисто числовое значение, каковыми иногда бывают хеши. | |||
return frame:extensionTag( 'ref', rendered, { name = '_' .. mw.hash.hashValue( 'fnv164', rendered ) } ) .. '[[Category:ЭАНМ:Статьи с источниками из Викиданных]]' | |||
end | end | ||
---@param frame frame | |||
---@return string | nil | |||
function p.testPersonNameToAuthorName( frame ) | |||
return personNameToAuthorName( frame.args[ 1 ] ) | |||
end | end | ||
function | ---@param frame frame | ||
---@return string | nil | |||
function p.testPersonNameToResponsibleName( frame ) | |||
return personNameToResponsibleName( frame.args[ 1 ] ) | |||
end | end | ||
return p | return p |
правка