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 10:34, 21 April 2020. The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
--- Example usage:
--- =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 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 ""),
			tostring(record.recoveries or ""),
			tostring(record.cases or ""),
			tostring(record.class4 or ""),
			tostring(record.class5 or ""),
			record.cases > 0 and lang:formatNum(record.cases) or "",
			record.cases > 0 and formatChange(prevRecord.cases, record.cases) or "",
			record.deaths > 0 and lang:formatNum(record.deaths) or "",
			record.deaths > 0 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