Fri Jun 23 16:28:43 CEST 2023
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
SHELL=/bin/bash
|
SHELL=/bin/bash
|
||||||
|
|
||||||
install:
|
install:
|
||||||
python3 -m pip install -r requirements.txt --break-system-packages
|
python3 -m pip install -r requirements.txt
|
||||||
|
|
||||||
./.config/mkdirs.sh
|
./.config/mkdirs.sh
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ SSH: `git clone git@github.com:mnjx/solen.git`
|
|||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
Upon startup, the user os prompted to enter their ŠkolaOnLine credentials.
|
Upon startup, the user is prompted to enter their ŠkolaOnLine credentials.
|
||||||
If the login proccess is successfull, the preconfigured functions in `/bin/solen`
|
If the login proccess is successful, the preconfigured functions in `/bin/solen`
|
||||||
will execute. By default, the functions are: showing the schedule for this week,
|
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
|
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
|
that file if you want to change what is being displayed. Alternatively, you can
|
||||||
@@ -57,4 +57,4 @@ Here they are:
|
|||||||
| -p, --password | provide the password for login |
|
| -p, --password | provide the password for login |
|
||||||
| -l, --local | only show the local data without attempting to connect to the internet |
|
| -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 |
|
| -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 |
|
| -b, --backup PASSPHRASE DESTINATION | backs up all local user data into a .tar.gz archive |
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
"""
|
||||||
|
Cryptography related stuff - mostly
|
||||||
|
encrypting and decrypting user data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
||||||
|
from cryptography.fernet import Fernet
|
||||||
|
import base64
|
||||||
|
import os
|
||||||
|
|
||||||
|
def get_key(password: str) -> bytes:
|
||||||
|
"""
|
||||||
|
Derives a bytes key
|
||||||
|
from a given password.
|
||||||
|
|
||||||
|
Useful when you want
|
||||||
|
to generate the same
|
||||||
|
key over and over again.
|
||||||
|
E.g. to encrypt/decrypt
|
||||||
|
locally stored user data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
password = password.encode()
|
||||||
|
|
||||||
|
salt = b'?\x0f\xa1\x97otT\xe48\xc9O\xb4\x1f\xef\xf5\xea'
|
||||||
|
|
||||||
|
kdf = PBKDF2HMAC(
|
||||||
|
algorithm=hashes.SHA256,
|
||||||
|
length=32,
|
||||||
|
salt=salt,
|
||||||
|
iterations=100000,
|
||||||
|
backend=default_backend()
|
||||||
|
)
|
||||||
|
|
||||||
|
key = base64.urlsafe_b64encode(kdf.derive(password))
|
||||||
|
return key
|
||||||
|
|
||||||
|
def encrypt_and_write_to_file(password: str, strdata: str, file_path: str) -> bool:
|
||||||
|
"""
|
||||||
|
Encrypts the given string data
|
||||||
|
and stores them in a file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
key = get_key(password)
|
||||||
|
|
||||||
|
f = Fernet(key)
|
||||||
|
|
||||||
|
encrypted = f.encrypt(strdata.encode())
|
||||||
|
|
||||||
|
with open(file_path, "wb") as ef:
|
||||||
|
ef.write(encrypted)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
except:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def read_from_file_and_decrypt(password: str, file_path: str) -> str:
|
||||||
|
"""
|
||||||
|
Reads contents of an encrypted file
|
||||||
|
and decrypts them.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
key = get_key(password)
|
||||||
|
|
||||||
|
f = Fernet(key)
|
||||||
|
|
||||||
|
with open(file_path, "rb") as ef:
|
||||||
|
decrypted = f.decrypt(ef.read())
|
||||||
|
|
||||||
|
return decrypted.decode()
|
||||||
|
except:
|
||||||
|
return ""
|
||||||
+113
-76
@@ -1,39 +1,38 @@
|
|||||||
|
"""
|
||||||
|
Functions for grades related stuff.
|
||||||
|
Like 1, 2, 3, 4 and 5, in what subject,
|
||||||
|
what is the average of a subject, ...
|
||||||
|
"""
|
||||||
|
|
||||||
from solenlib import networking as nw
|
from solenlib import networking as nw
|
||||||
|
from solenlib import crypto as cr
|
||||||
|
from cryptography.fernet import Fernet
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
import os
|
import os
|
||||||
|
|
||||||
not_login = 0
|
not_login = 0
|
||||||
|
|
||||||
def fetch_locally() -> list:
|
def fetch_locally(password, username) -> list:
|
||||||
|
"""
|
||||||
|
Helper function for exctracting
|
||||||
|
data from local files.
|
||||||
|
"""
|
||||||
|
|
||||||
global not_login
|
global not_login
|
||||||
|
|
||||||
if not_login == 1:
|
if not_login == 1:
|
||||||
not_login = 0
|
not_login = 0
|
||||||
#print("Could not sign in (you may have entered invalid login credentials)")
|
#print("Could not sign in (you may have entered invalid login credentials)")
|
||||||
#print("Fetching grades locally\n")
|
#print("Fetching grades locally\n")
|
||||||
grades = grades_from_file()
|
grades = grades_from_file(password, username)
|
||||||
return grades
|
return grades
|
||||||
|
|
||||||
|
def get_grades(password, url: str) -> list:
|
||||||
|
"""
|
||||||
|
Extracts the grades from the HTML soup
|
||||||
|
and returns them in a list of dictionaries.
|
||||||
|
"""
|
||||||
|
|
||||||
"""
|
|
||||||
# deprecated
|
|
||||||
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):
|
|
||||||
try:
|
try:
|
||||||
html = nw.get_html_from(url)
|
html = nw.get_html_from(url)
|
||||||
|
|
||||||
@@ -51,15 +50,21 @@ def get_grades(url: str):
|
|||||||
subject = rows[i].find("td").text
|
subject = rows[i].find("td").text
|
||||||
temp_dict = {}
|
temp_dict = {}
|
||||||
temp_dict["SUBJECT"] = subject
|
temp_dict["SUBJECT"] = subject
|
||||||
cell = rows[i].find_all("span", class_="VHVysl")
|
cells = rows[i].find_all("span", class_="VHVysl")
|
||||||
temp = []
|
temp = []
|
||||||
for j in range(len(cell)):
|
for j in range(len(cells)):
|
||||||
temp.append(cell[j].text)
|
if cells[j].parent.has_attr("class"):
|
||||||
temp_dict["GRADES"] = temp
|
if "VHUzaverka" not in cells[j].parent["class"]:
|
||||||
|
temp.append(cells[j].text)
|
||||||
|
else:
|
||||||
|
temp.append(cells[j].text)
|
||||||
|
temp_dict["FINAL"] = rows[i].find("td", class_="VHUzaverka CCATooltip").text.replace(" ", "")
|
||||||
|
|
||||||
avg = rows[i].find("td", class_="VHCelkPrumer").text
|
avg = rows[i].find("td", class_="VHCelkPrumer").text.replace(",", ".")
|
||||||
temp_dict["AVG"] = avg
|
temp_dict["AVG"] = avg
|
||||||
|
|
||||||
|
temp_dict["GRADES"] = temp
|
||||||
|
|
||||||
grades.append(temp_dict)
|
grades.append(temp_dict)
|
||||||
|
|
||||||
return grades
|
return grades
|
||||||
@@ -67,18 +72,23 @@ def get_grades(url: str):
|
|||||||
global not_login
|
global not_login
|
||||||
|
|
||||||
not_login = 1
|
not_login = 1
|
||||||
grades = fetch_locally()
|
grades = fetch_locally(password, username)
|
||||||
return grades
|
return grades
|
||||||
|
|
||||||
def show_grades(local=False):
|
def show_grades(password, username, local=False):
|
||||||
|
"""
|
||||||
|
Shows the grades in the terminal
|
||||||
|
in a pretty way.
|
||||||
|
"""
|
||||||
|
|
||||||
if local == False:
|
if local == False:
|
||||||
if nw.is_connected() == True:
|
if nw.is_connected() == True:
|
||||||
grades = get_grades("/SOL/App/Hodnoceni/KZH001_HodnVypisStud.aspx")
|
grades = get_grades(password, "/SOL/App/Hodnoceni/KZH001_HodnVypisStud.aspx")
|
||||||
grades_to_file(grades)
|
grades_to_file(password, username, grades)
|
||||||
else:
|
else:
|
||||||
grades = grades_from_file()
|
grades = grades_from_file(password, username)
|
||||||
else:
|
else:
|
||||||
grades = grades_from_file()
|
grades = grades_from_file(password, username)
|
||||||
|
|
||||||
whitespace = ""
|
whitespace = ""
|
||||||
max_len_of_subject = 0
|
max_len_of_subject = 0
|
||||||
@@ -98,73 +108,100 @@ def show_grades(local=False):
|
|||||||
print("N/A\n")
|
print("N/A\n")
|
||||||
import os
|
import os
|
||||||
|
|
||||||
if os.path.exists(f"{os.environ['HOME']}/.local/lib/solendata/grades.csv"):
|
if os.path.exists(f"{os.environ['HOME']}/.local/lib/solendata/{nw.get_user_hash(username)}-encrypted_grades"):
|
||||||
os.remove(f"{os.environ['HOME']}/.local/lib/solendata/grades.csv")
|
os.remove(f"{os.environ['HOME']}/.local/lib/solendata/{nw.get_user_hash(username)}-encrypted_grades")
|
||||||
return
|
return
|
||||||
|
|
||||||
for i in range(len(grades)):
|
for i in range(len(grades)):
|
||||||
if grades[i]["GRADES"] != []:
|
if grades[i]["GRADES"] != []:
|
||||||
whitespace = " " * (max_len_of_subject - len(grades[i]["SUBJECT"]))
|
whitespace = " " * (max_len_of_subject - len(grades[i]["SUBJECT"]))
|
||||||
print(grades[i]["SUBJECT"] + whitespace + "|", end=" ")
|
print(grades[i]["SUBJECT"] + whitespace + "|", end=" ")
|
||||||
|
print(grades[i]["FINAL"], "|", end=" ")
|
||||||
print(grades[i]["AVG"], "|", end=" ")
|
print(grades[i]["AVG"], "|", end=" ")
|
||||||
print("".join([grades[i]["GRADES"][j]+" " for j in range(len(grades[i]["GRADES"]))]), end=" ")
|
print("".join([grades[i]["GRADES"][j]+" " for j in range(len(grades[i]["GRADES"]))]))
|
||||||
print("")
|
else:
|
||||||
|
if grades[i]["FINAL"] is not None:
|
||||||
|
whitespace = " " * (max_len_of_subject - len(grades[i]["SUBJECT"]))
|
||||||
|
print(grades[i]["SUBJECT"] + whitespace + "|", end=" ")
|
||||||
|
print(grades[i]["FINAL"], "|")
|
||||||
|
|
||||||
|
def grades_to_file(password, username, grades=None):
|
||||||
|
"""
|
||||||
|
Writes the grades to a local file
|
||||||
|
for later use and extraction.
|
||||||
|
"""
|
||||||
|
|
||||||
def grades_to_file(grades=None): # !!! make sure the format of the grades list is saved correctly !!!
|
|
||||||
import csv
|
import csv
|
||||||
if grades is None:
|
if grades is None:
|
||||||
try:
|
try:
|
||||||
grades = get_grades("/SOL/App/Hodnoceni/KZH001_HodnVypisStud.aspx")
|
grades = get_grades(password, username, "/SOL/App/Hodnoceni/KZH001_HodnVypisStud.aspx")
|
||||||
except Exception:
|
except Exception:
|
||||||
print("An error occured, you probably can't access the internet right now")
|
print("An error occured, you probably can't access the internet right now")
|
||||||
return
|
return
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
|
lstrdata = []
|
||||||
|
|
||||||
with open(f"{os.environ['HOME']}/.local/lib/solendata/grades.csv", mode="w", newline="") as f:
|
for i in range(len(grades)):
|
||||||
for i in range(len(grades)):
|
temp = {}
|
||||||
temp = {}
|
temp["SUBJECT"] = grades[i]["SUBJECT"]
|
||||||
temp["SUBJECT"] = grades[i]["SUBJECT"]
|
temp["FINAL"] = grades[i]["FINAL"]
|
||||||
temp["AVG"] = grades[i]["AVG"]
|
temp["AVG"] = grades[i]["AVG"]
|
||||||
temp_grades = []
|
temp_grades = []
|
||||||
for j in range(len(grades[i]["GRADES"])):
|
for j in range(len(grades[i]["GRADES"])):
|
||||||
temp_grades.append(grades[i]["GRADES"][j])
|
temp_grades.append(grades[i]["GRADES"][j])
|
||||||
temp["GRADES"] = temp_grades
|
temp["GRADES"] = temp_grades
|
||||||
data.append(temp)
|
data.append(temp)
|
||||||
|
|
||||||
writer = csv.writer(f)
|
for row in data:
|
||||||
for row in data:
|
lstrdata.append(row["SUBJECT"])
|
||||||
_row = []
|
lstrdata.append(",")
|
||||||
_row.append(row["SUBJECT"])
|
lstrdata.append(row["FINAL"])
|
||||||
_row.append(row["AVG"])
|
lstrdata.append(",")
|
||||||
for i in row["GRADES"]:
|
lstrdata.append(row["AVG"])
|
||||||
_row.append(i)
|
lstrdata.append(",")
|
||||||
writer.writerow(_row)
|
for i in row["GRADES"]:
|
||||||
|
lstrdata.append(i)
|
||||||
|
lstrdata.append(",")
|
||||||
|
del lstrdata[-1]
|
||||||
|
lstrdata.append("\n")
|
||||||
|
|
||||||
def grades_from_file() -> list:
|
strdata = "".join(lstrdata)
|
||||||
|
|
||||||
|
cr.encrypt_and_write_to_file(password, strdata, f"{os.environ['HOME']}/.local/lib/solendata/{nw.get_user_hash(username)}-encrypted_grades")
|
||||||
|
|
||||||
|
def grades_from_file(password, username) -> list:
|
||||||
|
"""
|
||||||
|
Extracts the grades from the local
|
||||||
|
file and returns them in a better
|
||||||
|
format.
|
||||||
|
"""
|
||||||
import csv
|
import csv
|
||||||
grades = []
|
from io import StringIO
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(f"{os.environ['HOME']}/.local/lib/solendata/grades.csv", mode="r") as f:
|
strdata = cr.read_from_file_and_decrypt(password, f"{os.environ['HOME']}/.local/lib/solendata/{nw.get_user_hash(username)}-encrypted_grades")
|
||||||
reader = csv.reader(f)
|
except:
|
||||||
rows = []
|
print("Could not read the file")
|
||||||
for row in reader:
|
return
|
||||||
rows.append(row)
|
f = StringIO(strdata)
|
||||||
|
reader = csv.reader(f, delimiter=",")
|
||||||
|
grades = []
|
||||||
|
rows = []
|
||||||
|
for row in reader:
|
||||||
|
rows.append(row)
|
||||||
|
|
||||||
for row in range(len(rows)):
|
for row in range(len(rows)):
|
||||||
temp = {}
|
temp = {}
|
||||||
|
|
||||||
temp["SUBJECT"] = rows[row][0]
|
temp["SUBJECT"] = rows[row][0]
|
||||||
temp["AVG"] = rows[row][1]
|
temp["FINAL"] = rows[row][1]
|
||||||
|
temp["AVG"] = rows[row][2]
|
||||||
temp_lst = []
|
|
||||||
for i in range(2, len(rows[row])):
|
temp_lst = []
|
||||||
temp_lst.append(rows[row][i])
|
for i in range(3, len(rows[row])):
|
||||||
temp["GRADES"] = temp_lst
|
temp_lst.append(rows[row][i])
|
||||||
|
temp["GRADES"] = temp_lst
|
||||||
|
|
||||||
grades.append(temp)
|
grades.append(temp)
|
||||||
except FileNotFoundError:
|
|
||||||
print("No grades.csv file found\n")
|
|
||||||
|
|
||||||
return grades
|
return grades
|
||||||
|
|||||||
+42
-5
@@ -1,3 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Functions for network related stuff.
|
||||||
|
"""
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import hashlib
|
import hashlib
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
@@ -8,6 +12,11 @@ F_not_anymore = 0
|
|||||||
session = requests.Session()
|
session = requests.Session()
|
||||||
|
|
||||||
def _read_shadow() -> str:
|
def _read_shadow() -> str:
|
||||||
|
"""
|
||||||
|
Reads the shadow file and returns it in
|
||||||
|
a suitable format for other functions.
|
||||||
|
"""
|
||||||
|
|
||||||
result = ""
|
result = ""
|
||||||
with open(f"{os.environ['HOME']}/.local/lib/solendata/shadow", "r") as f:
|
with open(f"{os.environ['HOME']}/.local/lib/solendata/shadow", "r") as f:
|
||||||
for row in f:
|
for row in f:
|
||||||
@@ -16,6 +25,11 @@ def _read_shadow() -> str:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def _save_creds(user: str, pwd: str):
|
def _save_creds(user: str, pwd: str):
|
||||||
|
"""
|
||||||
|
Saves credentials into the shadow file
|
||||||
|
by appending them.
|
||||||
|
"""
|
||||||
|
|
||||||
creds = _get_creds_hash(user, pwd)
|
creds = _get_creds_hash(user, pwd)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -26,6 +40,11 @@ def _save_creds(user: str, pwd: str):
|
|||||||
print("No shadow file found")
|
print("No shadow file found")
|
||||||
|
|
||||||
def _eval_creds(creds: str) -> bool:
|
def _eval_creds(creds: str) -> bool:
|
||||||
|
"""
|
||||||
|
Evaluates the credentials for if
|
||||||
|
they are in the shadow file or not.
|
||||||
|
"""
|
||||||
|
|
||||||
shadow = _read_shadow()
|
shadow = _read_shadow()
|
||||||
|
|
||||||
if creds in shadow:
|
if creds in shadow:
|
||||||
@@ -34,13 +53,26 @@ def _eval_creds(creds: str) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def _get_creds_hash(user: str, pwd: str) -> str:
|
def _get_creds_hash(user: str, pwd: str) -> str:
|
||||||
|
"""
|
||||||
|
Creates a sha512 hash from the username
|
||||||
|
and password.
|
||||||
|
"""
|
||||||
|
|
||||||
user_hash = hashlib.sha512(bytes(user, 'utf-8')).hexdigest()
|
user_hash = hashlib.sha512(bytes(user, 'utf-8')).hexdigest()
|
||||||
pwd_hash = hashlib.sha512(bytes(pwd, 'utf-8')).hexdigest()
|
pwd_hash = hashlib.sha512(bytes(pwd, 'utf-8')).hexdigest()
|
||||||
creds = user_hash+":"+pwd_hash
|
creds = user_hash+":"+pwd_hash
|
||||||
|
|
||||||
return creds
|
return creds
|
||||||
|
|
||||||
|
def get_user_hash(user: str) -> str:
|
||||||
|
return str(hashlib.sha512(bytes(user, 'utf-8')).hexdigest())
|
||||||
|
|
||||||
def login(user: str, pwd: str) -> bool:
|
def login(user: str, pwd: str) -> bool:
|
||||||
|
"""
|
||||||
|
Tries to send a request to the SOL
|
||||||
|
servers in order to log in.
|
||||||
|
"""
|
||||||
|
|
||||||
if is_connected():
|
if is_connected():
|
||||||
url: str = "https://aplikace.skolaonline.cz/SOL/Prihlaseni.aspx"
|
url: str = "https://aplikace.skolaonline.cz/SOL/Prihlaseni.aspx"
|
||||||
data = {
|
data = {
|
||||||
@@ -61,21 +93,21 @@ def login(user: str, pwd: str) -> bool:
|
|||||||
|
|
||||||
post_login = session.post(url, data=data)
|
post_login = session.post(url, data=data)
|
||||||
if post_login.status_code != 200:
|
if post_login.status_code != 200:
|
||||||
#print("Could not sign in")
|
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
#print("Sign in successful")
|
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
#print("Could not sign in")
|
|
||||||
#print("Fetching data from local files")
|
|
||||||
|
|
||||||
if _eval_creds(_get_creds_hash(user, pwd)):
|
if _eval_creds(_get_creds_hash(user, pwd)):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_connected() -> bool:
|
def is_connected() -> bool:
|
||||||
|
"""
|
||||||
|
Checks whether there is internet
|
||||||
|
access present.
|
||||||
|
"""
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -88,6 +120,11 @@ def is_connected() -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def get_html_from(params=None) -> str:
|
def get_html_from(params=None) -> str:
|
||||||
|
"""
|
||||||
|
Fetches the HTML markup of
|
||||||
|
a specified subsite of SOL.
|
||||||
|
"""
|
||||||
|
|
||||||
base = "https://aplikace.skolaonline.cz"
|
base = "https://aplikace.skolaonline.cz"
|
||||||
|
|
||||||
if params is not None:
|
if params is not None:
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
"""
|
||||||
|
Functions related to today's
|
||||||
|
and tomorrow's schedule.
|
||||||
|
"""
|
||||||
|
|
||||||
from solenlib import networking as nw
|
from solenlib import networking as nw
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
import os
|
import os
|
||||||
@@ -5,16 +10,25 @@ import os
|
|||||||
not_login = 0
|
not_login = 0
|
||||||
|
|
||||||
def fetch_locally() -> list:
|
def fetch_locally() -> list:
|
||||||
|
"""
|
||||||
|
Helper function for exctracting
|
||||||
|
data from a local file.
|
||||||
|
"""
|
||||||
|
|
||||||
global not_login
|
global not_login
|
||||||
|
|
||||||
if not_login == 1:
|
if not_login == 1:
|
||||||
not_login = 0
|
not_login = 0
|
||||||
#print("Could not sign in (you may have entered invalid login credentials)")
|
|
||||||
#print("Fetching the schedule for this week locally\n")
|
|
||||||
schedule = schedule_from_file()
|
schedule = schedule_from_file()
|
||||||
return schedule
|
return schedule
|
||||||
|
|
||||||
def get_schedule(url: str):
|
def get_schedule(url: str) -> list:
|
||||||
|
"""
|
||||||
|
Filters & extracts the schedule from
|
||||||
|
the HTML soup.
|
||||||
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
html = nw.get_html_from(url)
|
html = nw.get_html_from(url)
|
||||||
|
|
||||||
@@ -120,10 +134,16 @@ def get_schedule(url: str):
|
|||||||
global not_login
|
global not_login
|
||||||
|
|
||||||
not_login = 1
|
not_login = 1
|
||||||
schedule = fetch_locally()
|
return fetch_locally()
|
||||||
return schedule
|
|
||||||
|
|
||||||
def schedule_from_file_to_list():
|
def schedule_from_file_to_list():
|
||||||
|
"""
|
||||||
|
Extracts the schedule from
|
||||||
|
a local file and returns
|
||||||
|
it in a list, which is better
|
||||||
|
for the get_whitespace() function.
|
||||||
|
"""
|
||||||
|
|
||||||
schedule = []
|
schedule = []
|
||||||
schedule_ = []
|
schedule_ = []
|
||||||
|
|
||||||
@@ -161,6 +181,13 @@ def schedule_from_file_to_list():
|
|||||||
return schedule
|
return schedule
|
||||||
|
|
||||||
def get_whitespace(key: str, idx=0) -> int:
|
def get_whitespace(key: str, idx=0) -> int:
|
||||||
|
"""
|
||||||
|
Helper function that calculates
|
||||||
|
how much whitespaces are needed
|
||||||
|
in order to display the table grid
|
||||||
|
correctly.
|
||||||
|
"""
|
||||||
|
|
||||||
schedule = schedule_from_file_to_list()
|
schedule = schedule_from_file_to_list()
|
||||||
max_len = 0
|
max_len = 0
|
||||||
|
|
||||||
@@ -178,6 +205,11 @@ def get_whitespace(key: str, idx=0) -> int:
|
|||||||
return max_len
|
return max_len
|
||||||
|
|
||||||
def show_schedule(local=False):
|
def show_schedule(local=False):
|
||||||
|
"""
|
||||||
|
Shows the schedule on the
|
||||||
|
terminal in a pretty way.
|
||||||
|
"""
|
||||||
|
|
||||||
if local == False:
|
if local == False:
|
||||||
# if there is internet access, load stuff from the web
|
# if there is internet access, load stuff from the web
|
||||||
# if not, load the schedule.csv file
|
# if not, load the schedule.csv file
|
||||||
@@ -228,6 +260,12 @@ def show_schedule(local=False):
|
|||||||
print("")
|
print("")
|
||||||
|
|
||||||
def schedule_to_file(schedule=None):
|
def schedule_to_file(schedule=None):
|
||||||
|
"""
|
||||||
|
Writes the schedule to
|
||||||
|
a local file for later
|
||||||
|
use.
|
||||||
|
"""
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
if schedule is None:
|
if schedule is None:
|
||||||
try:
|
try:
|
||||||
@@ -269,6 +307,10 @@ def schedule_to_file(schedule=None):
|
|||||||
writer.writerow(row)
|
writer.writerow(row)
|
||||||
|
|
||||||
def schedule_from_file() -> list:
|
def schedule_from_file() -> list:
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
schedule = []
|
schedule = []
|
||||||
|
|
||||||
|
|||||||
+42
-30
@@ -7,7 +7,8 @@ from solenlib import schedule_tdy_tmr as st
|
|||||||
import sys
|
import sys
|
||||||
import getpass
|
import getpass
|
||||||
import argparse
|
import argparse
|
||||||
import datetime
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(prog="ŠOLEn", description="ŠOLEn, an enhanced version of the ŠkolaOnLine online school system")
|
parser = argparse.ArgumentParser(prog="ŠOLEn", description="ŠOLEn, an enhanced version of the ŠkolaOnLine online school system")
|
||||||
@@ -20,9 +21,7 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument("-l", "--local", action="store_true", help="Only fetch local files, don't connect to the internet")
|
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("-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("-b", "--backup", action="store_true", help="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")
|
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()
|
args = parser.parse_args()
|
||||||
@@ -30,37 +29,51 @@ if __name__ == "__main__":
|
|||||||
if args.delete_local_files:
|
if args.delete_local_files:
|
||||||
try:
|
try:
|
||||||
print("Removing all schedules and grades ...", end=" ")
|
print("Removing all schedules and grades ...", end=" ")
|
||||||
os.system(f"rm -rf {os.environ['HOME']}/.local/lib/solendata/*.csv")
|
os.system(f"rm -rf {os.environ['HOME']}/.local/lib/solendata/*")
|
||||||
print("done")
|
print("done")
|
||||||
print("Removing all user credentials ...", end=" ")
|
print("Removing all user credentials ...", end=" ")
|
||||||
os.system(f"rm -rf {os.environ['HOME']}/.local/lib/solendata/shadow")
|
os.system(f"rm -rf {os.environ['HOME']}/.local/lib/solendata/shadow")
|
||||||
print("done")
|
print("done")
|
||||||
print("All user data removed successfully")
|
print("All user data removed successfully")
|
||||||
sys.exit(0)
|
|
||||||
except:
|
except:
|
||||||
print("Could not remove user data")
|
print("Could not remove user data")
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
elif args.backup:
|
elif args.backup:
|
||||||
try:
|
try:
|
||||||
if not args.backup_directory or not args.backup_password:
|
nowtime = str(datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
|
||||||
print("-D, --backup-directory BACKUP-DIRECTORY and -P, --backup-password BACKUP-PASSWORD are required")
|
try:
|
||||||
sys.exit(1)
|
print("Creating the backup archive ...", end=" ")
|
||||||
else:
|
os.system(f"tar cf solen-backup-{nowtime}.tar {os.getcwd()}")
|
||||||
nowtime = str(datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
|
print("done")
|
||||||
try:
|
print("Compressing the archive ...", end=" ")
|
||||||
print("Creating the backup archive ...", end=" ")
|
os.system(f"gzip {os.getcwd()}/solen-backup-{nowtime}.tar")
|
||||||
os.system(f"tar cf solen-backup-{nowtime} {os.path.abspath(args.backup-directory)}")
|
print("done")
|
||||||
print("done")
|
print("Backup archive created successfully")
|
||||||
print("Backup archive created successfully")
|
except:
|
||||||
sys.exit(0)
|
print("The 'tar' utility or the 'gzip' utility is not installed")
|
||||||
except:
|
|
||||||
print("The 'tar' utility is not installed")
|
|
||||||
sys.exit(1)
|
|
||||||
except:
|
except:
|
||||||
print("Could not backup user data")
|
print("Could not backup user data")
|
||||||
sys.exit(1)
|
elif args.backup_restore:
|
||||||
|
try:
|
||||||
|
backup_path = args.backup_restore
|
||||||
|
print("Preparing ...", end=" ")
|
||||||
|
os.system(f"mkdir {os.getcwd()}/.solentmp")
|
||||||
|
print("done")
|
||||||
|
print("Uncompressing the archive ...", end=" ")
|
||||||
|
os.system(f"gunzip {backup_path}")
|
||||||
|
print("done")
|
||||||
|
print("Unpacking the archive ...", end=" ")
|
||||||
|
os.system(f"tar xzjf {backup_path} {os.getcwd()}/.solentmp")
|
||||||
|
print("done")
|
||||||
|
print("Moving the files ...", end=" ")
|
||||||
|
os.system(f"mv {os.getcwd()}/.solentmp/* {os.environ['HOME']}/.local/lib/solendata")
|
||||||
|
print("done")
|
||||||
|
print("Cleaning up ...", end=" ")
|
||||||
|
os.system(f"rm -rf {os.getcwd()}/.solentmp")
|
||||||
|
print("done")
|
||||||
|
print("Backup restored successfully")
|
||||||
|
except:
|
||||||
|
print("Could not restore the backup")
|
||||||
else:
|
else:
|
||||||
if args.username:
|
if args.username:
|
||||||
user = str(args.username)
|
user = str(args.username)
|
||||||
@@ -76,7 +89,7 @@ if __name__ == "__main__":
|
|||||||
pwd = str(input("Zadejte heslo: "))
|
pwd = str(input("Zadejte heslo: "))
|
||||||
|
|
||||||
if not nw.login(user, pwd):
|
if not nw.login(user, pwd):
|
||||||
print("Login unsuccessfull")
|
print("Login unsuccessful")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
@@ -92,20 +105,19 @@ if __name__ == "__main__":
|
|||||||
st.show_schedule()
|
st.show_schedule()
|
||||||
if args.grades:
|
if args.grades:
|
||||||
if args.local:
|
if args.local:
|
||||||
gr.show_grades(local=True)
|
gr.show_grades(pwd, user, local=True)
|
||||||
else:
|
else:
|
||||||
gr.show_grades()
|
gr.show_grades(pwd, user)
|
||||||
|
|
||||||
if not args.grades and not args.today_tomorrow and not args.week:
|
if not args.grades and not args.today_tomorrow and not args.week:
|
||||||
if args.local:
|
if args.local:
|
||||||
sw.show_schedule(local=True)
|
sw.show_schedule(local=True)
|
||||||
st.show_schedule(local=True)
|
st.show_schedule(local=True)
|
||||||
gr.show_grades(local=True)
|
gr.show_grades(pwd, user, local=True)
|
||||||
else:
|
else:
|
||||||
sw.show_schedule()
|
sw.show_schedule()
|
||||||
st.show_schedule()
|
st.show_schedule()
|
||||||
gr.show_grades()
|
gr.show_grades(pwd, user)
|
||||||
sys.exit(0)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
raise e
|
||||||
print("Could not execute")
|
print("Could not execute")
|
||||||
sys.exit(1)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user