Jump to content

Module:Medical cases chart/data: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
Also fill in gaps in cases
Make more easily callable from other modules
Line 1: Line 1:
--- Example usage:
--- Example usage:
--- {{#invoke:Module:Medical cases chart/data|externalData|page name=COVID-19 Cases in Santa Clara County, California.tab|recoveries=hospitalized|cases=totalConfirmedCases}}
--- {{#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"}})
--- =p._externalData({["page name"]="COVID-19 Cases in Santa Clara County, California.tab",recoveries="hospitalized",cases="totalConfirmedCases"})


local p = {}
local p = {}
Line 7: Line 7:
local english = mw.getLanguage("en")
local english = mw.getLanguage("en")


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


function formatChange(previous, current)
local function formatChange(previous, current)
if not previous or previous == 0 then
if not previous or previous == 0 then
return
return
Line 24: Line 24:
end
end


function p.externalData(frame)
function p._externalData(args)
local data = mw.ext.data.get(frame.args["page name"])
local data = mw.ext.data.get(args["page name"])
local dateIndex
local dateIndex
Line 34: Line 34:
local class5Index
local class5Index
for i, field in ipairs(data.schema.fields) do
for i, field in ipairs(data.schema.fields) do
if field.name == "date" or field.name == frame.args.date then
if field.name == "date" or field.name == args.date then
dateIndex = i
dateIndex = i
elseif field.name == "deaths" or field.name == frame.args.deaths then
elseif field.name == "deaths" or field.name == args.deaths then
deathsIndex = i
deathsIndex = i
elseif field.name == "recoveries" or field.name == frame.args.recoveries then
elseif field.name == "recoveries" or field.name == args.recoveries then
recoveriesIndex = i
recoveriesIndex = i
elseif field.name == "cases" or field.name == frame.args.cases then
elseif field.name == "cases" or field.name == args.cases then
casesIndex = i
casesIndex = i
elseif field.name == "class4" or field.name == frame.args.class4 then
elseif field.name == "class4" or field.name == args.class4 then
class4Index = i
class4Index = i
elseif field.name == "class5" or field.name == frame.args.class5 then
elseif field.name == "class5" or field.name == args.class5 then
class5Index = i
class5Index = i
end
end
end
end
assert(dateIndex, "Date field not found.")
assert(dateIndex, "Date field not found.")
assert(deathsIndex or not frame.args.deaths, "Deaths field not found.")
assert(deathsIndex or not args.deaths, "Deaths field not found.")
assert(recoveriesIndex or not frame.args.recoveries, "Recoveries field not found.")
assert(recoveriesIndex or not args.recoveries, "Recoveries field not found.")
assert(casesIndex or not frame.args.cases, "Cases field not found.")
assert(casesIndex or not args.cases, "Cases field not found.")
assert(class4Index or not frame.args.class4, "Class 4 field not found.")
assert(class4Index or not args.class4, "Class 4 field not found.")
assert(class5Index or not frame.args.class5, "Class 5 field not found.")
assert(class5Index or not args.class5, "Class 5 field not found.")
-- Restructure the data as tables with keys.
-- Restructure the data as tables with keys.
Line 130: Line 130:
end
end


function p.externalData(frame)
return p._externalData(frame.args)
end
return p
return p

Revision as of 21:50, 22 April 2020

--- 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({["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")

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

local 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(args)
	local data = mw.ext.data.get(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 == args.date then
			dateIndex = i
		elseif field.name == "deaths" or field.name == args.deaths then
			deathsIndex = i
		elseif field.name == "recoveries" or field.name == args.recoveries then
			recoveriesIndex = i
		elseif field.name == "cases" or field.name == args.cases then
			casesIndex = i
		elseif field.name == "class4" or field.name == args.class4 then
			class4Index = i
		elseif field.name == "class5" or field.name == args.class5 then
			class5Index = i
		end
	end
	assert(dateIndex, "Date field not found.")
	assert(deathsIndex or not args.deaths, "Deaths field not found.")
	assert(recoveriesIndex or not args.recoveries, "Recoveries field not found.")
	assert(casesIndex or not args.cases, "Cases field not found.")
	assert(class4Index or not args.class4, "Class 4 field not found.")
	assert(class5Index or not 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 casesIndex and (prevRecord.cases or prevRecord.assumedCases) and not record.cases then
			record.assumedCases = prevRecord.cases or prevRecord.assumedCases
		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 record.assumedCases or ""),
			tostring(record.class4 or ""),
			tostring(record.class5 or ""),
			record.cases and lang:formatNum(record.cases) or "",
			record.cases and formatChange(prevRecord.cases or prevRecord.assumedCases, record.cases) or "",
			record.deaths and lang:formatNum(record.deaths) or "",
			record.deaths and formatChange(prevRecord.deaths or prevRecord.assumedDeaths, 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

function p.externalData(frame)
	return p._externalData(frame.args)
end
return p