Spaces:
Paused
Paused
import aiohttp | |
import pandas as pd | |
from io import StringIO | |
from bs4 import BeautifulSoup | |
from constants.constants import vtop_process_timetable_url | |
from models.period import Period | |
from utils.payloads import get_timetable_payload | |
DAYS_MAP = { | |
"MON": "Monday", | |
"TUE": "Tuesday", | |
"WED": "Wednesday", | |
"THU": "Thursday", | |
"FRI": "Friday", | |
"SAT": "Saturday", | |
"SUN": "Sunday", | |
} | |
VALID_DAYS = {"Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"} | |
async def _get_timetable_page( | |
sess: aiohttp.ClientSession, username: str, semID: str, csrf: str | |
) -> str: | |
async with sess.post( | |
vtop_process_timetable_url, data=get_timetable_payload(username, semID, csrf) | |
) as req: | |
return await req.text() | |
def _get_course_code_name_dict(soup: BeautifulSoup) -> dict: | |
course_data = soup.find("div", attrs={"id": "studentDetailsList"}).find_all( | |
"td", | |
attrs={ | |
"style": "padding: 3px; font-size: 12px; border-color: #b2b2b2;vertical-align: middle;" | |
}, | |
) | |
return { | |
data.text.split("-")[0].strip(): data.text.split("-")[1].split("\n")[0].strip() | |
for data in course_data | |
} | |
def _parse_course_vals(cell_str: str): | |
temp_arr = str(cell_str).strip().split("-") | |
course_code = temp_arr[1] | |
cls = "-".join(temp_arr[3 : len(temp_arr) - 1]) | |
return course_code, cls | |
def _get_end_time(start_time: str, is_theory: bool = True): | |
if is_theory: | |
return f'{start_time.split(":")[0]}:50' | |
else: | |
return f'{int(start_time.split(":")[0]) + 1}:40' | |
def _parse_timetable(timetable_page: str): | |
timetable = {day: [] for day in VALID_DAYS} | |
soup = BeautifulSoup(timetable_page, "lxml") | |
course_code_dict = _get_course_code_name_dict(soup) | |
dataframes = pd.read_html(StringIO(timetable_page)) | |
course_details, timetable_df = dataframes[0], dataframes[1] | |
for row in timetable_df.itertuples(index=False): | |
if len(row) < 2 or row[1].lower() not in {"theory", "lab"}: | |
continue | |
day = DAYS_MAP.get(row[0], "Sunday") | |
is_theory = row[1].lower() == "theory" | |
if day not in timetable: | |
continue | |
for col_idx, cell in enumerate(row[2:], start=2): | |
cell_str = str(cell).strip() | |
if len(cell_str) > 3 and cell_str.count("-") >= 3: | |
code, location = _parse_course_vals(cell_str) | |
class_id = course_details.loc[ | |
course_details["Slot - Venue"].str.contains( | |
cell_str.split("-")[0], na=False | |
), | |
"Class Nbr", | |
].iloc[0] | |
start_time = timetable_df.iloc[0, col_idx] | |
period = Period( | |
class_id=class_id, | |
slot=course_details.loc[ | |
course_details["Class Nbr"] == class_id, "Slot - Venue" | |
] | |
.iloc[0] | |
.split(" - ")[0], | |
courseName=course_code_dict[code], | |
code=code, | |
location=location, | |
startTime=start_time, | |
endTime=_get_end_time(start_time, is_theory), | |
) | |
if period not in timetable[day]: | |
timetable[day].append(period) | |
return timetable | |
async def get_timetable_data( | |
sess: aiohttp.ClientSession, username: str, semID: str, csrf: str | |
): | |
timetable = _parse_timetable(await _get_timetable_page(sess, username, semID, csrf)) | |
for key in timetable: | |
timetable[key].sort() | |
return { | |
key: [period.to_dict() for period in period_list] | |
for key, period_list in timetable.items() | |
} | |