This repository was archived by the owner on Aug 24, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
326 lines (280 loc) · 13.1 KB
/
main.py
File metadata and controls
326 lines (280 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
from time import sleep
from os import makedirs, system
from os.path import exists
from sys import exit as sys_exit
from requests import get, session as requestsSession, ConnectionError, Timeout
from platform import system as os_name
from bs4 import BeautifulSoup as bs
from colorama import init, Fore, Style, Back
init(autoreset=True)
def throw_error(text:str):
"""Show error to end user
Args:
text (str): The error text to show
"""
print("\n" + Back.RED + "FEJL")
print(Fore.RED + text + "\n")
while 1:
try_again = input(Fore.RED + "Vil du forsøge igen? ja/nej: ")
if try_again.lower() == "ja" or try_again.lower() == "nej":
if try_again.lower() == "ja":
print(Style.RESET_ALL)
return main()
else:
sys_exit()
else:
print(Fore.RED + "Du skal svare med ja eller nej.")
def documents_in_folder(session, folder_url:str):
"""Check if there are any documents in a given folder
Args:
session (Session): A logged in requests session
folder_url (str): The URL of the folder to check
Returns:
bool: Whether or not there are files in the folder
"""
sleep(1)
resp = session.get(url=folder_url)
return "Der kan ikke lægges dokumenter i denne mappe." not in resp.text and "Ingen tilgængelige dokumenter i mappen." not in resp.text
def download_documents(session, documents_url:str, folder_id:str, path:str):
# Check if the folder has files, otherwise ignore
if not documents_in_folder(session, f"{documents_url}&folderid={folder_id}"):
return
if not exists(path):
makedirs(path)
with session.get(url=f"{documents_url}&folderid={folder_id}", stream=True) as resp:
soup = bs(resp.content, "html.parser")
table_of_files = soup.find("div", {"id": "printfoldercontent"})
table_of_files = table_of_files.find("table")
table_elements = table_of_files.find_all("tr")
for table_element in table_elements[1:]: # [1:] AKA start at index 1 in list
try:
file_element = table_element.find_all("td")[1]
filename = file_element.a.text.strip().replace("/", "-")
if filename.endswith("..."):
continue
file_url = "https://www.lectio.dk" + file_element.a["href"]
resp = session.get(url=file_url)
with open(path+"/"+filename, "wb") as f:
f.write(resp.content)
sleep(0.1)
except Exception as e:
#print(e)
continue
def translate_subject_name(subject_name:str):
"""Translates subject abbreviation
Args:
subject_name (str): The subject abbreviation
Returns:
str: Either the translated abbreviation or the original subject name if failed to translate
"""
subjects = {
"fy": "Fysik",
"ng": "Naturgeografi",
"id": "Idræt",
"da": "Dansk",
"hi": "Historie",
"ma": "Matematik",
"ol": "Oldtidskundskab",
"re": "Religion",
"st": "Statistik",
"en": "Engelsk",
"ke": "Kemi",
"tyb": "Tysk",
"tyf": "Tysk",
"me": "Mediefag",
"ap": "Almen sprogforståelse",
"nv": "Naturvidenskab",
"sa": "Samfundsfag",
"if": "Informatik",
"as": "Astronomi",
"bi": "Biologi",
"bk": "Billedkunst",
"bt": "Bioteknologi",
"dr": "Dramatik",
"eø": "Erhvervsøkonomi",
"fi": "Filosofi",
"fr": "Fransk",
"frb": "Fransk",
"frf": "Fransk",
"it": "Italiensk",
"ki": "Kinesisk",
"la": "Latin",
"mu": "Musik",
"ps": "Psykologi",
"sp": "Spansk",
}
subject = subject_name.split(" ")[1].lower()
if subjects.get(subject[:-1]) != None:
return subjects[subject[:-1]]
if subjects.get(subject) != None:
return subjects[subject]
if len(subject) > 3:
return subject.capitalize()
return subject_name
def login(session, username:str, password:str, school_id:int):
"""Logs into Lectio
Args:
session (Session): A logged in requests session
username (str): Lectio username
password (str): Lectio password
school_id (int): School ID
Returns:
Session: The logged in requests session
str: The URL to the documents tab
"""
session.headers.update({"User-Agent": "Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0"})
login_url = f"https://www.lectio.dk/lectio/{school_id}/login.aspx"
resp = session.get(url=login_url)
soup = bs(resp.content, "html.parser")
event_validation = soup.find_all("input", {"id": "__EVENTVALIDATION"})[0]["value"]
try:
school = soup.find_all("td", {"id": "m_Content_schoolnametd"})[0].getText()
except IndexError:
throw_error("Skole ID'et er ugyldigt.")
form_data = {
"__EVENTTARGET": "m$Content$submitbtn2",
"__EVENTARGUMENT": "",
"__EVENTVALIDATION": event_validation,
"m$Content$username": username,
"m$Content$password": password,
}
print(Fore.GREEN + "Logger ind...", end="\r")
resp = session.post(url=login_url, data=form_data)
if any(error_text in resp.text for error_text in ["Fejl i Brugernavn og/eller adgangskode", "Der er ikke oprettet en adgangskode til dette login"]):
throw_error("Login fejlede, tjek om brugernavn, kodeord og skole ID er korrekt.")
soup = bs(resp.content, "html.parser")
name = soup.find_all("div", {"id": "s_m_HeaderContent_MainTitle"})[0].string.split(" ", 1)[1].split(",")[0]
print(Fore.GREEN + "Logget ind som:")
print(Fore.GREEN + "Elev:", name)
print(Fore.GREEN + "Skole:", school+"\n")
documents_url = "https://www.lectio.dk" + soup.find_all("a", {"id": "s_m_HeaderContent_subnavigator_ctl09"})[0]["href"]
return session, documents_url
def download_documents_in_subject_folder(session, subject, download_activities:bool, documents_url:str, term:int):
"""Downloads all folders and documents in a given subject"""
subject_name = subject.find("div", {"class": "TreeNode-title"}).string
subject_name = translate_subject_name(subject_name)
sublist = subject.find("div", {"lec-role": "ltv-sublist"})
sublist_trees = sublist.find_all("div", {"lec-role": "treeviewnodecontainer"}, recursive=False)
for folder in sublist_trees:
folder_name = folder.find("div", {"class": "TreeNode-title"}).string
folder_id = folder["lec-node-id"]
if folder_name == "Aktiviteter" and not download_activities:
continue
# Now it's getting dirty, leave a PR if you have an easier and more elegant way to do this
# Very messy, 2 girls 1 cup style
sub_folders_in_folder = folder.find_all("div", {"lec-role": "treeviewnodecontainer"})
if sub_folders_in_folder:
download_documents(session, documents_url, folder_id, f"./LectioDownloads/{term}/{subject_name}/{folder_name}")
for sub_folders in sub_folders_in_folder:
sub_folder_name = sub_folders.find("div", {"class": "TreeNode-title"}).string
sub_folder_id = sub_folders["lec-node-id"]
sub_sub_folders_in_folder = sub_folders.find_all("div", {"lec-role": "treeviewnodecontainer"})
if sub_sub_folders_in_folder:
download_documents(session, documents_url, sub_folder_id, f"./LectioDownloads/{term}/{subject_name}/{folder_name}/{sub_folder_name}")
for sub_sub_folders in sub_sub_folders_in_folder:
sub_sub_folder_name = sub_sub_folders.find("div", {"class": "TreeNode-title"}).string
sub_sub_folder_id = sub_sub_folders["lec-node-id"]
download_documents(session, documents_url, sub_sub_folder_id, f"./LectioDownloads/{term}/{subject_name}/{folder_name}/{sub_folder_name}/{sub_sub_folder_name}")
else:
download_documents(session, documents_url, sub_folder_id, f"./LectioDownloads/{term}/{subject_name}/{folder_name}/{sub_folder_name}")
else:
download_documents(session, documents_url, folder_id, f"./LectioDownloads/{term}/{subject_name}/{folder_name}")
def get_documents(username:str, password:str, school_id:int, download_activities:bool):
session, documents_url = login(requestsSession(), username, password, school_id)
student_id = documents_url.split("elevid=")[1].split("&")[0]
resp = session.get(url=documents_url)
soup = bs(resp.content, "html.parser")
options = soup.find("select", {"id": "s_m_ChooseTerm_term"})
options = [int(option["value"]) for option in options.find_all("option")]
terms = options[::-1]
event_validation = soup.find_all("input", {"id": "__EVENTVALIDATION"})[0]["value"]
if not exists("./LectioDownloads"):
makedirs("./LectioDownloads")
for n in range(0, len(options)):
if n != 0:
sleep(30) # Wait a little because of lectios internal rate limiting causing problems
print(f"Begynder download af filer fra {n+1}. g ({terms[n]})...")
viewstatex = soup.find_all("input", {"id": "__VIEWSTATEX"})[0]["value"]
form_data = {
"__EVENTTARGET": "s$m$ChooseTerm$term",
"__EVENTVALIDATION": event_validation,
"s$m$ChooseTerm$term": terms[n],
"__VIEWSTATEX": viewstatex,
}
for x in range(0,4):
resp = session.post(url=documents_url + f"&folderid=S{student_id}__2", data=form_data)
if "Der opstod en ukendt fejl" in resp.text:
if x == 3:
throw_error("En ukendt fejl opstod ved download.")
else:
print(Fore.RED + "FEJL: Fildownload fejlede. Venter 25 sekunder og prøver igen...")
sleep(25)
continue
else:
break
soup = bs(resp.content, "html.parser")
treenode = soup.find("div", {"lec-node-id": "S"+student_id+"__2"})
subjects_div = treenode.find("div", {"lec-role": "ltv-sublist"})
subjects = subjects_div.find_all("div", {"lec-role": "treeviewnodecontainer"}, recursive=False)
for subject in subjects:
download_documents_in_subject_folder(session, subject, download_activities, documents_url, terms[n])
input(Fore.GREEN + "Alle dokumenter er nu blevet downloaded! Filerne ligger inde i mappen \"LectioDownloads\". Ved at trykke på Enter, lukker du dette vindue.\nTak fordi du brugte dette program, jeg håber det virkede for dig og sparede dig en manuelt arbejde.")
sys_exit()
def get_all_inputs():
"""Requests all inputs and returns them all
Returns:
str: Lectio username
str: Lectio password
int: School ID
bool: Whether or not to download activity documents
"""
print(Fore.GREEN + "Nu skal du logge ind med samme oplysninger som du bruger til at logge ind på Lectio:\n")
while 1:
username = input("Brugernavn: ")
if username == "":
print(Fore.RED + "Du skal udfylde dette felt.")
else:
break
while 1:
password = input("Kodeord: ")
if password == "":
print(Fore.RED + "Du skal udfylde dette felt.")
else:
# \033[1A is used to start at the line above to censor password.
print(f"\033[1AKodeord: {'*' * len(password)}")
break
print(Fore.GREEN + "\nFor at finde dit skole ID, skal du gå ind på Lectio og vælge din skole på login siden. Når den er valgt kan du se linket i toppen af din browser, her står dit skole ID, se eksemplet nedenunder.")
print(Fore.GREEN + "https://www.lectio.dk/lectio/" + Back.RED + "93" + Style.RESET_ALL + Fore.GREEN + "/default.aspx")
print(Fore.GREEN + "Her er skole ID'et 93.\n")
while 1:
try:
school_id = int(input("Skole ID: "))
break
except:
print(Fore.RED + "Du skal svare med et tal.")
print()
download_activities = False
while 1:
download_activities_input = input("Skal filer fra moduler downloades? ja/nej: ")
if download_activities_input.lower() == "ja" or download_activities_input.lower() == "nej":
if download_activities_input.lower() == "ja":
download_activities = True
break
else:
print(Fore.RED + "Du skal svare med ja eller nej.")
print()
return username, password, school_id, download_activities
def main():
if os_name() == "Windows":
system("cls")
else:
system("clear")
try:
splash_text = get(url="https://raw.githubusercontent.com/JC-Integrations/LectioDL/main/splash.txt")
except (ConnectionError, Timeout) as exception:
throw_error("Der er ikke forbindelse til internettet.")
print(Fore.MAGENTA + splash_text.text + "\n")
username, password, school_id, download_activities = get_all_inputs()
get_documents(username, password, school_id, download_activities)
if __name__ == "__main__":
main()