From 90568dfdddbed737067cca2eec686d35c16f36b4 Mon Sep 17 00:00:00 2001 From: mnjx Date: Thu, 13 Apr 2023 12:36:08 +0200 Subject: [PATCH] update: file organization, README; fix: showing averages with grades; add: Makefile --- Makefile | 5 + README.md | 31 +- requirements.txt | 4 + scrape_data.py | 510 ------------------ src/lib/__init__.py | 0 src/lib/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 173 bytes src/lib/__pycache__/grades.cpython-311.pyc | Bin 0 -> 8386 bytes .../__pycache__/networking.cpython-311.pyc | Bin 0 -> 2235 bytes .../__pycache__/schedule_week.cpython-311.pyc | Bin 0 -> 15506 bytes src/lib/grades.py | 151 ++++++ src/lib/networking.py | 63 +++ src/lib/schedule_week.py | 304 +++++++++++ src/main.py | 17 + 13 files changed, 571 insertions(+), 514 deletions(-) create mode 100644 Makefile create mode 100644 requirements.txt delete mode 100644 scrape_data.py create mode 100644 src/lib/__init__.py create mode 100644 src/lib/__pycache__/__init__.cpython-311.pyc create mode 100644 src/lib/__pycache__/grades.cpython-311.pyc create mode 100644 src/lib/__pycache__/networking.cpython-311.pyc create mode 100644 src/lib/__pycache__/schedule_week.cpython-311.pyc create mode 100644 src/lib/grades.py create mode 100644 src/lib/networking.py create mode 100644 src/lib/schedule_week.py create mode 100644 src/main.py diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cc5a2df --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +install: + pip install -r requirements.txt + +run: + python3 "src/main.py" \ No newline at end of file diff --git a/README.md b/README.md index 88eae8c..94cc1a4 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,34 @@ # SOL Enhanced -An enhanced version of the SkolaOnLine (SOL) online school system. +An enhanced version of the ŠkolaOnLine (ŠOL) online school system. It aims to provide more readable data than the web version and -remove some of the annoyances of the mobile app. +remove some of the annoyances of the mobile app. It is intended +to be and Android app in the future, but so far it is **only for Linux** +(not tested anywhere else). Insiration was taken from [@JakubAndrysek](https://github.com/JakubAndrysek)'s [skola-online-stahovani-znamek](https://github.com/JakubAndrysek/skola-online-stahovani-znamek) project. -## Still in development! -#### It is not ready yet. +# Features + ++ Shows the schedule for the current week ++ Shows all grades and their arithmetic average (not accounting for weight) ++ Works offline (when the internet has been accessed at least once before) + +# Usage + +1. Clone the repo + +`git clone git@github.com:mnjx/sol-enhanced.git` + +2. Navigate into the repo directory + +`cd sol-enhanced` + +3. Install the requirements (required to run only once) + +`make install` + +4. Run the program + +`make run` diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..314f890 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +lxml +bs4 +requests + diff --git a/scrape_data.py b/scrape_data.py deleted file mode 100644 index 2a0246c..0000000 --- a/scrape_data.py +++ /dev/null @@ -1,510 +0,0 @@ -import requests -from bs4 import BeautifulSoup -import time - -session = requests.Session() -REMOTE_SERVER = "one.one.one.one" -not_login = 0 -F_not_anymore = 0 - -def fetch_locally(what: str) -> list: - global not_login, F_not_anymore - - if not_login == 1 and F_not_anymore == 0: - F_not_anymore = 1 - not_login = 0 - print("Could not sign in (you may have entered invalid login credentials)") - print("Fetching locally\n") - if what == "schedule": - schedule = schedule_from_file() - return schedule - elif what == "grades": - grades = grades_from_file() - return grades - -def login(user: str, pwd: str) -> bool: - if is_connected(REMOTE_SERVER) == True: - url: str = "https://aplikace.skolaonline.cz/SOL/Prihlaseni.aspx" - data = { - "__EVENTTARGET": "dnn$ctr994$SOLLogin$btnODeslat", - "__EVENTARGUMENT": "", - "__VIEWSTATE": "", - "__VIEWSTATEGENERATOR": "", - "__VIEWSTATEENCRYPTED": "", - "__PREVIOUSPAGE": "", - "__EVENTVALIDATION": "", - "dnn$dnnSearch$txtSearch": "", - "JmenoUzivatele": user, # username - "HesloUzivatele": pwd, # password - "ScrollTop": "", - "__dnnVariable": "", - "__RequestVerificationToken": "" - } - - post_login = session.post(url, data=data) - if post_login.status_code != 200: - #print("Could not sign in") - return False - else: - #print("Sign in successful") - return True - else: - #print("Could not sign in") - #print("Fetching data from local files") - return False - -def is_connected(hostname: str) -> bool: - import socket - - try: - host = socket.gethostbyname(hostname) - s = socket.create_connection((host, 80), 2) - s.close() - return True - except Exception: - pass - return False - -def get_html_from(params=None) -> str: - base = "https://aplikace.skolaonline.cz" - - if params is not None: - base += params - - response = session.get(base) - if response.status_code == 200: - #print("Download successful") - return response - else: - print("Download unsuccessful") - return "" - -def sort_calendar(cal: list[dict]) -> list[dict]: - sorted_cal: list[dict] = [{}, {}, {}, {}, {}] - - for i in range(len(cal)): - if cal[i]["DAY"] == "Po": - sorted_cal[0] = cal[i] - if cal[i]["DAY"] == "Út": - sorted_cal[1] = cal[i] - if cal[i]["DAY"] == "St": - sorted_cal[2] = cal[i] - if cal[i]["DAY"] == "Čt": - sorted_cal[3] = cal[i] - if cal[i]["DAY"] == "Pá": - sorted_cal[4] = cal[i] - - return sorted_cal - -def get_schedule(url: str): - try: - html = get_html_from(url) - - soup = BeautifulSoup(html.text, "lxml") - - table = soup.find("table", id="CCADynamicCalendarTable") - - rows_odd = table.find_all("tr", class_="RowOdd") - rows_even = table.find_all("tr", class_="RowEven") - rows = rows_odd + rows_even - - calendar = [] - - for i in range(len(rows)): - if rows[i].find("td", class_="KuvSuplovanaHodina") is not None and rows[i].find("td", class_="KuvSuplujiciHodina") is None: - continue - - subjects = [] - wheres = [] - cells = rows[i].find_all("td", attrs={"class": "DctCell"}) - for j in range(len(cells)): - if cells[j].text == "": - new_span = soup.new_tag("span", attrs={'class': 'KuvBunkaRozvrhNadpis'}) - new_span.string = "Volno" - cells[j].append(new_span) - - if rows[i].find("td", class_="KuvHeaderNadpis") is not None: - day = rows[i].find("td", class_="KuvHeaderNadpis").text - - date = rows[i].find("td", class_="KuvHeaderText").text - - subjects_ = rows[i].find_all("span", class_="KuvBunkaRozvrhNadpis") - temp = [] - for j in range(len(subjects_)): - temp.append(subjects_[j].text) - subjects = temp - - F_con = False - try: - while subjects[-1] == "Volno": - del subjects[-1] - if subjects == []: - F_con = True - except IndexError: - pass - - wheres_ = rows[i].find_all("span", class_="KuvBunkaRozvrhText") - temp = [] - for j in range(len(wheres_)): - if len(subjects[j]) < 10: - temp.append(wheres_[j].text[4:]) - else: - temp.append(" - ") - wheres = temp - - for j in range(len(cells)): - if cells[j].get("colspan") is not None: - if int(cells[j].get("colspan")) == 2: - subjects.insert(j, subjects[j]) - wheres.insert(j, wheres[j]) - for j in range(len(subjects)): - if subjects[j] == "Volno": - wheres.insert(j, " - ") - - if F_con == False: - calendar_row = { - "DAY": day, - "DATE": date, - "SUBJECTS": subjects, - "WHERES": wheres - } - - calendar.append(calendar_row) - - sorted_calendar = sort_calendar(calendar) - - return sorted_calendar - except AttributeError: - global not_login - - not_login = 1 - schedule = fetch_locally("schedule") - return schedule - -def schedule_from_file_to_list(): - schedule = [] - schedule_ = [] - - try: - with open("schedule.csv") as f: - import csv - reader = csv.reader(f) - for row in reader: - temp = [] - for col in row: - temp.append(col.strip()) - schedule_.append(temp) - - for i in range(0, len(schedule_), 2): - del schedule_[i][0] - - temp = [] - for i in range(1, len(schedule_), 2): - temp.append(schedule_[i][0]) - del schedule_[i][0] - schedule.append(temp) - - for i in range(10): - temp = [] - for j in range(10): - try: - temp.append(schedule_[j][0]) - del schedule_[j][0] - except IndexError: - pass - schedule.append(temp) - except FileNotFoundError: - print("No schedule.csv file found\n") - - return schedule - -def get_whitespace(key: str, idx=0) -> int: - schedule = schedule_from_file_to_list() - max_len = 0 - - if key == "DATE": - try: - max_len = len(max(schedule[0], key=len)) - except ValueError: - pass - elif key == "WHERES": - try: - max_len = len(max(schedule[idx], key=len)) - except ValueError: - pass - - return max_len - -def show_schedule(): - # if there is internet access, load stuff from the web - # if not, load the schedule.csv file - if is_connected(REMOTE_SERVER) == True: - schedule = get_schedule("/SOL/App/Kalendar/KZK001_KalendarTyden.aspx") - schedule_to_file(schedule) - else: - schedule = schedule_from_file() - - print("--------") - print(" Rozvrh") - print("--------\n") - - if schedule == []: - print("N/A\n") - import os - - if os.path.exists("schedule.csv"): - os.remove("schedule.csv") - return - - for i in range(len(schedule)): - max_len = get_whitespace("DATE") - - whitespace = " " * (max_len - len(schedule[i]["DAY"])) - print(schedule[i]["DAY"] + whitespace, end=" ") - - for j in range(len(schedule[i]["SUBJECTS"])): - max_len = get_whitespace("WHERES", j+1) - whitespace = " " * (max_len - len(schedule[i]["SUBJECTS"][j])) - print("| " + schedule[i]["SUBJECTS"][j] + whitespace, end=" ") - - print("") - - max_len = get_whitespace("DATE") - whitespace = " " * (max_len - len(schedule[i]["DATE"])) - print(schedule[i]["DATE"] + whitespace, end=" ") - - for j in range(len(schedule[i]["WHERES"])): - max_len = get_whitespace("WHERES", j+1) - whitespace = " " * (max_len - len(schedule[i]["WHERES"][j])) - print("| " + schedule[i]["WHERES"][j] + whitespace, end=" ") - - print("") - print("") - -def get_avg(grades: list) -> float: - if len(grades) < 1: - return "" - - sum_ = 0 - for i in grades: - sum_ += int(i) - avg = round(sum_ / len(grades), 2) - - return str(avg) - -def get_grades(url: str): - html = get_html_from(url) - - soup = BeautifulSoup(html.text, "lxml") - - rows = soup.find("table", id="G_ctl00xmainxCCADynamicUWGrid").find_all("tr") - for i in range(3): - r = soup.find("table", id="G_ctl00xmainxCCADynamicUWGrid").find("tr") - r.decompose() - - grades = [] - - for i in range(1, len(rows)): - if rows[i].find("td") is not None: - subject = rows[i].find("td").text - temp_dict = {} - temp_dict["SUBJECT"] = subject - cell = rows[i].find_all("span", class_="VHVysl") - temp = [] - for j in range(len(cell)): - temp.append(cell[j].text) - temp_dict["GRADES"] = temp - - grades.append(temp_dict) - - return grades - -def show_grades(): - if is_connected(REMOTE_SERVER) == True: - grades = get_grades("/SOL/App/Hodnoceni/KZH001_HodnVypisStud.aspx") - grades_to_file(grades) - else: - grades = grades_from_file() - - whitespace = "" - max_len_of_subject = 0 - subjects = [grades[j]["SUBJECT"] for j in range(len(grades))] - for i in subjects: - if len(i) > max_len_of_subject: - max_len_of_subject = len(i) - max_len_of_subject += 1 - - print("--------") - print(" Znamky") - print("--------\n") - - if grades == []: - print("N/A\n") - import os - - if os.path.exists("grades.csv"): - os.remove("grades.csv") - return - - for i in range(len(grades)): - whitespace = " " * (max_len_of_subject - len(grades[i]["SUBJECT"])) - print(grades[i]["SUBJECT"] + whitespace + "|", end=" ") - print("".join([grades[i]["GRADES"][j]+" " for j in range(len(grades[i]["GRADES"]))]), end=" ") - print("|", get_avg(grades[i]["GRADES"])) - print("") - print("") - -def schedule_to_file(schedule=None): - import csv - if schedule is None: - try: - schedule = get_schedule("/SOL/App/Kalendar/KZK001_KalendarTyden.aspx") - except Exception: - print("An error occured, you probably can't access the internet right now") - return - - with open("schedule.csv", mode="w", newline="") as f: - data = [] - - for i in range(len(schedule)): - temp = [] - try: - temp.append(schedule[i]["DAY"]) - for j in range(len(schedule[i]["SUBJECTS"])): - temp.append(schedule[i]["SUBJECTS"][j]) - data.append(temp) - - temp = [] - temp.append(schedule[i]["DATE"]) - for j in range(len(schedule[i]["WHERES"])): - temp.append(schedule[i]["WHERES"][j]) - data.append(temp) - - temp = [] - for j in range(len(schedule[i]["WHOS"])): - temp.append(schedule[i]["WHOS"][j]) - data.append(temp) - except KeyError: - pass - - writer = csv.writer(f) - for row in data: - writer.writerow(row) - -def schedule_from_file() -> list: - import csv - schedule = [] - - try: - with open("schedule.csv", mode="r") as f: - reader = csv.reader(f) - rows = [] - for row in reader: - rows.append(row) - - for row in range(0, len(rows), 3): - temp = {} - - temp["DAY"] = rows[row][0] - - try: - temp["DATE"] = rows[row+1][0] - except IndexError: - pass - - subjects = [] - for i in range(1, len(rows[row])): - subjects.append(rows[row][i]) - temp["SUBJECTS"] = subjects - - wheres = [] - try: - for i in range(1, len(rows[row+1])): - wheres.append(rows[row+1][i]) - except IndexError: - pass - temp["WHERES"] = wheres - - whos = [] - try: - for i in range(len(rows[row+2])): - whos.append(rows[row+2][i]) - except IndexError: - pass - temp["WHOS"] = whos - - schedule.append(temp) - except FileNotFoundError: - print("No schedule.csv file found\n") - - return schedule - -def grades_to_file(grades=None): # !!! make sure the format of the grades list is saved correctly !!! - import csv - if grades is None: - try: - grades = get_grades("/SOL/App/Hodnoceni/KZH001_HodnVypisStud.aspx") - except Exception: - print("An error occured, you probably can't access the internet right now") - return - - data = [] - - with open("grades.csv", mode="w", newline="") as f: - for i in range(len(grades)): - temp = {} - temp["SUBJECT"] = grades[i]["SUBJECT"] - temp_grades = [] - for j in range(len(grades[i]["GRADES"])): - temp_grades.append(grades[i]["GRADES"][j]) - temp["GRADES"] = temp_grades - data.append(temp) - - writer = csv.writer(f) - for row in data: - _row = [] - _row.append(row["SUBJECT"]) - for i in row["GRADES"]: - _row.append(i) - writer.writerow(_row) - -def grades_from_file() -> list: - import csv - grades = [] - - try: - with open("grades.csv", mode="r") as f: - reader = csv.reader(f) - rows = [] - for row in reader: - rows.append(row) - - for row in range(len(rows)): - temp = {} - - temp["SUBJECT"] = rows[row][0] - - temp_lst = [] - for i in range(1, len(rows[row])): - temp_lst.append(rows[row][i]) - temp["GRADES"] = temp_lst - - grades.append(temp) - except FileNotFoundError: - print("No grades.csv file found\n") - - return grades - -if __name__ == "__main__": - user = str(input("Zadejte prihlasovaci jmeno: ")) - pwd = str(input("Zadejte heslo: ")) - - if is_connected(REMOTE_SERVER) == True: - login(user, pwd) - #get_schedule("/SOL/App/Kalendar/KZK001_KalendarTyden.aspx") - show_schedule() - #get_grades("/SOL/App/Hodnoceni/KZH001_HodnVypisStud.aspx") - show_grades() - #schedule_to_file() - #grades_to_file() - #schedule_from_file() diff --git a/src/lib/__init__.py b/src/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/__pycache__/__init__.cpython-311.pyc b/src/lib/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..422999a387d2d04fe3f2d833abbb25fae284677b GIT binary patch literal 173 zcmZ3^%ge<81Se0Lr-10kAOZ#$p^VRLK*n^26oz01O-8?!3`I;p{%4TnF9-dM{M=Oi z+`Oy`eV6>?(%jU%l4AYj{FGGvqSS)?V*TR$9NpBsjKsX;)D->VqGbJ?%q0Ez_{_Y_ rlK6PNg34bUHo5sJr8%i~MXW#rK~@&?1BnmJjEsyQ7+^#ZGf)fwZo?`q literal 0 HcmV?d00001 diff --git a/src/lib/__pycache__/grades.cpython-311.pyc b/src/lib/__pycache__/grades.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a689d7bb92d6c666cb3fbd46eb79876d646cd96 GIT binary patch literal 8386 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 GyZ#?FEx0xS literal 0 HcmV?d00001 diff --git a/src/lib/__pycache__/networking.cpython-311.pyc b/src/lib/__pycache__/networking.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0ed49b6bd14e5f40b2b2016421a6cb98da8e8ae GIT binary patch literal 2235 zcmahKOKcNIboOhnW5SK9y2P*?BuQF6e%oNI>mNjOsA!l%8+T>al<8- zFJFp-{+4sMV8`cNdvY98OLrMcL|7!BPtT+?xm;@cMmoo$EfJ*K-@`QP%I9Y$(m!N# zsa%@%b_h4pne=ojH#vQ1Hl4XPeP=3{9%tS8{M2-MW@7Sob}Drv&3amvXHvH&##6b8 z$xP)0_YWY8bz&^U-4*v8$GX2OV%xc0Dg3Cr*ut#)TX48T*0Kh1EGy@fxc^{bMkfV* z4pt84^V9fV8B=!#lfrz#(A|P#=bR;ML#D{4?&@#;g2%p|kXg7u^M+&F*l@AQ0u)mU zYnfbfsLQ&jtGi{sz{ClK$gqd-^kq_+Sfr10A-*tg@mHl>HNwZ+tH}kYh?7NoaV0tK z7-hJLOOxD8l3>`8q>gnS+Y7pFU^7XHk+cePNgKP%4p}PLcgITinbI^$hQMeLfUW`Z z&@1`aT4p`-$lQ?!x8y-Ta@8B%?Y-pp5BSG>{gW~N>pS5O4EjSOe&5G_-++JYxZm6F zpB`<-Bd1jvj3JA#g#cvyW<-MF@C)~eglbM@#s?{i;?)|HbRbVoU}rJUIfMQfil zRMZu%#T!8cQr{jpfeen?gFi~Ml>P(;%3m;%g;tS9^id_DBtr15=63`uF%gbm*x`UPlQ-$IR zWC(Yl5_O=CI^fL|%)a5O-1UUQRO%Q@*kzHs*j<3I&)v86B4#}X!H}ZuG~(%_7?wjZ z)6y#jF7f0`NMsbuFq!XT0;T){kSQbvl$>azv%pXnkcWJs+l%q9A**7**P<`A*p3$4 z8cJ?!7wXytFYc@1m+H`tIDrhVe;xkl z;mrp(H{|VbtR9Ye1HK%t%L5zNcI4qLd3aar@sh8_z*ca0TO6s2BU|E#uXWXqlVLEK z5Hg7{_odn6D3j*sCBhwIn(efj5sO^Ub3wQ77af9GAp5SfwJu6s0#|YztJz=PB_MbE zcho$gnU-&ZUl$#-Y~d^9GN^bO&}V=kX#~MXQLp*=D7JO@w;PUlnt!avi}<0z+Gq99 z;CAR#J#@+oG~}2NeIzvykd4tM*^D>IQFJ9FbU!j12*^=15EjA>1ZWd1wD}Kl&71rO DPhm6t literal 0 HcmV?d00001 diff --git a/src/lib/__pycache__/schedule_week.cpython-311.pyc b/src/lib/__pycache__/schedule_week.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..80485a71e64a656c02ebb528e4f42ec60c407b9c GIT binary patch literal 15506 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 literal 0 HcmV?d00001 diff --git a/src/lib/grades.py b/src/lib/grades.py new file mode 100644 index 0000000..1776652 --- /dev/null +++ b/src/lib/grades.py @@ -0,0 +1,151 @@ +from lib import networking as nw +from bs4 import BeautifulSoup + +not_login = 0 +F_not_anymore = 0 + +def fetch_locally() -> list: + global not_login, F_not_anymore + + if not_login == 1 and F_not_anymore == 0: + F_not_anymore = 1 + not_login = 0 + print("Could not sign in (you may have entered invalid login credentials)") + print("Fetching locally\n") + grades = grades_from_file() + return grades + + +def get_avg(grades: list) -> str: + if len(grades) < 1: + return "" + + sum_ = 0 + for i in grades: + sum_ += int(i) + avg = round(sum_ / len(grades), 2) + + avg = str(avg) + if len(avg) == 3: + avg += "0" + + return avg + +def get_grades(url: str): + html = nw.get_html_from(url) + + soup = BeautifulSoup(html.text, "lxml") + + rows = soup.find("table", id="G_ctl00xmainxCCADynamicUWGrid").find_all("tr") + for i in range(3): + r = soup.find("table", id="G_ctl00xmainxCCADynamicUWGrid").find("tr") + r.decompose() + + grades = [] + + for i in range(1, len(rows)): + if rows[i].find("td") is not None: + subject = rows[i].find("td").text + temp_dict = {} + temp_dict["SUBJECT"] = subject + cell = rows[i].find_all("span", class_="VHVysl") + temp = [] + for j in range(len(cell)): + temp.append(cell[j].text) + temp_dict["GRADES"] = temp + + grades.append(temp_dict) + + return grades + +def show_grades(): + if nw.is_connected() == True: + grades = get_grades("/SOL/App/Hodnoceni/KZH001_HodnVypisStud.aspx") + grades_to_file(grades) + else: + grades = grades_from_file() + + whitespace = "" + max_len_of_subject = 0 + subjects = [grades[j]["SUBJECT"] for j in range(len(grades))] + for i in subjects: + if len(i) > max_len_of_subject: + max_len_of_subject = len(i) + max_len_of_subject += 1 + + print("--------") + print(" Známky ") + print("--------\n") + + if grades == []: + print("N/A\n") + import os + + if os.path.exists("src/lib/data/grades.csv"): + os.remove("src/lib/data/grades.csv") + return + + for i in range(len(grades)): + if grades[i]["GRADES"] != []: + whitespace = " " * (max_len_of_subject - len(grades[i]["SUBJECT"])) + print(grades[i]["SUBJECT"] + whitespace + "|", end=" ") + print(get_avg(grades[i]["GRADES"]), "|", end=" ") + print("".join([grades[i]["GRADES"][j]+" " for j in range(len(grades[i]["GRADES"]))]), end=" ") + print("") + print("") + +def grades_to_file(grades=None): # !!! make sure the format of the grades list is saved correctly !!! + import csv + if grades is None: + try: + grades = get_grades("/SOL/App/Hodnoceni/KZH001_HodnVypisStud.aspx") + except Exception: + print("An error occured, you probably can't access the internet right now") + return + + data = [] + + with open("src/lib/data/grades.csv", mode="w", newline="") as f: + for i in range(len(grades)): + temp = {} + temp["SUBJECT"] = grades[i]["SUBJECT"] + temp_grades = [] + for j in range(len(grades[i]["GRADES"])): + temp_grades.append(grades[i]["GRADES"][j]) + temp["GRADES"] = temp_grades + data.append(temp) + + writer = csv.writer(f) + for row in data: + _row = [] + _row.append(row["SUBJECT"]) + for i in row["GRADES"]: + _row.append(i) + writer.writerow(_row) + +def grades_from_file() -> list: + import csv + grades = [] + + try: + with open("src/lib/data/grades.csv", mode="r") as f: + reader = csv.reader(f) + rows = [] + for row in reader: + rows.append(row) + + for row in range(len(rows)): + temp = {} + + temp["SUBJECT"] = rows[row][0] + + temp_lst = [] + for i in range(1, len(rows[row])): + temp_lst.append(rows[row][i]) + temp["GRADES"] = temp_lst + + grades.append(temp) + except FileNotFoundError: + print("No grades.csv file found\n") + + return grades \ No newline at end of file diff --git a/src/lib/networking.py b/src/lib/networking.py new file mode 100644 index 0000000..068392d --- /dev/null +++ b/src/lib/networking.py @@ -0,0 +1,63 @@ +import requests +from bs4 import BeautifulSoup + +not_login = 0 +F_not_anymore = 0 +session = requests.Session() + +def login(user: str, pwd: str) -> bool: + if is_connected() == True: + url: str = "https://aplikace.skolaonline.cz/SOL/Prihlaseni.aspx" + data = { + "__EVENTTARGET": "dnn$ctr994$SOLLogin$btnODeslat", + "__EVENTARGUMENT": "", + "__VIEWSTATE": "", + "__VIEWSTATEGENERATOR": "", + "__VIEWSTATEENCRYPTED": "", + "__PREVIOUSPAGE": "", + "__EVENTVALIDATION": "", + "dnn$dnnSearch$txtSearch": "", + "JmenoUzivatele": user, # username + "HesloUzivatele": pwd, # password + "ScrollTop": "", + "__dnnVariable": "", + "__RequestVerificationToken": "" + } + + post_login = session.post(url, data=data) + if post_login.status_code != 200: + #print("Could not sign in") + return False + else: + #print("Sign in successful") + return True + else: + #print("Could not sign in") + #print("Fetching data from local files") + return False + +def is_connected() -> bool: + import socket + + try: + host = socket.gethostbyname("one.one.one.one") + s = socket.create_connection((host, 80), 2) + s.close() + return True + except Exception: + pass + return False + +def get_html_from(params=None) -> str: + base = "https://aplikace.skolaonline.cz" + + if params is not None: + base += params + + response = session.get(base) + if response.status_code == 200: + #print("Download successful") + return response + else: + print("Download unsuccessful") + return "" diff --git a/src/lib/schedule_week.py b/src/lib/schedule_week.py new file mode 100644 index 0000000..bfc1644 --- /dev/null +++ b/src/lib/schedule_week.py @@ -0,0 +1,304 @@ +from lib import networking as nw +from bs4 import BeautifulSoup + +not_login = 0 +F_not_anymore = 0 + +def fetch_locally() -> list: + global not_login, F_not_anymore + + if not_login == 1 and F_not_anymore == 0: + F_not_anymore = 1 + not_login = 0 + print("Could not sign in (you may have entered invalid login credentials)") + print("Fetching locally\n") + schedule_week = schedule_week_from_file() + return schedule_week + + +def sort_schedule_week(schedule_week: list[dict]) -> list[dict]: + sorted_schedule_week: list[dict] = [{}, {}, {}, {}, {}] + + for i in range(len(schedule_week)): + if schedule_week[i]["DAY"] == "Po": + sorted_schedule_week[0] = schedule_week[i] + if schedule_week[i]["DAY"] == "Út": + sorted_schedule_week[1] = schedule_week[i] + if schedule_week[i]["DAY"] == "St": + sorted_schedule_week[2] = schedule_week[i] + if schedule_week[i]["DAY"] == "Čt": + sorted_schedule_week[3] = schedule_week[i] + if schedule_week[i]["DAY"] == "Pá": + sorted_schedule_week[4] = schedule_week[i] + + return sorted_schedule_week + +def get_schedule_week(url: str): + try: + html = nw.get_html_from(url) + + soup = BeautifulSoup(html.text, "lxml") + + table = soup.find("table", id="CCADynamicCalendarTable") + + rows_odd = table.find_all("tr", class_="RowOdd") + rows_even = table.find_all("tr", class_="RowEven") + rows = rows_odd + rows_even + + schedule_week = [] + + for i in range(len(rows)): + if rows[i].find("td", class_="KuvSuplovanaHodina") is not None and rows[i].find("td", class_="KuvSuplujiciHodina") is None: + continue + + subjects = [] + wheres = [] + cells = rows[i].find_all("td", attrs={"class": "DctCell"}) + for j in range(len(cells)): + if cells[j].text == "": + new_span = soup.new_tag("span", attrs={'class': 'KuvBunkaRozvrhNadpis'}) + new_span.string = "Volno" + cells[j].append(new_span) + + if rows[i].find("td", class_="KuvHeaderNadpis") is not None: + day = rows[i].find("td", class_="KuvHeaderNadpis").text + + date = rows[i].find("td", class_="KuvHeaderText").text + + subjects_ = rows[i].find_all("span", class_="KuvBunkaRozvrhNadpis") + temp = [] + for j in range(len(subjects_)): + temp.append(subjects_[j].text) + subjects = temp + + F_con = False + try: + while subjects[-1] == "Volno": + del subjects[-1] + if subjects == []: + F_con = True + except IndexError: + pass + + wheres_ = rows[i].find_all("span", class_="KuvBunkaRozvrhText") + temp = [] + for j in range(len(wheres_)): + if len(subjects[j]) < 10: + temp.append(wheres_[j].text[4:]) + else: + temp.append(" - ") + wheres = temp + + for j in range(len(cells)): + if cells[j].get("colspan") is not None: + if int(cells[j].get("colspan")) == 2: + subjects.insert(j, subjects[j]) + wheres.insert(j, wheres[j]) + for j in range(len(subjects)): + if subjects[j] == "Volno": + wheres.insert(j, " - ") + + if F_con == False: + schedule_week_row = { + "DAY": day, + "DATE": date, + "SUBJECTS": subjects, + "WHERES": wheres + } + + schedule_week.append(schedule_week_row) + + sorted_schedule_week = sort_schedule_week(schedule_week) + + return sorted_schedule_week + except AttributeError: + global not_login + + not_login = 1 + schedule = fetch_locally() + return schedule + +def schedule_week_from_file_to_list(): + schedule = [] + schedule_ = [] + + try: + with open("src/lib/data/schedule_week.csv") as f: + import csv + reader = csv.reader(f) + for row in reader: + temp = [] + for col in row: + temp.append(col.strip()) + schedule_.append(temp) + + for i in range(0, len(schedule_), 2): + del schedule_[i][0] + + temp = [] + for i in range(1, len(schedule_), 2): + temp.append(schedule_[i][0]) + del schedule_[i][0] + schedule.append(temp) + + for i in range(10): + temp = [] + for j in range(10): + try: + temp.append(schedule_[j][0]) + del schedule_[j][0] + except IndexError: + pass + schedule.append(temp) + except FileNotFoundError: + print("No schedule_week.csv file found\n") + + return schedule + +def get_whitespace(key: str, idx=0) -> int: + schedule = schedule_week_from_file_to_list() + max_len = 0 + + if key == "DATE": + try: + max_len = len(max(schedule[0], key=len)) + except ValueError: + pass + elif key == "WHERES": + try: + max_len = len(max(schedule[idx], key=len)) + except ValueError: + pass + + return max_len + +def show_schedule_week(): + # if there is internet access, load stuff from the web + # if not, load the schedule.csv file + if nw.is_connected() == True: + schedule = get_schedule_week("/SOL/App/Kalendar/KZK001_KalendarTyden.aspx") + schedule_week_to_file(schedule) + else: + schedule = schedule_week_from_file() + + print("----------------") + print(" Týdenní rozvrh ") + print("----------------\n") + + if schedule == []: + print("N/A\n") + import os + + if os.path.exists("src/lib/data/schedule_week.csv"): + os.remove("src/lib/data/schedule_week.csv") + return + + for i in range(len(schedule)): + max_len = get_whitespace("DATE") + + whitespace = " " * (max_len - len(schedule[i]["DAY"])) + print(schedule[i]["DAY"] + whitespace, end=" ") + + for j in range(len(schedule[i]["SUBJECTS"])): + max_len = get_whitespace("WHERES", j+1) + whitespace = " " * (max_len - len(schedule[i]["SUBJECTS"][j])) + print("| " + schedule[i]["SUBJECTS"][j] + whitespace, end=" ") + + print("") + + max_len = get_whitespace("DATE") + whitespace = " " * (max_len - len(schedule[i]["DATE"])) + print(schedule[i]["DATE"] + whitespace, end=" ") + + for j in range(len(schedule[i]["WHERES"])): + max_len = get_whitespace("WHERES", j+1) + whitespace = " " * (max_len - len(schedule[i]["WHERES"][j])) + print("| " + schedule[i]["WHERES"][j] + whitespace, end=" ") + + print("") + print("") + +def schedule_week_to_file(schedule=None): + import csv + if schedule is None: + try: + schedule = get_schedule_week("/SOL/App/Kalendar/KZK001_KalendarTyden.aspx") + except Exception: + print("An error occured, you probably can't access the internet right now") + return + + with open("src/lib/data/schedule_week.csv", mode="w", newline="") as f: + data = [] + + for i in range(len(schedule)): + temp = [] + try: + temp.append(schedule[i]["DAY"]) + for j in range(len(schedule[i]["SUBJECTS"])): + temp.append(schedule[i]["SUBJECTS"][j]) + data.append(temp) + + temp = [] + temp.append(schedule[i]["DATE"]) + for j in range(len(schedule[i]["WHERES"])): + temp.append(schedule[i]["WHERES"][j]) + data.append(temp) + + temp = [] + for j in range(len(schedule[i]["WHOS"])): + temp.append(schedule[i]["WHOS"][j]) + data.append(temp) + except KeyError: + pass + + writer = csv.writer(f) + for row in data: + writer.writerow(row) + +def schedule_week_from_file() -> list: + import csv + schedule = [] + + try: + with open("src/lib/data/schedule_week.csv", mode="r") as f: + reader = csv.reader(f) + rows = [] + for row in reader: + rows.append(row) + + for row in range(0, len(rows), 3): + temp = {} + + temp["DAY"] = rows[row][0] + + try: + temp["DATE"] = rows[row+1][0] + except IndexError: + pass + + subjects = [] + for i in range(1, len(rows[row])): + subjects.append(rows[row][i]) + temp["SUBJECTS"] = subjects + + wheres = [] + try: + for i in range(1, len(rows[row+1])): + wheres.append(rows[row+1][i]) + except IndexError: + pass + temp["WHERES"] = wheres + + whos = [] + try: + for i in range(len(rows[row+2])): + whos.append(rows[row+2][i]) + except IndexError: + pass + temp["WHOS"] = whos + + schedule.append(temp) + except FileNotFoundError: + print("No schedule_week.csv file found\n") + + return schedule diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..f44ff38 --- /dev/null +++ b/src/main.py @@ -0,0 +1,17 @@ +from lib import grades as gr +from lib import networking as nw +from lib import schedule_week as sw + +if __name__ == "__main__": + user = str(input("Zadejte přihlašovací jméno: ")) + pwd = str(input("Zadejte heslo: ")) + + if nw.is_connected() == True: + nw.login(user, pwd) + #sw.get_schedule_week("/SOL/App/Kalendar/KZK001_KalendarTyden.aspx") + sw.show_schedule_week() + #gr.get_grades("/SOL/App/Hodnoceni/KZH001_HodnVypisStud.aspx") + gr.show_grades() + #sw.schedule_week_to_file() + #gr.grades_to_file() + #sw.schedule_week_from_file()