-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathregistrar.py
122 lines (98 loc) · 3.56 KB
/
registrar.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import json
import logging
import requests
from django.conf import settings
from tqdm import tqdm
from courses.util import get_current_semester, translate_semester
logger = logging.getLogger(__name__)
def get_token():
r = requests.post(
settings.OPEN_DATA_TOKEN_URL,
data={"grant_type": "client_credentials"},
auth=(settings.OPEN_DATA_CLIENT_ID, settings.OPEN_DATA_OIDC_SECRET),
)
if not r.ok:
raise ValueError(f"OpenData token URL responded with status code {r.status_code}: {r.text}")
return r.json()["access_token"]
def get_headers():
return {
"Authorization": "Bearer " + get_token(),
}
def make_api_request(params):
headers = get_headers()
url = f"{settings.OPEN_DATA_API_BASE}/v1/"
semester = params.get("term")
assert semester is not None, "make_api_request expects term param"
current_semester = get_current_semester()
if (
semester >= current_semester
or semester.endswith("B")
and (semester[:-1] + "C") >= current_semester
):
url += "course_section_search"
else:
url += "course_section_history_search"
r = requests.get(
url,
params=params,
headers=headers,
)
if not r.ok:
raise ValueError(f"OpenData API request failed with status code {r.status_code}: {r.text}")
return r.json()
def report_api_error(err):
try:
msg = json.loads(err)
logger.error(msg.get("service_meta", {}).get("error_text", "no error text"))
except json.JSONDecodeError:
logger.error("Penn API error", extra={"error_msg": err})
def get_all_course_status(semester):
semester = translate_semester(semester)
headers = get_headers()
url = f"{settings.OPEN_DATA_API_BASE}/v1/course_section_status/{semester}/all"
r = requests.get(url, headers=headers)
if r.status_code == requests.codes.ok:
return r.json().get("result_data", [])
else:
report_api_error(r.text)
raise RuntimeError(
f"Registrar API request failed with code {r.status_code}. "
f'Message returned: "{r.text}"'
)
def get_departments():
headers = get_headers()
url = f"{settings.OPEN_DATA_API_BASE}/v1/course_section_search_parameters"
r = requests.get(url, headers=headers)
if r.status_code == requests.codes.ok:
result_data = r.json().get("result_data", [])
if len(result_data) > 0:
return result_data[0]["subjects_map"]
else:
raise ValueError("OpenData API returned data with no populated result_data field.")
else:
raise ValueError(f"OpenData API responded with status code {r.status_code}: {r.text}.")
def get_courses(query, semester):
semester = translate_semester(semester)
params = {
"section_id": query,
"term": semester,
"page_number": 1,
"number_of_results_per_page": 200,
}
results = []
pbar = None
while True:
logger.info("making request for page #%d" % params["page_number"])
data = make_api_request(params)
if pbar is None:
pbar = tqdm(total=data["service_meta"]["number_of_pages"])
pbar.update(1)
next_page = data["service_meta"]["next_page_number"]
results.extend(data["result_data"])
if not next_page or int(next_page) <= params["page_number"]:
break
params["page_number"] = next_page
if pbar is not None:
pbar.close()
distinct_results = {r["section_id"]: r for r in results if r["section_id"]}.values()
return distinct_results