|
from __future__ import annotations |
|
from typing import List, Literal, Optional, NamedTuple |
|
import requests |
|
import re |
|
|
|
|
|
class GradeComponent(NamedTuple): |
|
name: str |
|
percentage: float |
|
grade: float |
|
|
|
@classmethod |
|
def from_payload(cls, payload: dict) -> GradeComponent: |
|
return cls( |
|
name=payload["COMPONENTNAME"], |
|
percentage=float(payload["PERCENTAGE"]), |
|
grade=float(payload["TSCORE"]), |
|
) |
|
|
|
|
|
class Grade: |
|
def __init__( |
|
self, |
|
id_: str, |
|
code: str, |
|
name: str, |
|
sks: int, |
|
academic_period: str, |
|
index: Optional[ |
|
Literal[ |
|
"A", "A-", "AB", "B+", "B", "B-", "BC", "C+", "C", "C-", "D", "E", "T" |
|
] |
|
], |
|
active_status: bool, |
|
last_status: bool, |
|
igracias_session: iGraciasSession, |
|
grade_components: List[GradeComponent] | None = None, |
|
) -> None: |
|
self.id = id_ |
|
self.code = code |
|
self.name = name |
|
self.sks = sks |
|
self.academic_period = academic_period |
|
self.index = index |
|
self.active_status = active_status |
|
self.last_status = last_status |
|
self.grade_components = grade_components |
|
self.igracias_session = igracias_session |
|
|
|
@classmethod |
|
def from_payload(cls, payload: dict, igracias_session: iGraciasSession) -> Grade: |
|
( |
|
course_code, |
|
course_name, |
|
course_sks, |
|
academic_period, |
|
index, |
|
active_status, |
|
last_status, |
|
course_id, |
|
*_, |
|
) = payload |
|
sks = int(course_sks) |
|
active_status = True if active_status.lower() == "y" else False |
|
last_status = True if last_status.lower() == "y" else False |
|
index = index if index else None |
|
return cls( |
|
id_=course_id, |
|
code=course_code, |
|
name=course_name, |
|
sks=sks, |
|
academic_period=academic_period, |
|
index=index, |
|
active_status=active_status, |
|
last_status=last_status, |
|
igracias_session=igracias_session, |
|
) |
|
|
|
def add_grade_components(self) -> None: |
|
headers = { |
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36(j)", |
|
"Accept": "application/json, text/javascript, */*; q=0.01", |
|
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", |
|
"X-Requested-With": "XMLHttpRequest", |
|
} |
|
|
|
payload = {"rId": self.id, "studentId": self.igracias_session.nim} |
|
response = self.igracias_session.session.post( |
|
"https://igracias.telkomuniversity.ac.id/libraries/ajax/ajax.score.php?act=getcomponentscore", |
|
data=payload, |
|
headers=headers, |
|
) |
|
|
|
self.grade_components = [] |
|
for grade_component_payload in response.json(): |
|
grade_component = GradeComponent.from_payload(grade_component_payload) |
|
self.grade_components.append(grade_component) |
|
|
|
def __repr__(self) -> str: |
|
return f'<Grade {" ".join(f"{key}={val}" for key, val in self.__dict__.items() if key not in ["grade_components", "igracias_session"])}>' |
|
|
|
|
|
class iGraciasSession: |
|
def __init__(self, username: str, password: str) -> None: |
|
self.username = username |
|
self.password = password |
|
self.session = requests.session() |
|
self.nim = None |
|
self.logged_in = False |
|
|
|
def login(self) -> None: |
|
payload = { |
|
"textUsername": self.username, |
|
"textPassword": self.password, |
|
"submit": "Login", |
|
} |
|
headers = { |
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36(j)", |
|
} |
|
response = self.session.post( |
|
"https://igracias.telkomuniversity.ac.id/index.php?pageid=2941", |
|
data=payload, |
|
headers=headers, |
|
) |
|
|
|
if ( |
|
not response.text |
|
): # somehow succesful login gives status code 500 and unsuccessful ones gives status code 200 |
|
raise Exception("Error occured.") |
|
|
|
response = self.session.post( |
|
"https://igracias.telkomuniversity.ac.id/index.php?pageid=2941", |
|
headers=headers, |
|
) |
|
pattern = re.compile(r"<title>\s*(\d+)\s*\|\s*Telkom University\s*</title>") |
|
match_ = pattern.search(response.text) |
|
self.nim = int(match_.group(1)) |
|
self.logged_in = True |
|
|
|
def get_grades(self, with_grade_components: bool = True) -> List[Grade]: |
|
if not self.logged_in: |
|
raise Exception("You are not logged in.") |
|
|
|
self.session.get("https://igracias.telkomuniversity.ac.id/score/?pageid=114") |
|
headers = { |
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36(j)", |
|
"Accept": "application/json, text/javascript, */*; q=0.01", |
|
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", |
|
"X-Requested-With": "XMLHttpRequest", |
|
} |
|
|
|
response = self.session.get( |
|
"https://igracias.telkomuniversity.ac.id/libraries/ajax/ajax.score.php?act=viewCompleteScoreStudent&iDisplayLength=100&studentId=1103202201=", |
|
headers=headers, |
|
) |
|
|
|
grades: List[Grade] = [] |
|
for payload in response.json()["aaData"]: |
|
grade = Grade.from_payload(payload, self) |
|
grades.append(grade) |
|
|
|
if with_grade_components: |
|
for grade in grades: |
|
grade.add_grade_components() |
|
|
|
return grades |