Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Status Updates and Cancelled Courses #694

Merged
merged 1 commit into from
Nov 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions backend/courses/management/commands/loadstatus.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import asyncio
import json
import logging

from django.core.management.base import BaseCommand
from tqdm import tqdm

from courses import registrar
from courses.management.commands.sync_path_status import (
get_all_course_status_path,
get_department_codes,
)
from courses.models import Course, Section
from courses.util import (
get_course_and_section,
Expand All @@ -25,6 +30,9 @@ def set_all_status(semester=None, add_status_update=False, verbose=False):
statuses_out_of_sync = []
status_updates_out_of_sync = []

department_codes = get_department_codes()
path_course_to_status = asyncio.run(get_all_course_status_path(semester, department_codes))

for status in tqdm(statuses):
section_code = status.get("section_id_normalized")
if section_code is None:
Expand All @@ -45,6 +53,10 @@ def set_all_status(semester=None, add_status_update=False, verbose=False):
except (Section.DoesNotExist, Course.DoesNotExist):
continue

if section_code in path_course_to_status:
# Defer judgement to Path@Penn status
course_status = path_course_to_status[section_code]

last_status_update = section.last_status_update
current_status = section.status

Expand Down
2 changes: 1 addition & 1 deletion backend/courses/management/commands/registrarimport.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def registrar_import(semester=None, query=""):
dept.save()

print("Loading course statuses from registrar...")
set_all_status(semester=semester)
set_all_status(semester=semester, add_status_update=True)

recompute_parent_courses(semesters=[semester], verbose=True)
recompute_soft_state(semesters=[semester], verbose=True)
Expand Down
53 changes: 43 additions & 10 deletions backend/courses/management/commands/sync_path_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,46 +22,75 @@
)


def map_path_to_opendata(course_status):
return "O" if course_status == "A" else "C"
def map_path_to_opendata(course_status: str) -> str:
match course_status:
case "A":
return "O"
case "F":
return "C"
case _:
return "X"


def normalize_status(course_status: str) -> str:
match course_status:
case "O":
return "Open"
case "C":
return "Closed"
case _:
return "Cancelled"


def denormalize_section_code(section_code: str) -> str:
return "".join(section_code.split("-"))


def format_webhook_request_body(
section_code: str, new_course_status: str, semester: str
section_code: str, previous_course_status: str, new_course_status: str, semester: str
) -> Dict[str, str]:
return {
"previous_status": "C" if new_course_status == "O" else "O",
"previous_status": previous_course_status,
"status": new_course_status,
"status_code_normalized": "Open" if new_course_status == "O" else "Closed",
"status_code_normalized": normalize_status(new_course_status),
"section_id": denormalize_section_code(section_code),
"section_id_normalized": section_code,
"term": semester,
}


async def send_webhook_request(
async_session: aiohttp.ClientSession, semester: str, course: str, course_status: str
async_session: aiohttp.ClientSession,
semester: str,
course: str,
previous_course_status: str,
course_status: str,
) -> None:
async with webhook_semaphore:
await async_session.post(
url="https://penncoursealert.com/webhook",
data=json.dumps(format_webhook_request_body(course, course_status, semester)),
data=json.dumps(
format_webhook_request_body(course, previous_course_status, course_status, semester)
),
headers={"Content-Type": "application/json", "Authorization": f"Basic {auth.decode()}"},
)


async def send_webhook_requests(
semester: str, course_list: List[str], path_course_to_status: Dict[str, str]
semester: str,
course_list: List[str],
db_course_to_status: Dict[str, str],
path_course_to_status: Dict[str, str],
) -> None:
async with aiohttp.ClientSession() as async_session:
tasks = [
asyncio.create_task(
coro=send_webhook_request(
async_session, semester, course, path_course_to_status[course]
async_session,
semester,
course,
db_course_to_status[course],
path_course_to_status[course],
)
)
for course in course_list
Expand Down Expand Up @@ -162,7 +191,11 @@ def resolve_path_differences(send_data_to_slack=False, verbose=False):
if verbose:
print(f"Inconsistent Courses: {inconsistent_courses}")

asyncio.run(send_webhook_requests(semester, inconsistent_courses, path_course_to_status))
asyncio.run(
send_webhook_requests(
semester, inconsistent_courses, db_course_to_status, path_course_to_status
)
)
if verbose and inconsistent_courses:
print("Sent updates to webhook.")

Expand Down
2 changes: 1 addition & 1 deletion k8s/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export class MyChart extends PennLabsChart {
})

new CronJob(this, 'sync-path-course-statuses', {
schedule: cronTime.everyHour(),
schedule: cronTime.every(30).minutes(),
image: backendImage,
secret,
cmd: ['python', 'manage.py', 'sync_path_status', '--slack'],
Expand Down
Loading