Модуль:Wikidata/chronology: различия между версиями
Перейти к навигации
Перейти к поиску
Admin (обсуждение | вклад) |
/>Oleg Yunakov м (Защитил страницу Модуль:Wikidata/chronology: критический шаблон или модуль: https://ru.wikipedia.org/w/index.php?title=ЭАНМ:Установка_защиты&oldid=117401039#Шаблоны ([Редактирование=администраторы и инженеры] (бессрочно) [Переименование=администраторы и инженеры] (бессрочно))) |
||
Строка 1: | Строка 1: | ||
# | local p = {} -- p stands for package | ||
-- technical functions for takeAdjacentNumbersFromStrings | |||
function isYear(number) -- TODO: settings not hardcoded | |||
return number >= 1800 and number <= 2100 | |||
end | |||
function isAr(number) | |||
return number >= 0 and number < 100 | |||
end | |||
function notTooFar(number1, number2, maximalPeriod) | |||
return number2 - number1 > 0 and number2 - number1 <= maximalPeriod | |||
end | |||
-- takes two strings and returns either two substrings that form the only difference between them or nil | |||
-- considers several possible formats of substrings given in options.formats | |||
function takeAdjacentNumbersFromStrings(options, string1, string2) | |||
if type(string1) == 'string' and type(string2) == 'string' then | |||
local formats = options.formats or '' -- TODO: not repeat with properties | |||
if formats == '' then | |||
formats = 'year, year/ar, year/year' | |||
elseif formats == '-' then | |||
formats = '' | |||
end | |||
local formats = mw.text.split(formats, ', ?') | |||
local line = '!' .. string1 .. '!!' .. string2.. '!' -- -- TODO: without hacks | |||
local maximalPeriod = tonumber(options.maximalPeriod) or 5 -- TODO: here or in main function? | |||
for _, format in pairs(formats) do | |||
if format == 'year' or format == 'number' then | |||
local match = {mw.ustring.match(line, '^(.*%D)(%d+)(%D.*)%1(%d+)%3$')} | |||
if #match > 0 then | |||
local number1 = tonumber(match[2]) | |||
local number2 = tonumber(match[4]) | |||
if notTooFar(number1, number2, maximalPeriod) then | |||
if (format == 'year' and isYear(number1) and isYear(number2)) | |||
or (format == 'number') | |||
then | |||
return {number1, number2} | |||
end | |||
end | |||
end | |||
elseif format == 'year/ar' or format == 'year/year' then | |||
local match = {mw.ustring.match(line, '^(.*%D)((%d+)[-–\/](%d+))(%D.*)%1((%d+)\/(%d+))%5$')} | |||
if #match > 0 then | |||
local period1 = match[2] | |||
local period1_start = tonumber(match[3]) | |||
local period1_end = tonumber(match[4]) | |||
local period2 = match[6] | |||
local period2_start = tonumber(match[7]) | |||
local period2_end = tonumber(match[8]) | |||
if notTooFar(period1_start, period2_start, maximalPeriod) and isYear(period1_start) and isYear(period2_start) | |||
and period2_start - period1_start == period2_end - period1_end | |||
then | |||
if (format == 'year/ar' and isAr(period1_end) and isAr(period2_end)) | |||
or (format == 'year/year' and isYear(period1_end) and isYear(period2_end)) | |||
then | |||
return {period1, period2} | |||
end | |||
end | |||
end | |||
end | |||
end | |||
end | |||
return nil | |||
end | |||
-- takes two Wikidata entities and returns either two strings or nil | |||
-- considers sitelinks and labels in the local langauge and in English | |||
function takeAdjacentNumbersFromEntities(options, entity1, entity2) | |||
if entity1 and entity2 then | |||
local adjacentNumbers = nil | |||
local languageCodes = {mw.getContentLanguage():getCode(), 'en'} | |||
for _, languageCode in pairs(languageCodes) do | |||
local wikiCode = languageCode .. 'wiki' | |||
local sitelink1 = entity1:getSitelink(wikiCode) | |||
local sitelink2 = entity2:getSitelink(wikiCode) | |||
adjacentNumbers = takeAdjacentNumbersFromStrings(options, sitelink1, sitelink2) | |||
if adjacentNumbers then | |||
return adjacentNumbers | |||
end | |||
local label1 = entity1:getLabel(languageCode) -- TODO: get rid of fallback? | |||
local label2 = entity2:getLabel(languageCode) | |||
adjacentNumbers = takeAdjacentNumbersFromStrings(options, label1, label2) | |||
if adjacentNumbers then | |||
return adjacentNumbers | |||
end | |||
end | |||
end | |||
return nil | |||
end | |||
function formatAdjacentSnak(context, options, snak) | |||
local direction = options.direction or '' | |||
if snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.id then | |||
local mainText = nil | |||
local adjacentEntity = mw.wikibase.getEntity(snak.datavalue.value.id) | |||
if direction == 'P155' then -- previous | |||
local adjacentNumbers = takeAdjacentNumbersFromEntities(options, adjacentEntity, options.entity) -- or reverse order | |||
if adjacentNumbers then | |||
mainText = adjacentNumbers[1] -- or 2 | |||
end | |||
elseif direction == 'P156' then -- next | |||
local adjacentNumbers = takeAdjacentNumbersFromEntities(options, options.entity, adjacentEntity) | |||
if adjacentNumbers then | |||
mainText = adjacentNumbers[2] | |||
end | |||
end | |||
-- options should not be changed, as they are reused for P155 and P156 | |||
local optionsCopy = {} | |||
for k, v in pairs(options) do | |||
optionsCopy[k] = v | |||
end | |||
if (not optionsCopy.text) and mainText then | |||
local prefix = options.prefix or '' | |||
local postfix = options.postfix or '' | |||
optionsCopy.text = prefix .. mainText .. postfix | |||
end | |||
local link = context.formatSnak(optionsCopy, snak) | |||
if mainText or (options.formats and options.formats == '-') then | |||
return link | |||
else | |||
return link .. '[[Категория:ЭАНМ:Статьи с несокращаемой хронологией]]' | |||
end | |||
end | |||
return nil | |||
end | |||
-- function is available from outside, see documentation | |||
function p.formatAdjacentProperty(context, options) | |||
if (not context) then error('context not specified'); end; | |||
if (not options) then error('options not specified'); end; | |||
if (not options.entity) then error('options.entity missing'); end; | |||
if options.value == '-' then | |||
return '' | |||
end | |||
if options.value then | |||
return options.value | |||
end | |||
local properties = options.property or '' -- TODO: properties vs. property? | |||
if properties == '' then | |||
properties = 'Q' | |||
elseif properties == '-' then | |||
properties = '' | |||
end | |||
local properties = mw.text.split(properties, ', ?') | |||
local direction = options.direction or '' -- TODO: not repeating inside | |||
local formattedClaims = {} | |||
for _, property in pairs(properties) do | |||
if property == 'Q' then | |||
local claims = context.selectClaims(options, direction) | |||
if claims then | |||
for _, claim in pairs(claims) do | |||
if claim.mainsnak then | |||
local link = formatAdjacentSnak(context, options, claim.mainsnak) | |||
if link and link ~= '' then | |||
local formattedClaim = '<span class="wikidata-claim" data-wikidata-property-id="' .. direction .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. link .. '</span>' | |||
table.insert(formattedClaims, formattedClaim) | |||
end | |||
end | |||
end | |||
end | |||
elseif mw.ustring.match(property, '^P%d+$') then | |||
local claims = context.selectClaims(options, property) | |||
if claims then | |||
for _, claim in pairs(claims) do | |||
if claim.qualifiers and claim.qualifiers[direction] then | |||
for _, snak in pairs(claim.qualifiers[direction]) do | |||
local link = formatAdjacentSnak(context, options, snak) | |||
if link and link ~= '' then | |||
local formattedClaim = '<span class="wikidata-claim" data-wikidata-property-id="' .. property .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. link .. '</span>' | |||
table.insert(formattedClaims, formattedClaim) | |||
end | |||
end | |||
end | |||
end | |||
end | |||
end | |||
end | |||
-- from wikidata.selectClaims | |||
if options.limit and options.limit ~= '' and options.limit ~= '-' then | |||
local limit = tonumber(options.limit, 10); | |||
while #formattedClaims > limit do | |||
table.remove(formattedClaims); | |||
end | |||
end | |||
-- from wikidata.formatPropertyDefault | |||
local out = mw.text.listToText(formattedClaims, options.separator, options.conjunction) | |||
if out ~= '' then | |||
if options.before then | |||
out = options.before .. out | |||
end | |||
if options.after then | |||
out = out .. options.after | |||
end | |||
end | |||
return out | |||
end | |||
return p |
Версия от 21:22, 24 октября 2021
Для документации этого модуля может быть создана страница Модуль:Wikidata/chronology/doc
local p = {} -- p stands for package -- technical functions for takeAdjacentNumbersFromStrings function isYear(number) -- TODO: settings not hardcoded return number >= 1800 and number <= 2100 end function isAr(number) return number >= 0 and number < 100 end function notTooFar(number1, number2, maximalPeriod) return number2 - number1 > 0 and number2 - number1 <= maximalPeriod end -- takes two strings and returns either two substrings that form the only difference between them or nil -- considers several possible formats of substrings given in options.formats function takeAdjacentNumbersFromStrings(options, string1, string2) if type(string1) == 'string' and type(string2) == 'string' then local formats = options.formats or '' -- TODO: not repeat with properties if formats == '' then formats = 'year, year/ar, year/year' elseif formats == '-' then formats = '' end local formats = mw.text.split(formats, ', ?') local line = '!' .. string1 .. '!!' .. string2.. '!' -- -- TODO: without hacks local maximalPeriod = tonumber(options.maximalPeriod) or 5 -- TODO: here or in main function? for _, format in pairs(formats) do if format == 'year' or format == 'number' then local match = {mw.ustring.match(line, '^(.*%D)(%d+)(%D.*)%1(%d+)%3$')} if #match > 0 then local number1 = tonumber(match[2]) local number2 = tonumber(match[4]) if notTooFar(number1, number2, maximalPeriod) then if (format == 'year' and isYear(number1) and isYear(number2)) or (format == 'number') then return {number1, number2} end end end elseif format == 'year/ar' or format == 'year/year' then local match = {mw.ustring.match(line, '^(.*%D)((%d+)[-–\/](%d+))(%D.*)%1((%d+)\/(%d+))%5$')} if #match > 0 then local period1 = match[2] local period1_start = tonumber(match[3]) local period1_end = tonumber(match[4]) local period2 = match[6] local period2_start = tonumber(match[7]) local period2_end = tonumber(match[8]) if notTooFar(period1_start, period2_start, maximalPeriod) and isYear(period1_start) and isYear(period2_start) and period2_start - period1_start == period2_end - period1_end then if (format == 'year/ar' and isAr(period1_end) and isAr(period2_end)) or (format == 'year/year' and isYear(period1_end) and isYear(period2_end)) then return {period1, period2} end end end end end end return nil end -- takes two Wikidata entities and returns either two strings or nil -- considers sitelinks and labels in the local langauge and in English function takeAdjacentNumbersFromEntities(options, entity1, entity2) if entity1 and entity2 then local adjacentNumbers = nil local languageCodes = {mw.getContentLanguage():getCode(), 'en'} for _, languageCode in pairs(languageCodes) do local wikiCode = languageCode .. 'wiki' local sitelink1 = entity1:getSitelink(wikiCode) local sitelink2 = entity2:getSitelink(wikiCode) adjacentNumbers = takeAdjacentNumbersFromStrings(options, sitelink1, sitelink2) if adjacentNumbers then return adjacentNumbers end local label1 = entity1:getLabel(languageCode) -- TODO: get rid of fallback? local label2 = entity2:getLabel(languageCode) adjacentNumbers = takeAdjacentNumbersFromStrings(options, label1, label2) if adjacentNumbers then return adjacentNumbers end end end return nil end function formatAdjacentSnak(context, options, snak) local direction = options.direction or '' if snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.id then local mainText = nil local adjacentEntity = mw.wikibase.getEntity(snak.datavalue.value.id) if direction == 'P155' then -- previous local adjacentNumbers = takeAdjacentNumbersFromEntities(options, adjacentEntity, options.entity) -- or reverse order if adjacentNumbers then mainText = adjacentNumbers[1] -- or 2 end elseif direction == 'P156' then -- next local adjacentNumbers = takeAdjacentNumbersFromEntities(options, options.entity, adjacentEntity) if adjacentNumbers then mainText = adjacentNumbers[2] end end -- options should not be changed, as they are reused for P155 and P156 local optionsCopy = {} for k, v in pairs(options) do optionsCopy[k] = v end if (not optionsCopy.text) and mainText then local prefix = options.prefix or '' local postfix = options.postfix or '' optionsCopy.text = prefix .. mainText .. postfix end local link = context.formatSnak(optionsCopy, snak) if mainText or (options.formats and options.formats == '-') then return link else return link .. '[[Категория:ЭАНМ:Статьи с несокращаемой хронологией]]' end end return nil end -- function is available from outside, see documentation function p.formatAdjacentProperty(context, options) if (not context) then error('context not specified'); end; if (not options) then error('options not specified'); end; if (not options.entity) then error('options.entity missing'); end; if options.value == '-' then return '' end if options.value then return options.value end local properties = options.property or '' -- TODO: properties vs. property? if properties == '' then properties = 'Q' elseif properties == '-' then properties = '' end local properties = mw.text.split(properties, ', ?') local direction = options.direction or '' -- TODO: not repeating inside local formattedClaims = {} for _, property in pairs(properties) do if property == 'Q' then local claims = context.selectClaims(options, direction) if claims then for _, claim in pairs(claims) do if claim.mainsnak then local link = formatAdjacentSnak(context, options, claim.mainsnak) if link and link ~= '' then local formattedClaim = '<span class="wikidata-claim" data-wikidata-property-id="' .. direction .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. link .. '</span>' table.insert(formattedClaims, formattedClaim) end end end end elseif mw.ustring.match(property, '^P%d+$') then local claims = context.selectClaims(options, property) if claims then for _, claim in pairs(claims) do if claim.qualifiers and claim.qualifiers[direction] then for _, snak in pairs(claim.qualifiers[direction]) do local link = formatAdjacentSnak(context, options, snak) if link and link ~= '' then local formattedClaim = '<span class="wikidata-claim" data-wikidata-property-id="' .. property .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. link .. '</span>' table.insert(formattedClaims, formattedClaim) end end end end end end end -- from wikidata.selectClaims if options.limit and options.limit ~= '' and options.limit ~= '-' then local limit = tonumber(options.limit, 10); while #formattedClaims > limit do table.remove(formattedClaims); end end -- from wikidata.formatPropertyDefault local out = mw.text.listToText(formattedClaims, options.separator, options.conjunction) if out ~= '' then if options.before then out = options.before .. out end if options.after then out = out .. options.after end end return out end return p