Jump to content

Module:Medical cases chart/data

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Mxn (talk | contribs) at 21:54, 21 April 2020 (Fill in gaps in deaths, which can be confusing since a gap in deaths causes the whole stacked bar to slide to the left). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
--- Example usage:
--- {{#invoke:Module:Medical cases chart/data|externalData|page name=COVID-19 Cases in Santa Clara County, California.tab|recoveries=hospitalized|cases=totalConfirmedCases}}
--- =p.externalData(mw.getCurrentFrame():newChild{args={["page name"]="COVID-19 Cases in Santa Clara County, California.tab",recoveries="hospitalized",cases="totalConfirmedCases"}})

local p = {}
local lang = mw.getContentLanguage()
local english = mw.getLanguage("en")

function round(x)
	return (math.modf(x + (x < 0 and -0.5 or 0.5)))
end

function formatChange(previous, current)
	if not previous or previous == 0 then
		return
	end
	if previous == current then
		return "="
	end
	
	local change = current / previous * 100 - 100
	local sign = change < 0 and "−" or "+"
	return mw.ustring.format("%s%s%%", sign, lang:formatNum(round(math.abs(change))))
end

function p.externalData(frame)
	local data = mw.ext.data.get(frame.args["page name"])
	
	local dateIndex
	local deathsIndex
	local recoveriesIndex
	local casesIndex
	local class4Index
	local class5Index
	for i, field in ipairs(data.schema.fields) do
		if field.name == "date" or field.name == frame.args.date then
			dateIndex = i
		elseif field.name == "deaths" or field.name == frame.args.deaths then
			deathsIndex = i
		elseif field.name == "recoveries" or field.name == frame.args.recoveries then
			recoveriesIndex = i
		elseif field.name == "cases" or field.name == frame.args.cases then
			casesIndex = i
		elseif field.name == "class4" or field.name == frame.args.class4 then
			class4Index = i
		elseif field.name == "class5" or field.name == frame.args.class5 then
			class5Index = i
		end
	end
	assert(dateIndex, "Date field not found.")
	assert(deathsIndex or not frame.args.deaths, "Deaths field not found.")
	assert(recoveriesIndex or not frame.args.recoveries, "Recoveries field not found.")
	assert(casesIndex or not frame.args.cases, "Cases field not found.")
	assert(class4Index or not frame.args.class4, "Class 4 field not found.")
	assert(class5Index or not frame.args.class5, "Class 5 field not found.")
	
	-- Restructure the data as tables with keys.
	local records = {}
	for i, row in ipairs(data.data) do
		local record = {
			date = row[dateIndex],
			deaths = deathsIndex and row[deathsIndex],
			recoveries = recoveriesIndex and row[recoveriesIndex],
			cases = casesIndex and row[casesIndex],
			class4 = class4Index and row[class4Index],
			class5 = class5Index and row[class5Index],
			options = {},
			streak = 1,
		}
		local prevRecord = records[#records] or {}
		if casesIndex and not prevRecord.cases and record.cases > 0 then
			record.options.firstright1 = "y"
		end
		if deathsIndex and prevRecord.deaths == 0 and record.deaths > 0 then
			record.options.firstright2 = "y"
		end
		if deathsIndex and (prevRecord.deaths or prevRecord.assumedDeaths) and not record.deaths then
			record.assumedDeaths = prevRecord.deaths or prevRecord.assumedDeaths
		end
		if record.deaths == prevRecord.deaths
			and record.recoveries == prevRecord.recoveries
			and record.cases == prevRecord.cases
			and record.class4 == prevRecord.class4
			and record.class5 == prevRecord.class5 then
			record.streak = prevRecord.streak + 1
		end
		table.insert(records, record)
	end
	
	-- Collapse streaks of identical data.
	for i = #records, 1, -1 do
		local record = records[i]
		if record.streak > 3 then
			for j = i, i - record.streak + 3, -1 do
				table.remove(records, j)
			end
			i = i - record.streak + 2
			record = records[i]
			record.options.collapse = "y"
			record.options.id = english:formatDate("M", record.date):lower()
			record.date = nil
		end
	end
	
	-- Stringify the data.
	local rows = {}
	for i, record in ipairs(records) do
		local prevRecord = records[i - 1] or {}
		local row = {
			record.date or "",
			tostring(record.deaths or record.assumedDeaths or ""),
			tostring(record.recoveries or ""),
			tostring(record.cases or ""),
			tostring(record.class4 or ""),
			tostring(record.class5 or ""),
			record.cases and lang:formatNum(record.cases) or "",
			record.cases and formatChange(prevRecord.cases, record.cases) or "",
			record.deaths and lang:formatNum(record.deaths) or "",
			record.deaths and formatChange(prevRecord.deaths, record.deaths) or "",
		}
		for k, v in pairs(record.options) do
			table.insert(row, string.format("%s=%s", k, v))
		end
		table.insert(rows, table.concat(row, ";"))
	end
	return table.concat(rows, "\n")
end

return p