22
22
import json
23
23
import requests
24
24
import utils
25
+ from utils import Term
25
26
26
27
URL = "https://fireroad.mit.edu/courses/all?full=true"
27
28
@@ -88,20 +89,19 @@ def parse_section(section):
88
89
return [slots , place ]
89
90
90
91
91
- def parse_schedule (course ):
92
+ def parse_schedule (schedule ):
92
93
"""
93
94
Parses the schedule string, which looks like:
94
95
"Lecture,32-123/TR/0/11/F/0/2;Recitation,2-147/MW/0/10,2-142/MW/0/11"
95
96
96
97
Args:
97
- * course (dict[ str, Union[bool, float, int, list[str], str]] ): The course object .
98
+ * schedule ( str): The schedule string .
98
99
99
100
Returns:
100
101
* dict[str, union[list, bool]: The parsed schedule
101
102
102
103
Raises AssertionError or KeyError if parse_section does.
103
104
"""
104
- schedule = course ["schedule" ]
105
105
section_tba = False
106
106
result = {}
107
107
@@ -205,18 +205,19 @@ def parse_prereqs(course):
205
205
return {"prereqs" : prereqs }
206
206
207
207
208
- def get_course_data (courses , course ):
208
+ def get_course_data (courses , course , term ):
209
209
"""
210
210
Parses a course from the Fireroad API, and puts it in courses. Skips the
211
- courses Fireroad doesn't have schedule info for . Returns False if skipped,
211
+ courses that are not offered in the current term . Returns False if skipped,
212
212
True otherwise. The `courses` variable is modified in place.
213
213
214
214
Args:
215
215
* courses (list[dict[str, Union[bool, float, int, list[str], str]]]): The list of courses.
216
216
* course (dict[str, Union[bool, float, int, list[str], str]]): The course in particular.
217
+ * term (Term): The current term (fall, IAP, or spring).
217
218
218
219
Returns:
219
- * bool: Whether Fireroad has schedule information for this course .
220
+ * bool: Whether the course was entered into courses .
220
221
"""
221
222
course_code = course ["subject_id" ]
222
223
course_num , course_class = course_code .split ("." )
@@ -226,41 +227,72 @@ def get_course_data(courses, course):
226
227
"subject" : course_class ,
227
228
}
228
229
229
- if "schedule" not in course :
230
- # TODO: Do something else with this?
231
- return False
230
+ # terms, prereqs
231
+ raw_class . update ( parse_terms ( course ))
232
+ raw_class . update ( parse_prereqs ( course ))
232
233
233
- # tb, s, l, r, b, lr, rr, br
234
- try :
235
- raw_class .update (parse_schedule (course ))
236
- except Exception as e :
237
- # if we can't parse the schedule, warn
238
- print (f"Can't parse schedule { course_code } : { e !r} " )
234
+ if term .name not in raw_class ["terms" ]:
239
235
return False
240
236
241
- # hh, ha, hs, he, ci, cw, re, la, pl
237
+ has_schedule = "schedule" in course
238
+
239
+ # tba, sectionKinds, lectureSections, recitationSections, labSections,
240
+ # designSections, lectureRawSections, recitationRawSections, labRawSections,
241
+ # designRawSections
242
+ if has_schedule :
243
+ try :
244
+ if term == Term .FA and "scheduleFall" in course :
245
+ raw_class .update (parse_schedule (course ["scheduleFall" ]))
246
+ elif term == Term .JA and "scheduleIAP" in course :
247
+ raw_class .update (parse_schedule (course ["scheduleIAP" ]))
248
+ elif term == Term .SP and "scheduleSpring" in course :
249
+ raw_class .update (parse_schedule (course ["scheduleSpring" ]))
250
+ else :
251
+ raw_class .update (parse_schedule (course ["schedule" ]))
252
+ except Exception as e :
253
+ # if we can't parse the schedule, warn
254
+ print (f"Can't parse schedule { course_code } : { e !r} " )
255
+ has_schedule = False
256
+ if not has_schedule :
257
+ raw_class .update (
258
+ {
259
+ "tba" : False ,
260
+ "sectionKinds" : [],
261
+ "lectureSections" : [],
262
+ "recitationSections" : [],
263
+ "labSections" : [],
264
+ "designSections" : [],
265
+ "lectureRawSections" : [],
266
+ "recitationRawSections" : [],
267
+ "labRawSections" : [],
268
+ "designRawSections" : [],
269
+ }
270
+ )
271
+
272
+ # hassH, hassA, hassS, hassE, cih, cihw, rest, lab, partLab
242
273
raw_class .update (parse_attributes (course ))
243
- raw_class .update (
244
- {
245
- "lectureUnits" : course ["lecture_units" ],
246
- "labUnits" : course ["lab_units" ],
247
- "preparationUnits" : course ["preparation_units" ],
248
- "level" : course ["level" ],
249
- "isVariableUnits" : course ["is_variable_units" ],
250
- "same" : ", " .join (course .get ("joint_subjects" , [])),
251
- "meets" : ", " .join (course .get ("meets_with_subjects" , [])),
252
- }
253
- )
254
- # This should be the case with variable-units classes, but just to make sure.
274
+ try :
275
+ raw_class .update (
276
+ {
277
+ "lectureUnits" : course ["lecture_units" ],
278
+ "labUnits" : course ["lab_units" ],
279
+ "preparationUnits" : course ["preparation_units" ],
280
+ "level" : course ["level" ],
281
+ "isVariableUnits" : course ["is_variable_units" ],
282
+ "same" : ", " .join (course .get ("joint_subjects" , [])),
283
+ "meets" : ", " .join (course .get ("meets_with_subjects" , [])),
284
+ }
285
+ )
286
+ except KeyError as e :
287
+ print (f"Can't parse { course_code } : { e !r} " )
288
+ return False
289
+ # This should be the case with variable-units classes, but just to make
290
+ # sure.
255
291
if raw_class ["isVariableUnits" ]:
256
292
assert raw_class ["lectureUnits" ] == 0
257
293
assert raw_class ["labUnits" ] == 0
258
294
assert raw_class ["preparationUnits" ] == 0
259
295
260
- # t, pr
261
- raw_class .update (parse_terms (course ))
262
- raw_class .update (parse_prereqs (course ))
263
-
264
296
raw_class .update (
265
297
{
266
298
"description" : course .get ("description" , "" ),
@@ -271,7 +303,7 @@ def get_course_data(courses, course):
271
303
}
272
304
)
273
305
274
- # nx, rp, u, f, hf, lm are from catalog.json, not here
306
+ # nonext, repeat, url, final, half, limited are from catalog.json, not here
275
307
276
308
if "old_id" in course :
277
309
raw_class ["oldNumber" ] = course ["old_id" ]
@@ -289,27 +321,33 @@ def get_course_data(courses, course):
289
321
return True
290
322
291
323
292
- def run ():
324
+ def run (is_semester_term ):
293
325
"""
294
326
The main entry point. All data is written to `fireroad.json`.
295
327
296
- There are no arguments and there is no return value.
328
+ Args:
329
+ * is_semester_term (bool): whether to look at the semester term (fall/spring) or the pre-semester term (summer/IAP).
330
+
331
+ Returns: none
297
332
"""
298
333
text = requests .get (URL ).text
299
334
data = json .loads (text )
300
335
courses = dict ()
336
+ term = utils .url_name_to_term (utils .get_term_info (is_semester_term )["urlName" ])
337
+ fname = "fireroad-sem.json" if is_semester_term else "fireroad-presem.json"
301
338
missing = 0
302
339
303
340
for course in data :
304
- has_schedule = get_course_data (courses , course )
305
- if not has_schedule :
341
+ included = get_course_data (courses , course , term )
342
+ if not included :
306
343
missing += 1
307
344
308
- with open ("fireroad.json" , "w" ) as f :
345
+ with open (fname , "w" ) as f :
309
346
json .dump (courses , f )
310
347
print (f"Got { len (courses )} courses" )
311
- print (f"Skipped { missing } courses due to missing schedules " )
348
+ print (f"Skipped { missing } courses that are not offered in the { term . value } term " )
312
349
313
350
314
351
if __name__ == "__main__" :
315
- run ()
352
+ run (False )
353
+ run (True )
0 commit comments