Jump to content

Module:Medical cases chart/data: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
Make more easily callable from other modules
rename "page name" to page to make calling it simpler, prepend arguments with "data" to avoid collisions with arguments for Module:Medical cases chart
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=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"})
--- =p._externalData({datapage="COVID-19 Cases in Santa Clara County, California.tab",datarecoveries="hospitalized",datacases="totalConfirmedCases"})


local p = {}
local p = {}
Line 25: Line 25:


function p._externalData(args)
function p._externalData(args)
local data = mw.ext.data.get(args["page name"])
local data = mw.ext.data.get(args.datapage)
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 == args.date then
if field.name == "date" or field.name == args.datadate then
dateIndex = i
dateIndex = i
elseif field.name == "deaths" or field.name == args.deaths then
elseif field.name == "deaths" or field.name == args.datadeaths then
deathsIndex = i
deathsIndex = i
elseif field.name == "recoveries" or field.name == args.recoveries then
elseif field.name == "recoveries" or field.name == args.datarecoveries then
recoveriesIndex = i
recoveriesIndex = i
elseif field.name == "cases" or field.name == args.cases then
elseif field.name == "cases" or field.name == args.datacases then
casesIndex = i
casesIndex = i
elseif field.name == "class4" or field.name == args.class4 then
elseif field.name == "class4" or field.name == args.dataclass4 then
class4Index = i
class4Index = i
elseif field.name == "class5" or field.name == args.class5 then
elseif field.name == "class5" or field.name == args.dataclass5 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 args.deaths, "Deaths field not found.")
assert(deathsIndex or not args.datadeaths, "Deaths field not found.")
assert(recoveriesIndex or not args.recoveries, "Recoveries field not found.")
assert(recoveriesIndex or not args.datarecoveries, "Recoveries field not found.")
assert(casesIndex or not args.cases, "Cases field not found.")
assert(casesIndex or not args.datacases, "Cases field not found.")
assert(class4Index or not args.class4, "Class 4 field not found.")
assert(class4Index or not args.dataclass4, "Class 4 field not found.")
assert(class5Index or not args.class5, "Class 5 field not found.")
assert(class5Index or not args.dataclass5, "Class 5 field not found.")
-- Restructure the data as tables with keys.
-- Restructure the data as tables with keys.
Line 131: Line 131:


function p.externalData(frame)
function p.externalData(frame)
local args = {}
return p._externalData(frame.args)
for k,v in pairs(frame.args) do
if (v or '') ~= '' then
args['data'..k] = v
end
end
return p._externalData(args)
end
end
return p
return p

Revision as of 22:00, 22 April 2020

--- Example usage:
--- {{#invoke:Module:Medical cases chart/data|externalData|page=COVID-19 Cases in Santa Clara County, California.tab|recoveries=hospitalized|cases=totalConfirmedCases}}
--- =p._externalData({datapage="COVID-19 Cases in Santa Clara County, California.tab",datarecoveries="hospitalized",datacases="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.datapage)
	
	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.datadate then
			dateIndex = i
		elseif field.name == "deaths" or field.name == args.datadeaths then
			deathsIndex = i
		elseif field.name == "recoveries" or field.name == args.datarecoveries then
			recoveriesIndex = i
		elseif field.name == "cases" or field.name == args.datacases then
			casesIndex = i
		elseif field.name == "class4" or field.name == args.dataclass4 then
			class4Index = i
		elseif field.name == "class5" or field.name == args.dataclass5 then
			class5Index = i
		end
	end
	assert(dateIndex, "Date field not found.")
	assert(deathsIndex or not args.datadeaths, "Deaths field not found.")
	assert(recoveriesIndex or not args.datarecoveries, "Recoveries field not found.")
	assert(casesIndex or not args.datacases, "Cases field not found.")
	assert(class4Index or not args.dataclass4, "Class 4 field not found.")
	assert(class5Index or not args.dataclass5, "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)
	local args = {}
	for k,v in pairs(frame.args) do
		if (v or '') ~= '' then
			args['data'..k] = v
		end
	end
	return p._externalData(args)
end
return p