From 4807eae4af78ea15fde92b68d288e946433df838 Mon Sep 17 00:00:00 2001 From: psvenk Date: Sat, 10 Apr 2021 15:17:34 -0400 Subject: [PATCH] Migrate schedule.json to TOML (#290) This helps readability and maintainability considerably. Some changes have been made to the frontend to work with native JavaScript Date objects instead of numbers. --- build-lite.js | 8 ++ package-lock.json | 11 ++ package.json | 1 + public/js/clock.js | 110 +++++++-------- public/schedule.json | 313 ------------------------------------------- public/schedule.toml | 305 +++++++++++++++++++++++++++++++++++++++++ serve.js | 7 + 7 files changed, 384 insertions(+), 371 deletions(-) delete mode 100755 public/schedule.json create mode 100644 public/schedule.toml diff --git a/build-lite.js b/build-lite.js index 45251112..4328bdae 100644 --- a/build-lite.js +++ b/build-lite.js @@ -6,6 +6,7 @@ const readline = require("readline"); const util = require("util"); const exec = util.promisify(require("child_process").exec); const marked = require("marked"); +const TOML = require("@iarna/toml"); const dep_mappings = require('./frontend-dependencies'); @@ -127,6 +128,13 @@ async function processJS(file) { await out.write(JSON.stringify(changelog) + "\n"); continue; } + if (line === "//#include SCHEDULE") { + const schedule = TOML.parse( + await fsp.readFile(__dirname + '/public/schedule.toml') + ); + await out.write(JSON.stringify(schedule) + "\n"); + continue; + } // All other include directives let match; if ((match = line.match(/^\/\/#include (.*)/))) { diff --git a/package-lock.json b/package-lock.json index 7aa93645..597e2d0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "license": "GPL-3.0-only", "dependencies": { "@fortawesome/fontawesome-free": "^5.15.3", + "@iarna/toml": "^3.0.0", "animate.css": "^4.1.1", "body-parser": "^1.19.0", "commander": "^7.2.0", @@ -48,6 +49,11 @@ "node": ">=6" } }, + "node_modules/@iarna/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-td6ZUkz2oS3VeleBcN+m//Q6HlCFCPrnI0FZhrt/h4XqLEdOyYp2u21nd8MdsR+WJy5r9PTDaHTDDfhf4H4l6Q==" + }, "node_modules/@types/eslint": { "version": "7.2.9", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.9.tgz", @@ -2500,6 +2506,11 @@ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.3.tgz", "integrity": "sha512-rFnSUN/QOtnOAgqFRooTA3H57JLDm0QEG/jPdk+tLQNL/eWd+Aok8g3qCI+Q1xuDPWpGW/i9JySpJVsq8Q0s9w==" }, + "@iarna/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-td6ZUkz2oS3VeleBcN+m//Q6HlCFCPrnI0FZhrt/h4XqLEdOyYp2u21nd8MdsR+WJy5r9PTDaHTDDfhf4H4l6Q==" + }, "@types/eslint": { "version": "7.2.9", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.9.tgz", diff --git a/package.json b/package.json index 44fd3c28..0de2a081 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ }, "dependencies": { "@fortawesome/fontawesome-free": "^5.15.3", + "@iarna/toml": "^3.0.0", "animate.css": "^4.1.1", "body-parser": "^1.19.0", "commander": "^7.2.0", diff --git a/public/js/clock.js b/public/js/clock.js index 9141934f..1f87fbf8 100755 --- a/public/js/clock.js +++ b/public/js/clock.js @@ -29,6 +29,18 @@ let date_override = undefined; function schedulesCallback(response) { schedules = response; + // Convert all start and end times to JavaScript Date objects + for (const [, schedule] of Object.entries(schedules)) { + for (entry of schedule) { + const regexp = /(\d+):(\d+):(\d+)\.(\d+)/; + // Convert each entry to a JavaScript Date object + // (get hours, minutes, seconds, milliseconds) + entry.start = new Date(2000, 0, 1, + ...regexp.exec(entry.start).slice(1)); + entry.end = new Date(2000, 0, 1, + ...regexp.exec(entry.end).slice(1)); + } + } let last_interval = 0; const redraw_clock_with_timestamp = timestamp => { @@ -43,16 +55,6 @@ function schedulesCallback(response) { redraw_clock_with_timestamp(); } -// Takes a time from schedule.json and formats it as a string -function formatTime(time) { - const hours = Math.floor(time / 1000 / 60 / 60); - const minutes = time / 1000 / 60 % 60; - return new Date(2000, 1, 1, hours, minutes).toLocaleTimeString([], { - hour: "numeric", - minute: "numeric", - }); -} - function update_formattedSchedule() { if (selected_day_of_week < 0) { const now = date_override ? new Date(date_override) : new Date(); @@ -74,9 +76,16 @@ function update_formattedSchedule() { } } - let time = formatTime(start); - if (name !== "After School") - time += "–" + formatTime(end); + let timeString = start.toLocaleTimeString([], { + hour: "numeric", + minute: "numeric", + }); + if (name !== "After School") { + timeString += "–" + end.toLocaleTimeString([], { + hour: "numeric", + minute: "numeric", + }); + } // The index (1 to 8) of the color to use for this class const color_number = i < 8 ? i + 1 : 8; @@ -90,7 +99,7 @@ function update_formattedSchedule() { return { period: name, room: room, - time: time, + time: timeString, class: class_name, color: color, }; @@ -106,7 +115,7 @@ $.ajax({ //#ifdef lite /* schedulesCallback( -//#include public/schedule.json +//#include SCHEDULE ); */ //#endif @@ -129,33 +138,23 @@ function drawFace(ctx, radius) { ctx.drawImage(logo, -radius, -radius, 2 * radius, 2 * radius); } -function drawNumber(ctx, radius, pos, number) { +function drawTime(ctx, radius, pos, time) { ctx.fillStyle = 'white'; ctx.textBaseline = "middle"; ctx.textAlign = "center"; ctx.font = "85px arial"; - // Get time in seconds - let time = number / 1000; - // Get first and second digit - - // hours - let d1 = Math.floor(time / 60 / 60); - // minutes - let d2 = Math.floor(time / 60 % 60); - // seconds - let d3 = Math.floor(time % 60); - - if(d1 < 10) { - d1 = `0${d1}`; - } - if(d2 < 10) { - d2 = `0${d2}`; - } - if(d3 < 10) { - d3 = `0${d3}`; - } - ctx.fillText(`${d1}:${d2}:${d3}`, 0, 0); + // Use 12-hour time without AM/PM + const hours = String.prototype.padStart.call( + time.getHours() % 12, 2, "0" + ); + const minutes = String.prototype.padStart.call( + time.getMinutes(), 2, "0" + ); + const seconds = String.prototype.padStart.call( + time.getSeconds(), 2, "0" + ); + ctx.fillText(`${hours}:${minutes}:${seconds}`, 0, 0); } function drawName(name) { @@ -297,13 +296,12 @@ function redraw_clock() { } // Fake call to get_period_name to set current_schedule get_period_name("Period 1", day_of_week); - let number = 0; + // Time to show on clock + let time = new Date(2000, 0, 1, 0, 0, 0, 0); let period_name = ""; // Time of day - const tod = now.getHours() * 60 * 60 * 1000 - + now.getMinutes() * 60 * 1000 - + now.getSeconds() * 1000 - + now.getMilliseconds(); + const tod = new Date(2000, 0, 1, now.getHours(), now.getMinutes(), + now.getSeconds(), now.getMilliseconds()); let pos; if (![0, 6].includes(now.getDay())) { // School day @@ -321,30 +319,26 @@ function redraw_clock() { // Realtime period_name = ""; pos = tod % (12 * 60 * 60 * 1000) / (12 * 60 * 60 * 1000); - number = tod % (12 * 60 * 60 * 1000); - if (number < 1 * 60 * 60 * 1000) { - number += 12 * 60 * 60 * 1000; - } - } - else if (tod > current_period.end) { // Between classes + time = tod; + } else if (tod > current_period.end) { // Between classes period_name = get_period_name(current_period.name, day_of_week) + " ➡ " + get_period_name(next_period.name, day_of_week); pos = (tod - current_period.end) / (next_period.start - current_period.end); - number = next_period.start - tod; - } - else { // In class + time = new Date( + time.getTime() + next_period.start.getTime() - tod.getTime() + ); + } else { // In class period_name = get_period_name(current_period.name, day_of_week); pos = (tod - current_period.start) / (current_period.end - current_period.start); - number = current_period.end - tod; + time = new Date( + time.getTime() + current_period.end.getTime() - tod.getTime() + ); } } else { // Realtime period_name = ""; pos = tod % (12 * 60 * 60 * 1000) / (12 * 60 * 60 * 1000); - number = tod % (12 * 60 * 60 * 1000); - if(number < 1 * 60 * 60 * 1000) { - number += 12 * 60 * 60 * 1000; - } + time = tod; } // conver 0-1 to 0-2pi @@ -353,10 +347,10 @@ function redraw_clock() { drawFace(small_ctx, small_radius); drawName(period_name); drawHand(small_ctx, small_radius, pos, small_radius * .94, small_radius * .095); - drawNumber(small_ctx, small_radius, pos, number); + drawTime(small_ctx, small_radius, pos, time); drawFace(large_ctx, large_radius); drawName(period_name); drawHand(large_ctx, large_radius, pos, large_radius * .94, large_radius * .095); - drawNumber(large_ctx, large_radius, pos, number); + drawTime(large_ctx, large_radius, pos, time); } diff --git a/public/schedule.json b/public/schedule.json deleted file mode 100755 index 77d06c6b..00000000 --- a/public/schedule.json +++ /dev/null @@ -1,313 +0,0 @@ -{ - "regular":[ - { - "name": "Before School", - "start": 24300000, - "end": 27900000 - }, - { - "name":"Period 1", - "start": 29100000, - "end": 33900000 - }, - { - "name":"CM/OTI", - "start": 34140000, - "end": 35040000 - }, - { - "name":"Period 2", - "start": 35280000, - "end": 40080000 - }, - { - "name":"Lunch A", - "start": 40320000, - "end": 42120000 - }, - { - "name":"Lunch B", - "start": 42840000, - "end": 44640000 - }, - { - "name":"Lunch C", - "start": 45360000, - "end": 47160000 - }, - { - "name":"Period 4", - "start": 47400000, - "end": 52200000 - }, - { - "name": "After School", - "start": 52200000, - "end": 52200001 - } - ], - "regular-a":[ - { - "name": "Before School", - "start": 24300000, - "end": 27900000 - }, - { - "name":"Period 1", - "start": 29100000, - "end": 33900000 - }, - { - "name":"CM/OTI", - "start": 34140000, - "end": 35040000 - }, - { - "name":"Period 2", - "start": 35280000, - "end": 40080000 - }, - { - "name":"Lunch", - "start": 40320000, - "end": 42120000 - }, - { - "name":"Period 3", - "start": 42360000, - "end": 47160000 - }, - { - "name":"Period 4", - "start": 47400000, - "end": 52200000 - }, - { - "name": "After School", - "start": 52200000, - "end": 52200001 - } - ], - "regular-b":[ - { - "name": "Before School", - "start": 24300000, - "end": 27900000 - }, - { - "name":"Period 1", - "start": 29100000, - "end": 33900000 - }, - { - "name":"CM/OTI", - "start": 34140000, - "end": 35040000 - }, - { - "name":"Period 2", - "start": 35280000, - "end": 40080000 - }, - { - "name":"Period 3", - "start": 40320000, - "end": 42720000 - }, - { - "name":"Lunch", - "start": 42840000, - "end": 44640000 - }, - { - "name":"Period 3", - "start": 44760000, - "end": 47160000 - }, - { - "name":"Period 4", - "start": 47400000, - "end": 52200000 - }, - { - "name": "After School", - "start": 52200000, - "end": 52200001 - } - ], - "regular-c":[ - { - "name": "Before School", - "start": 24300000, - "end": 27900000 - }, - { - "name":"Period 1", - "start": 29100000, - "end": 33900000 - }, - { - "name":"CM/OTI", - "start": 34140000, - "end": 35040000 - }, - { - "name":"Period 2", - "start": 35280000, - "end": 40080000 - }, - { - "name":"Period 3", - "start": 40320000, - "end": 45120000 - }, - { - "name":"Lunch", - "start": 45360000, - "end": 47160000 - }, - { - "name":"Period 4", - "start": 47400000, - "end": 52200000 - }, - { - "name": "After School", - "start": 52200000, - "end": 52200001 - } - ], - "covid":[ - { - "name": "Before School", - "start": 27300000, - "end": 30300000 - }, - { - "name":"Period 1", - "start": 33000000, - "end": 36000000 - }, - { - "name":"Period 2", - "start": 36600000, - "end": 39600000 - }, - { - "name":"Period 3", - "start": 40200000, - "end": 43200000 - }, - { - "name":"Lunch", - "start": 43200000, - "end": 46800000 - }, - { - "name":"Period 4", - "start": 46800000, - "end": 49800000 - }, - { - "name": "After School", - "start": 49800000, - "end": 49800001 - } - ], - "covid-mt": [ - { - "name": "Before School", - "start": 25200000, - "end": 30300000 - }, - { - "name": "Period 1", - "start": 30900000, - "end": 35700000 - }, - { - "name": "Period 2", - "start": 36300000, - "end": 41100000 - }, - { - "name": "Study Support", - "start": 41700000, - "end": 44100000 - }, - { - "name": "Lunch", - "start": 44100000, - "end": 47700000 - }, - { - "name": "Period 3", - "start": 47700000, - "end": 50700000 - }, - { - "name": "Period 4", - "start": 51000000, - "end": 54000000 - }, - { - "name": "After School", - "start": 54000000, - "end": 54000001 - } - ], - "covid-w": [ - { - "name": "Community Meeting", - "start": 34500000, - "end": 36000000 - }, - { - "name": "Advisory", - "start": 49800000, - "end": 54000000 - } - ], - "covid-rf": [ - { - "name": "Before School", - "start": 25200000, - "end": 30300000 - }, - { - "name": "Period 3", - "start": 30900000, - "end": 35700000 - }, - { - "name": "Period 4", - "start": 36300000, - "end": 41100000 - }, - { - "name": "Study Support", - "start": 41700000, - "end": 44100000 - }, - { - "name": "Lunch", - "start": 44100000, - "end": 47700000 - }, - { - "name": "Period 1", - "start": 47700000, - "end": 50700000 - }, - { - "name": "Period 2", - "start": 51000000, - "end": 54000000 - }, - { - "name": "After School", - "start": 54000000, - "end": 54000001 - } - ] -} diff --git a/public/schedule.toml b/public/schedule.toml new file mode 100644 index 00000000..ac1c2ccd --- /dev/null +++ b/public/schedule.toml @@ -0,0 +1,305 @@ +# Regular schedule +# In effect until March 13, 2020 +[[regular]] +name = "Before School" +start = 06:45:00.000 +end = 07:45:00.000 + +[[regular]] +name = "Period 1" +start = 08:05:00.000 +end = 09:25:00.000 + +[[regular]] +name = "CM/OTI" +start = 09:29:00.000 +end = 09:44:00.000 + +[[regular]] +name = "Period 2" +start = 09:48:00.000 +end = 11:08:00.000 + +[[regular]] +name = "Lunch A" +start = 11:12:00.000 +end = 11:42:00.000 + +[[regular]] +name = "Lunch B" +start = 11:54:00.000 +end = 12:24:00.000 + +[[regular]] +name = "Lunch C" +start = 12:36:00.000 +end = 13:06:00.000 + +[[regular]] +name = "Period 4" +start = 13:10:00.000 +end = 14:30:00.000 + +[[regular]] +name = "After School" +start = 14:30:00.000 +end = 14:30:00.001 + +# Lunch A variant +[[regular]] +name = "Before School" +start = 06:45:00.000 +end = 07:45:00.000 + +[[regular]] +name = "Period 1" +start = 08:05:00.000 +end = 09:25:00.000 + +[[regular]] +name = "CM/OTI" +start = 09:29:00.000 +end = 09:44:00.000 + +[[regular]] +name = "Period 2" +start = 09:48:00.000 +end = 11:08:00.000 + +[[regular]] +name = "Lunch" +start = 11:12:00.000 +end = 11:42:00.000 + +[[regular]] +name = "Period 3" +start = 11:46:00.000 +end = 13:06:00.000 + +[[regular]] +name = "Period 4" +start = 13:10:00.000 +end = 14:30:00.000 + +[[regular]] +name = "After School" +start = 14:30:00.000 +end = 14:30:00.001 + +# Lunch B variant +[[regular]] +name = "Before School" +start = 06:45:00.000 +end = 07:45:00.000 + +[[regular]] +name = "Period 1" +start = 08:05:00.000 +end = 09:25:00.000 + +[[regular]] +name = "CM/OTI" +start = 09:29:00.000 +end = 09:44:00.000 + +[[regular]] +name = "Period 2" +start = 09:48:00.000 +end = 11:08:00.000 + +[[regular]] +name = "Period 3" +start = 11:12:00.000 +end = 11:52:00.000 + +[[regular]] +name = "Lunch B" +start = 11:54:00.000 +end = 12:24:00.000 + +[[regular]] +name = "Period 3" +start = 12:26:00.000 +end = 13:06:00.000 + +[[regular]] +name = "Period 4" +start = 13:10:00.000 +end = 14:30:00.000 + +[[regular]] +name = "After School" +start = 14:30:00.000 +end = 14:30:00.001 + +# Lunch C variant +[[regular]] +name = "Before School" +start = 06:45:00.000 +end = 07:45:00.000 + +[[regular]] +name = "Period 1" +start = 08:05:00.000 +end = 09:25:00.000 + +[[regular]] +name = "CM/OTI" +start = 09:29:00.000 +end = 09:44:00.000 + +[[regular]] +name = "Period 2" +start = 09:48:00.000 +end = 11:08:00.000 + +[[regular]] +name = "Period 3" +start = 11:12:00.000 +end = 12:32:00.000 + +[[regular]] +name = "Lunch C" +start = 12:36:00.000 +end = 13:06:00.000 + +[[regular]] +name = "Period 4" +start = 13:10:00.000 +end = 14:30:00.000 + +[[regular]] +name = "After School" +start = 14:30:00.000 +end = 14:30:00.001 + +# Covid-19 schedule (Fall 2020) +# In effect from September 21, 2020, to February 4, 2021 +[[covid]] +name = "Before School" +start = 07:35:00.000 +end = 08:25:00.000 + +[[covid]] +name = "Period 1" +start = 09:10:00.000 +end = 10:00:00.000 + +[[covid]] +name = "Period 2" +start = 10:10:00.000 +end = 11:00:00.000 + +[[covid]] +name = "Period 3" +start = 11:10:00.000 +end = 12:00:00.000 + +[[covid]] +name = "Lunch" +start = 12:00:00.000 +end = 13:00:00.000 + +[[covid]] +name = "Period 4" +start = 13:00:00.000 +end = 13:50:00.000 + +[[covid]] +name = "After School" +start = 13:50:00.000 +end = 13:50:00.001 + +# Covid-19 Monday/Tuesday schedule (Spring 2021) +# In effect since February 4, 2021 +[[covid-mt]] +name = "Before School" +start = 07:00:00.000 +end = 08:25:00.000 + +[[covid-mt]] +name = "Period 1" +start = 08:35:00.000 +end = 09:55:00.000 + +[[covid-mt]] +name = "Period 2" +start = 10:05:00.000 +end = 11:25:00.000 + +[[covid-mt]] +name = "Study Support" +start = 11:35:00.000 +end = 12:15:00.000 + +[[covid-mt]] +name = "Lunch" +start = 12:15:00.000 +end = 13:15:00.000 + +[[covid-mt]] +name = "Period 3" +start = 13:15:00.000 +end = 14:05:00.000 + +[[covid-mt]] +name = "Period 4" +start = 14:10:00.000 +end = 15:00:00.000 + +[[covid-mt]] +name = "After School" +start = 15:00:00.000 +end = 15:00:00.001 + +# Wednesday schedule +[[covid-w]] +name = "Community Meeting" +start = 09:35:00.000 +end = 10:00:00.000 + +[[covid-w]] +name = "Advisory" +start = 13:50:00.000 +end = 15:00:00.000 + +# Thursday/Friday schedule (same as M/T but with 3-4-1-2 order of periods) +[[covid-rf]] +name = "Before School" +start = 07:00:00.000 +end = 08:25:00.000 + +[[covid-rf]] +name = "Period 3" +start = 08:35:00.000 +end = 09:55:00.000 + +[[covid-rf]] +name = "Period 4" +start = 10:05:00.000 +end = 11:25:00.000 + +[[covid-rf]] +name = "Study Support" +start = 11:35:00.000 +end = 12:15:00.000 + +[[covid-rf]] +name = "Lunch" +start = 12:15:00.000 +end = 13:15:00.000 + +[[covid-rf]] +name = "Period 1" +start = 13:15:00.000 +end = 14:05:00.000 + +[[covid-rf]] +name = "Period 2" +start = 14:10:00.000 +end = 15:00:00.000 + +[[covid-rf]] +name = "After School" +start = 15:00:00.000 +end = 15:00:00.001 diff --git a/serve.js b/serve.js index c38f1e46..0fd8dd7c 100644 --- a/serve.js +++ b/serve.js @@ -9,6 +9,7 @@ const https = require('https'); const compression = require('compression'); const child_process = require('child_process'); const marked = require('marked'); +const TOML = require('@iarna/toml'); const scraper = require('./js/scrape'); const dep_mappings = require('./frontend-dependencies'); @@ -21,6 +22,9 @@ const version = child_process.execSync('git describe') const changelog = marked( fs.readFileSync(__dirname + '/CHANGELOG.md').toString() ); +const schedule = TOML.parse( + fs.readFileSync(__dirname + '/public/schedule.toml') +); program .version(version) @@ -97,6 +101,9 @@ app.get('/version', (req, res) => res.send(version)); // Endpoint to expose rendered changelog to client app.get('/updates', (req, res) => res.send(changelog)); +// Endpoint to expose specification of schedules to client +app.get('/schedule.json', (req, res) => res.send(schedule)); + app.use(function(req, res, next) { // enable cors res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");