Skip to content

Commit 08d1cf9

Browse files
committed
implement loading subjects (todo: consider just removing cookies dialog and adding privacy policy?)
1 parent 6a02de8 commit 08d1cf9

File tree

6 files changed

+264
-29
lines changed

6 files changed

+264
-29
lines changed

package.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"@solidjs/meta": "^0.29.4",
2727
"@solidjs/router": "^0.15.2",
2828
"@solidjs/start": "^1.0.11",
29+
"localforage": "^1.10.0",
2930
"lucide-solid": "^0.469.0",
3031
"solid-js": "^1.9.4",
3132
"ua-parser-js": "^2.0.0",
@@ -64,5 +65,8 @@
6465
"url": "git+https://github.com/sipb/courseroad3.git"
6566
},
6667
"license": "MIT",
67-
"keywords": ["mit", "courseroad"]
68+
"keywords": [
69+
"mit",
70+
"courseroad"
71+
]
6872
}

pnpm-lock.yaml

+22
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/context/component.tsx

+161-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { makePersisted } from "@solid-primitives/storage";
22
import { type ParentComponent, createResource } from "solid-js";
33
import { createStore, produce, reconcile } from "solid-js/store";
4+
import { isServer } from "solid-js/web";
5+
6+
import localforage from "localforage";
47

58
import {
69
CourseDataContext,
@@ -14,6 +17,7 @@ const CourseDataProvider: ParentComponent = (props) => {
1417
createStore(structuredClone(defaultState)),
1518
{
1619
name: "courseRoadStore",
20+
storage: !isServer ? localforage : undefined,
1721
},
1822
);
1923

@@ -179,8 +183,130 @@ const CourseDataProvider: ParentComponent = (props) => {
179183
}),
180184
);
181185
},
182-
parseGenericCourses: () => {},
183-
parseGenericIndex: () => {},
186+
parseGenericCourses: () => {
187+
setStore(
188+
"genericCourses",
189+
produce((genericCourses) => {
190+
// clears array
191+
genericCourses.length = 0;
192+
193+
const girAttributes = {
194+
PHY1: ["Physics 1 GIR", "p1"],
195+
PHY2: ["Physics 2 GIR", "p2"],
196+
CHEM: ["Chemistry GIR", "c"],
197+
BIOL: ["Biology GIR", "b"],
198+
CAL1: ["Calculus I GIR", "m1"],
199+
CAL2: ["Calculus II GIR", "m2"],
200+
LAB: ["Lab GIR", "l1"],
201+
REST: ["REST GIR", "r"],
202+
} as const;
203+
204+
const hassAttributes = {
205+
"HASS-A": ["HASS Arts", "ha"],
206+
"HASS-S": ["HASS Social Sciences", "hs"],
207+
"HASS-H": ["HASS Humanities", "hh"],
208+
"HASS-E": ["HASS Elective", "ht"],
209+
} as const;
210+
211+
const ciAttributes = {
212+
"CI-H": ["Communication Intensive", "hc"],
213+
"CI-HW": ["Communication Intensive with Writing", "hw"],
214+
} as const;
215+
216+
const baseGeneric = {
217+
description:
218+
"Use this generic subject to indicate that you are fulfilling a requirement, but do not yet have a specific subject selected.",
219+
total_units: 12,
220+
} as const;
221+
222+
const baseurl =
223+
"http://student.mit.edu/catalog/search.cgi?search=&style=verbatim&when=*&termleng=4&days_offered=*&start_time=*&duration=*&total_units=*" as const;
224+
225+
for (const gir in girAttributes) {
226+
const offeredGir = actions.getMatchingAttributes(
227+
gir,
228+
undefined,
229+
undefined,
230+
);
231+
232+
genericCourses.push({
233+
...baseGeneric,
234+
...offeredGir,
235+
236+
gir_attribute: gir as keyof typeof girAttributes,
237+
title: `Generic ${girAttributes[gir as keyof typeof girAttributes][0]}`,
238+
subject_id: gir,
239+
url: `${baseurl}&cred=${girAttributes[gir as keyof typeof girAttributes][1]}&commun_int=*`,
240+
});
241+
}
242+
243+
for (const hass in hassAttributes) {
244+
const offeredHass = actions.getMatchingAttributes(
245+
undefined,
246+
hass,
247+
undefined,
248+
);
249+
250+
genericCourses.push({
251+
...baseGeneric,
252+
...offeredHass,
253+
hass_attribute: hass as keyof typeof hassAttributes,
254+
title: `Generic ${hass}`,
255+
subject_id: hass,
256+
url: `${baseurl}&cred=${hassAttributes[hass as keyof typeof hassAttributes][1]}&commun_int=*`,
257+
});
258+
259+
const offeredHassCI = actions.getMatchingAttributes(
260+
undefined,
261+
hass,
262+
"CI-H",
263+
);
264+
265+
genericCourses.push({
266+
...baseGeneric,
267+
...offeredHassCI,
268+
hass_attribute: hass as keyof typeof hassAttributes,
269+
communication_requirement: "CI-H",
270+
title: `Generic CI-H ${hass}`,
271+
subject_id: `CI-H ${hass}`,
272+
url: `${baseurl}&cred=${hassAttributes[hass as keyof typeof hassAttributes][1]}&commun_int=${ciAttributes["CI-H"][1]}`,
273+
});
274+
}
275+
276+
for (const ci in ciAttributes) {
277+
const offeredCI = actions.getMatchingAttributes(
278+
undefined,
279+
undefined,
280+
ci,
281+
);
282+
283+
genericCourses.push({
284+
...baseGeneric,
285+
...offeredCI,
286+
communication_requirement: ci as keyof typeof ciAttributes,
287+
title: `Generic ${ci}`,
288+
hass_attribute: "HASS",
289+
subject_id: ci as keyof typeof ciAttributes,
290+
url: `${baseurl}&cred=*&commun_int=${ciAttributes[ci as keyof typeof ciAttributes][1]}`,
291+
});
292+
}
293+
}),
294+
);
295+
},
296+
parseGenericIndex: () => {
297+
setStore(
298+
"genericIndex",
299+
reconcile(
300+
store.genericCourses.reduce(
301+
(obj, item, index) => {
302+
obj[item.subject_id] = index;
303+
return obj;
304+
},
305+
{} as Record<string, number>,
306+
),
307+
),
308+
);
309+
},
184310
parseSubjectsIndex: () => {},
185311
popClassStack: () => {},
186312
pushClassStack: (id) => {},
@@ -209,7 +335,9 @@ const CourseDataProvider: ParentComponent = (props) => {
209335
setActiveRoad: (activeRoad) => {
210336
setStore("activeRoad", activeRoad);
211337
},
212-
setFullSubjectsInfoLoaded: (isFull) => {},
338+
setFullSubjectsInfoLoaded: (isFull) => {
339+
setStore("fullSubjectsInfoLoaded", isFull);
340+
},
213341
setLoggedIn: (newLoggedIn) => {
214342
setStore("loggedIn", newLoggedIn);
215343
},
@@ -243,7 +371,9 @@ const CourseDataProvider: ParentComponent = (props) => {
243371
}),
244372
);
245373
},
246-
setSubjectsInfo: (data) => {},
374+
setSubjectsInfo: (data) => {
375+
setStore("subjectsInfo", reconcile(data));
376+
},
247377
setCurrentSemester: (sem) => {
248378
setStore("currentSemester", Math.max(1, sem));
249379
},
@@ -255,11 +385,34 @@ const CourseDataProvider: ParentComponent = (props) => {
255385
resetFulfillmentNeeded: () => {
256386
setStore("fulfillmentNeeded", "all");
257387
},
258-
setLoadSubjectsPromise: (promise) => {},
259-
setSubjectsLoaded: () => {},
388+
setLoadSubjectsPromise: (promise) => {
389+
setStore("loadSubjectsPromise", promise);
390+
},
391+
setSubjectsLoaded: () => {
392+
setStore("subjectsLoaded", true);
393+
},
260394
queueRoadMigration: (roadID) => {},
261-
clearMigrationQueue: () => {},
262-
loadSubjects: async () => {},
395+
clearMigrationQueue: () => {
396+
setStore("roadsToMigrate", reconcile([]));
397+
},
398+
loadAllSubjects: async () => {
399+
const promise = fetch(
400+
`${import.meta.env.VITE_FIREROAD_URL}/courses/all?full=true`,
401+
).then((response) => response.json() as Promise<SubjectFull[]>);
402+
403+
actions.setLoadSubjectsPromise(promise);
404+
const response = await promise;
405+
actions.setSubjectsLoaded();
406+
actions.setSubjectsInfo(response);
407+
actions.setFullSubjectsInfoLoaded(true);
408+
actions.parseGenericCourses();
409+
actions.parseGenericIndex();
410+
actions.parseSubjectsIndex();
411+
for (const roadID of store.roadsToMigrate) {
412+
actions.migrateOldSubjects(roadID);
413+
}
414+
actions.clearMigrationQueue();
415+
},
263416
addAtPlaceholder: (index) => {},
264417
waitLoadSubjects: async () => {},
265418
waitAndMigrateOldSubjects: (roadID) => {},

src/context/create.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export const defaultActions = {
122122
setSubjectsLoaded: () => {},
123123
queueRoadMigration: (roadID: string) => {},
124124
clearMigrationQueue: () => {},
125-
loadSubjects: async () => {},
125+
loadAllSubjects: async () => {},
126126
addAtPlaceholder: (index: number) => {},
127127
waitLoadSubjects: async () => {},
128128
waitAndMigrateOldSubjects: (roadID: string) => {},

src/context/types.ts

+13-13
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export interface Subject {
66
offered_IAP: boolean;
77
offered_spring: boolean;
88
offered_summer: boolean;
9-
public: boolean;
9+
public?: boolean;
1010
semester?: number; // specifically for when in a road...
1111
units?: number; // is manually set in some places
1212
index?: number; // is also manually set idk why
@@ -21,32 +21,32 @@ export interface Subject {
2121
not_offered_year?: string;
2222
instructors?: string[];
2323
communication_requirement?: "CI-H" | "CI-HW";
24-
hass_attribute?: "HASS-A" | "HASS-E" | "HASS-H" | "HASS-S";
24+
hass_attribute?: "HASS-A" | "HASS-E" | "HASS-H" | "HASS-S" | "HASS";
2525
gir_attribute?:
2626
| "BIOL"
2727
| "CAL1"
2828
| "CAL2"
2929
| "CHEM"
3030
| "LAB"
3131
| "LAB2"
32-
| "GIR:PHY1"
33-
| "GIR:PHY2"
34-
| "GIR:REST";
32+
| "PHY1"
33+
| "PHY2"
34+
| "REST";
3535
children?: Subject["subject_id"][];
3636
parent?: Subject["subject_id"];
3737
prereqs?: string;
3838
virtual_status?: "In-Person" | "Virtual";
39-
old_id: string;
39+
old_id?: string;
4040
}
4141

4242
export interface SubjectFull extends Subject {
43-
lecture_units: number;
44-
lab_units: number;
45-
design_units: number;
46-
preparation_units: number;
47-
is_variable_units: boolean;
48-
is_half_class: boolean;
49-
has_final: boolean;
43+
lecture_units?: number;
44+
lab_units?: number;
45+
design_units?: number;
46+
preparation_units?: number;
47+
is_variable_units?: boolean;
48+
is_half_class?: boolean;
49+
has_final?: boolean;
5050
description?: string;
5151
prerequisites?: string;
5252
corequisites?: string;

0 commit comments

Comments
 (0)