From d0975a6e7ee57de4debda94e823011d813fbf4a1 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Sun, 5 Sep 2021 21:39:24 -0500 Subject: Initial rewrite in svelte --- src/util/parser.ts | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/util/parser.ts (limited to 'src/util/parser.ts') diff --git a/src/util/parser.ts b/src/util/parser.ts new file mode 100644 index 0000000..6d706d7 --- /dev/null +++ b/src/util/parser.ts @@ -0,0 +1,107 @@ +import EventInfo from "../models/EventInfo"; +import Lab from "../models/Lab"; +import PeerTeacher from "../models/PeerTeacher"; +import { PeerTeacherImportError } from "./error"; + +interface LabSchedule { + data: { + courseNumber: string, + sequenceNumber: string, + meetingsFaculty: { + meetingTime: { + beginTime: string | null, + building: string, + endTime: string | null, + friday: boolean, + meetingType: string, + monday: boolean, + room: string, + thursday: boolean, + tuesday: boolean, + wednesday: boolean + } + }[] + }[] +}; + +/** + * Parses a peer teacher schedule + * @param schedule The schedule to parse + * @returns A peer teacher + */ +export function parsePTSchedule(schedule: string) { + // namePatter: + const namePattern = /^(.*)\s(.*)\s(\d{9})/; + // eventPattern (24hr time): MTWRF hh:mm - hh:mm + const eventPattern = /^(M?T?W?R?F?)\s(\d{1,2}:\d{2})\s?-\s?(\d{1,2}:\d{2})/; + const lines = schedule.split("\n").filter(line => line.trim()); + + const nameLine = lines.find(line => line.match(namePattern)); + if(nameLine === undefined) { + throw new PeerTeacherImportError(`No peer teacher in schedule`); + } + + const [, firstname, lastname, uin] = nameLine.match(namePattern) as RegExpMatchArray; + const peerTeacher = new PeerTeacher(uin, firstname, lastname); + + const events = lines + .filter(line => line.match(eventPattern)) + .map(line => { + let [, days, start, end] = line.match(eventPattern) as RegExpMatchArray; + start = start.replace(":", ""); + end = end.replace(":", ""); + return new EventInfo(days, start, end); + }); + + peerTeacher.events = events; + return peerTeacher; +} + +/** + * Parses the course schedule into labs attended by PTs + * @param schedule The course schedule object from Howdy + * @returns An array of labs + */ +export function parseLabSchedule(schedule: LabSchedule) { + const taughtCourses = ['110', '111', '121', '206', '221', '312', '313', '315']; + const results: Lab[] = []; + + const courses = schedule.data; + for(const course of courses) { + if(!taughtCourses.includes(course.courseNumber)) { + continue; + } + + for(const meeting of course.meetingsFaculty) { + const { meetingTime } = meeting; + + if(meetingTime.meetingType !== "LAB") { + continue; + } + + let days = ""; + days += meetingTime.monday ? 'M' : ''; + days += meetingTime.tuesday ? 'T' : ''; + days += meetingTime.wednesday ? 'W' : ''; + days += meetingTime.thursday ? 'R' : ''; + days += meetingTime.friday ? 'F' : ''; + + const start = meetingTime.beginTime === null ? -1 : meetingTime.beginTime; + const end = meetingTime.endTime === null ? -1 : meetingTime.endTime; + const { courseNumber, sequenceNumber } = course; + const { building, room} = meetingTime; + + results.push( + new Lab( + courseNumber, + sequenceNumber, + new EventInfo(days, start, end), + building, + room + ) + ); + } + } + + return results; +} \ No newline at end of file -- cgit v1.2.3 From fea56ae09cd612003e1bafd2459556b67a5950e9 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Sun, 5 Sep 2021 22:56:45 -0500 Subject: Add import/export functionality --- src/components/EditorActions.svelte | 48 ++++++++++++++++++++++++++++++++++--- src/logic/EditorActions.ts | 13 +++++++++- src/models/EventInfo.ts | 10 ++++++++ src/models/Lab.ts | 18 +++++++++++++- src/models/PeerTeacher.ts | 21 +++++++++++++++- src/util/parser.ts | 48 +++++++++++++++++++++++++++++++++++++ 6 files changed, 152 insertions(+), 6 deletions(-) (limited to 'src/util/parser.ts') diff --git a/src/components/EditorActions.svelte b/src/components/EditorActions.svelte index a0372ed..fa2a041 100644 --- a/src/components/EditorActions.svelte +++ b/src/components/EditorActions.svelte @@ -3,7 +3,11 @@ import IconButton from "@smui/icon-button"; import Snackbar, { Actions } from "@smui/snackbar"; import FileUpload from "./FileUpload.svelte"; - import { parseLabScheduleFile, parsePTFile } from "../logic/EditorActions"; + import { + parseDatabaseFile, + parseLabScheduleFile, + parsePTFile, + } from "../logic/EditorActions"; import { labStore, ptStore } from "../stores"; let ptSchedules: FileList | null; @@ -50,10 +54,48 @@ } $: { - if (dbFile?.length) console.log(dbFile); + if (dbFile?.length) { + parseDatabaseFile(dbFile[0]) + .then((database) => { + labStore.set(database.labs); + ptStore.set(database.peerTeachers); + }) + .catch(() => { + snackbarText = "Failed to import database. See console for details."; + snackbar.open(); + }); + } } - function exportDB() {} + function exportDB() { + const peerTeachers = [...$ptStore.values()]; + const labs = [...$labStore.values()]; + const database = { + labs: labs, + peerTeachers: peerTeachers, + }; + + const dbObj = JSON.stringify(database, (_, value) => { + // Need to manually convert the PeerTeacher objects' + // `labs` set to an array because `JSON.stringify` doesn't + // support "stringing" it out of the box + if (typeof value === "object" && value instanceof Set) { + return [...value]; + } + return value; + }); + + const blob = new Blob([dbObj], { type: "text/json" }); + const anchor = document.createElement("a"); + const url = window.URL.createObjectURL(blob); + anchor.href = url; + anchor.download = "pt-db.json"; + anchor.style.display = "none"; + document.body.appendChild(anchor); + anchor.click(); + document.body.removeChild(anchor); + window.URL.revokeObjectURL(url); + }
diff --git a/src/logic/EditorActions.ts b/src/logic/EditorActions.ts index 875fdc4..4a83fbc 100644 --- a/src/logic/EditorActions.ts +++ b/src/logic/EditorActions.ts @@ -1,4 +1,4 @@ -import { parseLabSchedule, parsePTSchedule } from "../util/parser"; +import { parseDatabase, parseLabSchedule, parsePTSchedule } from "../util/parser"; export async function parsePTFile(file: File) { try { @@ -20,3 +20,14 @@ export async function parseLabScheduleFile(file: File) { throw error; } } + +export async function parseDatabaseFile(file: File) { + const text = await file.text(); + try { + const database = JSON.parse(text); + return parseDatabase(database); + } catch (error) { + console.error(file.name, error); + throw error; + } +} diff --git a/src/models/EventInfo.ts b/src/models/EventInfo.ts index d20682c..6fce60b 100644 --- a/src/models/EventInfo.ts +++ b/src/models/EventInfo.ts @@ -1,3 +1,9 @@ +interface EventInfoSerializeInfo { + days: string, + start: number, + end: number +} + export default class EventInfo { days: string; start: number; @@ -16,6 +22,10 @@ export default class EventInfo { this.end = end; } + static fromJSON({days, start, end}: EventInfoSerializeInfo) { + return new EventInfo(days, start, end); + } + static timeToStr(time: number) { let hour = Math.floor(time / 100); const minute = time % 100; diff --git a/src/models/Lab.ts b/src/models/Lab.ts index 8636f74..d23cffb 100644 --- a/src/models/Lab.ts +++ b/src/models/Lab.ts @@ -1,4 +1,16 @@ -import type EventInfo from "./EventInfo"; +import EventInfo from "./EventInfo"; + +interface LabSerializeInfo { + course: number, + section: number, + event: { + days: string, + start: number, + end: number + }, + building: string, + room: string +} export default class Lab { id: number; @@ -24,6 +36,10 @@ export default class Lab { this.room = room; } + static fromJSON({course, section, event, building, room}: LabSerializeInfo) { + return new Lab(course, section, EventInfo.fromJSON(event), building, room); + } + get time() { return this.event.info; } diff --git a/src/models/PeerTeacher.ts b/src/models/PeerTeacher.ts index 6518a80..f1a8739 100644 --- a/src/models/PeerTeacher.ts +++ b/src/models/PeerTeacher.ts @@ -1,4 +1,16 @@ -import type EventInfo from "./EventInfo"; +import EventInfo from "./EventInfo"; + +interface PeerTeacherSerializeInfo { + id: number, + firstname: string, + lastname: string, + events: { + days: string, + start: number, + end: number + }[], + labs: number[] +} export default class PeerTeacher { id: number; @@ -18,4 +30,11 @@ export default class PeerTeacher { this.events = []; this.labs = new Set(); } + + static fromJSON({id, firstname, lastname, events, labs}: PeerTeacherSerializeInfo) { + const pt = new PeerTeacher(id, firstname, lastname); + pt.events = events.map(e => EventInfo.fromJSON(e)); + pt.labs = new Set(labs); + return pt; + } } \ No newline at end of file diff --git a/src/util/parser.ts b/src/util/parser.ts index 6d706d7..2f1bdd4 100644 --- a/src/util/parser.ts +++ b/src/util/parser.ts @@ -24,6 +24,32 @@ interface LabSchedule { }[] }; +interface DatabaseFile { + labs: { + id: number, + course: number, + section: number, + event: { + days: string, + start: number, + end: number + }, + building: string, + room: string + }[], + peerTeachers: { + id: number, + firstname: string, + lastname: string, + events: { + days: string, + start: number, + end: number + }[], + labs: number[] + }[] +} + /** * Parses a peer teacher schedule * @param schedule The schedule to parse @@ -104,4 +130,26 @@ export function parseLabSchedule(schedule: LabSchedule) { } return results; +} + +/** + * Parses a database file into maps of Lab and PeerTeacher objects + * @param database The database object from a database file + * @returns And object with lab and peer teacher maps + */ +export function parseDatabase(database: DatabaseFile) { + const result = { + labs: new Map(), + peerTeachers: new Map() + } + + database.labs.forEach(lab => { + result.labs.set(lab.id, Lab.fromJSON(lab)); + }); + + database.peerTeachers.forEach(pt => { + result.peerTeachers.set(pt.id, PeerTeacher.fromJSON(pt)); + }); + + return result; } \ No newline at end of file -- cgit v1.2.3