Модуль:WikidataChart
Материал из вики «Вече»
Дополнительные действия
Для документации этого модуля может быть создана страница Модуль:WikidataChart/doc
local wikibase = require("Модуль:Wikibase")
-- на основе [[en:Module:Wikidata#L-30]], странно что нет в общем модуле
local defaultFormat = {
[0] = "Y", -- precision: billion years
[1] = "Y", -- precision: hundred million years
[2] = "Y", -- precision: ten million years
[3] = "Y", -- precision: million years
[4] = "Y", -- precision: hundred thousand years
[5] = "Y", -- precision: ten thousand years
[6] = "Y", -- precision: millennium
[7] = "Y", -- precision: century
[8] = "Y", -- precision: decade
[9] = "Y", -- precision: year
[10] = "M Y", -- precision: month
[11] = "j F Y", -- precision: day
[12] = "j F Y ga", -- precision: hour
[13] = "j F Y g:ia", -- precision: minute
[14] = "j F Y g:i:sa" -- precision: second
}
local p = {}
function p.plotWrapper(frame)
frame = frame:getParent()
local property = frame.args[1] or error("Wikidata property to chart required")
local xQualifier = frame.args[2] or "P585"
local yIds = mw.text.split(frame.args["pageIds"] or "", ",", true) or {mw.wikibase.getEntityIdForCurrentPage()}
local xMin = frame.args["xMin"]
local xMax = frame.args["xMax"]
local yMin = tonumber(frame.args["yMin"]) or -math.huge
local yMax = tonumber(frame.args["yMax"]) or math.huge
local precision = tonumber(frame.args["precision"])
local chart = frame.args["chart"] or error("Commons chart required")
local invert = frame.args["invert"]
local zoom = tonumber(frame.args["zoom"])
-- собрать данные
local series = { captions = {}, points = {} }
for seriesIdx, id in ipairs(yIds) do
if id == "" then id = nil end
local entity = mw.wikibase.getEntity(id)
local labels = entity.labels or {}
series.captions[seriesIdx] = (labels.ru or labels.en or {}).value or id
local property = entity.claims[property]
if property == nil or #property <= 3 then return "[[Категория:Википедия:Мало точек для графика]]" end
for _, item in ipairs(property) do
if item.qualifiers and item.qualifiers[xQualifier] and item.qualifiers[xQualifier][1] then
local qualifier = item.qualifiers[xQualifier][1]
if qualifier.snaktype ~= "value" or qualifier.datatype ~= "time" then
error("'xQualifier' parameter must be a time")
end
local x = applyPrecision(mw.text.trim(qualifier.datavalue.value.time, "+"), precision)
if (not xMin or x >= xMin) and (not xMax or string.sub(x, 1, #xMax) <= xMax) and qualifier.datavalue.value.precision >= (precision or 0) then
local mainsnak = item.mainsnak
if mainsnak.snaktype ~= "value" or mainsnak.datatype ~= "quantity" then
error("'property' parameter must be numeric")
end
local y = tonumber(mainsnak.datavalue.value.amount)
if y >= yMin and y <= yMax then
if not series.points[x] then series.points[x] = {} end
series.points[x][seriesIdx] = invert and -y or y
end
end
end
end
end
-- сортируем значения х и убираем дубли
local xValues = {}
for k in pairs(series.points) do table.insert(xValues, k) end
table.sort(xValues)
local xAxisFormat = frame.args["xAxisFormat"] or defaultFormat[precision or 9]
local xValuesFormated = {}
local seen = {}
for index, k in ipairs(xValues) do
local formatted = mw.getContentLanguage():formatDate(xAxisFormat, k)
if seen[formatted] then
table.remove(xValues, index)
else
table.insert(xValuesFormated, formatted)
seen[formatted] = true
end
end
local chartArgs =
{
["xType"] = (precision or 0) <= 9 and "number" or "date",
["x"] = table.concat(xValuesFormated, ","),
["yType"] = "number",
}
-- Legenden/Reihentitel setzen
for seriesIdx, caption in ipairs(series.captions) do
chartArgs["y" .. seriesIdx] = ""
chartArgs["y" .. seriesIdx .. "Title"] = caption
end
-- установить значения
local seriesCount = #series.captions
for _, x in ipairs(xValues) do
yValues = series.points[x]
for seriesIdx = 1, seriesCount do
chartArgs["y" .. seriesIdx] = chartArgs["y" .. seriesIdx] .. "," .. (yValues[seriesIdx] or "")
end
end
-- удалить разделители в начале
for seriesIdx, _ in ipairs(series.captions) do
chartArgs["y" .. seriesIdx] = mw.ustring.sub(chartArgs["y" .. seriesIdx], 2)
end
local chartArgsString = ""
for k, v in pairs(chartArgs) do
chartArgsString = chartArgsString .. "|arg:" .. k .. "=" .. v
end
local chart = frame:preprocess( string.format("{{#chart:%s.chart%s}}", chart, chartArgsString) )
if zoom ~= nil then
chart = string.format("<div style='transform:scale(%s);transform-origin:0 0;margin-bottom:%spx;width:fit-content>%s</div>", zoom / 100, 616 * (zoom / 100 - 1), chart)
end
return chart
end
function applyPrecision(date, precision)
if not precision then precision = math.huge end
local _, _, year, month, day, hour, minute, second, timezone = string.find(date, "^(.?%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)(.+)$")
if precision < 14 then second = "00" end
if precision < 13 then minute = "00" end
if precision < 12 then hour = "00" end
if precision < 11 or day == "00" then day = "01" end
if precision < 10 or month == "00" then month = "01" end
return year .. "-" .. month .. "-" .. day .. "T" .. hour .. ":" .. minute .. ":" .. second .. timezone
end
return p