IGNOU-Telegram-Bot/bot/ignou.py
2023-02-19 19:35:17 +05:30

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()}