Модуль:Sources/песочница
Файл:Sandbox.png | Эта страница — песочница для модуля Модуль:Sources (разница с модулем). См. также сопроводительную подстраницу с контрольными примерами. |
Тесты[править код]
7 тестов провалено.
Тест | Ожидаемое значение | Фактическое значение | |
---|---|---|---|
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Ломоносов, Михаил Васильевич }} | Ломоносов М. В. | Ломоносов М. В. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Ломоносов, Михаил }} | Ломоносов М. | Ломоносов М. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Михаил Васильевич Ломоносов }} | Ломоносов М. В. | Ломоносов М. В. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Михаил Ломоносов }} | Ломоносов М. | Ломоносов М. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | М. В. Ломоносов }} | Ломоносов М. В. | Ломоносов М. В. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | М. Ломоносов }} | Ломоносов М. | Ломоносов М. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Ломоносов М. В. }} | Ломоносов М. В. | Ломоносов М. В. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Ломоносов М. }} | Ломоносов М. | Ломоносов М. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Топчибашев, Мустафа Агабек оглы }} | Топчибашев М. А. | Топчибашев М. А. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Гельмонт, Ян Баптиста ван }} | ван Гельмонт Я. Б. | ван Гельмонт Я. Б. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Jan Baptista van Helmont }} | van Helmont J. B. | van Helmont J. B. |
Тест | Ожидаемое значение | Фактическое значение | |
---|---|---|---|
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Ломоносов, Михаил Васильевич }} | М. В. Ломоносов | М. В. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Ломоносов, Михаил }} | М. Ломоносов | М. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Михаил Васильевич Ломоносов }} | М. В. Ломоносов | М. В. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Михаил Ломоносов }} | М. Ломоносов | М. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | М. В. Ломоносов }} | М. В. Ломоносов | М. В. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | М. Ломоносов }} | М. Ломоносов | М. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Ломоносов М. В. }} | М. В. Ломоносов | М. В. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Ломоносов М. }} | М. Ломоносов | М. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Топчибашев, Мустафа Агабек оглы }} | М. А. Топчибашев | М. А. Топчибашев |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Гельмонт, Ян Баптиста ван }} | Я. Б. ван Гельмонт | Я. Б. ван Гельмонт |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Jan Baptista van Helmont }} | J. B. van Helmont | J. B. van Helmont |
Тест | Ожидаемое значение | Фактическое значение | |
---|---|---|---|
Файл:X mark.svg | {{#invoke:Sources | renderSource | Q20750516}} | президент Российской Федерации Указ Президента Российской Федерации от 15 января 1992 г. № 23 «О Генеральном директоре Агентства федеральной безопасности Российской Федерации и Министре внутренних дел Российской Федерации» // Собрание законодательства Российской Федерации — 1992. | Ошибка Lua в Модуль:Sources на строке 512: attempt to index field 'wikibase' (a nil value). |
Файл:X mark.svg | {{#invoke:Sources | renderSource | Q21683979}} | Advances in Cryptology — EUROCRYPT 2004 (англ.): International Conference on the Theory and Applications of Cryptographic Techniques, Interlaken, Switzerland, May 2-6, 2004. Proceedings / C. Cachin, J. L. Camenisch — Springer, Berlin, Heidelberg, 2004. — 630 p. — ISBN 978-3-540-21935-4 — doi:10.1007/B97182 | Ошибка Lua в Модуль:Sources на строке 512: attempt to index field 'wikibase' (a nil value). |
Файл:X mark.svg | {{#invoke:Sources | renderSource | Q21683981}} | Nguyen P. Can We Trust Cryptographic Software? Cryptographic Flaws in GNU Privacy Guard v1.2.3 (англ.) // Advances in Cryptology — EUROCRYPT 2004: International Conference on the Theory and Applications of Cryptographic Techniques, Interlaken, Switzerland, May 2-6, 2004. Proceedings / C. Cachin, J. L. Camenisch — Springer, Berlin, Heidelberg, 2004. — P. 555—570. — 630 p. — ISBN 978-3-540-21935-4 — doi:10.1007/978-3-540-24676-3_33 | Ошибка Lua в Модуль:Sources на строке 512: attempt to index field 'wikibase' (a nil value). |
Файл:X mark.svg | {{#invoke:Sources | renderSource | Q21725400}} | Eichenauer J., Lehn J. A non-linear congruential pseudo random number generator (англ.) // Statistische Hefte — Springer Berlin Heidelberg, Springer Science+Business Media, 1986. — Vol. 27, Iss. 1. — P. 315—326. — ISSN 0932-5026; 1613-9798 — doi:10.1007/BF02932576 | Ошибка Lua в Модуль:Sources на строке 512: attempt to index field 'wikibase' (a nil value). |
Файл:X mark.svg | {{#invoke:Sources | renderSource | Q21725116}} | Menezes A. J., Oorschot P. v., Vanstone S. A. Handbook of Applied Cryptography (англ.) — CRC Press, 1996. — 816 p. — (Discrete Mathematics and Its Applications) — ISBN 978-0-8493-8523-0 | Ошибка Lua в Модуль:Sources на строке 512: attempt to index field 'wikibase' (a nil value). |
Файл:X mark.svg | {{#invoke:Sources | renderSource | Q27450585}} | Введение в криптографию / под ред. В. В. Ященко — М.: МЦНМО, 2000. — 271 с. — ISBN 978-5-900916-26-2 | Ошибка Lua в Модуль:Sources на строке 512: attempt to index field 'wikibase' (a nil value). |
Тест | Ожидаемое значение | Фактическое значение | |
---|---|---|---|
Файл:X mark.svg | {{Source | Q21725116}} | Menezes A. J., Oorschot P. v., Vanstone S. A. Handbook of Applied Cryptography (англ.) — CRC Press, 1996. — 816 p. — (Discrete Mathematics and Its Applications) — ISBN 978-0-8493-8523-0 | Ошибка Lua в Модуль:Sources на строке 512: attempt to index field 'wikibase' (a nil value). |
Ошибка Lua на строке 450: attempt to index field 'wikibase' (a nil value).
Ошибка Lua на строке 450: attempt to index field 'wikibase' (a nil value).
Ошибка Lua на строке 450: attempt to index field 'wikibase' (a nil value).
[1]Ошибка Lua: not enough memory.
Ошибка Lua: not enough memory.
Генерирует сноски и ссылки на источники.
---@alias args table
---@alias frame { args: args, extensionTag: function }
---@alias source table<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 table<string, string[]>
---@alias mwText { listToText: ( fun( list: table, separator: string, conjunction: string ): string ), trim: ( fun( text: string ): string ) }
---@alias mwWikibase { getAllStatements: ( fun( entityId: string, propertyId: string ): table<number, statement> ), getBestStatements: ( fun( entityId: string, propertyId: string ): table<number, statement> ), getEntityIdForCurrentPage: ( fun(): string ), getLabel: ( fun( entityId: string, language: string | nil ): string | nil ), getReferencedEntityId: ( fun( fromEntityId: string, propertyId: string, toEntityIds: string[] ): string | nil ), getSitelink: ( fun( entityId: string, project: string | nil ): string | nil ) }
---@type { clone: ( fun( in: table ): table ), hash: table, language: table, log: fun( text: string ), logObject: fun( object: any, name: string ), text: mwText, uri: { anchorEncode: function }, ustring: table, wikibase: mwWikibase }
--local mw = require( 'mw' )
--mw.ustring = string
---@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 = {
author = { 'P50', 'P2093' },
lang = { 'P407', 'P364' },
part = { 'P958', 'P1810' },
title = { 'P1476' },
subtitle = { 'P1680' },
url = { 'P953', 'P1065', 'P854', 'P973', 'P2699' },
editor = { 'P98' },
translator = { 'P655' },
[ 'publication-id' ] = { 'P1433' },
edition = { 'P393' },
publisher = { 'P123' },
place = { 'P291' },
volume = { 'P478' },
issue = { 'P433' },
dateOfCreation = { 'P571' },
dateOfPublication = { 'P577' },
pages = { 'P304' },
numberOfPages = { 'P1104' },
tirage = { 'P1092' },
isbn = { 'P212', 'P957' },
issn = { 'P236' },
-- accessdate = { 'P813' }, -- disable, creates duplicate references
docNumber = { 'P1545' },
type = { 'P31' },
arxiv = { 'P818' },
doi = { 'P356' },
pmid = { 'P698' },
url = { 'P888' },
}
---@type map
local PUBLICATION_PROPERTY_MAP = {
title = { 'P1433', 'P1160', 'P1476' },
subtitle = { 'P1680' },
}
-- table.insert( PROPERTY_MAP.url, 'P856' ) -- only as qualifier
---@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 = {
fr = '',
de = 'Hrsg.: ',
es = '',
en = '',
it = '',
ru = 'под ред. ',
uk = 'за ред. ',
}
---@type table<string, string>
local i18nTranslators = {
fr = '',
de = '',
es = '',
en = '',
it = '',
ru = 'пер. ',
uk = 'пер. ',
}
---@type table<string, string>
local i18nVolume = {
de = 'Vol.',
fr = 'Vol.',
es = 'Vol.',
en = 'Vol.',
it = 'Vol.',
ru = 'Т.',
uk = 'Т.',
}
---@type table<string, string>
local i18nIssue = {
en = 'Iss.',
ru = 'вып.',
uk = 'вип.',
}
---@type table<string, string>
local i18nPages = {
fr = 'P.',
de = 'S.',
es = 'P.',
en = 'P.',
it = 'P.',
ru = 'С.',
uk = 'С.',
}
---@type table<string, string>
local i18nNumberOfPages = {
en = 'p.',
ru = 'с.',
}
---@type table<string, string>
local i18nTirage = {
en = 'ed. size: %d',
ru = '%d экз.',
}
---Returns formatted pair {Family name(s), First name(s)}
---@param fullName string
---@return table<number, string>
local function tokenizeName( fullName )
local start = '^%s*' -- matches beginning of the string + arbitrary number of spaces
local finish = '%s*$' -- matches end of the string + arbitrary number of spaces
local comma = ',%s+' -- matches comma + single or more spacing character
local space = '%s+' -- matches single or more spacing character
local name = "(%a[%a%-']*)%.?" -- matches single name, have to start with letter, can contain apostrophe and hyphen, may end with dot
local surname = "(%a[%a%-']*)" -- same as name, but can't end with dot
local f, f2, i, i2, o
f, i = mw.ustring.match( fullName, start .. surname .. comma .. name .. finish )
if f then
mw.log( 'tokenizeName: «' .. fullName .. '»: have «Fa, Im» match' )
return { f, mw.ustring.sub( i, 1, 1 ) .. '.' }
end
f, i, o = mw.ustring.match( fullName, start .. surname .. comma .. name .. space .. name .. finish )
if f then
mw.log( 'tokenizeName: «' .. fullName .. '»: have «Fa, Im Ot» match' )
return { f, mw.ustring.sub( i, 1, 1 ) .. '. '
.. mw.ustring.sub( o, 1, 1 ) .. '.' }
end
f, f2, i = mw.ustring.match( fullName, start .. surname .. space .. surname .. comma .. name .. finish )
if f then
mw.log( 'tokenizeName: «' .. fullName .. '»: have «Fa Fa, Im» match' )
return { f .. ' ' .. f2, mw.ustring.sub( i, 1, 1 ) .. '.' }
end
i, o, f = mw.ustring.match( fullName, start .. name .. space .. name .. space .. 'оглы' .. space .. surname .. finish )
if f then
mw.log( 'tokenizeName: «' .. fullName .. '»: have «Im Ot оглы Fa» match' )
return { f, mw.ustring.sub( i, 1, 1 ) .. '. ' .. mw.ustring.sub( o, 1, 1 ) .. '.' }
end
i, i2, f = mw.ustring.match( fullName, start .. name .. space .. name .. space .. 'de' .. space .. surname .. finish )
if f then
mw.log( 'tokenizeName: «' .. fullName .. '»: have «Im Im de Fa» match' )
return { f, mw.ustring.sub( i, 1, 1 ) .. '. ' .. mw.ustring.sub( i2, 1, 1 ) .. '.' }
end
-- Try matching k names + surname
for k = 1, 4 do
pattern = start .. string.rep( name .. space, k ) .. surname .. finish
---@type string[]
local matched = { mw.ustring.match( fullName, pattern ) }
if #matched ~= 0 then
mw.log( 'tokenizeName: «' .. fullName .. '»: have «Im (x' .. k .. ') Fa» match' )
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
mw.log( 'Unmatched any pattern: «' .. fullName .. '»' )
return { fullName }
end
---@param fullName string
---@return string
local function personNameToAuthorName( fullName )
if not fullName then return fullName end
local tokenized = tokenizeName( fullName )
if #tokenized == 1 then
return tokenized[ 1 ]
else
return tokenized[ 1 ] .. ' ' .. tokenized[ 2 ]
end
end
---@param fullName string
---@return string
local function personNameToResponsibleName( fullName )
if not fullName then return fullName end
local tokenized = tokenizeName( fullName )
if #tokenized == 1 then
return tokenized[ 1 ]
else
return tokenized[ 2 ] .. ' ' .. tokenized[ 1 ]
end
end
---@alias options { separator: string, conjunction: string, format: ( fun( src: string ): string ), nolinks: boolean, preferids: boolean, short: boolean }
---@type options
local options_commas = {
separator = ', ',
conjunction = ', ',
format = function( src ) return src 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( src ) return "''" .. src .. "''" 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( src ) return 'citetype_' .. src 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 result table
---@param resultProperty string
---@return void
local function appendImpl_toTable( result, resultProperty )
if not result[ resultProperty ] then
result[ resultProperty ] = {}
elseif ( type( result[ resultProperty ] ) == 'string' or ( type( result[ resultProperty ] ) == 'table' and type( result[ resultProperty ].id ) == 'string' ) ) then
result[ resultProperty ] = { result[ resultProperty ] }
end
end
---@param datavalue table
---@param qualifiers snaks
---@param result table
---@param propertyName string
---@param options table
local function appendImpl( datavalue, qualifiers, result, propertyName, options )
if 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( result, propertyName)
local pos = getSingleStringQualifierValue( qualifiers, 'P1545' )
if pos then
table.insert( result[ propertyName ], tonumber(pos), value )
else
table.insert( result[ propertyName ], value )
end
elseif datavalue.type == 'url' then
local value = datavalue.value
if options.format then
value = options.format( value )
end
appendImpl_toTable( result, propertyName)
table.insert( result[ propertyName ], value )
elseif datavalue.type == 'monolingualtext' then
local value = datavalue.value.text
if options.format then
value = options.format( value )
end
appendImpl_toTable( result, propertyName)
table.insert( result[ 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( result, propertyName)
table.insert( result[ propertyName ], value )
elseif datavalue.type == 'wikibase-entityid' then
local pos = getSingleStringQualifierValue( qualifiers, 'P1545' )
local value = datavalue.value
appendImpl_toTable( result, 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( result[ propertyName ], tonumber( pos ), toInsert )
else
table.insert( result[ propertyName ], toInsert )
end
elseif datavalue.type == 'time' then
local value = datavalue.value
if options.format then
value = options.format( value )
end
appendImpl_toTable( result, propertyName)
table.insert( result[ 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
---@param entityId string
---@param propertyId string
---@param result table
---@param propertyName string
---@param options table?
---@return void
local function appendEntitySnaks( entityId, propertyId, result, propertyName, options )
options = options or {}
-- do not populate twice
if result[ propertyName ] and ( propertyName ~= 'author' or result[ propertyId ] ) then
return
end
local statements = getBestStatements( entityId, propertyId )
if propertyName == 'author' then
result[ propertyId ] = true
end
if propertyId == 'P1680' then -- if there is a russian
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 == i18nDefaultLanguage
then
--found russian string
appendImpl( statement.mainsnak.datavalue, statement.qualifiers, result, propertyName, options )
return
end
end
end
for _, snak in pairs( statements ) do
if snak and snak.mainsnak and snak.mainsnak.datavalue then
appendImpl( snak.mainsnak.datavalue, snak.qualifiers, result, propertyName, options )
end
end
end
---@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 argName string
---@param arg any | nil
---@return void
local function assertNotNull( argName, arg )
if not arg then
error( argName .. ' is not specified' )
end
end
---@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
---@param propertyId string
---@param strValue string
---@return snak
local function toStringSnak( propertyId, strValue )
assertNotNull( 'propertyId', strValue )
assertNotNull( 'strValue', strValue )
return {
snaktype = "value",
property = propertyId,
datatype = 'string',
datavalue = {
value = strValue,
type = 'string',
},
}
end
---@param propertyId string
---@param strValue string
---@return snak
local function toUrlSnak( propertyId, strValue )
assertNotNull( 'propertyId', strValue )
assertNotNull( 'strValue', strValue )
return {
snaktype = 'value',
property = propertyId,
datatype = 'string',
datavalue = {
value = strValue,
type = 'url',
},
}
end
---@param args args
---@param snaks snaks
---@return void
local function copyArgsToSnaks( args, snaks )
if not isEmpty( args.part ) then snaks.P958 = { toStringSnak( 'P958', tostring( args.part ) ) } end
if not isEmpty( args.pages ) then snaks.P304 = { toStringSnak( 'P304', tostring( args.pages ) ) } end
if not isEmpty( args.issue ) then snaks.P433 = { toStringSnak( 'P433', tostring( args.issue ) ) } end
if not isEmpty( args.volume ) then snaks.P478 = { toStringSnak( 'P478', tostring( args.volume ) ) } end
if not isEmpty( args.url ) then snaks.P953 = { toUrlSnak( 'P953', tostring( args.url ) ) } end
if not isEmpty( args.parturl ) then snaks.P953 = { toUrlSnak( 'P953', tostring( args.parturl ) ) } end
end
---@param langEntityId string
---@return string | nil
local function getLangCode( langEntityId )
if not langEntityId then
return nil
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
---@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 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
---@param entityId string
---@param typeEntityId string
---@return boolean
local function isInstanceOf( entityId, typeEntityId )
return findClaimsByValue( entityId, 'P31', typeEntityId )[ 1 ] ~= nil
end
---@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 parameterName, propertyIds in pairs( map ) do
for _, propertyId in pairs( propertyIds ) do
if 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
---@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
if claims and claims.P361 then
---@type table<number, statement>
local propertyClaims = claims.P361
for _, claim in pairs( propertyClaims ) do
if ( claim and claim.mainsnak
and claim.mainsnak.datavalue
and claim.mainsnak.datavalue.value
and claim.mainsnak.datavalue.value.id
) then
local possibleBookSeriesEntityId = claim.mainsnak.datavalue.value.id
if isInstanceOf( possibleBookSeriesEntityId, 'Q277759' ) then
appendImpl_toTable( data, 'bookSeries' )
table.insert( data.bookSeries, { id = possibleBookSeriesEntityId } )
appendQualifiers( { claim }, 'P478', data, 'bookSeriesVolume', {} )
appendQualifiers( { claim }, 'P433', data, 'bookSeriesIssue', {} )
end
end
end
end
for parameterName, propertyIds in pairs( map ) do
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
---@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 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 '(unknown)'
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.getLabel, entityId, lang )
if not wbStatus then
return ''
end
return label or ''
end
---@param lang string
---@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 .. '»' )
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 .. '»' )
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 toString( 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, '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, i18nDefaultLanguage )
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, toString( lang, tableValue, options ) )
end
return mw.text.listToText( resultList, options.separator, options.conjunction )
end
---@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, PROPERTY_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 = toString( '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 label = getLabel( entityId, i18nDefaultLanguage )
if label ~= '' then
data.title = { label }
end
end
return data
end
---@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, data, PROPERTY_MAP )
end
end
---Expand special types of references when additional data could be found in OTHER entity properties
---@param currentEntityId string
---@param snaks snaks
---@param data source
---@return void
local function expandSpecials( currentEntityId, snaks, data )
local sourceId
if ( snaks.P805
and snaks.P805[ 1 ]
and snaks.P805[ 1 ].datavalue
and snaks.P805[ 1 ].datavalue.value.id ) then
sourceId = snaks.P805[ 1 ].datavalue.value.id
elseif ( snaks.P248
and snaks.P248[ 1 ]
and snaks.P248[ 1 ].datavalue
and snaks.P248[ 1 ].datavalue.value.id ) then
sourceId = snaks.P248[ 1 ].datavalue.value.id
end
if sourceId then
data.sourceId = sourceId
-- Gemeinsame Normdatei -- specified by P227
if sourceId == 'Q36578' then
appendEntitySnaks( currentEntityId, 'P227', data, 'part', { format = function(gnd ) return 'Record #' .. gnd; end } )
appendEntitySnaks( currentEntityId, 'P227', data, 'url', { format = function(gnd ) return 'http://d-nb.info/gnd/' .. gnd .. '/'; end } )
data.year = '2012—2016'
expandSpecialsQualifiers( currentEntityId, 'P227', data )
-- BNF -- specified by P268
elseif sourceId == 'Q15222191' then
appendEntitySnaks( currentEntityId, 'P268', data, 'part', { format = function(id ) return 'Record #' .. id; end } )
appendEntitySnaks( currentEntityId, 'P268', data, 'url', { format = function(id ) return 'http://catalogue.bnf.fr/ark:/12148/cb' .. id; end } )
expandSpecialsQualifiers( currentEntityId, 'P268', data )
-- VIAF -- specified by P214
elseif sourceId == 'Q54919' then
appendEntitySnaks( currentEntityId, 'P214', data, 'part', { format = function(id ) return 'Record #' .. id; end } )
appendEntitySnaks( currentEntityId, 'P214', data, 'url', { format = function(id ) return 'https://viaf.org/viaf/' .. id; end } )
expandSpecialsQualifiers( currentEntityId, 'P214', data )
-- generic property search
else
for _, sourceClaim in pairs( getBestStatements( 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( currentEntityId, sourcePropertyId, data, 'url', {
format = function( id )
return mw.ustring.gsub( mw.ustring.gsub( sourcePropertyClaim.mainsnak.datavalue.value, '$1', id ), ' ', '%%20' )
end
} )
expandSpecialsQualifiers( currentEntityId, sourcePropertyId, data )
break
end
end
end
end
end
-- do we have appropriate record in P1433 ?
local claims = findClaimsByValue( currentEntityId, 'P1343', sourceId )
if claims and #claims ~= 0 then
for _, claim in pairs( claims ) do
populateDataFromSnaks( claim.qualifiers, data, PROPERTY_MAP )
populateDataFromEntity( 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
---@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
---@param src source
---@param lang string
---@return void
local function preprocessPlace( src, lang )
if not src.place then
return
end
---@type table<number, string>
local newPlace = {}
for index, place in pairs( src.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
src.place = newPlace
end
---@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
mw.log( 'Custom label provided for ' .. entityId )
return options.format( providedLabel )
end
if lang == 'mul' then
lang = i18nDefaultLanguage
end
---@type string | nil
local personName = getLabel( entityId, lang )
if personName then
mw.log( 'Got person name of ' .. entityId .. ' from label: «' .. personName .. '»' )
end
if isEmpty( personName ) then
return '\'\'(not translated to ' .. lang .. ')\'\''
end
mw.logObject( providedLabel, 'providedLabel' )
mw.logObject( entityId, 'entityId' )
mw.logObject( isInstanceOf( entityId, 'Q5' ), 'isInstanceOf' )
if not isInstanceOf( entityId, 'Q5' ) then
mw.log( 'Entity ' .. entityId .. ' is not a person' )
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
---@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 src source
---@return string
local function generateAuthorLinks( lang, src )
local result = ''
if src.author then
result = getPeopleAsWikitext( src.author, lang, options_commas_authors )
result = '<i class="wef_low_priority_links">' .. result .. '</i> '
end
return result
end
---@param lang string
---@param src source
---@param conjunction string
---@param propertyName string
---@param urlPropertyName string?
---@return string
local function appendProperty( lang, src, conjunction, propertyName, urlPropertyName )
if not src[ propertyName ] then
return ''
end
local out
if urlPropertyName and src[ urlPropertyName ] then
out = conjunction .. wrapInUrl( src[ urlPropertyName ], toString( lang, src[ propertyName ], options_commas_nolinks ) )
else
out = toString( lang, src[ propertyName ], options_commas )
end
if not out or out == '' then
return ''
end
return conjunction .. out
end
---@param lang string
---@param src source
---@return string
local function appendTitle( lang, src )
local conjunction = ''
local result = ''
if src.part then
result = result .. appendProperty( lang, src, '', 'part', 'parturl' )
conjunction = ' // '
end
result = result .. appendProperty( lang, src, conjunction, 'title', 'url' )
return result
end
---@param lang string
---@return string
local function appendLanguage( lang )
if lang == i18nDefaultLanguage then
return ''
end
---@type { getRefHtml: ( fun( lang: string ): string ) }
local langs = require( 'Module:Languages' )
return langs.list_ref( p.currentFrame:newChild{ args = { lang } } )
end
---@param lang string
---@param src source
---@return string
local function appendSubtitle( lang, src )
return appendProperty( lang, src, ': ', 'subtitle', nil )
end
---@param lang string
---@param src source
---@return string
local function appendOriginalTitle( lang, src )
return appendProperty( lang, src, ' = ', 'originaltitle', nil )
end
---@param lang string
---@param src source
---@return string
local function appendPublication( lang, src )
if not src.publication then
return ''
end
local result = ' // ' .. toString( lang, src.publication.title, options_commas_it_short )
if src.publication.subtitle and src.publication.subtitle ~= '' then
result = result .. ': ' .. toString( lang, src.publication.subtitle, options_commas_it_short )
end
return result
end
---@param lang string
---@param src source
---@return string
local function appendEditor( lang, src )
if not src.editor and not src.translator then
return ''
end
local result = ' / '
if src.editor then
local prefix = i18nEditors[ lang ] or i18nEditors[ i18nDefaultLanguage ]
result = result .. prefix .. getPeopleAsWikitext( src.editor, lang, options_commas_responsible )
if src.translator then
result = result .. ', '
end
end
if src.translator then
local prefix = i18nTranslators[ lang ] or i18nTranslators[ i18nDefaultLanguage ]
result = result .. prefix .. getPeopleAsWikitext( src.translator, lang, options_commas_responsible )
end
return result
end
---@param lang string
---@param src source
local function appendEdition( lang, src )
return appendProperty( lang, src, ' — ', 'edition', nil )
end
---@param lang string
---@param src source
---@return string
local function appendPublicationData( lang, src )
if not src.place and not src.publisher and not src.year then
return ''
end
local result = ' — '
if src.place then
result = result .. toString( lang, src.place, options_commas_short )
if src.publisher or src.year then
result = result .. ': '
end
end
if src.publisher then
result = result .. toString( lang, src.publisher, options_commas_short )
if src.year then
result = result .. ', '
end
end
if src.year then
result = result .. toString( lang, src.year, options_commas )
end
result = result .. '.'
return result
end
---@param lang string
---@param src source
---@return string
local function appendVolumeAndIssue( lang, src )
if not src.volume and not src.issue then
return ''
end
local result = ' — '
local letter_vol = i18nVolume[ lang ] or i18nVolume[ i18nDefaultLanguage ]
local letter_iss = i18nIssue[ lang ] or i18nIssue[ i18nDefaultLanguage ]
if src.volume then
result = result .. appendProperty( lang, src, letter_vol .. ' ', 'volume', nil )
result = result ..appendProperty( lang, src, ', ' .. letter_iss .. ' ', 'issue', nil )
else
result = result .. appendProperty( lang, src, letter_iss .. ' ', 'issue', nil )
end
result = result .. '.'
return result
end
---@param lang string
---@param src source
---@return string
local function appendPages( lang, src )
if not src.pages then
return ''
end
local letter = i18nPages[ lang ] or i18nPages[ i18nDefaultLanguage ]
local strPages = toString( lang, src.pages, options_commas )
strPages = mw.ustring.gsub( strPages, '[-—]', '—' )
return ' — ' .. letter .. ' ' .. strPages .. '.'
end
---@param lang string
---@param src source
---@return string
local function appendNumberOfPages( lang, src )
if not src.numberOfPages then
return ''
end
local letter = i18nNumberOfPages[ lang ] or i18nNumberOfPages[ i18nDefaultLanguage ]
return appendProperty( lang, src, ' — ', 'numberOfPages', nil ) .. ' ' .. letter
end
---@param lang string
---@param src source
---@return string
local function appendBookSeries( lang, src )
if not src.bookSeries then
return ''
end
local result = appendProperty( lang, src, ' — (', 'bookSeries', nil )
if src.bookSeriesVolume or src.bookSeriesIssue then
result = result .. '; '
local letter_vol = i18nVolume[ lang ] or i18nVolume[ i18nDefaultLanguage ]
local letter_iss = i18nIssue[ lang ] or i18nIssue[ i18nDefaultLanguage ]
if src.bookSeriesVolume then
result = result .. appendProperty( lang, src, letter_vol .. ' ', 'bookSeriesVolume', nil )
result = result .. appendProperty( lang, src, ', ' .. letter_iss .. ' ', 'bookSeriesIssue', nil )
else
result = result .. appendProperty( lang, src, letter_iss .. ' ', 'bookSeriesIssue', nil )
end
end
result = result .. ')'
return result
end
---@param lang string
---@param src source
---@return string
local function appendTirage( lang, src )
if not src.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 ' — ' .. toString( lang, src.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 '' ) .. toString( lang, value, options )
end
---@param result string
---@param lang string
---@param src source
---@return string
local function wrapSourceId( result, lang, src )
local citeType = src.type and toString( lang, src.type, options_citetypes ) or 'citetype_unknown'
return '<span class="wikidata_cite ' .. citeType .. '" data-entity-id="' .. getSingle( src.sourceId ) .. '">' .. result .. '</span>'
end
---@param src source
---@return string
local function appendAccessDate( src )
if not src.accessdate then
return ''
end
local date = getSingle( src.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 src source
---@param lang string
---@return void
local function populateUrl( src, lang )
if src.sourceId and not src.url then
local sitelink = getSitelink( src.sourceId, lang .. 'wikisource' )
if sitelink then
src.url = ':' .. lang .. ':s:' .. sitelink
end
end
end
---@param src source
---@return void
local function populateYear( src )
if not src.year and src.dateOfPublication then
local date = getSingle( src.dateOfPublication )
src.year = mw.ustring.sub( date, 2, 5 )
end
if not src.year and src.dateOfCreation then
local date = getSingle( src.dateOfCreation )
src.year = mw.ustring.sub( date, 2, 5 )
end
end
---@param src source
---@return void
local function populateTitle( src )
src.title = src.title or getSingle( src.url ) or '\'\'(unspecified title)\'\''
end
---@param src source
---@return string
local function renderSource( src )
options_commas_authors.format = personNameToAuthorName
options_commas_responsible.format = personNameToResponsibleName
local lang = getLangCode( getSingle( src.lang ) ) or i18nDefaultLanguage
preprocessPlace( src, lang )
populateUrl( src, lang )
populateTitle( src )
populateYear( src )
local result = generateAuthorLinks( lang, src )
result = result .. appendTitle( lang, src )
result = result .. appendLanguage( lang )
result = result .. appendSubtitle( lang, src )
result = result .. appendOriginalTitle( lang, src )
result = result .. appendPublication( lang, src )
result = result .. '<span class="wef_low_priority_links">'
result = result .. appendEditor( lang, src ) -- Might take current editor instead of actual. Use with caution
result = result .. appendEdition( lang, src )
result = result .. appendPublicationData( lang, src )
result = result .. appendVolumeAndIssue( lang, src )
result = result .. appendPages( lang, src )
result = result .. appendNumberOfPages( lang, src )
result = result .. appendBookSeries( lang, src )
result = result .. appendTirage( lang, src )
result = result .. appendIdentifier( lang, src.isbn, options_commas, 'ISBN ' )
result = result .. appendIdentifier( lang, src.issn, options_issn, 'ISSN ' )
result = result .. appendIdentifier( lang, src.doi, options_doi, nil )
result = result .. appendIdentifier( lang, src.pmid, options_pmid, nil )
result = result .. appendIdentifier( lang, src.arxiv, options_arxiv, nil )
result = result .. appendAccessDate( src )
result = result .. '</span>'
return wrapSourceId( result, lang, src )
end
---@param data source Данные в простом формате, согласованном с модулями формирования библиографического описания
---@param snaks snaks
---@return string | nil
local function renderReferenceImpl( data, snaks )
-- забрать данные из reference
populateDataFromSnaks( snaks, data, PROPERTY_MAP )
populateDataFromEntity( data.sourceId, data, PROPERTY_MAP )
expandSpecials( data.sourceId, snaks, data )
populateSourceDataImpl( data.sourceId, data, PROPERTY_MAP )
if data[ 'publication-id' ] then
local publicationId = getSingle( data[ 'publication-id' ] )
data.publication = {
sourceId = publicationId,
title = data[ 'publication-title' ],
subtitle = data[ 'publication-subtitle' ],
}
populateDataFromSnaks( snaks, data.publication, PUBLICATION_PROPERTY_MAP )
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
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
---@param args args
---@param snaks snaks
---@return string | nil
local function getEntityId( args, snaks )
if args[ 1 ] then
return mw.text.trim( args[ 1 ] )
end
---@type table<number, snak>
local propertySnaks = snaks.P248 or snaks.P805 or {}
for _, snak in pairs( propertySnaks ) do
if snak.datavalue.type == 'value' then
return tostring( snak.datavalue.value.id )
end
end
return nil
end
---@param frame frame
---@param currentEntityId string
---@param reference table
---@return string | nil
function p.renderSource( frame, currentEntityId, reference )
reference = reference or {}
p.currentFrame = frame
---@type source
local data = mw.clone( frame.args or {} )
data.sourceId = currentEntityId
-- template call {{source[-ref]|Q123}}
if not data.sourceId or data.sourceId == '' then
data.sourceId = getEntityId( data, reference.snaks or {} )
if not data.sourceId then
return ''
end
if reference.snaks then
reference.snaks.P248 = nil
reference.snaks.P805 = nil
end
end
---@type string
local rendered = renderReferenceImpl( data, reference.snaks or {} )
if not rendered then
return ''
end
return rendered
end
---@param frame frame
---@param currentEntityId string
---@param reference table
---@return string
function p.renderReference( frame, currentEntityId, reference )
local rendered = p.renderReference( 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
return p