Skip to content
This repository has been archived by the owner on May 2, 2022. It is now read-only.

Commit

Permalink
Add endpoint for fetching aggregated data
Browse files Browse the repository at this point in the history
  • Loading branch information
Snikanes committed Mar 16, 2020
1 parent b61cb00 commit 1717420
Show file tree
Hide file tree
Showing 9 changed files with 248 additions and 15 deletions.
7 changes: 7 additions & 0 deletions app/domain/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,10 @@ export enum Symptom {
HEADACHE = 'HEADACHE',
SORE_THROAT = 'SORE_THROAT'
}

export interface AggregatedCovidReportData {
numberOfReports: number;
numberOfPeopleShowingSymptoms: number;
numberOfConfirmedInfected: number;
numberOfTested: number;
}
13 changes: 12 additions & 1 deletion app/repository/CovidReportRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { getInstance, SqlLiteDatabase } from './SqlLiteDatabase';
import { CovidReport } from '../domain/types';

const SELECT_ALL_COVID_REPORTS = 'select json_dump from covid_report';
const SELECT_ALL_COVID_REPORTS = 'select passcode, json_dump from covid_report';

const SELECT_COVID_REPORT = 'select * from covid_report where passcode = (?)';

Expand Down Expand Up @@ -57,6 +57,17 @@ export class CovidReportRepository {
);
}

async getLatestCovidReports(): Promise<CovidReport[]> {
const rows = await this.db.getAll(SELECT_ALL_COVID_REPORTS);
const latestReports: { [key: string]: CovidReport } = {};
rows.forEach((row: CovidReportRow) => {
latestReports[row.passcode] = this.parseJsonDumpToCovidReport(
row.json_dump
);
});
return Object.values(latestReports);
}

private parseJsonDumpToCovidReport(jsonDump: string): CovidReport {
return JSON.parse(jsonDump);
}
Expand Down
19 changes: 19 additions & 0 deletions app/routes/api-routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import express from 'express';

import { CovidReportRepository } from '../repository/CovidReportRepository';
import { aggregateCovidReports } from '../util/report-aggregator';

const router = express.Router();
const reportRepo = new CovidReportRepository();

router.get('/aggregated', async (req, res) => {
const reports = await reportRepo.getLatestCovidReports();
const aggregated = aggregateCovidReports(reports);
res.send(aggregated);
});

router.get('/', (req, res) => {
return res.render('pages/map');
});

export default router;
2 changes: 2 additions & 0 deletions app/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import bodyParser from 'body-parser';
import path from 'path';
import reportRoutes from './routes/report-routes';
import mapRoutes from './routes/map-routes';
import apiRoutes from './routes/api-routes';
import { getInstance } from './repository/SqlLiteDatabase';

const SqLiteStore = require('connect-sqlite3')(session);
Expand Down Expand Up @@ -38,6 +39,7 @@ app.set('views', [

app.use('/', reportRoutes);
app.use('/kart', mapRoutes);
app.use('/api', apiRoutes);

app.use(
'/static',
Expand Down
112 changes: 112 additions & 0 deletions app/util/report-aggregator.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { aggregateCovidReports } from './report-aggregator';
import {
CovidReport,
Sex,
TestResult,
Symptom,
AggregatedCovidReportData
} from '../domain/types';

const reports: CovidReport[] = [
{
yearOfBirth: '1993', // Deprecated
sex: Sex.MALE,
postalCode: '1234',
hasBeenTested: false,
testedAt: new Date(), // Not in use
symptomStart: '2020-03-02', // YYYY-MM-DD
testResult: TestResult.POSITIVE,
inQuarantine: true,
hasBeenAbroadLastTwoWeeks: true,
symptoms: {
[Symptom.DRY_COUGH]: true,
[Symptom.EXHAUSTION]: false,
[Symptom.FEVER]: false,
[Symptom.HEAVY_BREATHING]: false,
[Symptom.MUSCLE_ACHING]: false,
[Symptom.DIARRHEA]: false,
[Symptom.HEADACHE]: false,
[Symptom.SORE_THROAT]: false
},
submissionTimestamp: 123123123,
age: '50'
},
{
yearOfBirth: '1993', // Deprecated
sex: Sex.MALE,
postalCode: '1234',
hasBeenTested: false,
testedAt: new Date(), // YYYY-MM-DD
symptomStart: '2020-03-02', // YYYY-MM-DD
testResult: undefined,
inQuarantine: false,
hasBeenAbroadLastTwoWeeks: false,
symptoms: {
[Symptom.DRY_COUGH]: true,
[Symptom.EXHAUSTION]: true,
[Symptom.FEVER]: true,
[Symptom.HEAVY_BREATHING]: false,
[Symptom.MUSCLE_ACHING]: false,
[Symptom.DIARRHEA]: false,
[Symptom.HEADACHE]: false,
[Symptom.SORE_THROAT]: false
},
submissionTimestamp: 123123123,
age: '50'
},
{
yearOfBirth: '1993', // Deprecated
sex: Sex.MALE,
postalCode: '1234',
hasBeenTested: false,
testedAt: new Date(), // YYYY-MM-DD
symptomStart: '2020-03-02', // YYYY-MM-DD
testResult: TestResult.PENDING,
inQuarantine: false,
hasBeenAbroadLastTwoWeeks: false,
symptoms: {
[Symptom.DRY_COUGH]: false,
[Symptom.EXHAUSTION]: false,
[Symptom.FEVER]: false,
[Symptom.HEAVY_BREATHING]: false,
[Symptom.MUSCLE_ACHING]: false,
[Symptom.DIARRHEA]: false,
[Symptom.HEADACHE]: false,
[Symptom.SORE_THROAT]: false
},
submissionTimestamp: 123123123,
age: '50'
},
{
yearOfBirth: '1993', // Deprecated
sex: Sex.MALE,
postalCode: '1234',
hasBeenTested: false,
testedAt: new Date(), // YYYY-MM-DD
symptomStart: '2020-03-02', // YYYY-MM-DD
testResult: TestResult.NEGATIVE,
inQuarantine: false,
hasBeenAbroadLastTwoWeeks: false,
symptoms: {
[Symptom.DRY_COUGH]: true,
[Symptom.EXHAUSTION]: false,
[Symptom.FEVER]: false,
[Symptom.HEAVY_BREATHING]: false,
[Symptom.MUSCLE_ACHING]: false,
[Symptom.DIARRHEA]: false,
[Symptom.HEADACHE]: false,
[Symptom.SORE_THROAT]: false
},
submissionTimestamp: 123123123,
age: '50'
}
];

it('should count all correctly', () => {
const aggregated: AggregatedCovidReportData = aggregateCovidReports(reports);

expect(aggregated.numberOfReports).toBe(4);
expect(aggregated.numberOfPeopleShowingSymptoms).toBe(3);
expect(aggregated.numberOfConfirmedInfected).toBe(1);
expect(aggregated.numberOfTested).toBe(3);
});
33 changes: 33 additions & 0 deletions app/util/report-aggregator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
CovidReport,
AggregatedCovidReportData,
TestResult
} from '../domain/types';

const isShowingAtLeastOneSymptom = (report: CovidReport): boolean => {
return Object.values(report.symptoms).filter(value => !!value).length > 0;
};

export const aggregateCovidReports = (
reports: CovidReport[]
): AggregatedCovidReportData => {
const aggredatedData: AggregatedCovidReportData = {
numberOfReports: 0,
numberOfPeopleShowingSymptoms: 0,
numberOfConfirmedInfected: 0,
numberOfTested: 0
};
for (const report of reports) {
aggredatedData.numberOfReports += 1;
if (isShowingAtLeastOneSymptom(report)) {
aggredatedData.numberOfPeopleShowingSymptoms += 1;
}
if (report.testResult) {
aggredatedData.numberOfTested += 1;
}
if (report.testResult === TestResult.POSITIVE) {
aggredatedData.numberOfConfirmedInfected += 1;
}
}
return aggredatedData;
};
3 changes: 3 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
preset: 'ts-jest'
};
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@types/body-parser": "^1.19.0",
"@types/express": "^4.17.3",
"@types/express-session": "^1.17.0",
"@types/jest": "^25.1.4",
"@types/node-fetch": "^2.5.5",
"@typescript-eslint/eslint-plugin": "^2.23.0",
"@typescript-eslint/parser": "^2.23.0",
Expand All @@ -38,7 +39,8 @@
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.2",
"jest": "^25.1.0",
"prettier": "^1.19.1"
"prettier": "^1.19.1",
"ts-jest": "^25.2.1"
},
"eslintConfig": {
"settings": {
Expand Down
Loading

0 comments on commit 1717420

Please sign in to comment.