Модуль:Источники по теме/wikidata
Документация
Сборщик данных из Викиданных — ссылок на энциклопедии и братские википроекты по теме исходной страницы. Используется в Модуль:Источники по теме.
local RU = 'ru'
local RUWIKISOURCE = 'ruwikisource'
local wd = require("Module:WD")
local util = require("Module:Util")
local encyclopediasData = util.get_json("MediaWiki:Encyclopedias settings.json") -- настройки заголовков энциклопедий и их id в ВД
local encyclopedias_sites_cfg = util.get_json("MediaWiki:Encyclopedias sites.json") -- настройки энциклопедий на внеших сайтах
local p = {}
local page
function p.getQualifierValues(statement, qualifierName)
local result = {}
if statement then
local q = statement.qualifiers
if q and q[qualifierName] ~= nil then
for _, qualifier in pairs(q[qualifierName]) do
local d = qualifier.datavalue
if d and d.type ~= nil and d.value ~= nil then
if d.type == "wikibase-entityid" then
result[#result + 1] = d.value["id"]
end
end
end
end
end
return result
end
-- deprecated, не используется
local function check_backlink(entity)
local has_backlink
if entity and entity.claims and entity.claims.P1343 then
for _, refs in pairs(entity.claims.P1343) do
if refs.qualifiers and refs.qualifiers.P805 then
for _, ref in pairs(refs.qualifiers.P805) do
if ref.datavalue and ref.datavalue.value and ref.datavalue.value.id then
local ref_id = ref.datavalue.value.id
if ref_id == page.entity.id then has_backlink = true break end
end
end
if has_backlink then break end
end
end
end
return has_backlink
end
function p.get_sitelinks_from_entity(entity, result)
-- populate from entity interwiki
local result = result or {}
local sitelinks = entity.sitelinks
if sitelinks ~= nil then
for sitecode, sitelink in pairs(sitelinks) do
-- mw.log(" "..sitecode.." = "..sitelink.title)
if result[sitecode] == nil then
result[sitecode] = sitelink.title
end
end
end
if result['commons'] == nil then
local commonsCategories = wd.getClaimValues_ranked(entity, 'P373')
for _, commonsCategory in pairs(commonsCategories) do
result['commons'] = 'Category:' .. commonsCategory
break
end
-- если commons нет в интервиках и в P373, то использовать изображение из P18
if result['commons'] == nil then
local commonsCategories = wd.getClaimValues_ranked(entity, 'P18')
for _, commonsCategory in pairs(commonsCategories) do
result['commons'] = 'File:' .. commonsCategory
break
end
end
end
return result
end
function p.get_ext_enc_sites(entity, result)
local result = page.external_sites
for _, site in pairs(encyclopedias_sites_cfg) do
local enc_name, property_id, url_pattern = site.argument, site.property, site.url_pattern
local id
local ids = wd.getClaimValues_ranked(entity, property_id); if ids then id = ids[1] end
-- local ids_sts = entity:getBestStatements(site.property); if ids_sts then id = wd.get_statement_value(ids_sts, wd.DT_EXT_ID, wd.VT_STRING)[1] end
if id and not result[enc_name] then
site.wikilink = util.make_ext_link(url_pattern:gsub("$1", id), enc_name)
site.id = id
result[enc_name] = site
end
end
return result
end
-- загрузка ссылок из элементов в списке указанного свойства
local function get_links_from_items_of_property(entity, property_id)
local targets = wd.getClaimValues_ranked(entity, property_id)
-- mw.logObject(targets, "targets")
if #targets > 0 then
for key, entity_id in pairs(targets) do
if property_id == 'P921' then -- P921 "основная тема произведения"
page.has_topic_property = true
end
local entity_target = wd.get_entity_by_id(entity_id)
if entity_target then
p.populate_data(entity_target)
end
end
end
end
-- загрузка данных из элемента страницы
local is_topic_entity_reached = false
local is_edition_as_topic_reached = false
function p.populate_data(entity)
if not entity or not entity.claims then return end
if wd.has_valid_item_value(entity, "P31", 'Q3331189') and not is_topic_entity_reached and not is_edition_as_topic_reached then -- Q3331189 "версия или издание"
-- Переменные `is_topic_entity_reached` и `is_edition_as_topic_reached` ограничивают глубину подгрузки элементов.
-- Проверка:
-- На странице [[Ветхий Завет (Макарий)]] [[d:Q4350033]] должна показываться ссылка на [[w:Перевод Библии архимандрита Макария]]. При этом элемент является Q3331189 версией Библии.
-- На странице [[ЕЭБЕ/Пешитта]] [[d:Q24922704]] должна показываться ссылка на [[w:Пешитта]]. При этом темой указана "Пешита", которая является Q3331189 версией Библии.
-- Не должно быть перехода на глубокий элемент [[w:Библия]].
local sitelinks_ = p.get_sitelinks_from_entity(entity)
sitelinks_['ruwikisource'] = nil
if next( sitelinks_ ) then
is_edition_as_topic_reached = true
end
end
if wd.has_valid_item_value(entity, "P31", 'Q3331189') and not is_topic_entity_reached and not is_edition_as_topic_reached then -- Q3331189 "версия или издание"
-- загрузка из элементов в списке P629 "является изданием или переводом"
get_links_from_items_of_property(entity, "P629")
elseif wd.is_encyclopedic_article(entity) and entity.claims['P921'] and not is_topic_entity_reached then
-- для энциклопедических, словарных статей и перенаправлений (P31 in [Q10389811, Q13433827, Q1580166, Q1302249])
-- загрузка из элементов в списке P921 "основная тема произведения"
if wd.has_valid_item_value(entity, "P31", 'Q1302249') then -- Q1302249 "перекрёстная ссылка"
page.has_xref_property = true
end
is_topic_entity_reached = true
get_links_from_items_of_property(entity, "P921")
else
-- загрузка из текущего элемента
local label = entity.labels and ((entity.labels.ru and entity.labels.ru.value) or (entity.labels.en and entity.labels.en.value)) or entity.id
table.insert( page.sitelinks, { label = label, id = entity.id, data = p.get_sitelinks_from_entity(entity) } )
page.sitelinks[#page.sitelinks]['data']['wikidata'] = entity.id
page.encyclopedias_ids = p.get_ids_of_described_encs(entity)
p.get_ext_enc_sites(entity)
end
return page
end
-- проход P1343 - списка «описывается в источниках»
function p.get_ids_of_described_encs(entity)
local result = page.encyclopedias_ids
-- local describedByClaim = wd.getClaimValues_ranked( entity, 'P1343' )
local describedByClaim = entity:getBestStatements('P1343') -- P1343 «описывается в источниках»
for _, statement in pairs( describedByClaim ) do
local snak = statement["mainsnak"]
if snak.datavalue.type == wd.VT_ENTITY_ID then
local dictId = snak.datavalue.value["id"]
local qualifiers = p.getQualifierValues(statement, 'P805') -- P805 тема утверждения
if qualifiers == nil or #qualifiers == 0 then
qualifiers = p.getQualifierValues(statement, 'P248') -- P248 утверждается в (deprecated)
end
for _, articleId in pairs(qualifiers) do
for _, enc in pairs(encyclopediasData) do
local dictionaryShortTitle = enc.argument
if enc.id == dictId then
-- todo: enc.id == dictId не работает для элементов отдельных томов, напр. [[d:Q23892934]], ибо их нет в encyclopediasData. Надо см. наличие P361 "часть от" и сверять с ним, если эти лишние запросы вообще нужны.
local dictLinks = result[dictionaryShortTitle]
if dictLinks == nil then
dictLinks = {}
result[dictionaryShortTitle] = dictLinks
end
dictLinks[articleId] = articleId
break
end
end
if page.entity and articleId == page.entity.id then page.has_backlink = true end -- проверка обратной ссылки, используется для категоризации
end
end
end
return result
end
--[[
is_match_pagenames("ЭСБЕ/Пупкин, Иван Васильевич/ДО", "ЭСБЕ/$1/ДО") → "Пупкин, Иван Васильевич"
is_match_pagenames("ЭСБЕ/Пупкин, Иван Васильевич", "ЭСБЕ/$1/ДО") → nil
]]
function p.is_match_pagenames(s_sitelink, s_pattern)
local s_regexp = string.gsub(s_pattern, "%(", "%%(")
s_regexp = string.gsub(s_regexp, "%)", "%%)")
s_regexp = "^" .. string.gsub(s_regexp, "$1", "(.+)") .. "$"
-- mw.log("regexp: "..s_regexp)
return string.match(s_sitelink, s_regexp)
end
--[[
{{#invoke:Другие источники|test_is_match_pagenames|ЭСБЕ/Пупкин, Иван Васильевич/ДО|ЭСБЕ/$1/ДО}} → "Пупкин, Иван Васильевич"
{{#invoke:Другие источники|test_is_match_pagenames|МЭСБЕ/Аконит|МЭСБЕ/$1}} → "Аконит"
{{#invoke:Другие источники|test_is_match_pagenames|ЭСБЕ/Аконит|МЭСБЕ/$1}} → nil
]]
local function test_is_match_pagenames(frame)
local s_sitelink = tostring(frame.args[1])
local s_pattern = tostring(frame.args[2])
local s_title = p.is_match_pagenames(s_sitelink, s_pattern)
if s_title == nil then
return "nil"
else
return '"' .. s_title .. '"'
end
end
local function getDefaultTitle()
for _, enc in pairs(encyclopediasData) do
if enc.project == RUWIKISOURCE and enc.titleDO then
local DO = (enc.default == 'DO')
local titleVT, titleDO = enc.titleVT, enc.titleDO
local nameVT, nameDO = p.is_match_pagenames(page.title, titleVT), p.is_match_pagenames(page.title, titleDO)
if nameDO then
if DO then return titleDO:gsub('$1', nameDO) else return titleVT:gsub('$1', nameDO) end
elseif nameVT then
if DO then return titleDO:gsub('$1', nameVT) else return titleVT:gsub('$1', nameVT) end
end
end
end
return page.title
end
function p.getLink(enc, entityId, isPRS)
if enc.project ~= RUWIKISOURCE then
local entity = wd.get_entity_by_id(entityId)
if not entity then
mw.log("Невозможно загрузить " .. tostring(entityId))
return nil
end
if (entity.sitelinks == nil) or (entity.sitelinks[enc.project] == nil) then
mw.log("В " .. tostring(entityId) .. " нет ссылки на " .. enc.project .. " для " .. enc.argument)
return nil
end
return ':' .. enc.projectCode .. entity.sitelinks[enc.project].title
end
local s_sitelink = mw.wikibase.sitelink(entityId)
if s_sitelink == nil then
return nil
end
local s_primary_pattern = nil
local s_secondary_pattern = nil
if isPRS then
s_primary_pattern = enc.titleDO
s_secondary_pattern = enc.titleVT
else
s_primary_pattern = enc.titleVT
s_secondary_pattern = enc.titleDO
end
-- mw.log(isPRS, s_primary_pattern, s_secondary_pattern, s_sitelink)
if p.is_match_pagenames(s_sitelink, s_primary_pattern) ~= nil then -- ссылка из Викиданных соответствует признаку isPRS
return s_sitelink -- т.к. ссылка получена из Викиданных, поверять её существование не надо, просто её возвращаем
end
local s_article_title
if enc.titleDO then
s_article_title = p.is_match_pagenames(s_sitelink, s_secondary_pattern)
end
if not s_article_title then
-- ссылка не соответствует ни titleDO, ни titleVT
mw.log("ссылка на " .. enc.title .. " нарушает правила именования: " .. s_sitelink)
return s_sitelink
end
local s_link
if enc.id == "Q1970746" then -- для ТСД
s_link = "ТСД/" .. s_article_title
if isPRS then
s_link = s_link .. "/ДО"
end
else
s_link = string.gsub(s_primary_pattern, "$1", s_article_title)
end
if mw.title.new(s_link, 0).exists then
return s_link
else
return s_sitelink
end
end
-- загружает элемент текущей страницы
function p.get_page_entity()
entity = mw.wikibase.getEntity()
if not entity then
local default_title = getDefaultTitle()
entity = mw.wikibase.getEntity(mw.wikibase.getEntityIdForTitle(default_title))
end
return entity
end
-- загружает элемент темы если даны параметры шаблона ВИКИДАННЫЕ или ВИКИПЕДИЯ
function p.get_page_entity_topic_by_template_arg(args)
local topic_id = is(args['ВИКИДАННЫЕ'])
local wp_pagename = is(args['ВИКИПЕДИЯ'])
if topic_id or wp_pagename then
if not topic_id then
local lang, title = string.match(wp_pagename, '^:?([a-z]+):(.+)') -- ссылка формата ':код_языка:Название' в параметрах
if not lang then
lang, title = RU, wp_pagename
end
topic_id = mw.wikibase.getEntityIdForTitle(title, lang .. 'wiki')
end
return wd.get_entity_by_id(topic_id)
end
end
function p.get_links(_page)
page = _page
page.entity = p.get_page_entity()
page.entity_topic_by_template_arg = p.get_page_entity_topic_by_template_arg(page.args) -- элемент темы, указанный в параметрах ВИКИПЕДИЯ/ВИКИДАННЫЕ или в элементе Викиданных
page.has_encyclopedic_article_status_in_wd = wd.is_encyclopedic_article(page.entity)
local entity = page.entity_topic_by_template_arg or page.entity -- загружать ссылки из элемента темы если задан, иначе - из элемента страницы
p.populate_data(entity)
-- mw.logObject(page, "page")
return page
end
-- проверка переменной, возврат её или nil если пустая
function is(var) if (var == '' or var == nil) then return nil else return var end end
return p