-
Notifications
You must be signed in to change notification settings - Fork 1
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 #730 from frog-pond/drew/mealtime-data-bonapp
- Loading branch information
Showing
7 changed files
with
205 additions
and
0 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
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,15 @@ | ||
import {ONE_HOUR} from '../../ccc-lib/constants.js' | ||
import * as report from '../../reports-bonapp/index.js' | ||
import mem from 'memoize' | ||
|
||
const getReport = mem(report.report, {maxAge: ONE_HOUR}) | ||
|
||
export const REPORT_URLS = { | ||
stav: 'https://www.stolaf.edu/apps/mealtimes/', | ||
} | ||
|
||
export async function stavMealtimeReport(ctx) { | ||
ctx.cacheControl(ONE_HOUR) | ||
|
||
ctx.body = await getReport(REPORT_URLS.stav) | ||
} |
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,31 @@ | ||
import {suite, test} from 'node:test' | ||
import assert from 'node:assert/strict' | ||
import lodash from 'lodash' | ||
|
||
import * as reports from './reports.js' | ||
import {StavReportType} from '../../reports-bonapp/types.js' | ||
|
||
const {noop} = lodash | ||
|
||
const reportFunctions = { | ||
stav: reports.stavMealtimeReport, | ||
} | ||
|
||
suite('sanity checks', () => { | ||
test('all report info endpoints are represented in this test file', () => { | ||
assert.deepStrictEqual( | ||
Object.keys(reportFunctions), | ||
Object.keys(reports.REPORT_URLS), | ||
) | ||
}) | ||
}) | ||
|
||
suite('endpoints should not throw', {concurrency: 4}, () => { | ||
for (const report of Object.keys(reports.REPORT_URLS)) { | ||
test(`${report} report endpoint should return a StavReportType struct`, async () => { | ||
let ctx = {cacheControl: noop, body: null} | ||
await assert.doesNotReject(() => reportFunctions[report](ctx)) | ||
assert.doesNotThrow(() => StavReportType.parse(ctx.body)) | ||
}) | ||
} | ||
}) |
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,12 @@ | ||
import {StavReportType} from './types.js' | ||
|
||
/** @returns {StavReportType} */ | ||
export function CustomReportType({message}) { | ||
return StavReportType.parse([ | ||
{ | ||
title: message, | ||
times: [], | ||
data: [], | ||
}, | ||
]) | ||
} |
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,117 @@ | ||
import {get} from '../ccc-lib/http.js' | ||
import {ONE_MINUTE} from '../ccc-lib/constants.js' | ||
|
||
import {JSDOM, VirtualConsole} from 'jsdom' | ||
import mem from 'memoize' | ||
import * as Sentry from '@sentry/node' | ||
|
||
import {CustomReportType} from './helpers.js' | ||
import {StavReportType} from './types.js' | ||
|
||
const getBonAppPage = mem(get, {maxAge: ONE_MINUTE}) | ||
|
||
const NUMBER_OF_CHARTS_TO_PARSE = 7 | ||
|
||
/** | ||
* @param {string|URL} url | ||
* @return {Promise<JSDOM>} | ||
*/ | ||
async function getBonAppReportWebpage(url) { | ||
const virtualConsole = new VirtualConsole() | ||
virtualConsole.sendTo(console, {omitJSDOMErrors: true}) | ||
virtualConsole.on('jsdomError', (err) => { | ||
let jsdomErrorMessagesToSkip = [ | ||
'Uncaught [ReferenceError: wp is not defined]', | ||
'Uncaught [ReferenceError: jQuery is not defined]', | ||
'Uncaught [Error: Create skia surface failed]', | ||
"Uncaught [TypeError: Cannot read properties of undefined (reading 'slice')]", | ||
'Not implemented: HTMLCanvasElement.prototype.getContext (without installing the canvas npm package)', | ||
] | ||
if (jsdomErrorMessagesToSkip.includes(err.message)) { | ||
return | ||
} | ||
console.error(err) | ||
}) | ||
|
||
const body = await getBonAppPage(url).text() | ||
return new JSDOM(body, { | ||
runScripts: 'dangerously', | ||
resources: 'usable', | ||
virtualConsole, | ||
beforeParse(window) { | ||
window.fetch = global.fetch | ||
}, | ||
}) | ||
} | ||
|
||
/** | ||
* @param {string|URL} reportUrl | ||
* @returns {Promise<StavReportType>} | ||
*/ | ||
async function _report(reportUrl) { | ||
let dom = await getBonAppReportWebpage(reportUrl) | ||
|
||
dom.window.console.error = (error) => { | ||
let errorMessagesToSkip = [ | ||
"Failed to create chart: can't acquire context from the given item", | ||
] | ||
if (errorMessagesToSkip.includes(error)) { | ||
return | ||
} | ||
console.error(error) | ||
} | ||
|
||
dom.window.console.info = (info) => { | ||
let infoMessagesToSkip = [ | ||
'Initialized global navigation scripts', | ||
'Initialized mobile menu script scripts', | ||
'Initialized tools navigation scripts', | ||
'Initialized all javascript that targeted document ready.', | ||
] | ||
if (infoMessagesToSkip.includes(info)) { | ||
return | ||
} | ||
console.info(info) | ||
} | ||
|
||
return new Promise((resolve, _reject) => { | ||
dom.window.onload = () => { | ||
const charts = dom.window.Chart.instances | ||
|
||
const parse = (chart) => { | ||
const {labels, datasets} = chart.data | ||
return { | ||
title: datasets[0].label, | ||
times: labels, | ||
data: datasets[0].data, | ||
} | ||
} | ||
|
||
const payload = [] | ||
|
||
for (let i = 0; i < NUMBER_OF_CHARTS_TO_PARSE; ++i) { | ||
try { | ||
payload.push(parse(charts[i])) | ||
} catch (err) { | ||
console.warn({error: err.message}) | ||
} | ||
} | ||
|
||
resolve(StavReportType.parse(payload)) | ||
} | ||
}) | ||
} | ||
|
||
/** | ||
* @param {string|URL} reportUrl | ||
* @returns {Promise<StavReportType>} | ||
*/ | ||
export function report(reportUrl) { | ||
try { | ||
return _report(reportUrl) | ||
} catch (err) { | ||
console.error(err) | ||
Sentry.isInitialized() && Sentry.captureException(err) | ||
return CustomReportType({message: 'Could not load BonApp report'}) | ||
} | ||
} |
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,17 @@ | ||
import {suite, test} from 'node:test' | ||
import assert from 'node:assert/strict' | ||
|
||
import * as bonAppReport from './index.js' | ||
import {StavReportType} from './types.js' | ||
import {REPORT_URLS} from '../ccci-stolaf-college/v1/reports.js' | ||
|
||
suite('report info', {concurrency: true}, () => { | ||
test('fetching the data should not throw', async () => { | ||
await assert.doesNotReject(() => bonAppReport.report(REPORT_URLS.stav)) | ||
}) | ||
|
||
test('it should return a StavReportType struct', async () => { | ||
let data = await bonAppReport.report(REPORT_URLS.stav) | ||
assert.doesNotThrow(() => StavReportType.parse(data)) | ||
}) | ||
}) |
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,9 @@ | ||
import {z} from 'zod' | ||
|
||
export const StavReportType = z.array( | ||
z.object({ | ||
title: z.string(), | ||
times: z.array(z.string()), | ||
data: z.array(z.number()), | ||
}), | ||
) |