added more case data, table styling
This commit is contained in:
parent
a4afdeb8a6
commit
c08e7373e2
35
generate.js
35
generate.js
@ -235,8 +235,8 @@ const processRecords = async () => {
|
||||
return Math.pow((hi / lo), pow) - 1;
|
||||
};
|
||||
|
||||
const getGrowthRate = (record) => {
|
||||
return calcGrowthRate(record.timeSeriesDaily, record.timeSeriesDaily.length - 1, 7);
|
||||
const getGrowthRate = (ts) => {
|
||||
return calcGrowthRate(ts, ts.length - 1, 7);
|
||||
};
|
||||
|
||||
const normalizeRecord = (record) => {
|
||||
@ -404,6 +404,7 @@ const processRecords = async () => {
|
||||
}
|
||||
|
||||
record.cases = {
|
||||
total: confirmedCases.timeSeriesDaily[confirmedCases.timeSeriesDaily.length - 1].value,
|
||||
timeSeriesDaily: confirmedCases.timeSeriesDaily,
|
||||
timeSeriesMonthly: confirmedCases.timeSeriesMonthly,
|
||||
rollingAverageDaily: confirmedCases.rollingAverageDaily,
|
||||
@ -428,7 +429,9 @@ const processRecords = async () => {
|
||||
}
|
||||
|
||||
record.deathsPerMillion = !!record.population ? record.total / record.population * 1000000 : 0;
|
||||
record.deathGrowthRate = getGrowthRate(record);
|
||||
record.casesPerMillion = !!record.population ? record.cases.total / record.population * 1000000 : 0;
|
||||
record.deathGrowthRate = getGrowthRate(record.timeSeriesDaily);
|
||||
record.caseGrowthRate = getGrowthRate(record.cases.timeSeriesDaily);
|
||||
});
|
||||
|
||||
tsGlobalRecords.sort((a, b) => {
|
||||
@ -447,9 +450,12 @@ const processRecords = async () => {
|
||||
population: 0,
|
||||
timeSeriesDaily: {},
|
||||
timeSeriesMonthly: {},
|
||||
deathsPerMillion: 0,
|
||||
casesPerMillion: 0,
|
||||
states: [],
|
||||
safeName: record.countrySafeName,
|
||||
cases: {
|
||||
total: 0,
|
||||
timeSeriesDaily: {},
|
||||
timeSeriesMonthly: {},
|
||||
},
|
||||
@ -479,9 +485,11 @@ const processRecords = async () => {
|
||||
total: 0,
|
||||
population: 0,
|
||||
deathsPerMillion: 0,
|
||||
casesPerMillion: 0,
|
||||
timeSeriesDaily: {},
|
||||
timeSeriesMonthly: {},
|
||||
cases: {
|
||||
total: 0,
|
||||
timeSeriesDaily: {},
|
||||
timeSeriesMonthly: {},
|
||||
},
|
||||
@ -499,6 +507,7 @@ const processRecords = async () => {
|
||||
}
|
||||
|
||||
stateItem.total += record.total;
|
||||
stateItem.cases.total += record.cases.total;
|
||||
record.timeSeriesDaily.forEach((ts) => {
|
||||
stateItem.timeSeriesDaily[ts.key] = stateItem.timeSeriesDaily[ts.key] || {
|
||||
value: 0,
|
||||
@ -545,6 +554,8 @@ const processRecords = async () => {
|
||||
}
|
||||
|
||||
item.total += record.total;
|
||||
item.cases.total += record.cases.total;
|
||||
|
||||
record.timeSeriesDaily.forEach((ts) => {
|
||||
item.timeSeriesDaily[ts.key] = item.timeSeriesDaily[ts.key] || {
|
||||
value: 0,
|
||||
@ -596,6 +607,7 @@ const processRecords = async () => {
|
||||
counties: item.counties,
|
||||
population: item.population,
|
||||
deathsPerMillion: item.population > 0 ? item.total / item.population * 1000000 : 0,
|
||||
casesPerMillion: item.population > 0 ? item.cases.total / item.population * 1000000 : 0,
|
||||
timeSeriesDaily: Object.keys(item.timeSeriesDaily).sort().map((date) => {
|
||||
return {
|
||||
key: date,
|
||||
@ -611,6 +623,7 @@ const processRecords = async () => {
|
||||
};
|
||||
}),
|
||||
cases: {
|
||||
total: item.cases.total,
|
||||
timeSeriesDaily: Object.keys(item.cases.timeSeriesDaily).sort().map((date) => {
|
||||
return {
|
||||
key: date,
|
||||
@ -628,7 +641,8 @@ const processRecords = async () => {
|
||||
},
|
||||
};
|
||||
|
||||
stateItem.deathGrowthRate = getGrowthRate(stateItem);
|
||||
stateItem.deathGrowthRate = getGrowthRate(stateItem.timeSeriesDaily);
|
||||
stateItem.caseGrowthRate = getGrowthRate(stateItem.cases.timeSeriesDaily);
|
||||
stateItem.rollingAverageDaily = getRollingAverage(stateItem);
|
||||
stateItem.doublingDaily = getDoublingTime(stateItem);
|
||||
stateItem.cases.rollingAverageDaily = getRollingAverage(stateItem.cases);
|
||||
@ -657,6 +671,7 @@ const processRecords = async () => {
|
||||
states: item.states,
|
||||
population: item.population,
|
||||
deathsPerMillion: item.population > 0 ? item.total / item.population * 1000000 : 0,
|
||||
casesPerMillion: item.population > 0 ? item.cases.total / item.population * 1000000 : 0,
|
||||
timeSeriesDaily: Object.keys(item.timeSeriesDaily).sort().map((date) => {
|
||||
return {
|
||||
key: date,
|
||||
@ -672,6 +687,7 @@ const processRecords = async () => {
|
||||
};
|
||||
}),
|
||||
cases: {
|
||||
total: item.cases.total,
|
||||
timeSeriesDaily: Object.keys(item.cases.timeSeriesDaily).sort().map((date) => {
|
||||
return {
|
||||
key: date,
|
||||
@ -689,7 +705,8 @@ const processRecords = async () => {
|
||||
},
|
||||
};
|
||||
|
||||
countryItem.deathGrowthRate = getGrowthRate(countryItem);
|
||||
countryItem.deathGrowthRate = getGrowthRate(countryItem.timeSeriesDaily);
|
||||
countryItem.caseGrowthRate = getGrowthRate(countryItem.cases.timeSeriesDaily);
|
||||
countryItem.rollingAverageDaily = getRollingAverage(countryItem);
|
||||
countryItem.doublingDaily = getDoublingTime(countryItem);
|
||||
countryItem.cases.rollingAverageDaily = getRollingAverage(countryItem.cases);
|
||||
@ -704,6 +721,7 @@ const processRecords = async () => {
|
||||
timeSeriesDaily: {},
|
||||
timeSeriesMonthly: {},
|
||||
cases: {
|
||||
total: 0,
|
||||
timeSeriesDaily: {},
|
||||
timeSeriesMonthly: {},
|
||||
},
|
||||
@ -711,6 +729,7 @@ const processRecords = async () => {
|
||||
|
||||
countryArr.forEach((countryData) => {
|
||||
worldData.total += countryData.total;
|
||||
worldData.cases.total += countryData.cases.total;
|
||||
|
||||
countryData.timeSeriesDaily.forEach((ts) => {
|
||||
worldData.timeSeriesDaily[ts.key] = worldData.timeSeriesDaily[ts.key] || {
|
||||
@ -778,13 +797,15 @@ const processRecords = async () => {
|
||||
};
|
||||
});
|
||||
|
||||
worldData.deathGrowthRate = getGrowthRate(worldData);
|
||||
worldData.deathGrowthRate = getGrowthRate(worldData.timeSeriesDaily);
|
||||
worldData.casesGrowthRate = getGrowthRate(worldData.cases.timeSeriesDaily);
|
||||
worldData.rollingAverageDaily = getRollingAverage(worldData);
|
||||
worldData.doublingDaily = getDoublingTime(worldData);
|
||||
worldData.cases.rollingAverageDaily = getRollingAverage(worldData.cases);
|
||||
|
||||
worldData.population = 7781841000;
|
||||
worldData.deathsPerMillion = worldData.total / worldData.population * 1000000;
|
||||
worldData.casesPerMillion = worldData.cases.total / worldData.population * 1000000;
|
||||
|
||||
console.log(`transformed data in ${Date.now() - start}ms`);
|
||||
|
||||
@ -792,7 +813,7 @@ const processRecords = async () => {
|
||||
const worldTmpl = path.join(templatesDir, 'world.pug');
|
||||
const worldHtml = pug.renderFile(worldTmpl, {
|
||||
data: worldData,
|
||||
$title: 'The World',
|
||||
$title: 'Worldwide',
|
||||
lastUpdate,
|
||||
});
|
||||
|
||||
|
@ -13,6 +13,18 @@ html
|
||||
th.sorted, td.sorted {
|
||||
background-color: #e0eaf7;
|
||||
}
|
||||
thead.headers th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
thead.headers th:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
bottom: -1px;
|
||||
border-bottom: 2px solid #b5b5b5;
|
||||
}
|
||||
.geo-bg-dark {
|
||||
background-color: #8e8e8e;
|
||||
color: white;
|
||||
@ -92,20 +104,30 @@ html
|
||||
trends: [],
|
||||
};
|
||||
|
||||
function makeSparkline(id, data) {
|
||||
function makeSparkline(id, deathData, caseData) {
|
||||
const canvas = document.getElementById(id);
|
||||
const maxValue = data.reduce((max, value) => Math.max(max, value), 0);
|
||||
const maxValue = caseData.reduce((max, value) => Math.max(max, value), 0);
|
||||
const max = maxValue > 0 ? Math.pow(10, Math.ceil(Math.log10(maxValue))) : 0
|
||||
const chart = new Chart(canvas.getContext('2d'), {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: new Array(data.length),
|
||||
datasets: [{
|
||||
data: data,
|
||||
borderColor: 'rgb(53, 120, 193)',
|
||||
borderWidth: 1,
|
||||
backgroundColor: 'rgba(148, 193, 250, 0.50)',
|
||||
}],
|
||||
labels: new Array(deathData.length),
|
||||
datasets: [
|
||||
{
|
||||
data: caseData,
|
||||
borderColor: 'rgb(161,150,20)',
|
||||
borderWidth: 1,
|
||||
backgroundColor: 'rgba(161,150,20, 0.25)',
|
||||
fill: '1',
|
||||
},
|
||||
{
|
||||
data: deathData,
|
||||
borderColor: 'rgb(196, 64, 64)',
|
||||
borderWidth: 1,
|
||||
backgroundColor: 'rgba(196, 64, 64, 0.25)',
|
||||
fill: 'origin',
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
responsive: false,
|
||||
@ -344,7 +366,7 @@ html
|
||||
mixin heroChart()
|
||||
div.card.mb-4
|
||||
div.card-body.position-relative
|
||||
div.position-absolute(style="top: 10px; right: 10px")
|
||||
div.position-absolute(style="top: 10px; right: 10px; z-index: 2;")
|
||||
div.btn-group
|
||||
button.btn.btn-secondary.btn-sm.set-axis-linear(
|
||||
type="button"
|
||||
@ -357,7 +379,8 @@ html
|
||||
autocomplete="off"
|
||||
disabled
|
||||
) Logarithmic
|
||||
canvas.mx-auto(id="main-chart" width="1024" height="576")
|
||||
div.mx-auto.position-relative(style="max-width: 1024px; z-index: 1")
|
||||
canvas.mx-auto(id="main-chart" width="1024" height="576")
|
||||
-
|
||||
const growthRate = '+' + (data.deathGrowthRate * 100).toFixed(2) + '%';
|
||||
const population = 'pop. ' + data.population.toLocaleString();
|
||||
@ -382,19 +405,25 @@ html
|
||||
mixin dataTable(items, label, type)
|
||||
- const hasPopulation = type !== 'state' || data.name === 'United States';
|
||||
|
||||
div#table.table-responsive: table.table.table-sm.table-hover
|
||||
div#table: table.table.table-sm.table-hover
|
||||
thead: tr
|
||||
th.text-center.font-weight-bold.geo-bg-dark(colspan=(hasPopulation ? 3 : 2)) Geography
|
||||
th.text-center.font-weight-bold.cases-bg-dark(colspan="2") Cases
|
||||
th.text-center.font-weight-bold.deaths-bg-dark(colspan="100") Deaths
|
||||
th.text-center.font-weight-bold.cases-bg-dark(colspan=(hasPopulation ? 6 : 5)) Cases
|
||||
th.text-center.font-weight-bold.deaths-bg-dark(colspan=(hasPopulation ? 6 : 5)) Deaths
|
||||
th.text-center.font-weight-bold.geo-bg-dark Trends
|
||||
thead.headers: tr
|
||||
th.geo-bg #
|
||||
th.geo-bg(data-col="name"): +sortableLinks("name", true)= label
|
||||
if hasPopulation
|
||||
th.geo-bg(data-col="population"): +sortableLinks("population") Population
|
||||
|
||||
if hasPopulation
|
||||
th.cases-bg(data-col="cases-million"): +sortableLinks("cases-million") per 1M
|
||||
th.cases-bg(data-col="cases-total"): +sortableLinks("cases-total") Total
|
||||
th.cases-bg(data-col="cases-today"): +sortableLinks("cases-today") Today
|
||||
th.cases-bg(data-col="cases-yesterday"): +sortableLinks("cases-yesterday") Yesterday
|
||||
th.cases-bg(data-col="cases-last7"): +sortableLinks("cases-last7") Last 7
|
||||
th.cases-bg(data-col="cases-growth"): +sortableLinks("cases-growth") Growth
|
||||
|
||||
if hasPopulation
|
||||
th.deaths-bg(data-col="million"): +sortableLinks("million") per 1M
|
||||
@ -402,9 +431,8 @@ html
|
||||
th.sorted.deaths-bg(data-col="today"): +sortableLinks("today") Today
|
||||
th.deaths-bg(data-col="yesterday"): +sortableLinks("yesterday") Yesterday
|
||||
th.deaths-bg(data-col="last7"): +sortableLinks("last7") Last 7
|
||||
th.deaths-bg(data-col="last30"): +sortableLinks("last30") Last 30
|
||||
th.deaths-bg(data-col="growth"): +sortableLinks("growth") Growth
|
||||
th.text-center.deaths-bg Trend
|
||||
th.text-center.geo-bg Last 14 days
|
||||
|
||||
-
|
||||
items.sort((a, b) => {
|
||||
@ -426,48 +454,55 @@ html
|
||||
const today = getDelta(1);
|
||||
const yesterday = getDelta(2);
|
||||
const last7 = getValue(1) - getValue(7);
|
||||
const last30 = getValue(1) - getValue(30);
|
||||
const casesTotal = getValueCases(1);
|
||||
const casesToday = getDeltaCases(1);
|
||||
const casesLast7 = getValueCases(1) - getValueCases(7);
|
||||
const casesYesterday = getDeltaCases(1);
|
||||
tr(
|
||||
id=("row-" + (item.safeName || '_'))
|
||||
data-name=(item.name || '_')
|
||||
data-cases-total=casesTotal
|
||||
data-cases-total=item.cases.total
|
||||
data-cases-today=casesToday
|
||||
data-cases-last7=casesLast7
|
||||
data-cases-million=item.casesPerMillion
|
||||
data-cases-yesterday=casesYesterday
|
||||
data-cases-growth=item.caseGrowthRate
|
||||
data-population=item.population
|
||||
data-total=item.total
|
||||
data-million=item.deathsPerMillion
|
||||
data-today=today
|
||||
data-yesterday=yesterday
|
||||
data-last7=last7
|
||||
data-last30=last30
|
||||
data-growth=item.deathGrowthRate
|
||||
)
|
||||
td.sort-order= i + 1
|
||||
td: +renderItemName(item)
|
||||
if hasPopulation
|
||||
td.text-right: code: +formatNumber(item.population)
|
||||
td.text-right: code: +formatNumber(casesTotal)
|
||||
|
||||
if hasPopulation
|
||||
td.text-right: code: +formatNumber(Math.round(item.casesPerMillion))
|
||||
td.text-right: code: +formatNumber(item.cases.total)
|
||||
td.text-right: code: +formatNumber(casesToday)
|
||||
td.text-right: code: +formatNumber(casesYesterday)
|
||||
td.text-right: code: +formatNumber(casesLast7)
|
||||
td.text-right: code= Number(item.caseGrowthRate * 100).toFixed(2) + '%'
|
||||
if hasPopulation
|
||||
td.text-right: code: +formatNumber(Math.round(item.deathsPerMillion))
|
||||
td.text-right: code: +formatNumber(item.total)
|
||||
td.text-right.sorted: code: +formatNumber(today)
|
||||
td.text-right: code: +formatNumber(yesterday)
|
||||
td.text-right: code: +formatNumber(last7)
|
||||
td.text-right: code: +formatNumber(last30)
|
||||
td.text-right: code= Number(item.deathGrowthRate * 100).toFixed(2) + '%'
|
||||
td
|
||||
canvas.mx-auto(id="sparkline-" + i width="100" height="35")
|
||||
script.
|
||||
makeSparkline(
|
||||
"sparkline-#{i}",
|
||||
#{JSON.stringify(item.rollingAverageDaily.slice(-14).map(x => x.delta))}
|
||||
#{JSON.stringify(item.rollingAverageDaily.slice(-14).map(x => x.delta))},
|
||||
#{JSON.stringify(item.cases.rollingAverageDaily.slice(-14).map(x => x.delta))},
|
||||
);
|
||||
|
||||
|
||||
|
||||
div.container.mt-2
|
||||
div.container-fluid.mt-2(style="max-width: 1600px")
|
||||
h1.text-center Covid-19 Data
|
||||
div.d-flex.justify-content-around.font-italic.small
|
||||
div
|
||||
@ -561,10 +596,13 @@ html
|
||||
case 'total':
|
||||
case 'cases-total':
|
||||
case 'cases-today':
|
||||
case 'cases-yesterday':
|
||||
case 'cases-million':
|
||||
case 'cases-last7':
|
||||
case 'cases-growth':
|
||||
case 'today':
|
||||
case 'yesterday':
|
||||
case 'last7':
|
||||
case 'last30':
|
||||
case 'population':
|
||||
case 'million':
|
||||
case 'growth':
|
||||
|
Loading…
Reference in New Issue
Block a user