update: file organization, README; fix: showing averages with grades; add: Makefile
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
install:
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
run:
|
||||||
|
python3 "src/main.py"
|
||||||
@@ -1,11 +1,34 @@
|
|||||||
# SOL Enhanced
|
# 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
|
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.
|
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!
|
# Features
|
||||||
#### It is not ready yet.
|
|
||||||
|
+ 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`
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
lxml
|
||||||
|
bs4
|
||||||
|
requests
|
||||||
|
|
||||||
-510
@@ -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()
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||||
@@ -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 ""
|
||||||
@@ -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
|
||||||
+17
@@ -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()
|
||||||
Reference in New Issue
Block a user