Module:WikidataChart

local graph = require("Module:Graph")

local p = {}

function p.plot(frame) local property = frame.args[1] or error("Wikidata property to chart required") local xQualifier = frame.args[2] or error("Wikidata qualifier for x axis required") local yIds = mw.text.split(frame.args["pageIds"] or "", ",", true) local xStart = frame.args["xStart"] local xEnd = frame.args["xEnd"] local precision = tonumber(frame.args["precision"]) -- Collect data 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.en or labels.de or {}).value or id

local property = entity.claims[property] 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 xStart or x >= xStart) and (not xEnd or string.sub(x, 1, #xEnd) <= xEnd) 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 not series.points[x] then series.points[x] = {} end series.points[x][seriesIdx] = y				end end end end

-- Sort x values local xValues = {} for k in pairs(series.points) do table.insert(xValues, k) end table.sort(xValues)

local chartArgs = {		type = "line", xType = "date", xAxisTitle = mw.wikibase.label(xQualifier), x = table.concat(xValues, ","), yType = "number", yAxisTitle = mw.wikibase.label(property) }	-- Set legends / series titles for seriesIdx, caption in ipairs(series.captions) do chartArgs["y" .. seriesIdx] = "" chartArgs["y" .. seriesIdx .. "Title"] = caption end -- Set values 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 -- Remove separators at the beginning for seriesIdx, _ in ipairs(series.captions) do chartArgs["y" .. seriesIdx] = mw.ustring.sub(chartArgs["y" .. seriesIdx], 2) end -- Transfer diagram parameters (all parameters that start with chart_ are transferred directly to the Graph module without a prefix) for k, v in pairs(frame.args) do		local chartParam = string.match(k, "^chart_(.+)") if chartParam then chartArgs[chartParam] = v end end

return graph.chart({ args = chartArgs }) end

function p.plotWrapper(frame) return p.plot(frame:getParent) 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