-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add attendance * Minor refactor * Remove unused properties * Add TODO
- Loading branch information
1 parent
897541d
commit 8607303
Showing
6 changed files
with
159 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
from .course import Course | ||
from .course import Course, Attendance | ||
from .profile import Profile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,29 @@ | ||
from typing import Optional | ||
|
||
|
||
class Attendance: | ||
def __init__( | ||
self, | ||
attended_classes: Optional[int] = None, | ||
total_classes: Optional[int] = None, | ||
percentage: Optional[float] = None | ||
): | ||
self.attended_classes = attended_classes | ||
self.total_classes = total_classes | ||
self.percentage = percentage | ||
|
||
def __str__(self): | ||
return f"{self.__dict__}" | ||
|
||
|
||
class Course: | ||
def __init__(self, code: str, title: str, _type: str, status: str): | ||
def __init__(self, code: str, title: str, _type: Optional[str] = None, status: Optional[str] = None, | ||
attendance: Optional[Attendance] = None): | ||
self.code = code | ||
self.title = title | ||
self.type = _type | ||
self.status = status | ||
self.attendance = attendance | ||
|
||
def __str__(self): | ||
return f"{self.__dict__}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import datetime | ||
from typing import Optional | ||
|
||
import requests_html | ||
from bs4 import BeautifulSoup | ||
|
||
from pesu_academy.models import Course, Attendance | ||
|
||
|
||
def get_attendance_in_semester(session: requests_html.HTMLSession, semester_value: Optional[int] = None): | ||
try: | ||
url = "https://www.pesuacademy.com/Academy/s/studentProfilePESUAdmin" | ||
query = { | ||
"menuId": "660", | ||
"controllerMode": "6407", | ||
"actionType": "8", | ||
"batchClassId": f"{semester_value}", | ||
"_": str(int(datetime.datetime.now().timestamp() * 1000)), | ||
} | ||
response = session.get(url, allow_redirects=False, params=query) | ||
if response.status_code != 200: | ||
raise ConnectionError("Unable to fetch attendance data.") | ||
soup = BeautifulSoup(response.text, "lxml") | ||
except Exception: | ||
raise ConnectionError("Unable to fetch profile data.") | ||
|
||
attendance = [] | ||
table = soup.find("table", attrs={"class": "table box-shadow"}) | ||
table_body = table.find("tbody") | ||
for row in table_body.find_all("tr"): | ||
columns = row.find_all("td") | ||
if len(columns) == 1 and columns[0].text.strip() == 'Data Not\n\t\t\t\t\tAvailable': | ||
break | ||
course_code = columns[0].text.strip() | ||
course_title = columns[1].text.strip() | ||
attended_and_total_classes = columns[2].text.strip() | ||
if '/' in attended_and_total_classes: | ||
attended_classes, total_classes = list(map(int, attended_and_total_classes.split('/'))) | ||
else: | ||
attended_classes, total_classes = None, None | ||
percentage = columns[3].text.strip() | ||
percentage = float(percentage) if percentage != "NA" else None | ||
course = Course(course_code, course_title, attendance=Attendance(attended_classes, total_classes, percentage)) | ||
attendance.append(course) | ||
return attendance | ||
|
||
|
||
def get_attendance_page( | ||
session: requests_html.HTMLSession, | ||
semester_ids: dict | ||
) -> dict[int, list[Course]]: | ||
attendance = dict() | ||
for semester_number in semester_ids: | ||
attendance_in_semester = get_attendance_in_semester(session, semester_ids[semester_number]) | ||
attendance[semester_number] = attendance_in_semester | ||
attendance = dict(sorted(attendance.items())) | ||
return attendance |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import datetime | ||
from typing import Optional | ||
|
||
import requests_html | ||
from bs4 import BeautifulSoup | ||
|
||
|
||
def get_semester_list(session: requests_html.HTMLSession, csrf_token: str, semester: Optional[int] = None): | ||
try: | ||
url = "https://www.pesuacademy.com/Academy/a/studentProfilePESU/getStudentSemestersPESU" | ||
query = {"_": str(int(datetime.datetime.now().timestamp() * 1000))} | ||
headers = { | ||
"accept": "*/*", | ||
"accept-language": "en-IN,en-US;q=0.9,en-GB;q=0.8,en;q=0.7", | ||
"content-type": "application/x-www-form-urlencoded", | ||
"referer": "https://www.pesuacademy.com/Academy/s/studentProfilePESU", | ||
"sec-ch-ua": '"Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"', | ||
"sec-ch-ua-mobile": "?0", | ||
"sec-ch-ua-platform": "Windows", | ||
"sec-fetch-dest": "empty", | ||
"sec-fetch-mode": "cors", | ||
"sec-fetch-site": "same-origin", | ||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", | ||
"x-csrf-token": csrf_token, | ||
"x-requested-with": "XMLHttpRequest" | ||
} | ||
response = session.get(url, allow_redirects=False, params=query, headers=headers) | ||
if response.status_code != 200: | ||
raise ConnectionError("Unable to fetch course data.") | ||
except Exception: | ||
raise ConnectionError("Unable to fetch course data.") | ||
|
||
option_tags = response.json() | ||
option_tags = BeautifulSoup(option_tags, "lxml") | ||
option_tags = option_tags.find_all("option") | ||
semester_string_ids = list(map(lambda x: x.attrs["value"], option_tags)) | ||
# TODO: Handle CIE semesters (sometimes the tag is <option value="972">CIE - Level2 (Odd Sem)</option> | ||
semester_numbers = list(map(lambda x: int(x.text.split("Sem-")[1]), option_tags)) | ||
semesters = dict(zip(semester_numbers, semester_string_ids)) | ||
return semesters |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters