host-fastapi / get_timetable.py
sanjay7178's picture
Upload 31 files
5161c7a verified
raw
history blame
3.86 kB
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()
}