-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #157 from dali-lab/dev
Feat/prepare histogram data
- Loading branch information
Showing
8 changed files
with
197 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Histogram Operations | ||
|
||
## `GET /histogram` | ||
|
||
Returns the histogram data stored in the database. | ||
|
||
## `POST /histogram/update` | ||
|
||
Expects authorization header with Bearer token. | ||
If no histogram data is stored in the database, creates one. If there is previous data, updates the existing data with the results of latest calculations. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,9 @@ | ||
# Route Documentation | ||
|
||
- [Blog](./BLOG.md) | ||
- [Healthcheck](./HEALTHCHECK.md) | ||
- [Histogram](./HISTOGRAM.md) | ||
- [Summarized County Data](./SUMMARIZED-COUNTY.md) | ||
- [Summarized Ranger District Data](./SUMMARIZED-RD.md) | ||
- [Unsummarized Trapping Data](./UNSUMMARIZED.md) | ||
- [Users](./USERS.md) | ||
- [Blog](./BLOG.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { COLLECTION_NAMES, RESPONSE_CODES } from '../constants'; | ||
import { queryFetch } from '../utils'; | ||
import { saveHistogramData, computeHistogramData } from '../utils/histogram-service'; | ||
|
||
/** | ||
* @description retrieves histogram data object | ||
* @returns {Promise<HistogramData>} promise that resolves to histogram data object or error | ||
*/ | ||
export const getHistogramData = async () => { | ||
try { | ||
const histogramData = await queryFetch(COLLECTION_NAMES.histogram); | ||
return { ...RESPONSE_CODES.SUCCESS, data: histogramData[0] }; | ||
} catch (error) { | ||
console.error(error); | ||
return error; | ||
} | ||
}; | ||
|
||
/** | ||
* @description recalculates and saves histogram data object to the database | ||
* @returns {Promise<HistogramData>} promise that resolves to histogram data object or error | ||
*/ | ||
export const updateHistogramData = async () => { | ||
try { | ||
const histogramData = await computeHistogramData(); | ||
|
||
const savedHistogramData = await saveHistogramData(histogramData); | ||
return { ...RESPONSE_CODES.SUCCESS, data: savedHistogramData }; | ||
} catch (error) { | ||
console.error(error); | ||
return error; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
/* eslint-disable import/prefer-default-export */ | ||
import * as User from './user'; | ||
import * as Blog from './blog'; | ||
import * as Histogram from './histogram'; | ||
|
||
export { User, Blog }; | ||
export { User, Blog, Histogram }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { Router } from 'express'; | ||
import { | ||
generateResponse, | ||
RESPONSE_TYPES, | ||
RESPONSE_CODES, | ||
} from '../constants'; | ||
import { Histogram } from '../controllers'; | ||
import { updateHistogramData } from '../controllers/histogram'; | ||
import { requireAuth } from '../middleware'; | ||
|
||
const histogramRouter = Router(); | ||
|
||
// returns histogram data | ||
histogramRouter.route('/').get(async (_req, res) => { | ||
try { | ||
const data = await Histogram.getHistogramData(); | ||
|
||
res.send(generateResponse(RESPONSE_TYPES.SUCCESS, data.data)); | ||
} catch (error) { | ||
res | ||
.status(RESPONSE_CODES.INTERNAL_ERROR.status) | ||
.send(generateResponse(RESPONSE_TYPES.INTERNAL_ERROR, error)); | ||
} | ||
}); | ||
|
||
// calculates histogram data | ||
histogramRouter.route('/update').post([requireAuth], async (req, res) => { | ||
try { | ||
const data = await updateHistogramData(); | ||
|
||
res.send(generateResponse(RESPONSE_TYPES.SUCCESS, data)); | ||
} catch (error) { | ||
res | ||
.status(RESPONSE_CODES.INTERNAL_ERROR.status) | ||
.send(generateResponse(RESPONSE_TYPES.INTERNAL_ERROR, error)); | ||
} | ||
}); | ||
|
||
export default histogramRouter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import { COLLECTION_NAMES, RESPONSE_CODES } from '../constants'; | ||
import { specifiedQueryFetch } from './query-fetch'; | ||
|
||
// x axis of the histogram - predicted probability of an outbreak | ||
export const probRanges = [ | ||
{ min: 0, max: 0.025 }, | ||
{ min: 0.025, max: 0.05 }, | ||
{ min: 0.05, max: 0.15 }, | ||
{ min: 0.15, max: 0.25 }, | ||
{ min: 0.25, max: 0.4 }, | ||
{ min: 0.4, max: 0.6 }, | ||
{ min: 0.6, max: 0.8 }, | ||
{ min: 0.8, max: 1 }, | ||
]; | ||
|
||
// y axis of the histogram - actual number of spots observed | ||
const spotsRanges = [ | ||
{ min: 0, max: 0 }, | ||
{ min: 1, max: 9 }, | ||
{ min: 10, max: 19 }, | ||
{ min: 20, max: 49 }, | ||
{ min: 50, max: 99 }, | ||
{ min: 100, max: 249 }, | ||
{ min: 250, max: Infinity }, | ||
]; | ||
|
||
const categorizeRecords = (records) => { | ||
const frequencyArray = probRanges.map(({ min, max }) => { | ||
const rangeLabel = `${min === 0 ? '0' : min}-${max}`; | ||
const rangeRecords = records.filter((record) => { | ||
return record.probSpotsGT50 > min && record.probSpotsGT50 <= max; | ||
}); | ||
|
||
// eslint-disable-next-line no-shadow | ||
const data = spotsRanges.map(({ min, max }) => { | ||
return rangeRecords.filter((record) => { | ||
return record.spotst0 >= min && record.spotst0 <= max; | ||
}).length; | ||
}); | ||
|
||
return { | ||
range: rangeLabel, | ||
frequency: rangeRecords.length, | ||
withBorder: false, | ||
data, | ||
}; | ||
}); | ||
|
||
return frequencyArray; | ||
}; | ||
function validateRecords(records) { | ||
return records.filter((record) => { | ||
return ( | ||
record.probSpotsGT50 !== null | ||
&& record.probSpotsGT50 !== undefined | ||
&& record.spotst0 !== null | ||
&& record.spotst0 !== undefined | ||
); | ||
}); | ||
} | ||
|
||
export const computeHistogramData = async () => { | ||
try { | ||
const counties = await specifiedQueryFetch( | ||
COLLECTION_NAMES.summarizedCounty, | ||
{ | ||
hasPredictionAndOutcome: 1, | ||
}, | ||
); | ||
|
||
const rangerDistricts = await specifiedQueryFetch( | ||
COLLECTION_NAMES.summarizedRangerDistrict, | ||
{ | ||
hasPredictionAndOutcome: 1, | ||
}, | ||
); | ||
|
||
const data = [...counties, ...rangerDistricts]; | ||
const validRecords = validateRecords(data); | ||
const frequency = validRecords.length; | ||
const frequencyArray = categorizeRecords(validRecords); | ||
|
||
return { ...RESPONSE_CODES.SUCCESS, frequency, frequencyArray }; | ||
} catch (e) { | ||
console.error(e); | ||
return e; | ||
} | ||
}; | ||
|
||
export const saveHistogramData = async (histogramData) => { | ||
const cursor = global.connection.collection('histogram'); | ||
|
||
const { frequency, frequencyArray } = histogramData; | ||
|
||
const savedData = await cursor.updateOne( | ||
{ _id: 'chartData' }, | ||
{ | ||
$set: { | ||
timestamp: new Date(), | ||
frequencyArray, | ||
frequency, | ||
}, | ||
}, | ||
{ upsert: true }, | ||
); | ||
|
||
return savedData; | ||
}; |