added more case data, table styling

This commit is contained in:
tmont 2020-05-02 18:57:59 -07:00
parent a4afdeb8a6
commit c08e7373e2
2 changed files with 93 additions and 34 deletions

View File

@ -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,
});

View File

@ -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':