225 lines
6.3 KiB
Python
225 lines
6.3 KiB
Python
from collections import OrderedDict
|
|
from collections.abc import Mapping
|
|
|
|
import httpx
|
|
from bs4 import BeautifulSoup
|
|
from prettytable import PrettyTable
|
|
|
|
|
|
class DotDict(OrderedDict):
|
|
"""
|
|
Quick and dirty implementation of a dot-able dict, which allows access and
|
|
assignment via object properties rather than dict indexing.
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
# we could just call super(DotDict, self).__init__(*args, **kwargs)
|
|
# but that won't get us nested dotdict objects
|
|
od = OrderedDict(*args, **kwargs)
|
|
for key, val in od.items():
|
|
if isinstance(val, Mapping):
|
|
value = DotDict(val)
|
|
else:
|
|
value = val
|
|
self[key] = value
|
|
|
|
def __getattr__(self, k):
|
|
return self.get(k, "-")
|
|
|
|
__setattr__ = OrderedDict.__setitem__
|
|
|
|
|
|
class ResultNotFoundError(Exception):
|
|
pass
|
|
|
|
|
|
class IgnouResult:
|
|
def __init__(self) -> None:
|
|
transport = httpx.AsyncHTTPTransport(verify=False)
|
|
self.session: httpx.AsyncClient = httpx.AsyncClient(transport=transport)
|
|
|
|
async def get_grade_result(self, program_id: str, enrollment_no: int):
|
|
|
|
program_id = program_id.upper()
|
|
|
|
params = {
|
|
"prog": program_id,
|
|
"eno": enrollment_no,
|
|
}
|
|
|
|
if program_id in [
|
|
"BCA",
|
|
"MCA",
|
|
"MP",
|
|
"MBP",
|
|
"PGDHRM",
|
|
"PGDFM",
|
|
"PGDOM",
|
|
"PGDMM",
|
|
"PGDFMP",
|
|
]:
|
|
params["type"] = 1
|
|
elif program_id in ["ASSSO", "BA", "BCOM", "BDP", "BSC"]:
|
|
params["type"] = 2
|
|
elif program_id in [
|
|
"BAEGH",
|
|
"BAG",
|
|
"BAHDH",
|
|
"BAHIH",
|
|
"BAPAH",
|
|
"BAPCH",
|
|
"BAPSH",
|
|
"BASOH",
|
|
"BAVTM",
|
|
"BCOMG",
|
|
"BSCANH",
|
|
"BSCBCH",
|
|
"BSCG",
|
|
"BSWG",
|
|
]:
|
|
params["type"] = 4
|
|
else:
|
|
params["type"] = 3
|
|
|
|
grade_card_page = "https://gradecard.ignou.ac.in/gradecard/view_gradecard.aspx"
|
|
|
|
response = await self.session.get(grade_card_page, params=params)
|
|
|
|
soup = BeautifulSoup(response.text, "lxml")
|
|
|
|
name = soup.find(id="ctl00_ContentPlaceHolder1_lblDispname").string
|
|
|
|
try:
|
|
trs = soup.find(string="COURSE").findParent("table")
|
|
except AttributeError:
|
|
raise ResultNotFoundError
|
|
|
|
trs = soup.find(string="COURSE").findParent("table").find_all("tr")
|
|
|
|
data = DotDict()
|
|
data.enrollment_no = enrollment_no
|
|
data.program_id = program_id
|
|
data.name = name
|
|
|
|
courses = []
|
|
data.courses = courses
|
|
|
|
total = DotDict()
|
|
data.total = total
|
|
total.total_passed = 0
|
|
total.total_failed = 0
|
|
total.total_marks = 0
|
|
total.total_obtained_marks = 0
|
|
|
|
column_index = {}
|
|
# get index of coulmn by name
|
|
|
|
cols_list = [data.get_text() for data in trs[0].find_all("th")]
|
|
for index, col in enumerate(cols_list):
|
|
if "COURSE" in col:
|
|
column_index["COURSE"] = index
|
|
elif "ASG" in col.upper():
|
|
column_index["ASIGN"] = index
|
|
elif "THEORY" in col:
|
|
column_index["THEORY"] = index
|
|
elif "PRACTICAL" in col:
|
|
column_index["PRACTICAL"] = index
|
|
elif "STATUS" in col:
|
|
column_index["STATUS"] = index
|
|
|
|
for tr in trs[1:-1]:
|
|
course = DotDict()
|
|
course.max_marks = 100
|
|
total.total_marks += course.max_marks
|
|
|
|
td = tr.find_all("td")
|
|
|
|
# course name
|
|
course.name = td[column_index["COURSE"]].string.strip()
|
|
|
|
# assignments marks
|
|
if assign := td[column_index["ASIGN"]].string.strip():
|
|
if assign.isnumeric():
|
|
course.assignment_marks = int(assign)
|
|
total.total_obtained_marks += course.assignment_marks
|
|
|
|
# theory marks
|
|
try:
|
|
if theory := td[column_index["THEORY"]].string.strip():
|
|
if theory.isnumeric():
|
|
course.theory_marks = int(theory)
|
|
total.total_obtained_marks += course.theory_marks
|
|
except Exception:
|
|
course.theory_marks = "-"
|
|
|
|
# lab marks
|
|
try:
|
|
if lab := td[column_index["PRACTICAL"]].string.strip():
|
|
if lab.isnumeric():
|
|
course.lab_marks = int(lab)
|
|
total.total_obtained_marks += course.lab_marks
|
|
except Exception:
|
|
course.lab_marks = "-"
|
|
|
|
# Status # ✅ ❌
|
|
if "NOT" not in td[column_index["STATUS"]].string.strip():
|
|
course.status = True
|
|
total.total_passed += 1
|
|
|
|
else:
|
|
course.status = False
|
|
total.total_failed += 1
|
|
|
|
courses.append(course)
|
|
|
|
total.total_courses = len(courses)
|
|
|
|
return {
|
|
"name": name,
|
|
"enrollment_no": enrollment_no,
|
|
"program_id": program_id,
|
|
"courses": courses,
|
|
**total,
|
|
}
|
|
|
|
async def gradeResultData(
|
|
self,
|
|
program_id: str,
|
|
enrollment_no: int,
|
|
):
|
|
|
|
data = DotDict(await self.get_grade_result(program_id, enrollment_no))
|
|
|
|
x = PrettyTable()
|
|
x.padding_width = 0
|
|
x.field_names = ["Course", "Asign", "Lab", "Term", "Status"]
|
|
|
|
header = "Name : {}\nProg : {} [{}]\n".format(
|
|
data.name, data.program_id, data.enrollment_no
|
|
)
|
|
|
|
for course in data.courses:
|
|
tick = "✅" if course.status else "❌"
|
|
|
|
x.add_row(
|
|
[
|
|
course.name,
|
|
course.assignment_marks,
|
|
course.lab_marks,
|
|
course.theory_marks,
|
|
tick,
|
|
]
|
|
)
|
|
|
|
x.add_row(
|
|
[
|
|
"Total",
|
|
"T:{}".format(data.total_courses),
|
|
"-", # data.total_obtained_marks,
|
|
"-", # data.total_marks,
|
|
f"[{data.total_passed}/{data.total_failed}]",
|
|
]
|
|
)
|
|
|
|
return {"header": header, "table": x.get_string()}
|