Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 44 additions & 18 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,81 @@ const express = require('express');
const app = express();
const lookup = require('country-code-lookup');
const morgan = require('morgan');
const stripAnsi = require('strip-ansi');

const port = process.env.PORT || 3001;

const { getCountryTable, getJSONData, getJSONDataForCountry } = require('./lib/byCountry');
const { getCompleteTable } = require('./lib/corona');
const { countryUpperCase, lookupCountry } = require('./lib/helpers');
const { lookupCountry } = require('./lib/helpers');
const { getLiveUpdates } = require('./lib/reddit.js');


function errorHandler(error, res) {
console.error(error);
return res.send('I am sorry. Something went wrong. Please report it');
return res.send(`
I am sorry. Something went wrong. Please report it \n
${error.message}
`);
}

app.use(morgan(':remote-addr :remote-user :method :url :status :res[content-length] - :response-time ms'));
app.use((req, res, next) => {
res.setHeader('Cache-Control', 'no-cache');
next();
});



app.get('/', (req, res) => {
const isCurl = req.headers['user-agent'].match(/\bcurl\b/gmi) !== null;
const format = req.query.format ? req.query.format : '';
const minimal = req.query.minimal === 'true' ? true : false;
const emojis = req.query.emojis === 'true' ? true : false;
const top = req.query.top ? Number(req.query.top) : 1000;

if (format.toLowerCase() === 'json') {
return getJSONData().then(result => {
res.setHeader('Cache-Control', 's-maxage=900');
return res.json(result);
}).catch(error => errorHandler(error, res));
}

return getCompleteTable().then(result => {
res.setHeader('Cache-Control', 's-maxage=900');
return getCompleteTable({ isCurl, emojis, minimal, top })
.then(result => {
return res.send(result);
}).catch(error => errorHandler(error, res));
});

app.get('/updates', (req, res) => {
const format = req.query.format ? req.query.format : '';
const isCurl = req.headers['user-agent'].match(/\bcurl\b/gmi) !== null;
if (format.toLowerCase() === 'json') {
return getLiveUpdates({ json: true, isCurl }).then(result => {
return res.json(result);
}).catch(error => errorHandler(error, res));
}
return getLiveUpdates({ json: false, isCurl }).then(result => {
return res.send(result);
}).catch(error => errorHandler(error, res));
});

app.get('/:country', (req, res) => {
const { country } = req.params;
const isCurl = req.headers['user-agent'].match(/\bcurl\b/gmi) !== null;
const format = req.query.format ? req.query.format : '';

const minimal = req.query.minimal === 'true' ? true : false;
const emojis = req.query.emojis === 'true' ? true : false;
if (!country || 'ALL' === country.toUpperCase()) {
if (format.toLowerCase() === 'json') {
return getJSONData().then(result => {
res.setHeader('Cache-Control', 's-maxage=900');
return res.json(result);
}).catch(error => errorHandler(error, res));
}


return getCompleteTable().then(result => {
res.setHeader('Cache-Control', 's-maxage=900');
return res.send(result);
}).catch(error => errorHandler(error, res));
return getCompleteTable({ isCurl, emojis, minimal })
.then(result => {
return res.send(result);
}).catch(error => errorHandler(error, res));
}

let lookupObj = lookupCountry(country);
Expand All @@ -61,23 +88,22 @@ app.get('/:country', (req, res) => {
Ex:
- /UK: for United Kingdom
- /US: for United States of America.
- /India: for India.
- /Italy: for Italy.
`);
}

const { iso2 } = lookupObj;

if (format.toLowerCase() === 'json') {
return getJSONDataForCountry(iso2).then(result => {
res.setHeader('Cache-Control', 's-maxage=900');
return res.json(result);
}).catch(error => errorHandler(error, res));
}

return getCountryTable(iso2).then(result => {
res.setHeader('Cache-Control', 's-maxage=900');
return res.send(result);
}).catch(error => errorHandler(error, res));
return getCountryTable({ countryCode: iso2, isCurl, emojis, minimal })
.then(result => {
return res.send(result);
}).catch(error => errorHandler(error, res));
});


Expand Down
23 changes: 17 additions & 6 deletions bin/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
#!/usr/bin/env node
const yargonaut = require('yargonaut')
.style('green');
const yargonaut = require('yargonaut').style('green');
const yargs = require('yargs');
const chalk = require('chalk');
const { getCompleteTable } = require('../lib/corona');
const { getCountryTable } = require('../lib/byCountry');
const { lookupCountry } = require('../lib/helpers');

const { argv } = yargs
.command('$0 [country]','Tool to COVID-19 statistics for the world or the given country', yargs =>
.command('$0 [country]','Tool to track COVID-19 statistics from terminal', yargs =>
yargs.positional('country', {
coerce(arg) {
if ('ALL' === arg.toUpperCase()) {
Expand Down Expand Up @@ -42,16 +41,28 @@ const { argv } = yargs
alias: 'color',
describe: 'Show colors formatted output',
type: 'boolean'
},
m: {
alias: 'minimal',
describe: 'Remove borders & padding from table',
type: 'boolean',
default: false,
},
t: {
alias: 'top',
describe: 'Filter table by rank',
type: 'int'
}
})
.strict()
.help('help');

const { emojis, country } = argv;

const { emojis, country, minimal, top } = argv;
(
country === 'ALL'
? getCompleteTable(emojis)
: getCountryTable(country, emojis)
? getCompleteTable({ emojis, minimal, top })
: getCountryTable({ countryCode: country, emojis, minimal })
)
.then(console.log)
.catch(console.error);
26 changes: 26 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Changelog

## Version 0.6.0

* Added filter to show top N countries. ``corona --top=20``

## Version 0.5.0

* Added minimal / comapct table command. ``corona --minimal``
* Added world total stats at the bottom of the table too.
* Refactor: moved table formatting functions to helpers.
* Added total stats object when using `?format=json`

## Version 0.4.0

* Added country filter. Ex: ``corona Italy``
* Added command to show emojis. Ex: ``corona --emojis``
* Added command to disable colors using. Ex: ``corona --color=false``

## Version 0.2.0

* Added daily and weekly column.

## Version 0.1.0

* Lauched command `corona`
2 changes: 1 addition & 1 deletion lib/api.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const NodeCache = require( "node-cache" );
const NodeCache = require('node-cache');
const axios = require('axios');
const myCache = new NodeCache( { stdTTL: 100, checkperiod: 600 } );
const CORONA_ALL_KEY = 'coronaAll';
Expand Down
59 changes: 38 additions & 21 deletions lib/byCountry.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const _ = require('lodash');
const chalk = require('chalk');
const helpers = require('./helpers');
const api = require('./api');
const stripAnsi = require('strip-ansi');
const {
getCountry,
getConfirmed,
Expand Down Expand Up @@ -68,7 +69,9 @@ exports.getJSONData = async () => {
const data = await api.getCoronaData();
const { latest, confirmed, deaths, recovered } = data;
const countryData = getDataByState(confirmed, deaths, recovered);
return countryData;
const totalStats = getTotalStats(countryData);
totalStats.country = 'World';
return countryData.concat(totalStats);
}

exports.getJSONDataForCountry = async (countryCode) => {
Expand All @@ -79,27 +82,16 @@ exports.getJSONDataForCountry = async (countryCode) => {
return countryData;
}

exports.getCountryTable = async (countryCode, emojis = false) => {
const head = [
'',
'State',
'Confirmed',
`Recovered${emojis ? ' 😀' : ''}`,
`Deaths${emojis ? ' 😞' : ''}`,
`Active${emojis ? ' 😷' : ''}`,
'Mortality %',
'Recovered %',
'1 Day ▲',
'1 Week ▲',
// 'RoG',
...( emojis ? ['🏳'] : [] ),
];
exports.getCountryTable = async ({
countryCode,
emojis = false,
isCurl = true,
minimal = false,
}) => {
const table = new Table({
head,
chars: { 'top': '═' , 'top-mid': '╤' , 'top-left': '╔' , 'top-right': '╗'
, 'bottom': '═' , 'bottom-mid': '╧' , 'bottom-left': '╚' , 'bottom-right': '╝'
, 'left': '║' , 'left-mid': '╟' , 'mid': '─' , 'mid-mid': '┼'
, 'right': '║' , 'right-mid': '╢' , 'middle': '│' }
head: helpers.getTableHeaders(emojis, 'State'),
chars: helpers.getTableBorders(minimal),
style: helpers.getTableStyles(minimal),
});
const data = await api.getCoronaData();
const { latest, confirmed, deaths, recovered } = data;
Expand Down Expand Up @@ -147,5 +139,30 @@ exports.getCountryTable = async (countryCode, emojis = false) => {
});
}
const lastUpdated = countryData[0].lastUpdated;
if (!isCurl) {
const template = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono&display=swap" rel="stylesheet">
<title>Coronavirus Tracker</title>
<style>
body {
background: #0D0208;
color: #00FF41;
}
pre {
font-family: 'Roboto Mono', monospace;
white-space: pre;
}
</style>
</head>
<body>
<pre>${table.toString() + footer(lastUpdated)}</pre>
</body>
</html>`;
return stripAnsi(template);
}
return table.toString() + footer(lastUpdated);
}
62 changes: 54 additions & 8 deletions lib/corona.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const _ = require('lodash');
const chalk = require('chalk');
const helpers = require('./helpers');
const api = require('./api');
const stripAnsi = require('strip-ansi');
const {
getCountry,
getConfirmed,
Expand Down Expand Up @@ -79,7 +80,12 @@ function extraStats(dataArr) {
);
}

exports.getCompleteTable = async (emojis = false) => {
exports.getCompleteTable = async ({
isCurl = true,
emojis = false,
minimal = false,
top = 1000
}) => {
const head = [
'',
'Country',
Expand All @@ -94,12 +100,11 @@ exports.getCompleteTable = async (emojis = false) => {
// 'RoG',
...( emojis ? ['🏳'] : [] ),
];

const table = new Table({
head,
chars: { 'top': '═' , 'top-mid': '╤' , 'top-left': '╔' , 'top-right': '╗'
, 'bottom': '═' , 'bottom-mid': '╧' , 'bottom-left': '╚' , 'bottom-right': '╝'
, 'left': '║' , 'left-mid': '╟' , 'mid': '─' , 'mid-mid': '┼'
, 'right': '║' , 'right-mid': '╢' , 'middle': '│' }
head: helpers.getTableHeaders(emojis, 'Country'),
chars: helpers.getTableBorders(minimal),
style: helpers.getTableStyles(minimal),
});
const data = await api.getCoronaData();
const { latest, confirmed, deaths, recovered } = data;
Expand All @@ -121,7 +126,7 @@ exports.getCompleteTable = async (emojis = false) => {
]
})
let rank = 1;
countryData.forEach(cd => {
countryData.some(cd => {
const countryEmoji = getEmoji(cd.countryCode);
const values = [
getCountry(`${cd.country} (${cd.countryCode})`),
Expand All @@ -137,7 +142,48 @@ exports.getCompleteTable = async (emojis = false) => {
...(emojis ? [countryEmoji] : [])
]
table.push({ [rank++]: values })
return rank == top + 1;
});
table.push({
'': [
'World',
getConfirmed(worldStats.confirmed),
getRecovered(worldStats.recovered),
getDeaths(worldStats.deaths),
getActive(worldStats.active),
getMortalityPer(worldStats.mortalityPer),
getRecoveredPer(worldStats.recoveredPer),
getOneDayChange(worldStats),
getOneWeekChange(worldStats),
// '',
...( emojis ? ['🌎'] : [] )
]
})
const lastUpdated = countryData[0].lastUpdated;
if (!isCurl) {
const template = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono&display=swap" rel="stylesheet">
<title>Coronavirus Tracker</title>
<style>
body {
background: #0D0208;
color: #00FF41;
}
pre {
font-family: 'Roboto Mono', monospace;
white-space: pre;
}
</style>
</head>
<body>
<pre>${table.toString() + footer(lastUpdated)}</pre>
</body>
</html>`
return stripAnsi(template);
}
return table.toString() + footer(lastUpdated);
}
}
Loading