Skip to content

Instantly share code, notes, and snippets.

@krisanalfa
Last active March 23, 2020 05:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save krisanalfa/4b4210b322caec175d93eda531aeae80 to your computer and use it in GitHub Desktop.
Save krisanalfa/4b4210b322caec175d93eda531aeae80 to your computer and use it in GitHub Desktop.
COVID-19 in a shell :)
import { request, RequestOptions } from 'https';
import { promisify } from 'util';
interface IAuthResponse {
EmbedToken: string;
EmbedUrl: string;
ReportId: string;
}
interface IRequestOptions extends RequestOptions {
body?: any;
}
interface IDailyQueryResult {
jobId: string;
result: {
data: {
dsr: {
DS: ReadonlyArray<{
PH: ReadonlyArray<{
DM0: ReadonlyArray<{
// - UNIX Timestamp with miliseconds
// - Total case
// - New case
// - New recovery case
// - New death case
C: [number, number, number, number, number];
}>;
}>;
}>;
};
timestamp: string;
};
};
}
interface IDailySummaryReport {
jobIds: ReadonlyArray<string>;
results: ReadonlyArray<IDailyQueryResult>;
}
const requestSync = (
options: IRequestOptions,
callback: (error: any | null, data: any) => any,
) => {
const req = request(options, res => {
if (!res.statusCode) {
callback(null, null);
return;
}
if ((res.statusCode >= 200 && res.statusCode < 300) === false) {
callback(
{
message: 'HTTP_STATUS_NOT_OK',
code: res.statusCode,
headers: res.headers,
options,
},
null,
);
return;
}
let data = '';
res.on('data', chunk => {
data += chunk;
});
res.on('end', () => callback(null, data.trim()));
});
req.on('error', err =>
callback(
{
...err,
options,
},
null,
),
);
if (options.body) {
req.write(options.body);
}
req.end();
};
const requestAsync: (options: IRequestOptions) => Promise<string> = promisify(
requestSync,
);
const parseJsonp = <T>(jsonp: string, callback: string): T => {
const callbackFn = (data: any) => data;
// tslint:disable-next-line: no-eval
return eval(jsonp.replace(new RegExp(`^${callback}`), callbackFn.name)) as T;
};
const fetchDailySummaryReport = async (auth: IAuthResponse) => {
const body = JSON.stringify({
version: '1.0.0',
modelId: 67177,
queries: [
{
Query: {
Commands: [
{
SemanticQueryDataShapeCommand: {
Query: {
Version: 2,
From: [
{
Name: 'd',
Entity: 'df_daily',
},
],
Select: [
{
Column: {
Expression: {
SourceRef: {
Source: 'd',
},
},
Property: 'DT',
},
Name: 'df_daily.DT',
},
{
Aggregation: {
Expression: {
Column: {
Expression: {
SourceRef: {
Source: 'd',
},
},
Property: 'Jumlah kasus',
},
},
Function: 0,
},
Name: 'Sum(df_daily.Jumlah kasus)',
},
{
Measure: {
Expression: {
SourceRef: {
Source: 'd',
},
},
Property: '# Kasus Baru',
},
Name: 'df_daily.# Kasus Baru',
},
{
Measure: {
Expression: {
SourceRef: {
Source: 'd',
},
},
Property: '# Sembuh (baru)',
},
Name: 'df_daily.# Sembuh (baru)',
},
{
Measure: {
Expression: {
SourceRef: {
Source: 'd',
},
},
Property: '# Meninggal (baru)',
},
Name: 'df_daily.# Meninggal (baru)',
},
],
},
Binding: {
Primary: {
Groupings: [
{
Projections: [0, 1, 2, 3, 4],
},
],
},
DataReduction: {
DataVolume: 4,
Primary: {
Sample: {},
},
},
Version: 1,
},
},
},
],
},
},
],
});
const response = await requestAsync({
method: 'POST',
hostname: 'wabi-south-east-asia-b-primary-redirect.analysis.windows.net',
host: 'wabi-south-east-asia-b-primary-redirect.analysis.windows.net',
port: 443,
path: '/explore/querydata',
timeout: 1000,
headers: {
Authorization: `EmbedToken ${auth.EmbedToken}`,
Accept: '*/*',
'Content-Type': 'application/json',
'Content-Length': body.length,
},
body,
});
return JSON.parse(response) as IDailySummaryReport;
};
(async () => {
try {
const rawAuthResponse: string = await requestAsync({
method: 'GET',
hostname: 'kc19-statistik-harian2.azurewebsites.net',
host: 'kc19-statistik-harian2.azurewebsites.net',
port: 443,
path:
'/api/statharian?code=Ug6TSCa2OT/6PDRako7odgtj1xpPjRZQO7sqUARQRSjYl5Becv4qLQ==',
timeout: 1000,
});
const authResponse = parseJsonp<IAuthResponse>(rawAuthResponse, 'callback');
const dailySummaryResponse = await fetchDailySummaryReport(authResponse);
const [result] = dailySummaryResponse.results;
const [ds] = result.result.data.dsr.DS;
const [ph] = ds.PH;
// tslint:disable-next-line: no-console
console.table(
ph.DM0.map(({ C }) => {
const [date, totalCase, newCase, recovered, death] = C;
return {
Date: (new Date(date)).toLocaleDateString('id-ID', { year: 'numeric', month: 'long', day: 'numeric' }),
'Total case': totalCase || 0,
'New case': newCase || 0,
Recovered: recovered || 0,
Death: death || 0,
};
}),
);
} catch (error) {
// tslint:disable-next-line: no-console
console.error('CAUGHT AN ERROR:', error);
}
})();
@krisanalfa
Copy link
Author

Run the script with ts-node:

ts-node covid-19.id.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment