File size: 3,860 Bytes
5161c7a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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()
    }