Перейти к содержимому страницы
Открыть меню
Переключить меню настроек
Открыть персональное меню
Вы не представились системе
Ваш IP-адрес будет виден всем, если вы внесёте какие-либо изменения.

Модуль:WikidataChart

Материал из вики «Вече»
Версия от 15:49, 8 июня 2026; wp>ru.wikipedia (Импорт из русской Википедии, версия 153461497)
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)

Для документации этого модуля может быть создана страница Модуль: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