Wed Jun 21 15:43:39 CEST 2023

This commit is contained in:
mnjx
2023-06-21 17:52:01 +02:00
12 changed files with 290 additions and 159 deletions
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
if command -v solen > /dev/null
then
solen
else
python3 "./src/main.py"
fi
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
if [ ! -d "$HOME/.local/lib/python3.11/site-packages/solenlib" ]; then
sudo mkdir --parents "$HOME/.local/lib/python3.11/site-packages/solenlib"
fi
if [ ! -d "$HOME/.local/lib/solendata" ]; then
sudo mkdir --parents "$HOME/.local/lib/solendata"
fi
+2 -3
View File
@@ -1,16 +1,15 @@
SHELL=/bin/bash
install:
python3 -m pip install -r requirements.txt
python3 -m pip install -r requirements.txt --break-system-packages
./.config/mkdirs.sh
cp ./src/main.py /bin/solen
chmod 774 /bin/solen
chmod 774 /usr/bin/solen
cp ./src/lib/*.py $(HOME)/.local/lib/python3.11/site-packages/solenlib
cp ./src/lib/shadow $(HOME)/.local/lib/solendata
cp ./src/lib/data/*.csv $(HOME)/.local/lib/solendata
run:
./.config/launch.sh
+20 -8
View File
@@ -1,4 +1,4 @@
# SOL Enhanced
# Solen (ŠOL Enhanced)
An enhanced version of the ŠkolaOnLine (ŠOL) online school system.
@@ -11,15 +11,15 @@ Insiration was taken from [@JakubAndrysek](https://github.com/JakubAndrysek)'s [
# 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)
+ Shows the schedule for the current week
+ Shows all grades and their weighted average
+ Works offline (when the internet has been accessed at least once before)
# Installation
**Clone the repo**
`git clone https://github.com/mnjx/sol-enhanced.git`
`git clone https://github.com/mnjx/sol-enhanced`
**Navigate into the repo directory**
@@ -31,8 +31,9 @@ Insiration was taken from [@JakubAndrysek](https://github.com/JakubAndrysek)'s [
**Run the program**
From the repo folder: `make run`
From anywhere on the computer: `solen`
| ------------------------------ | ---------- |
| From the repo folder: | `make run` |
| From anywhere on the computer: | `solen` |
# Usage
@@ -42,4 +43,15 @@ will execute. By default, the functions are: showing the schedule for this week,
showing the schedule for today and tomorrow and showing the grades. You can edit
that file if you want to change what is being displayed. Alternatively, you can
edit the `src/main.py` and then do the install step (`sudo make install`).
An option for command-line arguments will be added in the future.
There are optional command-line arguments for filtering what should be displayed.
Here they are:
| ----------------------------------- | --------------------------------------------------------------------------------------- |
| -t, --today-tomorrow | show the schedule for today and tomorrow |
| -w, --week | show the schedule for this week |
| -g, --grades | show the grades |
| -u, --username | provide the username for login |
| -p, --password | provide the password for login |
| -l, --local | only show the local data without attempting to connect to the internet |
| -d, --delete-local-files | deletes all locally stored user data, including schedules, grades and login credentials |
| -b, --backup PASSPHRASE DESTINATION | securly backs up all local user data into a .tar.gz archive |
-1
View File
@@ -1 +0,0 @@
-1
View File
@@ -1 +0,0 @@
-1
View File
@@ -1 +0,0 @@
+130 -118
View File
@@ -5,154 +5,166 @@ import os
not_login = 0
def fetch_locally() -> list:
global not_login
global not_login
if not_login == 1:
not_login = 0
#print("Could not sign in (you may have entered invalid login credentials)")
#print("Fetching grades locally\n")
grades = grades_from_file()
return grades
if not_login == 1:
not_login = 0
#print("Could not sign in (you may have entered invalid login credentials)")
#print("Fetching grades locally\n")
grades = grades_from_file()
return grades
"""
# deprecated
def get_avg(grades: list) -> str:
if len(grades) < 1:
return ""
if len(grades) < 1:
return ""
sum_ = 0
for i in grades:
sum_ += int(i)
avg = round(sum_ / len(grades), 2)
sum_ = 0
for i in grades:
sum_ += int(i)
avg = round(sum_ / len(grades), 2)
avg = str(avg)
if len(avg) == 3:
avg += "0"
avg = str(avg)
if len(avg) == 3:
avg += "0"
return avg
return avg
"""
def get_grades(url: str):
try:
html = nw.get_html_from(url)
try:
html = nw.get_html_from(url)
soup = BeautifulSoup(html.text, "lxml")
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
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)
avg = rows[i].find("td", class_="VHCelkPrumer").text
temp_dict["AVG"] = avg
return grades
except AttributeError:
global not_login
grades.append(temp_dict)
not_login = 1
grades = fetch_locally()
return grades
return grades
except AttributeError:
global not_login
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()
not_login = 1
grades = fetch_locally()
return grades
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
def show_grades(local=False):
if local == False:
if nw.is_connected() == True:
grades = get_grades("/SOL/App/Hodnoceni/KZH001_HodnVypisStud.aspx")
grades_to_file(grades)
else:
grades = grades_from_file()
else:
grades = grades_from_file()
print("")
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")
print("")
if grades == []:
print("N/A\n")
import os
print("--------")
print(" Známky ")
print("--------\n")
if os.path.exists(f"{os.environ['HOME']}/.local/lib/solendata/grades.csv"):
os.remove(f"{os.environ['HOME']}/.local/lib/solendata/grades.csv")
return
if grades == []:
print("N/A\n")
import os
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("")
if os.path.exists(f"{os.environ['HOME']}/.local/lib/solendata/grades.csv"):
os.remove(f"{os.environ['HOME']}/.local/lib/solendata/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(grades[i]["AVG"], "|", end=" ")
print("".join([grades[i]["GRADES"][j]+" " for j in range(len(grades[i]["GRADES"]))]), end=" ")
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
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 = []
data = []
with open(f"{os.environ['HOME']}/.local/lib/solendata/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)
with open(f"{os.environ['HOME']}/.local/lib/solendata/grades.csv", mode="w", newline="") as f:
for i in range(len(grades)):
temp = {}
temp["SUBJECT"] = grades[i]["SUBJECT"]
temp["AVG"] = grades[i]["AVG"]
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)
writer = csv.writer(f)
for row in data:
_row = []
_row.append(row["SUBJECT"])
_row.append(row["AVG"])
for i in row["GRADES"]:
_row.append(i)
writer.writerow(_row)
def grades_from_file() -> list:
import csv
grades = []
import csv
grades = []
try:
with open(f"{os.environ['HOME']}/.local/lib/solendata/grades.csv", mode="r") as f:
reader = csv.reader(f)
rows = []
for row in reader:
rows.append(row)
try:
with open(f"{os.environ['HOME']}/.local/lib/solendata/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 = {}
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
temp["SUBJECT"] = rows[row][0]
temp["AVG"] = rows[row][1]
temp_lst = []
for i in range(2, 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")
grades.append(temp)
except FileNotFoundError:
print("No grades.csv file found\n")
return grades
return grades
+2 -2
View File
@@ -41,7 +41,7 @@ def _get_creds_hash(user: str, pwd: str) -> str:
return creds
def login(user: str, pwd: str) -> bool:
if is_connected() == True:
if is_connected():
url: str = "https://aplikace.skolaonline.cz/SOL/Prihlaseni.aspx"
data = {
"__EVENTTARGET": "dnn$ctr994$SOLLogin$btnODeslat",
@@ -83,7 +83,7 @@ def is_connected() -> bool:
s = socket.create_connection((host, 80), 2)
s.close()
return True
except Exception:
except:
pass
return False
+10 -7
View File
@@ -85,7 +85,7 @@ def get_schedule(url: str):
temp.append(" - ")
wheres = temp
for j in range(len(cells)):
for j in range(len(cells)):
if cells[j].get("colspan") is not None:
if int(cells[j].get("colspan")) == 2:
try:
@@ -177,12 +177,15 @@ def get_whitespace(key: str, idx=0) -> int:
return max_len
def show_schedule():
# 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("/SOL/App/Spolecne/KZZ010_RychlyPrehled.aspx")
schedule_to_file(schedule)
def show_schedule(local=False):
if local == False:
# 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("/SOL/App/Spolecne/KZZ010_RychlyPrehled.aspx")
schedule_to_file(schedule)
else:
schedule = schedule_from_file()
else:
schedule = schedule_from_file()
+9 -6
View File
@@ -200,12 +200,15 @@ def get_whitespace(key: str, idx=0) -> int:
return max_len
def show_schedule():
# 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("/SOL/App/Kalendar/KZK001_KalendarTyden.aspx")
schedule_to_file(schedule)
def show_schedule(local=False):
if local == False:
# 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("/SOL/App/Kalendar/KZK001_KalendarTyden.aspx")
schedule_to_file(schedule)
else:
schedule = schedule_from_file()
else:
schedule = schedule_from_file()
+100 -12
View File
@@ -6,18 +6,106 @@ from solenlib import schedule_week as sw
from solenlib import schedule_tdy_tmr as st
import sys
import getpass
import argparse
import datetime
if __name__ == "__main__":
user = str(input("Zadejte přihlašovací jméno: "))
try:
pwd = str(getpass.getpass("Zadejte heslo: "))
except:
pwd = str(input("Zadejte heslo: "))
parser = argparse.ArgumentParser(prog="ŠOLEn", description="ŠOLEn, an enhanced version of the ŠkolaOnLine online school system")
parser.add_argument("-t", "--today-tomorrow", action="store_true", help="Show the schedule for today and tomorrow")
parser.add_argument("-w", "--week", action="store_true", help="Show the schedule for this week")
parser.add_argument("-g", "--grades", action="store_true", help="Show the grades")
parser.add_argument("-u", "--username", action="store", default="", help="Provide the username for login")
parser.add_argument("-p", "--password", action="store", default="", help="Provide the password for login")
parser.add_argument("-l", "--local", action="store_true", help="Only fetch local files, don't connect to the internet")
parser.add_argument("-d", "--delete-local-files", action="store_true", help="Delete all local user data, including schedules, grades and user credentials")
parser.add_argument("-b", "--backup", action="store_true", help="Securely back up all user data and store them in a .tar.gz archive in the destination directory")
parser.add_argument("-D", "--backup-directory", action="store", help="Backup destination directory")
parser.add_argument("-P", "--backup-password", action="store", help="Backup password to encrypt the archive with")
parser.add_argument("-R", "--backup-restore", action="store", nargs=1, metavar="PATH", help="Specify the path to a .tar.gz archive that was earlier created with ŠOLEn to restore backed up user data")
args = parser.parse_args()
if args.delete_local_files:
try:
print("Removing all schedules and grades ...", end=" ")
os.system(f"rm -rf {os.environ['HOME']}/.local/lib/solendata/*.csv")
print("done")
print("Removing all user credentials ...", end=" ")
os.system(f"rm -rf {os.environ['HOME']}/.local/lib/solendata/shadow")
print("done")
print("All user data removed successfully")
sys.exit(0)
except:
print("Could not remove user data")
sys.exit(1)
elif args.backup:
try:
if not args.backup_directory or not args.backup_password:
print("-D, --backup-directory BACKUP-DIRECTORY and -P, --backup-password BACKUP-PASSWORD are required")
sys.exit(1)
else:
nowtime = str(datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
try:
print("Creating the backup archive ...", end=" ")
os.system(f"tar cf solen-backup-{nowtime} {os.path.abspath(args.backup-directory)}")
print("done")
print("Backup archive created successfully")
sys.exit(0)
except:
print("The 'tar' utility is not installed")
sys.exit(1)
except:
print("Could not backup user data")
sys.exit(1)
else:
if args.username:
user = str(args.username)
else:
user = str(input("Zadejte přihlašovací jméno: "))
if args.password:
pwd = str(args.password)
else:
try:
pwd = str(getpass.getpass("Zadejte heslo: "))
except:
pwd = str(input("Zadejte heslo: "))
if not nw.login(user, pwd):
print("LOGIN UNSUCCESSFULL")
sys.exit(1)
else:
sw.show_schedule()
st.show_schedule()
gr.show_grades()
if not nw.login(user, pwd):
print("Login unsuccessfull")
sys.exit(1)
else:
try:
if args.week:
if args.local:
sw.show_schedule(local=True)
else:
sw.show_schedule()
if args.today_tomorrow:
if args.local:
st.show_schedule(local=True)
else:
st.show_schedule()
if args.grades:
if args.local:
gr.show_grades(local=True)
else:
gr.show_grades()
if not args.grades and not args.today_tomorrow and not args.week:
if args.local:
sw.show_schedule(local=True)
st.show_schedule(local=True)
gr.show_grades(local=True)
else:
sw.show_schedule()
st.show_schedule()
gr.show_grades()
sys.exit(0)
except Exception as e:
print("Could not execute")
sys.exit(1)