<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://stockhub.co/index.php?action=history&amp;feed=atom&amp;title=Module%3AMedical_cases_chart%2Fdata</id>
	<title>Module:Medical cases chart/data - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://stockhub.co/index.php?action=history&amp;feed=atom&amp;title=Module%3AMedical_cases_chart%2Fdata"/>
	<link rel="alternate" type="text/html" href="https://stockhub.co/index.php?title=Module:Medical_cases_chart/data&amp;action=history"/>
	<updated>2026-05-24T10:14:35Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://stockhub.co/index.php?title=Module:Medical_cases_chart/data&amp;diff=143948&amp;oldid=prev</id>
		<title>imported&gt;Mxn: Fixed off-by-one error</title>
		<link rel="alternate" type="text/html" href="https://stockhub.co/index.php?title=Module:Medical_cases_chart/data&amp;diff=143948&amp;oldid=prev"/>
		<updated>2021-09-01T05:04:01Z</updated>

		<summary type="html">&lt;p&gt;Fixed off-by-one error&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;--- Example usage:&lt;br /&gt;
--- {{#invoke:Medical cases chart/data|externalData|page=COVID-19 cases in Santa Clara County, California.tab|recoveries=hospitalized|cases=totalConfirmedCases}}&lt;br /&gt;
--- =p._externalData({datapage=&amp;quot;COVID-19 cases in Santa Clara County, California.tab&amp;quot;,datarecoveries=&amp;quot;hospitalized&amp;quot;,datacases=&amp;quot;totalConfirmedCases&amp;quot;})&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
local lang = mw.getContentLanguage()&lt;br /&gt;
local english = mw.getLanguage(&amp;quot;en&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
local function round(x)&lt;br /&gt;
	return (math.modf(x + (x &amp;lt; 0 and -0.5 or 0.5)))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function formatChange(previous, current)&lt;br /&gt;
	if not previous or previous == 0 then&lt;br /&gt;
		return&lt;br /&gt;
	end&lt;br /&gt;
	if previous == current then&lt;br /&gt;
		return &amp;quot;=&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local change = current / previous * 100 - 100&lt;br /&gt;
	local sign = change &amp;lt; 0 and &amp;quot;−&amp;quot; or &amp;quot;+&amp;quot;&lt;br /&gt;
	&lt;br /&gt;
	if (math.abs(change)) &amp;gt;= 10 then&lt;br /&gt;
		return mw.ustring.format(&amp;quot;%s%.0f%%&amp;quot;, sign, (math.abs(change)))&lt;br /&gt;
	end&lt;br /&gt;
	if (math.abs(change)) &amp;lt; 1 then&lt;br /&gt;
		return mw.ustring.format(&amp;quot;%s%.3f%%&amp;quot;, sign, (math.abs(change)))&lt;br /&gt;
	end&lt;br /&gt;
	return mw.ustring.format(&amp;quot;%s%.2f%%&amp;quot;, sign, (math.abs(change)))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._externalData(args)&lt;br /&gt;
	local data = mw.ext.data.get(args.datapage)&lt;br /&gt;
	&lt;br /&gt;
	local dateIndex&lt;br /&gt;
	local deathsIndex&lt;br /&gt;
	local recoveriesIndex&lt;br /&gt;
	local casesIndex&lt;br /&gt;
	local class4Index&lt;br /&gt;
	local class5Index&lt;br /&gt;
	for i, field in ipairs(data.schema.fields) do&lt;br /&gt;
		if field.name == &amp;quot;date&amp;quot; or field.name == args.datadate then&lt;br /&gt;
			dateIndex = i&lt;br /&gt;
		elseif field.name == &amp;quot;deaths&amp;quot; or field.name == args.datadeaths then&lt;br /&gt;
			deathsIndex = i&lt;br /&gt;
		elseif field.name == &amp;quot;recoveries&amp;quot; or field.name == args.datarecoveries then&lt;br /&gt;
			recoveriesIndex = i&lt;br /&gt;
		elseif field.name == &amp;quot;cases&amp;quot; or field.name == args.datacases then&lt;br /&gt;
			casesIndex = i&lt;br /&gt;
		elseif field.name == &amp;quot;class4&amp;quot; or field.name == args.dataclass4 then&lt;br /&gt;
			class4Index = i&lt;br /&gt;
		elseif field.name == &amp;quot;class5&amp;quot; or field.name == args.dataclass5 then&lt;br /&gt;
			class5Index = i&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	assert(dateIndex, &amp;quot;Date field not found.&amp;quot;)&lt;br /&gt;
	assert(deathsIndex or not args.datadeaths, &amp;quot;Deaths field not found.&amp;quot;)&lt;br /&gt;
	assert(recoveriesIndex or not args.datarecoveries, &amp;quot;Recoveries field not found.&amp;quot;)&lt;br /&gt;
	assert(casesIndex or not args.datacases, &amp;quot;Cases field not found.&amp;quot;)&lt;br /&gt;
	assert(class4Index or not args.dataclass4, &amp;quot;Class 4 field not found.&amp;quot;)&lt;br /&gt;
	assert(class5Index or not args.dataclass5, &amp;quot;Class 5 field not found.&amp;quot;)&lt;br /&gt;
	&lt;br /&gt;
	-- Restructure the data as tables with keys.&lt;br /&gt;
	local records = {}&lt;br /&gt;
	for i, row in ipairs(data.data) do&lt;br /&gt;
		local record = {&lt;br /&gt;
			date = row[dateIndex],&lt;br /&gt;
			deaths = deathsIndex and row[deathsIndex],&lt;br /&gt;
			recoveries = recoveriesIndex and row[recoveriesIndex],&lt;br /&gt;
			cases = casesIndex and row[casesIndex],&lt;br /&gt;
			class4 = class4Index and row[class4Index],&lt;br /&gt;
			class5 = class5Index and row[class5Index],&lt;br /&gt;
			options = {},&lt;br /&gt;
			streak = 1,&lt;br /&gt;
		}&lt;br /&gt;
		local prevRecord = records[#records] or {}&lt;br /&gt;
		if casesIndex and not prevRecord.cases and record.cases and record.cases &amp;gt; 0 then&lt;br /&gt;
			record.options.firstright1 = &amp;quot;y&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		if deathsIndex and prevRecord.deaths == 0 and record.deaths and record.deaths &amp;gt; 0 then&lt;br /&gt;
			record.options.firstright2 = &amp;quot;y&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		if deathsIndex and (prevRecord.deaths or prevRecord.assumedDeaths) and not record.deaths then&lt;br /&gt;
			record.assumedDeaths = prevRecord.deaths or prevRecord.assumedDeaths&lt;br /&gt;
		end&lt;br /&gt;
		if casesIndex and (prevRecord.cases or prevRecord.assumedCases) and not record.cases then&lt;br /&gt;
			record.assumedCases = prevRecord.cases or prevRecord.assumedCases&lt;br /&gt;
		end&lt;br /&gt;
		if record.deaths == prevRecord.deaths&lt;br /&gt;
			and record.recoveries == prevRecord.recoveries&lt;br /&gt;
			and record.cases == prevRecord.cases&lt;br /&gt;
			and record.class4 == prevRecord.class4&lt;br /&gt;
			and record.class5 == prevRecord.class5 then&lt;br /&gt;
			record.streak = prevRecord.streak + 1&lt;br /&gt;
		end&lt;br /&gt;
		table.insert(records, record)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Collapse streaks of identical data.&lt;br /&gt;
	for i = #records, 1, -1 do&lt;br /&gt;
		local record = records[i]&lt;br /&gt;
		if i &amp;lt; #records and record.streak &amp;gt; 3 then&lt;br /&gt;
			for j = i, i - record.streak + 3, -1 do&lt;br /&gt;
				table.remove(records, j)&lt;br /&gt;
			end&lt;br /&gt;
			i = i - record.streak + 2&lt;br /&gt;
			record = records[i]&lt;br /&gt;
			record.options.collapse = &amp;quot;y&amp;quot;&lt;br /&gt;
			record.options.id = english:formatDate(&amp;quot;M&amp;quot;, record.date):lower()&lt;br /&gt;
			record.date = nil&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Stringify the data.&lt;br /&gt;
	local rows = {}&lt;br /&gt;
	for i, record in ipairs(records) do&lt;br /&gt;
		local prevRecord = records[i - 1] or {}&lt;br /&gt;
		local row = {&lt;br /&gt;
			record.date or &amp;quot;&amp;quot;,&lt;br /&gt;
			tostring(record.deaths or record.assumedDeaths or &amp;quot;&amp;quot;),&lt;br /&gt;
			tostring(record.recoveries or &amp;quot;&amp;quot;),&lt;br /&gt;
			tostring(record.cases or record.assumedCases or &amp;quot;&amp;quot;),&lt;br /&gt;
			tostring(record.class4 or &amp;quot;&amp;quot;),&lt;br /&gt;
			tostring(record.class5 or &amp;quot;&amp;quot;),&lt;br /&gt;
			record.cases and lang:formatNum(record.cases) or &amp;quot;&amp;quot;,&lt;br /&gt;
			record.cases and formatChange(prevRecord.cases or prevRecord.assumedCases, record.cases) or &amp;quot;&amp;quot;,&lt;br /&gt;
			record.deaths and lang:formatNum(record.deaths) or &amp;quot;&amp;quot;,&lt;br /&gt;
			record.deaths and formatChange(prevRecord.deaths or prevRecord.assumedDeaths, record.deaths) or &amp;quot;&amp;quot;,&lt;br /&gt;
		}&lt;br /&gt;
		for k, v in pairs(record.options) do&lt;br /&gt;
			table.insert(row, string.format(&amp;quot;%s=%s&amp;quot;, k, v))&lt;br /&gt;
		end&lt;br /&gt;
		table.insert(rows, table.concat(row, &amp;quot;;&amp;quot;))&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat(rows, &amp;quot;\n&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.externalData(frame)&lt;br /&gt;
	local args = {}&lt;br /&gt;
	for k,v in pairs(frame.args) do&lt;br /&gt;
		if (v or &amp;#039;&amp;#039;) ~= &amp;#039;&amp;#039; then&lt;br /&gt;
			args[&amp;#039;data&amp;#039;..k] = v&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return p._externalData(args)&lt;br /&gt;
end&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>imported&gt;Mxn</name></author>
	</entry>
</feed>