Spaces:
Paused
Paused
Upload 31 files
Browse files- .gitignore +6 -0
- Dockerfile +13 -0
- app.py +128 -0
- constants/__pycache__/bitmaps.cpython-310.pyc +0 -0
- constants/__pycache__/bitmaps.cpython-311.pyc +0 -0
- constants/__pycache__/constants.cpython-310.pyc +0 -0
- constants/__pycache__/constants.cpython-311.pyc +0 -0
- constants/bitmaps.py +0 -0
- constants/constants.py +14 -0
- constants/weights.json +0 -0
- gen_session.py +46 -0
- get_attendance.py +116 -0
- get_exam_schedule.py +65 -0
- get_grades.py +48 -0
- get_marks.py +53 -0
- get_profile.py +55 -0
- get_sem_id.py +32 -0
- get_timetable.py +113 -0
- models/__pycache__/period.cpython-310.pyc +0 -0
- models/__pycache__/period.cpython-311.pyc +0 -0
- models/period.py +31 -0
- requirements.txt +8 -0
- source/conf.py +28 -0
- source/index.rst +17 -0
- utils/__pycache__/captcha_solver.cpython-310.pyc +0 -0
- utils/__pycache__/captcha_solver.cpython-311.pyc +0 -0
- utils/__pycache__/payloads.cpython-310.pyc +0 -0
- utils/__pycache__/payloads.cpython-311.pyc +0 -0
- utils/captcha_solver.py +80 -0
- utils/payloads.py +72 -0
- utils/sem_ids.py +4 -0
.gitignore
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
tests/
|
2 |
+
constants/priv_constants.py
|
3 |
+
__pycache__/
|
4 |
+
venv/
|
5 |
+
test.py
|
6 |
+
.vscode
|
Dockerfile
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.9
|
2 |
+
|
3 |
+
RUN useradd -m -u 1000 user
|
4 |
+
USER user
|
5 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
6 |
+
|
7 |
+
WORKDIR /app
|
8 |
+
|
9 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
10 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
11 |
+
|
12 |
+
COPY --chown=user . /app
|
13 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
app.py
ADDED
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import uvicorn
|
2 |
+
from aiohttp import ClientSession
|
3 |
+
from gen_session import gen_session
|
4 |
+
from get_marks import get_marks_data
|
5 |
+
from get_grades import get_grades_data
|
6 |
+
from get_profile import get_profile_data
|
7 |
+
from fastapi.responses import JSONResponse
|
8 |
+
from get_timetable import get_timetable_data
|
9 |
+
from get_attendance import get_attendance_data
|
10 |
+
from get_exam_schedule import get_examSchedule_data
|
11 |
+
from get_sem_id import _get_all_sem_ids, _get_sem_id
|
12 |
+
from fastapi import FastAPI, Request, HTTPException, status, Form
|
13 |
+
|
14 |
+
app = FastAPI()
|
15 |
+
|
16 |
+
|
17 |
+
def basic_creds_check(username: str | None, password: str | None):
|
18 |
+
if not username or not password:
|
19 |
+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
|
20 |
+
|
21 |
+
|
22 |
+
@app.get("/")
|
23 |
+
async def root():
|
24 |
+
return {"message": "VTOP-AP API"}
|
25 |
+
|
26 |
+
|
27 |
+
async def handle_request(data_func, num_parameters, username, password):
|
28 |
+
basic_creds_check(username, password)
|
29 |
+
async with ClientSession() as sess:
|
30 |
+
session_result = await gen_session(sess, username, password)
|
31 |
+
if session_result == 0:
|
32 |
+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
|
33 |
+
if num_parameters == 3:
|
34 |
+
return await data_func(sess, username, session_result)
|
35 |
+
else:
|
36 |
+
return await data_func(
|
37 |
+
sess,
|
38 |
+
username,
|
39 |
+
await _get_sem_id(sess, username, session_result),
|
40 |
+
session_result,
|
41 |
+
)
|
42 |
+
|
43 |
+
|
44 |
+
@app.post("/api/attendance")
|
45 |
+
@app.post("/api/timetable")
|
46 |
+
@app.post("/api/examSchedule")
|
47 |
+
async def handle_4param_data_functions(
|
48 |
+
request: Request, username: str = Form(...), password: str = Form(...)
|
49 |
+
):
|
50 |
+
data_func = {
|
51 |
+
"/api/attendance": get_attendance_data,
|
52 |
+
"/api/timetable": get_timetable_data,
|
53 |
+
"/api/examSchedule": get_examSchedule_data,
|
54 |
+
}
|
55 |
+
return await handle_request(data_func[request.url.path], 4, username, password)
|
56 |
+
|
57 |
+
|
58 |
+
@app.post("/api/grades")
|
59 |
+
@app.post("/api/profile")
|
60 |
+
@app.post("/api/semIDs")
|
61 |
+
async def handle_3param_data_functions(
|
62 |
+
request: Request, username: str = Form(...), password: str = Form(...)
|
63 |
+
):
|
64 |
+
data_func = {
|
65 |
+
"/api/grades": get_grades_data,
|
66 |
+
"/api/profile": get_profile_data,
|
67 |
+
"/api/semIDs": _get_all_sem_ids,
|
68 |
+
}
|
69 |
+
return await handle_request(data_func[request.url.path], 3, username, password)
|
70 |
+
|
71 |
+
|
72 |
+
@app.post("/api/verify")
|
73 |
+
async def verify_creds(username: str = Form(...), password: str = Form(...)):
|
74 |
+
basic_creds_check(username, password)
|
75 |
+
|
76 |
+
async with ClientSession() as sess:
|
77 |
+
session_result = await gen_session(sess, username, password)
|
78 |
+
if session_result == 0:
|
79 |
+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
|
80 |
+
else:
|
81 |
+
return JSONResponse(
|
82 |
+
content={"csrf_token": session_result}, status_code=status.HTTP_200_OK
|
83 |
+
)
|
84 |
+
|
85 |
+
|
86 |
+
@app.post("/api/all")
|
87 |
+
async def all_data(username: str = Form(...), password: str = Form(...)):
|
88 |
+
basic_creds_check(username, password)
|
89 |
+
|
90 |
+
async with ClientSession() as sess:
|
91 |
+
session_result = await gen_session(sess, username, password)
|
92 |
+
if session_result == 0:
|
93 |
+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
|
94 |
+
semID = await _get_sem_id(sess, username, session_result)
|
95 |
+
data = {
|
96 |
+
"profile": await get_profile_data(sess, username, session_result),
|
97 |
+
"attendance": await get_attendance_data(
|
98 |
+
sess, username, semID, session_result
|
99 |
+
),
|
100 |
+
"semIDs": await _get_all_sem_ids(sess, username, session_result),
|
101 |
+
"grades": await get_grades_data(sess, username, session_result),
|
102 |
+
"examSchedule": await get_examSchedule_data(
|
103 |
+
sess, username, semID, session_result
|
104 |
+
),
|
105 |
+
"timetable": await get_timetable_data(
|
106 |
+
sess, username, semID, session_result
|
107 |
+
),
|
108 |
+
}
|
109 |
+
|
110 |
+
return data
|
111 |
+
|
112 |
+
|
113 |
+
@app.post("/api/marks")
|
114 |
+
async def marks(
|
115 |
+
username: str = Form(...), password: str = Form(...), semID: str = Form(...)
|
116 |
+
):
|
117 |
+
basic_creds_check(username, password)
|
118 |
+
|
119 |
+
async with ClientSession() as sess:
|
120 |
+
session_result = await gen_session(sess, username, password)
|
121 |
+
if session_result == 0:
|
122 |
+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
|
123 |
+
|
124 |
+
return await get_marks_data(sess, username, semID, session_result)
|
125 |
+
|
126 |
+
|
127 |
+
if __name__ == "__main__":
|
128 |
+
uvicorn.run(app, host="localhost", port=7860)
|
constants/__pycache__/bitmaps.cpython-310.pyc
ADDED
Binary file (98.3 kB). View file
|
|
constants/__pycache__/bitmaps.cpython-311.pyc
ADDED
Binary file (117 kB). View file
|
|
constants/__pycache__/constants.cpython-310.pyc
ADDED
Binary file (951 Bytes). View file
|
|
constants/__pycache__/constants.cpython-311.pyc
ADDED
Binary file (1.08 kB). View file
|
|
constants/bitmaps.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
constants/constants.py
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
user_agent_header = {
|
2 |
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.67"
|
3 |
+
}
|
4 |
+
|
5 |
+
vtop_base_url = "https://vtop.vitap.ac.in/vtop/"
|
6 |
+
|
7 |
+
vtop_profile_url = f"{vtop_base_url}studentsRecord/StudentProfileAllView"
|
8 |
+
vtop_process_timetable_url = f"{vtop_base_url}processViewTimeTable"
|
9 |
+
vtop_semID_list_url = f"{vtop_base_url}academics/common/StudentAttendance"
|
10 |
+
vtop_process_attendance_url = f"{vtop_base_url}processViewStudentAttendance"
|
11 |
+
vtop_process_attendance_detail_url = f"{vtop_base_url}processViewAttendanceDetail"
|
12 |
+
vtop_doMarks_view_url = f"{vtop_base_url}examinations/doStudentMarkView"
|
13 |
+
vtop_gradeHistory_url = f"{vtop_base_url}examinations/examGradeView/StudentGradeHistory"
|
14 |
+
vtop_doExamSchedule_url = f"{vtop_base_url}examinations/doSearchExamScheduleForStudent"
|
constants/weights.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
gen_session.py
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import aiohttp
|
2 |
+
from constants.constants import user_agent_header
|
3 |
+
from utils.captcha_solver import solve_base64
|
4 |
+
from utils.payloads import get_login_payload
|
5 |
+
import re
|
6 |
+
|
7 |
+
|
8 |
+
async def gen_session(sess: aiohttp.ClientSession, username: str, password: str):
|
9 |
+
try:
|
10 |
+
async with sess.get(
|
11 |
+
"https://vtop.vitap.ac.in/vtop/", headers=user_agent_header
|
12 |
+
):
|
13 |
+
async with sess.get("https://vtop.vitap.ac.in/vtop/open/page") as csrf:
|
14 |
+
csrf_token = re.search(
|
15 |
+
r'name="_csrf" value="(.*)"', await csrf.text()
|
16 |
+
).group(1)
|
17 |
+
await sess.post(
|
18 |
+
"https://vtop.vitap.ac.in/vtop/prelogin/setup",
|
19 |
+
data={"_csrf": csrf_token, "flag": "VTOP"},
|
20 |
+
)
|
21 |
+
await sess.get("https://vtop.vitap.ac.in/vtop/init/page")
|
22 |
+
async with sess.get("https://vtop.vitap.ac.in/vtop/login") as req:
|
23 |
+
captcha = solve_base64(
|
24 |
+
re.search(r';base64,(.+)"', await req.text()).group(1)
|
25 |
+
)
|
26 |
+
async with sess.post(
|
27 |
+
"https://vtop.vitap.ac.in/vtop/login",
|
28 |
+
data=get_login_payload(
|
29 |
+
csrf_token,
|
30 |
+
username,
|
31 |
+
password,
|
32 |
+
captcha,
|
33 |
+
),
|
34 |
+
) as final:
|
35 |
+
csrf = re.search(
|
36 |
+
r'var csrfValue = "(.*)";', await final.text()
|
37 |
+
).group(1)
|
38 |
+
if "Invalid" in await final.text():
|
39 |
+
return 0
|
40 |
+
elif len(csrf) == 36:
|
41 |
+
return csrf
|
42 |
+
else:
|
43 |
+
return await gen_session(sess, username, password)
|
44 |
+
|
45 |
+
except Exception:
|
46 |
+
return await gen_session(sess, username, password)
|
get_attendance.py
ADDED
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
import aiohttp
|
3 |
+
import pandas as pd
|
4 |
+
from io import StringIO
|
5 |
+
|
6 |
+
from constants.constants import (
|
7 |
+
vtop_process_attendance_url,
|
8 |
+
vtop_process_attendance_detail_url,
|
9 |
+
)
|
10 |
+
from utils.payloads import (
|
11 |
+
get_attendance_payload,
|
12 |
+
get_attendance_detail_payload,
|
13 |
+
)
|
14 |
+
|
15 |
+
|
16 |
+
async def _get_attendance_page(
|
17 |
+
sess: aiohttp.ClientSession, username: str, semID: str, csrf: str
|
18 |
+
):
|
19 |
+
async with sess.post(
|
20 |
+
vtop_process_attendance_url, data=get_attendance_payload(username, semID, csrf)
|
21 |
+
) as req:
|
22 |
+
return await req.text()
|
23 |
+
|
24 |
+
|
25 |
+
async def _get_attendance_detail_page(
|
26 |
+
sess: aiohttp.ClientSession, csrf, semID, username, courseID, courseType
|
27 |
+
):
|
28 |
+
async with sess.post(
|
29 |
+
vtop_process_attendance_detail_url,
|
30 |
+
data=get_attendance_detail_payload(csrf, semID, username, courseID, courseType),
|
31 |
+
) as req:
|
32 |
+
return await req.text()
|
33 |
+
|
34 |
+
|
35 |
+
def _get_class_type(classType: str):
|
36 |
+
if classType == "Embedded Theory":
|
37 |
+
return "ETH"
|
38 |
+
elif classType == "Embedded Lab":
|
39 |
+
return "ELA"
|
40 |
+
elif classType == "Theory Only":
|
41 |
+
return "TH"
|
42 |
+
elif classType == "Lab Only":
|
43 |
+
return "LO"
|
44 |
+
|
45 |
+
|
46 |
+
def _parse_attendance_detail(attendance_detail_page: str):
|
47 |
+
attendance_detail_table = pd.read_html(StringIO(attendance_detail_page))
|
48 |
+
if len(attendance_detail_table) < 2:
|
49 |
+
return {}
|
50 |
+
attendance_detail_table = attendance_detail_table[1]
|
51 |
+
attendance_detail = {}
|
52 |
+
|
53 |
+
for index, row in attendance_detail_table.iterrows():
|
54 |
+
attendance_detail[str(row["Sl.No."])] = {
|
55 |
+
"status": row["Status"],
|
56 |
+
"date": row["Date"],
|
57 |
+
"time": row["Day / Time"],
|
58 |
+
}
|
59 |
+
|
60 |
+
return attendance_detail
|
61 |
+
|
62 |
+
|
63 |
+
async def _parse_attendance(
|
64 |
+
attendance_page: str,
|
65 |
+
sess: aiohttp.ClientSession,
|
66 |
+
username: str,
|
67 |
+
csrf: str,
|
68 |
+
semID: str,
|
69 |
+
):
|
70 |
+
table_df = pd.read_html(StringIO(attendance_page))[0]
|
71 |
+
|
72 |
+
attendance = []
|
73 |
+
|
74 |
+
for index, row in table_df.iterrows():
|
75 |
+
code = row["Course Detail"].split("-")[0].strip()
|
76 |
+
slot = row["Class Detail"].split("-")[1].strip()
|
77 |
+
if "Total Number Of Credits" in code:
|
78 |
+
if "0" in code:
|
79 |
+
raise Exception
|
80 |
+
continue
|
81 |
+
|
82 |
+
attendance.append(
|
83 |
+
{
|
84 |
+
"classID": row["Class Detail"].split("-")[0].strip(),
|
85 |
+
"name": row["Course Detail"].split("-")[1].strip(),
|
86 |
+
"courseType": row["Course Detail"].split("-")[2].strip(),
|
87 |
+
"slot": slot,
|
88 |
+
"totalClasses": str(row["Total Classes"]),
|
89 |
+
"attendedClasses": str(row["Attended Classes"]),
|
90 |
+
"attendancePercentage": row["Attendance Percentage"][:-1],
|
91 |
+
"attendanceDetail": _parse_attendance_detail(
|
92 |
+
await _get_attendance_detail_page(
|
93 |
+
sess,
|
94 |
+
csrf,
|
95 |
+
semID,
|
96 |
+
username,
|
97 |
+
re.search(f";(\w*_{code}_\d*)&", attendance_page).group(1),
|
98 |
+
_get_class_type(row["Course Detail"].split("-")[2].strip()),
|
99 |
+
)
|
100 |
+
),
|
101 |
+
}
|
102 |
+
)
|
103 |
+
|
104 |
+
return attendance
|
105 |
+
|
106 |
+
|
107 |
+
async def get_attendance_data(
|
108 |
+
sess: aiohttp.ClientSession, username: str, semID: str, csrf: str
|
109 |
+
):
|
110 |
+
return await _parse_attendance(
|
111 |
+
await _get_attendance_page(sess, username, semID, csrf),
|
112 |
+
sess,
|
113 |
+
username,
|
114 |
+
csrf,
|
115 |
+
semID,
|
116 |
+
)
|
get_exam_schedule.py
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
import aiohttp
|
3 |
+
import pandas as pd
|
4 |
+
from io import StringIO
|
5 |
+
|
6 |
+
from utils.payloads import get_examSchedule_payload
|
7 |
+
from constants.constants import vtop_doExamSchedule_url
|
8 |
+
|
9 |
+
|
10 |
+
async def _get_examSchedule_page(
|
11 |
+
sess: aiohttp.ClientSession, username: str, semID: str, csrf: str
|
12 |
+
) -> str:
|
13 |
+
async with sess.post(
|
14 |
+
vtop_doExamSchedule_url, data=get_examSchedule_payload(username, semID, csrf)
|
15 |
+
) as req:
|
16 |
+
return await req.text()
|
17 |
+
|
18 |
+
|
19 |
+
def _return_dash_if_not_str(value):
|
20 |
+
if isinstance(value, str):
|
21 |
+
return value
|
22 |
+
else:
|
23 |
+
return "-"
|
24 |
+
|
25 |
+
|
26 |
+
async def _parse_examSchedule(examSchedule_page: str):
|
27 |
+
try:
|
28 |
+
examSchedule_table = pd.read_html(StringIO(examSchedule_page))[0]
|
29 |
+
except ValueError:
|
30 |
+
return {}
|
31 |
+
examSchedule_data = {}
|
32 |
+
|
33 |
+
current_exam = ""
|
34 |
+
for index, row in examSchedule_table.iterrows():
|
35 |
+
if index == 0:
|
36 |
+
continue
|
37 |
+
if re.search("\D", row[0]):
|
38 |
+
current_exam = row[0]
|
39 |
+
examSchedule_data[current_exam] = {}
|
40 |
+
else:
|
41 |
+
examSchedule_data[current_exam][row[1]] = {
|
42 |
+
"name": row[2],
|
43 |
+
"type": row[3],
|
44 |
+
"classID": row[4],
|
45 |
+
"slot": row[5],
|
46 |
+
"date": _return_dash_if_not_str(row[6]),
|
47 |
+
"session": _return_dash_if_not_str(row[7]),
|
48 |
+
"reportingTime": _return_dash_if_not_str(row[8]),
|
49 |
+
"duration": _return_dash_if_not_str(row[9]),
|
50 |
+
"venue": row[10].split("-")[0],
|
51 |
+
"roomNo": row[10].split("-")[1],
|
52 |
+
"seatLocation": row[11],
|
53 |
+
"seatNo": row[12],
|
54 |
+
}
|
55 |
+
|
56 |
+
examSchedule_data.pop("S.No.")
|
57 |
+
return examSchedule_data
|
58 |
+
|
59 |
+
|
60 |
+
async def get_examSchedule_data(
|
61 |
+
sess: aiohttp.ClientSession, username: str, semID: str, csrf: str
|
62 |
+
) -> dict:
|
63 |
+
examSchedule_page = await _get_examSchedule_page(sess, username, semID, csrf)
|
64 |
+
examSchedule_data = await _parse_examSchedule(examSchedule_page)
|
65 |
+
return examSchedule_data
|
get_grades.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import aiohttp
|
2 |
+
import pandas as pd
|
3 |
+
from io import StringIO
|
4 |
+
from constants.constants import vtop_gradeHistory_url
|
5 |
+
from utils.payloads import get_gradeHistory_payload
|
6 |
+
|
7 |
+
|
8 |
+
async def _get_grades_page(
|
9 |
+
sess: aiohttp.ClientSession, username: str, csrf: str
|
10 |
+
) -> str:
|
11 |
+
async with sess.post(
|
12 |
+
vtop_gradeHistory_url, data=get_gradeHistory_payload(username, csrf)
|
13 |
+
) as req:
|
14 |
+
return await req.text()
|
15 |
+
|
16 |
+
|
17 |
+
async def get_grades_data(sess: aiohttp.ClientSession, username: str, csrf: str):
|
18 |
+
grades_page = await _get_grades_page(sess, username, csrf)
|
19 |
+
|
20 |
+
try:
|
21 |
+
tables = pd.read_html(StringIO(grades_page))
|
22 |
+
data_table = tables[1]
|
23 |
+
data_summary_table = tables[-1]
|
24 |
+
except Exception:
|
25 |
+
return {}
|
26 |
+
|
27 |
+
grade_data = {}
|
28 |
+
|
29 |
+
grade_data["creditsEarned"] = str(data_summary_table.iloc[0, 1])
|
30 |
+
grade_data["cgpa"] = str(data_summary_table.iloc[0, 2])
|
31 |
+
|
32 |
+
grade_data["numOfEachGrade"] = {}
|
33 |
+
grade_data["subjects"] = {}
|
34 |
+
grade_data["numOfEachGrade"]["S"] = str(data_summary_table.iloc[0, 3])
|
35 |
+
for i in range(0, 6):
|
36 |
+
grade_data["numOfEachGrade"][chr(65 + i)] = str(
|
37 |
+
data_summary_table.iloc[0, 4 + i]
|
38 |
+
)
|
39 |
+
|
40 |
+
for i in range(2, len(data_table)):
|
41 |
+
grade_data["subjects"][data_table.iloc[i, 1]] = {
|
42 |
+
"name": data_table.iloc[i, 2],
|
43 |
+
"type": data_table.iloc[i, 3],
|
44 |
+
"credits": data_table.iloc[i, 4],
|
45 |
+
"grade": data_table.iloc[i, 5],
|
46 |
+
}
|
47 |
+
|
48 |
+
return grade_data
|
get_marks.py
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import aiohttp
|
2 |
+
import pandas as pd
|
3 |
+
from io import StringIO
|
4 |
+
|
5 |
+
from constants.constants import vtop_doMarks_view_url
|
6 |
+
from utils.payloads import get_doMarks_view_payload
|
7 |
+
|
8 |
+
|
9 |
+
async def _get_doMarks_view_page(
|
10 |
+
sess: aiohttp.ClientSession, username: str, semID: str, csrf: str
|
11 |
+
) -> str:
|
12 |
+
async with sess.post(
|
13 |
+
vtop_doMarks_view_url, data=get_doMarks_view_payload(username, semID, csrf)
|
14 |
+
) as req:
|
15 |
+
return await req.text()
|
16 |
+
|
17 |
+
|
18 |
+
def _parse_marks(marks_page):
|
19 |
+
try:
|
20 |
+
tables = pd.read_html(StringIO(marks_page))
|
21 |
+
except ValueError:
|
22 |
+
return {}
|
23 |
+
|
24 |
+
course_details = tables[0].iloc[1::2, :]
|
25 |
+
marks_data = {}
|
26 |
+
|
27 |
+
for i in range(course_details.shape[0]):
|
28 |
+
course = course_details.iloc[i]
|
29 |
+
marks_data[course[1]] = {
|
30 |
+
"courseName": course[3],
|
31 |
+
"courseType": course[4],
|
32 |
+
"professor": course[6],
|
33 |
+
"courseSlot": course[7],
|
34 |
+
"marks": {},
|
35 |
+
}
|
36 |
+
|
37 |
+
current_course_table = tables[i + 1]
|
38 |
+
for j in range(1, current_course_table.shape[0]):
|
39 |
+
entry = current_course_table.iloc[j]
|
40 |
+
marks_data[course[1]]["marks"][entry[1]] = {
|
41 |
+
"maxMarks": entry[2],
|
42 |
+
"maxWeightageMarks": entry[3],
|
43 |
+
"scoredMarks": entry[5],
|
44 |
+
"scoredWeightageMarks": entry[6],
|
45 |
+
}
|
46 |
+
|
47 |
+
return marks_data
|
48 |
+
|
49 |
+
|
50 |
+
async def get_marks_data(
|
51 |
+
sess: aiohttp.ClientSession, username: str, semID: str, csrf: str
|
52 |
+
):
|
53 |
+
return _parse_marks(await _get_doMarks_view_page(sess, username, semID, csrf))
|
get_profile.py
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
import string
|
3 |
+
import aiohttp
|
4 |
+
import pandas as pd
|
5 |
+
from io import StringIO
|
6 |
+
|
7 |
+
from constants.constants import vtop_profile_url
|
8 |
+
from utils.payloads import get_profile_payload
|
9 |
+
|
10 |
+
|
11 |
+
def _get_value_from_column1(text: str, df: pd.DataFrame):
|
12 |
+
row = df[df[0] == text]
|
13 |
+
if not row.empty:
|
14 |
+
return str(row.iloc[0, 1])
|
15 |
+
else:
|
16 |
+
return str(None)
|
17 |
+
|
18 |
+
|
19 |
+
async def _get_profile_page(
|
20 |
+
sess: aiohttp.ClientSession, username: str, csrf: str
|
21 |
+
) -> str:
|
22 |
+
async with sess.post(
|
23 |
+
vtop_profile_url, data=get_profile_payload(username, csrf)
|
24 |
+
) as req:
|
25 |
+
return await req.text()
|
26 |
+
|
27 |
+
|
28 |
+
async def get_profile_data(
|
29 |
+
sess: aiohttp.ClientSession, username: str, csrf: str
|
30 |
+
) -> dict:
|
31 |
+
profile_page = await _get_profile_page(sess, username, csrf)
|
32 |
+
data = {}
|
33 |
+
tables = pd.read_html(StringIO(profile_page))
|
34 |
+
|
35 |
+
desired_fields_table_0 = {
|
36 |
+
"Student Name": "STUDENT NAME",
|
37 |
+
"Application Number": "APPLICATION NUMBER",
|
38 |
+
}
|
39 |
+
|
40 |
+
desired_fields_table_3 = {
|
41 |
+
"Mentor Name": "FACULTY NAME",
|
42 |
+
"Mentor Cabin": "CABIN",
|
43 |
+
"Mentor Email": "FACULTY EMAIL",
|
44 |
+
"Mentor intercom": "FACULTY INTERCOM",
|
45 |
+
"Mentor Mobile Number": "FACULTY MOBILE NUMBER",
|
46 |
+
}
|
47 |
+
|
48 |
+
data["image"] = re.findall(r'src="data:null;base64,(.*)"', profile_page)[0]
|
49 |
+
data["VIT Registration Number"] = username
|
50 |
+
for key, field in desired_fields_table_0.items():
|
51 |
+
data[key] = string.capwords(_get_value_from_column1(field, tables[0]))
|
52 |
+
for key, field in desired_fields_table_3.items():
|
53 |
+
data[key] = _get_value_from_column1(field, tables[3])
|
54 |
+
|
55 |
+
return data
|
get_sem_id.py
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
import aiohttp
|
3 |
+
from bs4 import BeautifulSoup
|
4 |
+
from constants.constants import vtop_semID_list_url
|
5 |
+
from utils.payloads import get_attendance_semID_list_payload
|
6 |
+
|
7 |
+
|
8 |
+
async def _get_sem_id(sess: aiohttp.ClientSession, username: str, csrf: str):
|
9 |
+
async with sess.post(
|
10 |
+
vtop_semID_list_url,
|
11 |
+
data=get_attendance_semID_list_payload(username, csrf),
|
12 |
+
) as req:
|
13 |
+
return re.search('<option value="(A.*)"', await req.text()).group(1)
|
14 |
+
|
15 |
+
|
16 |
+
async def _get_all_sem_ids(sess: aiohttp.ClientSession, username: str, csrf: str):
|
17 |
+
async with sess.post(
|
18 |
+
vtop_semID_list_url,
|
19 |
+
data=get_attendance_semID_list_payload(username, csrf),
|
20 |
+
) as req:
|
21 |
+
# sem as list
|
22 |
+
# return re.findall('<option value="(A.*)"', await req.text())
|
23 |
+
|
24 |
+
soup = BeautifulSoup(await req.text(), "lxml")
|
25 |
+
|
26 |
+
sem_ids = {}
|
27 |
+
sem_ids_soup = soup.findAll("option")
|
28 |
+
for elem in sem_ids_soup:
|
29 |
+
id = elem["value"]
|
30 |
+
if id != "":
|
31 |
+
sem_ids[id] = elem.text.strip()
|
32 |
+
return sem_ids
|
get_timetable.py
ADDED
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import aiohttp
|
2 |
+
import pandas as pd
|
3 |
+
from io import StringIO
|
4 |
+
from bs4 import BeautifulSoup
|
5 |
+
|
6 |
+
from constants.constants import vtop_process_timetable_url
|
7 |
+
from models.period import Period
|
8 |
+
from utils.payloads import get_timetable_payload
|
9 |
+
|
10 |
+
|
11 |
+
DAYS_MAP = {
|
12 |
+
"MON": "Monday",
|
13 |
+
"TUE": "Tuesday",
|
14 |
+
"WED": "Wednesday",
|
15 |
+
"THU": "Thursday",
|
16 |
+
"FRI": "Friday",
|
17 |
+
"SAT": "Saturday",
|
18 |
+
"SUN": "Sunday",
|
19 |
+
}
|
20 |
+
|
21 |
+
VALID_DAYS = {"Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
|
22 |
+
|
23 |
+
|
24 |
+
async def _get_timetable_page(
|
25 |
+
sess: aiohttp.ClientSession, username: str, semID: str, csrf: str
|
26 |
+
) -> str:
|
27 |
+
async with sess.post(
|
28 |
+
vtop_process_timetable_url, data=get_timetable_payload(username, semID, csrf)
|
29 |
+
) as req:
|
30 |
+
return await req.text()
|
31 |
+
|
32 |
+
|
33 |
+
def _get_course_code_name_dict(soup: BeautifulSoup) -> dict:
|
34 |
+
course_data = soup.find("div", attrs={"id": "studentDetailsList"}).find_all(
|
35 |
+
"td",
|
36 |
+
attrs={
|
37 |
+
"style": "padding: 3px; font-size: 12px; border-color: #b2b2b2;vertical-align: middle;"
|
38 |
+
},
|
39 |
+
)
|
40 |
+
return {
|
41 |
+
data.text.split("-")[0].strip(): data.text.split("-")[1].split("\n")[0].strip()
|
42 |
+
for data in course_data
|
43 |
+
}
|
44 |
+
|
45 |
+
|
46 |
+
def _parse_course_vals(cell_str: str):
|
47 |
+
temp_arr = str(cell_str).strip().split("-")
|
48 |
+
course_code = temp_arr[1]
|
49 |
+
cls = "-".join(temp_arr[3 : len(temp_arr) - 1])
|
50 |
+
|
51 |
+
return course_code, cls
|
52 |
+
|
53 |
+
|
54 |
+
def _get_end_time(start_time: str, is_theory: bool = True):
|
55 |
+
if is_theory:
|
56 |
+
return f'{start_time.split(":")[0]}:50'
|
57 |
+
else:
|
58 |
+
return f'{int(start_time.split(":")[0]) + 1}:40'
|
59 |
+
|
60 |
+
|
61 |
+
def _parse_timetable(timetable_page: str):
|
62 |
+
timetable = {day: [] for day in VALID_DAYS}
|
63 |
+
soup = BeautifulSoup(timetable_page, "lxml")
|
64 |
+
course_code_dict = _get_course_code_name_dict(soup)
|
65 |
+
dataframes = pd.read_html(StringIO(timetable_page))
|
66 |
+
course_details, timetable_df = dataframes[0], dataframes[1]
|
67 |
+
|
68 |
+
for row in timetable_df.itertuples(index=False):
|
69 |
+
if len(row) < 2 or row[1].lower() not in {"theory", "lab"}:
|
70 |
+
continue
|
71 |
+
day = DAYS_MAP.get(row[0], "Sunday")
|
72 |
+
is_theory = row[1].lower() == "theory"
|
73 |
+
if day not in timetable:
|
74 |
+
continue
|
75 |
+
for col_idx, cell in enumerate(row[2:], start=2):
|
76 |
+
cell_str = str(cell).strip()
|
77 |
+
if len(cell_str) > 3 and cell_str.count("-") >= 3:
|
78 |
+
code, location = _parse_course_vals(cell_str)
|
79 |
+
class_id = course_details.loc[
|
80 |
+
course_details["Slot - Venue"].str.contains(
|
81 |
+
cell_str.split("-")[0], na=False
|
82 |
+
),
|
83 |
+
"Class Nbr",
|
84 |
+
].iloc[0]
|
85 |
+
start_time = timetable_df.iloc[0, col_idx]
|
86 |
+
period = Period(
|
87 |
+
class_id=class_id,
|
88 |
+
slot=course_details.loc[
|
89 |
+
course_details["Class Nbr"] == class_id, "Slot - Venue"
|
90 |
+
]
|
91 |
+
.iloc[0]
|
92 |
+
.split(" - ")[0],
|
93 |
+
courseName=course_code_dict[code],
|
94 |
+
code=code,
|
95 |
+
location=location,
|
96 |
+
startTime=start_time,
|
97 |
+
endTime=_get_end_time(start_time, is_theory),
|
98 |
+
)
|
99 |
+
if period not in timetable[day]:
|
100 |
+
timetable[day].append(period)
|
101 |
+
return timetable
|
102 |
+
|
103 |
+
|
104 |
+
async def get_timetable_data(
|
105 |
+
sess: aiohttp.ClientSession, username: str, semID: str, csrf: str
|
106 |
+
):
|
107 |
+
timetable = _parse_timetable(await _get_timetable_page(sess, username, semID, csrf))
|
108 |
+
for key in timetable:
|
109 |
+
timetable[key].sort()
|
110 |
+
return {
|
111 |
+
key: [period.to_dict() for period in period_list]
|
112 |
+
for key, period_list in timetable.items()
|
113 |
+
}
|
models/__pycache__/period.cpython-310.pyc
ADDED
Binary file (1.23 kB). View file
|
|
models/__pycache__/period.cpython-311.pyc
ADDED
Binary file (1.84 kB). View file
|
|
models/period.py
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
class Period:
|
2 |
+
def __init__(self, class_id, slot, courseName, code, location, startTime, endTime):
|
3 |
+
self.class_id = class_id
|
4 |
+
self.slot = slot
|
5 |
+
self.courseName = courseName
|
6 |
+
self.code = code
|
7 |
+
self.location = location
|
8 |
+
self.startTime = startTime
|
9 |
+
self.endTime = endTime
|
10 |
+
|
11 |
+
def __eq__(self, other):
|
12 |
+
return (
|
13 |
+
self.slot == other.slot or self.slot[0] == other.slot[0] == "L"
|
14 |
+
) and self.code == other.code
|
15 |
+
|
16 |
+
def __lt__(self, other):
|
17 |
+
return self.startTime < other.startTime
|
18 |
+
|
19 |
+
def to_dict(self):
|
20 |
+
return {
|
21 |
+
"classId": self.class_id,
|
22 |
+
"slot": self.slot,
|
23 |
+
"courseName": self.courseName,
|
24 |
+
"code": self.code,
|
25 |
+
"location": self.location,
|
26 |
+
"startTime": self.startTime,
|
27 |
+
"endTime": self.endTime,
|
28 |
+
}
|
29 |
+
|
30 |
+
def __repr__(self) -> str:
|
31 |
+
return str(self.to_dict())
|
requirements.txt
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
aiohttp==3.10.5
|
2 |
+
beautifulsoup4==4.12.3
|
3 |
+
fastapi==0.112.2
|
4 |
+
numpy==2.1.0
|
5 |
+
pandas==2.2.2
|
6 |
+
Pillow==10.4.0
|
7 |
+
uvicorn==0.30.6
|
8 |
+
python-multipart
|
source/conf.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Configuration file for the Sphinx documentation builder.
|
2 |
+
#
|
3 |
+
# For the full list of built-in configuration values, see the documentation:
|
4 |
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
5 |
+
|
6 |
+
# -- Project information -----------------------------------------------------
|
7 |
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
8 |
+
|
9 |
+
project = 'sanjay7178'
|
10 |
+
copyright = '2024, sanjay'
|
11 |
+
author = 'sanjay'
|
12 |
+
release = 'v1'
|
13 |
+
|
14 |
+
# -- General configuration ---------------------------------------------------
|
15 |
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
16 |
+
|
17 |
+
extensions = []
|
18 |
+
|
19 |
+
templates_path = ['_templates']
|
20 |
+
exclude_patterns = []
|
21 |
+
|
22 |
+
|
23 |
+
|
24 |
+
# -- Options for HTML output -------------------------------------------------
|
25 |
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
26 |
+
|
27 |
+
html_theme = 'alabaster'
|
28 |
+
html_static_path = ['_static']
|
source/index.rst
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.. sanjay7178 documentation master file, created by
|
2 |
+
sphinx-quickstart on Tue Aug 27 20:15:42 2024.
|
3 |
+
You can adapt this file completely to your liking, but it should at least
|
4 |
+
contain the root `toctree` directive.
|
5 |
+
|
6 |
+
sanjay7178 documentation
|
7 |
+
========================
|
8 |
+
|
9 |
+
Add your content using ``reStructuredText`` syntax. See the
|
10 |
+
`reStructuredText <https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html>`_
|
11 |
+
documentation for details.
|
12 |
+
|
13 |
+
|
14 |
+
.. toctree::
|
15 |
+
:maxdepth: 2
|
16 |
+
:caption: Contents:
|
17 |
+
|
utils/__pycache__/captcha_solver.cpython-310.pyc
ADDED
Binary file (3.77 kB). View file
|
|
utils/__pycache__/captcha_solver.cpython-311.pyc
ADDED
Binary file (6.77 kB). View file
|
|
utils/__pycache__/payloads.cpython-310.pyc
ADDED
Binary file (2.21 kB). View file
|
|
utils/__pycache__/payloads.cpython-311.pyc
ADDED
Binary file (3.17 kB). View file
|
|
utils/captcha_solver.py
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
from PIL import Image
|
3 |
+
import json
|
4 |
+
from constants.bitmaps import bitmaps
|
5 |
+
import base64
|
6 |
+
from io import BytesIO
|
7 |
+
|
8 |
+
def pre_img(img):
|
9 |
+
avg = sum(sum(e) for e in img) / (24 * 22)
|
10 |
+
bits = [[1 if val > avg else 0 for val in row] for row in img]
|
11 |
+
return bits
|
12 |
+
|
13 |
+
|
14 |
+
def saturation(d):
|
15 |
+
saturate = np.round(((np.max(d, axis=1) - np.min(d, axis=1)) * 255) / np.max(d, axis=1))
|
16 |
+
img = saturate.reshape((40, 200))
|
17 |
+
bls = [img[7 + 5 * (i % 2) + 1:35 - 5 * ((i + 1) % 2), (i + 1) * 25 + 2:(i + 2) * 25 + 1] for i in range(6)]
|
18 |
+
return bls
|
19 |
+
|
20 |
+
|
21 |
+
def flatten(arr):
|
22 |
+
return [val for sublist in arr for val in sublist]
|
23 |
+
|
24 |
+
|
25 |
+
def mat_mul(a, b):
|
26 |
+
x, z, y = len(a), len(a[0]), len(b[0])
|
27 |
+
product_row = [0] * y
|
28 |
+
product = [[0] * y for _ in range(x)]
|
29 |
+
|
30 |
+
for i in range(x):
|
31 |
+
for j in range(y):
|
32 |
+
for k in range(z):
|
33 |
+
product[i][j] += a[i][k] * b[k][j]
|
34 |
+
|
35 |
+
return product
|
36 |
+
|
37 |
+
|
38 |
+
def mat_add(a, b):
|
39 |
+
return [a[i] + b[i] for i in range(len(a))]
|
40 |
+
|
41 |
+
|
42 |
+
def max_soft(a):
|
43 |
+
n = list(a)
|
44 |
+
s = sum(np.exp(f) for f in n)
|
45 |
+
n = [np.exp(f) / s for f in n]
|
46 |
+
return n
|
47 |
+
|
48 |
+
|
49 |
+
HEIGHT = 40
|
50 |
+
WIDTH = 200
|
51 |
+
|
52 |
+
def solve(img):
|
53 |
+
weights = None
|
54 |
+
biases = None
|
55 |
+
label_txt = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
|
56 |
+
|
57 |
+
with open("constants/weights.json", "r") as f:
|
58 |
+
data = json.load(f)
|
59 |
+
weights = data["weights"]
|
60 |
+
biases = data["biases"]
|
61 |
+
|
62 |
+
img_data = img.convert("RGB").getdata()
|
63 |
+
img_array = np.array(list(img_data))
|
64 |
+
|
65 |
+
bls = saturation(img_array)
|
66 |
+
out = ""
|
67 |
+
|
68 |
+
for i in range(6):
|
69 |
+
bls[i] = pre_img(bls[i])
|
70 |
+
bls[i] = [flatten(bls[i])]
|
71 |
+
bls[i] = mat_mul(bls[i], weights)
|
72 |
+
bls[i] = mat_add(*bls[i], biases)
|
73 |
+
bls[i] = max_soft(bls[i])
|
74 |
+
index = bls[i].index(max(bls[i]))
|
75 |
+
out += label_txt[index]
|
76 |
+
|
77 |
+
return out
|
78 |
+
|
79 |
+
def solve_base64(img_base64: str) -> str:
|
80 |
+
return solve(Image.open(BytesIO(base64.b64decode(img_base64))))
|
utils/payloads.py
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import datetime
|
2 |
+
|
3 |
+
|
4 |
+
def get_current_time() -> str:
|
5 |
+
return datetime.datetime.now(datetime.timezone.utc).strftime("%c GMT")
|
6 |
+
|
7 |
+
|
8 |
+
def get_login_payload(
|
9 |
+
csrf_token: str, username: str, password: str, captcha: str
|
10 |
+
) -> dict:
|
11 |
+
return {
|
12 |
+
"_csrf": csrf_token,
|
13 |
+
"username": username,
|
14 |
+
"password": password,
|
15 |
+
"captchaStr": captcha,
|
16 |
+
}
|
17 |
+
|
18 |
+
|
19 |
+
def get_profile_payload(username: str, csrf: str) -> dict:
|
20 |
+
return {
|
21 |
+
"verifyMenu": "true",
|
22 |
+
"authorizedID": username,
|
23 |
+
"_csrf": csrf,
|
24 |
+
"nocache": "@(new Date().getTime()",
|
25 |
+
}
|
26 |
+
|
27 |
+
|
28 |
+
def get_timetable_payload(username: str, semID: str, csrf: str) -> dict:
|
29 |
+
return {
|
30 |
+
"_csrf": csrf,
|
31 |
+
"semesterSubId": semID,
|
32 |
+
"authorizedID": username,
|
33 |
+
"x": get_current_time(),
|
34 |
+
}
|
35 |
+
|
36 |
+
|
37 |
+
def get_attendance_payload(username: str, semID: str, csrf: str) -> dict:
|
38 |
+
return get_timetable_payload(username, semID, csrf)
|
39 |
+
|
40 |
+
|
41 |
+
def get_attendance_semID_list_payload(username: str, csrf: str) -> dict:
|
42 |
+
return get_profile_payload(username, csrf)
|
43 |
+
|
44 |
+
|
45 |
+
def get_attendance_detail_payload(
|
46 |
+
csrf: str, semID: str, username: str, courseID: str, courseType: str
|
47 |
+
) -> dict:
|
48 |
+
return {
|
49 |
+
"_csrf": csrf,
|
50 |
+
"semesterSubId": semID,
|
51 |
+
"registerNumber": username,
|
52 |
+
"courseId": courseID,
|
53 |
+
"courseType": courseType,
|
54 |
+
"authorizedID": username,
|
55 |
+
"x": get_current_time(),
|
56 |
+
}
|
57 |
+
|
58 |
+
|
59 |
+
def get_doMarks_view_payload(username: str, semID: str, csrf: str) -> dict:
|
60 |
+
return {"authorizedID": username, "semesterSubId": semID, "_csrf": csrf}
|
61 |
+
|
62 |
+
|
63 |
+
def get_gradeHistory_payload(username: str, csrf: str) -> dict:
|
64 |
+
return get_profile_payload(username, csrf)
|
65 |
+
|
66 |
+
|
67 |
+
def get_examSchedule_payload(username: str, semID: str, csrf: str) -> dict:
|
68 |
+
return get_doMarks_view_payload(username, semID, csrf)
|
69 |
+
|
70 |
+
|
71 |
+
def get_goto_page_payload(username: str, csrf: str) -> dict:
|
72 |
+
return get_profile_payload(username, csrf)
|
utils/sem_ids.py
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import os
|
3 |
+
|
4 |
+
semIDs = json.load(open(os.path.join(os.path.dirname(__file__), '../constants/sem_ids.json'))).keys()
|