ladok3 4.10__py3-none-any.whl → 5.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- doc/ltxobj/ladok3.pdf +0 -0
- ladok3/Makefile +6 -0
- ladok3/__init__.py +1511 -3386
- ladok3/api.nw +1653 -225
- ladok3/cli.nw +118 -53
- ladok3/cli.py +323 -252
- ladok3/data.nw +92 -15
- ladok3/data.py +79 -3
- ladok3/ladok.bash +35 -17
- ladok3/ladok3.nw +288 -16
- ladok3/report.nw +183 -117
- ladok3/report.py +135 -63
- ladok3/scripts.nw +244 -0
- ladok3/student.nw +69 -4
- ladok3/student.py +98 -42
- ladok3/undoc.nw +62 -3119
- {ladok3-4.10.dist-info → ladok3-5.4.dist-info}/LICENSE +1 -1
- {ladok3-4.10.dist-info → ladok3-5.4.dist-info}/METADATA +39 -17
- ladok3-5.4.dist-info/RECORD +21 -0
- {ladok3-4.10.dist-info → ladok3-5.4.dist-info}/WHEEL +1 -1
- ladok3/.gitignore +0 -10
- ladok3-4.10.dist-info/RECORD +0 -21
- {ladok3-4.10.dist-info → ladok3-5.4.dist-info}/entry_points.txt +0 -0
ladok3/cli.py
CHANGED
|
@@ -26,92 +26,131 @@ import ladok3.student
|
|
|
26
26
|
|
|
27
27
|
dirs = appdirs.AppDirs("ladok", "dbosk@kth.se")
|
|
28
28
|
|
|
29
|
+
|
|
29
30
|
def err(rc, msg):
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
"""Print error message to stderr and exit with given return code.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
rc (int): Return code to exit with.
|
|
35
|
+
msg (str): Error message to display.
|
|
36
|
+
"""
|
|
37
|
+
print(f"{sys.argv[0]}: error: {msg}", file=sys.stderr)
|
|
38
|
+
sys.exit(rc)
|
|
39
|
+
|
|
32
40
|
|
|
33
41
|
def warn(msg):
|
|
34
|
-
|
|
42
|
+
"""Print warning message to stderr.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
msg (str): Warning message to display.
|
|
46
|
+
"""
|
|
47
|
+
print(f"{sys.argv[0]}: {msg}", file=sys.stderr)
|
|
48
|
+
|
|
49
|
+
|
|
35
50
|
def store_ladok_session(ls, credentials):
|
|
36
|
-
|
|
37
|
-
os.makedirs(dirs.user_cache_dir)
|
|
51
|
+
"""Store a LadokSession object to disk with encryption.
|
|
38
52
|
|
|
39
|
-
|
|
53
|
+
Saves the session object as an encrypted pickle file in the user's cache directory.
|
|
54
|
+
The credentials are used to derive an encryption key for security.
|
|
40
55
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
Args:
|
|
57
|
+
ls (LadokSession): The session object to store.
|
|
58
|
+
credentials (tuple): Tuple of (institution, vars) used for key derivation.
|
|
44
59
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
credentials = list(credentials.values())
|
|
51
|
-
salt = credentials[0]
|
|
52
|
-
passwd = credentials[1]
|
|
53
|
-
else:
|
|
54
|
-
salt = credentials[0]
|
|
55
|
-
passwd = credentials[1]
|
|
56
|
-
|
|
57
|
-
kdf = PBKDF2HMAC(
|
|
58
|
-
algorithm=hashes.SHA256(),
|
|
59
|
-
length=32,
|
|
60
|
-
salt=salt.encode("utf-8"),
|
|
61
|
-
iterations=100000
|
|
62
|
-
)
|
|
63
|
-
key = base64.urlsafe_b64encode(kdf.derive(passwd.encode("utf-8")))
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
fernet_protocol = Fernet(key)
|
|
67
|
-
encrypted_ls = fernet_protocol.encrypt(pickled_ls)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
with open(file_path, "wb") as file:
|
|
71
|
-
file.write(encrypted_ls)
|
|
72
|
-
|
|
73
|
-
def restore_ladok_session(credentials):
|
|
74
|
-
file_path = dirs.user_cache_dir + "/LadokSession"
|
|
60
|
+
Raises:
|
|
61
|
+
ValueError: If credentials are missing or invalid.
|
|
62
|
+
"""
|
|
63
|
+
if not os.path.isdir(dirs.user_cache_dir):
|
|
64
|
+
os.makedirs(dirs.user_cache_dir)
|
|
75
65
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
66
|
+
file_path = dirs.user_cache_dir + "/LadokSession"
|
|
67
|
+
|
|
68
|
+
pickled_ls = pickle.dumps(ls)
|
|
69
|
+
if not credentials or len(credentials) < 2:
|
|
80
70
|
raise ValueError(f"Missing credentials, see `ladok login -h`.")
|
|
81
71
|
|
|
82
|
-
|
|
72
|
+
if isinstance(credentials, dict):
|
|
83
73
|
try:
|
|
84
|
-
|
|
85
|
-
|
|
74
|
+
salt = credentials["username"]
|
|
75
|
+
passwd = credentials["password"]
|
|
86
76
|
except KeyError:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
77
|
+
credentials = list(credentials.values())
|
|
78
|
+
salt = credentials[0]
|
|
79
|
+
passwd = credentials[1]
|
|
80
|
+
else:
|
|
91
81
|
salt = credentials[0]
|
|
92
82
|
passwd = credentials[1]
|
|
93
83
|
|
|
94
|
-
|
|
84
|
+
kdf = PBKDF2HMAC(
|
|
95
85
|
algorithm=hashes.SHA256(),
|
|
96
86
|
length=32,
|
|
97
87
|
salt=salt.encode("utf-8"),
|
|
98
|
-
iterations=100000
|
|
99
|
-
|
|
100
|
-
|
|
88
|
+
iterations=100000,
|
|
89
|
+
)
|
|
90
|
+
key = base64.urlsafe_b64encode(kdf.derive(passwd.encode("utf-8")))
|
|
101
91
|
|
|
92
|
+
fernet_protocol = Fernet(key)
|
|
93
|
+
encrypted_ls = fernet_protocol.encrypt(pickled_ls)
|
|
94
|
+
|
|
95
|
+
with open(file_path, "wb") as file:
|
|
96
|
+
file.write(encrypted_ls)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def restore_ladok_session(credentials):
|
|
100
|
+
"""Restore a LadokSession object from disk.
|
|
101
|
+
|
|
102
|
+
Attempts to load and decrypt a previously stored session object. Returns None
|
|
103
|
+
if no cached session exists or decryption fails.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
credentials (tuple): Tuple of (institution, vars) used for key derivation.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
LadokSession or None: The restored session object, or None if unavailable.
|
|
110
|
+
"""
|
|
111
|
+
file_path = dirs.user_cache_dir + "/LadokSession"
|
|
112
|
+
|
|
113
|
+
if os.path.isfile(file_path):
|
|
114
|
+
with open(file_path, "rb") as file:
|
|
115
|
+
encrypted_ls = file.read()
|
|
116
|
+
if not credentials or len(credentials) < 2:
|
|
117
|
+
raise ValueError(f"Missing credentials, see `ladok login -h`.")
|
|
118
|
+
|
|
119
|
+
if isinstance(credentials, dict):
|
|
120
|
+
try:
|
|
121
|
+
salt = credentials["username"]
|
|
122
|
+
passwd = credentials["password"]
|
|
123
|
+
except KeyError:
|
|
124
|
+
credentials = list(credentials.values())
|
|
125
|
+
salt = credentials[0]
|
|
126
|
+
passwd = credentials[1]
|
|
127
|
+
else:
|
|
128
|
+
salt = credentials[0]
|
|
129
|
+
passwd = credentials[1]
|
|
130
|
+
|
|
131
|
+
kdf = PBKDF2HMAC(
|
|
132
|
+
algorithm=hashes.SHA256(),
|
|
133
|
+
length=32,
|
|
134
|
+
salt=salt.encode("utf-8"),
|
|
135
|
+
iterations=100000,
|
|
136
|
+
)
|
|
137
|
+
key = base64.urlsafe_b64encode(kdf.derive(passwd.encode("utf-8")))
|
|
138
|
+
|
|
139
|
+
fernet_protocol = Fernet(key)
|
|
140
|
+
try:
|
|
141
|
+
pickled_ls = fernet_protocol.decrypt(encrypted_ls)
|
|
142
|
+
except Exception as err:
|
|
143
|
+
warn(f"cache was corrupted, cannot decrypt: {err}")
|
|
144
|
+
pickled_ls = None
|
|
145
|
+
if pickled_ls:
|
|
146
|
+
return pickle.loads(pickled_ls)
|
|
147
|
+
|
|
148
|
+
return None
|
|
102
149
|
|
|
103
|
-
fernet_protocol = Fernet(key)
|
|
104
|
-
try:
|
|
105
|
-
pickled_ls = fernet_protocol.decrypt(encrypted_ls)
|
|
106
|
-
except Exception as err:
|
|
107
|
-
warn(f"cache was corrupted, cannot decrypt: {err}")
|
|
108
|
-
pickled_ls = None
|
|
109
|
-
if pickled_ls:
|
|
110
|
-
return pickle.loads(pickled_ls)
|
|
111
150
|
|
|
112
|
-
return None
|
|
113
151
|
def update_credentials_in_keyring(ls, args):
|
|
114
|
-
|
|
152
|
+
print(
|
|
153
|
+
"""
|
|
115
154
|
This login process is exactly the same as when you log in using
|
|
116
155
|
the web browser. You need three things:
|
|
117
156
|
|
|
@@ -126,57 +165,66 @@ the web browser. You need three things:
|
|
|
126
165
|
|
|
127
166
|
3) Your password at your institution.
|
|
128
167
|
|
|
129
|
-
"""
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
168
|
+
"""
|
|
169
|
+
)
|
|
170
|
+
while True:
|
|
171
|
+
institution = input("Institution: ")
|
|
172
|
+
matches = sa.find_entity_data_by_name(institution)
|
|
133
173
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
174
|
+
if not matches:
|
|
175
|
+
print("No match, try again.")
|
|
176
|
+
continue
|
|
137
177
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
178
|
+
if len(matches) > 1:
|
|
179
|
+
print("More than one match. Which one?")
|
|
180
|
+
for match in matches:
|
|
181
|
+
print(f"- {match['title']}")
|
|
182
|
+
continue
|
|
143
183
|
|
|
144
|
-
|
|
184
|
+
match = matches[0]
|
|
145
185
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
186
|
+
print(
|
|
187
|
+
f"Matched uniquely, using {match['title']}\n"
|
|
188
|
+
f" with domain {match['domain']} and\n"
|
|
189
|
+
f" unique identifier {match['id']}."
|
|
190
|
+
)
|
|
149
191
|
|
|
150
|
-
|
|
151
|
-
|
|
192
|
+
institution = match["id"]
|
|
193
|
+
break
|
|
152
194
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
195
|
+
vars = {
|
|
196
|
+
"username": input("Institution username: "),
|
|
197
|
+
"password": getpass.getpass("Institution password: [input is hidden] "),
|
|
198
|
+
}
|
|
199
|
+
while True:
|
|
200
|
+
temp_ls = ladok3.LadokSession(institution, vars=vars)
|
|
201
|
+
|
|
202
|
+
try:
|
|
203
|
+
temp_ls.user_info_JSON()
|
|
204
|
+
except weblogin.AuthenticationError as err:
|
|
205
|
+
adjust_vars(vars, err.variables)
|
|
206
|
+
else:
|
|
207
|
+
break
|
|
159
208
|
|
|
160
209
|
try:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
clear_cache(ls, args)
|
|
210
|
+
keyring.set_password("ladok3", "institution", institution)
|
|
211
|
+
keyring.set_password("ladok3", "vars", ";".join(vars.keys()))
|
|
212
|
+
for key, value in vars.items():
|
|
213
|
+
keyring.set_password("ladok3", key, value)
|
|
214
|
+
except Exception as err:
|
|
215
|
+
globals()["err"](
|
|
216
|
+
-1,
|
|
217
|
+
f"You don't seem to have a working keyring. "
|
|
218
|
+
f"Use one of the other methods, see "
|
|
219
|
+
f"`ladok login -h`: {err}.",
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
clear_cache(ls, args)
|
|
223
|
+
|
|
224
|
+
|
|
178
225
|
def adjust_vars(vars, form_variables):
|
|
179
|
-
|
|
226
|
+
print(
|
|
227
|
+
"""
|
|
180
228
|
Some part of the authentication went wrong. Either you typed your username or
|
|
181
229
|
password incorrectly, or your institution requires some adjustments. We'll
|
|
182
230
|
guide you through it.
|
|
@@ -191,125 +239,139 @@ when it should be 'dbosk@ug.kth.se' --- or something similar. Use your
|
|
|
191
239
|
institution's login page to figure this out.
|
|
192
240
|
|
|
193
241
|
Note: Your password will be visible on screen during this process.
|
|
194
|
-
"""
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
key
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
242
|
+
"""
|
|
243
|
+
)
|
|
244
|
+
input("\nPress return to continue.\n")
|
|
245
|
+
|
|
246
|
+
for key, value in form_variables.items():
|
|
247
|
+
key = key.casefold()
|
|
248
|
+
new_val = input(f"{key} = '{value}' " f"[enter new value, blank to keep] ")
|
|
249
|
+
if new_val:
|
|
250
|
+
vars[key] = new_val
|
|
251
|
+
|
|
252
|
+
|
|
203
253
|
def load_credentials(filename="config.json"):
|
|
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
|
-
|
|
254
|
+
"""
|
|
255
|
+
Loads credentials from environment or file named filename.
|
|
256
|
+
Returns the tuple (instituation, credential dictionary) that
|
|
257
|
+
can be passed to `LadokSession(instiution, credential dictionary)`.
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
try:
|
|
261
|
+
institution = os.environ["LADOK_INST"]
|
|
262
|
+
except:
|
|
263
|
+
institution = "KTH Royal Institute of Technology"
|
|
264
|
+
try:
|
|
265
|
+
vars = {
|
|
266
|
+
"username": os.environ["LADOK_USER"],
|
|
267
|
+
"password": os.environ["LADOK_PASS"],
|
|
268
|
+
}
|
|
269
|
+
if institution and vars["username"] and vars["password"]:
|
|
270
|
+
return institution, vars
|
|
271
|
+
except:
|
|
272
|
+
pass
|
|
273
|
+
try:
|
|
274
|
+
vars_keys = os.environ["LADOK_VARS"]
|
|
275
|
+
|
|
276
|
+
vars = {}
|
|
277
|
+
for key in vars_keys.split(":"):
|
|
278
|
+
try:
|
|
279
|
+
value = os.environ[key]
|
|
280
|
+
if value:
|
|
281
|
+
vars[key] = value
|
|
282
|
+
except KeyError:
|
|
283
|
+
warn(f"Variable {key} not set, ignoring.")
|
|
284
|
+
|
|
285
|
+
if institution and vars:
|
|
286
|
+
return institution, vars
|
|
287
|
+
except:
|
|
288
|
+
pass
|
|
289
|
+
try:
|
|
290
|
+
with open(filename) as conf_file:
|
|
291
|
+
config = json.load(conf_file)
|
|
292
|
+
|
|
293
|
+
institution = config.pop("institution", "KTH Royal Institute of Technology")
|
|
294
|
+
return institution, config
|
|
295
|
+
except:
|
|
296
|
+
pass
|
|
297
|
+
try:
|
|
298
|
+
institution = keyring.get_password("ladok3", "institution")
|
|
299
|
+
vars_keys = keyring.get_password("ladok3", "vars")
|
|
300
|
+
|
|
301
|
+
vars = {}
|
|
302
|
+
for key in vars_keys.split(";"):
|
|
303
|
+
value = keyring.get_password("ladok3", key)
|
|
304
|
+
if value:
|
|
305
|
+
vars[key] = value
|
|
306
|
+
|
|
307
|
+
if institution and vars:
|
|
308
|
+
return institution, vars
|
|
309
|
+
except:
|
|
310
|
+
pass
|
|
311
|
+
try:
|
|
312
|
+
institution = "KTH Royal Institute of Technology"
|
|
313
|
+
username = keyring.get_password("ladok3", "username")
|
|
314
|
+
password = keyring.get_password("ladok3", "password")
|
|
315
|
+
if username and password:
|
|
316
|
+
return institution, {"username": username, "password": password}
|
|
317
|
+
except:
|
|
318
|
+
pass
|
|
319
|
+
|
|
320
|
+
return None, None
|
|
321
|
+
|
|
322
|
+
|
|
268
323
|
def clear_cache(ls, args):
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
324
|
+
"""Clear the cached LADOK session data.
|
|
325
|
+
|
|
326
|
+
Removes the stored encrypted session file from the user's cache directory.
|
|
327
|
+
Silently ignores if the file doesn't exist.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
ls (LadokSession): The LADOK session (unused but required by interface).
|
|
331
|
+
args: Command line arguments (unused).
|
|
332
|
+
"""
|
|
333
|
+
try:
|
|
334
|
+
os.remove(dirs.user_cache_dir + "/LadokSession")
|
|
335
|
+
except FileNotFoundError as err:
|
|
336
|
+
pass
|
|
337
|
+
|
|
338
|
+
sys.exit(0)
|
|
339
|
+
|
|
273
340
|
|
|
274
|
-
sys.exit(0)
|
|
275
341
|
def main():
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
required=True
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
342
|
+
"""Run the command-line interface for the ladok command"""
|
|
343
|
+
argp = argparse.ArgumentParser(
|
|
344
|
+
description="This is a CLI-ification of LADOK3's web GUI.",
|
|
345
|
+
epilog="Web: https://github.com/dbosk/ladok3",
|
|
346
|
+
)
|
|
347
|
+
argp.add_argument(
|
|
348
|
+
"-f",
|
|
349
|
+
"--config-file",
|
|
350
|
+
default=f"{dirs.user_config_dir}/config.json",
|
|
351
|
+
help="Path to configuration file "
|
|
352
|
+
f"(default: {dirs.user_config_dir}/config.json) "
|
|
353
|
+
"or set LADOK_USER and LADOK_PASS environment variables.",
|
|
354
|
+
)
|
|
355
|
+
subp = argp.add_subparsers(title="commands", dest="command", required=True)
|
|
356
|
+
login_parser = subp.add_parser(
|
|
357
|
+
"login",
|
|
358
|
+
help="Manage login credentials",
|
|
359
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
360
|
+
description=f"""
|
|
295
361
|
Manages the user's LADOK login credentials. There are three ways to supply the
|
|
296
362
|
login credentials, in order of priority:
|
|
297
363
|
|
|
298
|
-
1) Through the
|
|
299
|
-
enter the credentials and they will be stored in the keyring. Note that for
|
|
300
|
-
this to work on the WSL platform (and possibly on Windows), you need to
|
|
301
|
-
install the `keyrings.alt` package: `python3 -m pip install keyrings.alt`.
|
|
364
|
+
1) Through the environment: Just set the environment variables
|
|
302
365
|
|
|
303
|
-
|
|
366
|
+
a) LADOK_INST, the name of the institution, e.g. KTH Royal Institute of
|
|
367
|
+
Technology;
|
|
304
368
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
LADOK_USER (the username, e.g. dbosk@ug.kth.se) and
|
|
310
|
-
LADOK_PASS (the password) instead.
|
|
369
|
+
b) LADOK_VARS, a colon-separated list of environment variables, similarly to
|
|
370
|
+
what's done in `ladok login` --- most don't need this, but can rather set
|
|
371
|
+
LADOK_USER (the username, e.g. dbosk@ug.kth.se) and LADOK_PASS (the
|
|
372
|
+
password) instead.
|
|
311
373
|
|
|
312
|
-
|
|
374
|
+
2) Through the configuration file: Just write
|
|
313
375
|
|
|
314
376
|
{{
|
|
315
377
|
"institution": "the name of the university"
|
|
@@ -321,42 +383,51 @@ def main():
|
|
|
321
383
|
option). (The keys 'username' and 'password' can be renamed to correspond to
|
|
322
384
|
the necessary values if the university login system uses other names.)
|
|
323
385
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
386
|
+
3) Through the system keyring: Just run `ladok login` and you'll be asked to
|
|
387
|
+
enter the credentials and they will be stored in the keyring. Note that for
|
|
388
|
+
this to work on the WSL platform (and possibly on Windows), you need to
|
|
389
|
+
install the `keyrings.alt` package: `python3 -m pip install keyrings.alt`.
|
|
390
|
+
|
|
391
|
+
The keyring is the most secure. However, sometimes one want to try different
|
|
392
|
+
credentials, so the environment should override the keyring. Also, on WSL the
|
|
393
|
+
keyring might require you to enter a password in the terminal---this is very
|
|
394
|
+
inconvenient in scripts. However, when logging in, we first try to store the
|
|
395
|
+
credentials in the keyring.
|
|
396
|
+
|
|
397
|
+
""",
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
login_parser.set_defaults(func=update_credentials_in_keyring)
|
|
401
|
+
cache_parser = subp.add_parser(
|
|
402
|
+
"cache", help="Manage cache", description="Manages the cache of LADOK data"
|
|
403
|
+
)
|
|
404
|
+
cache_subp = cache_parser.add_subparsers(
|
|
405
|
+
title="subcommands", dest="subcommand", required=True
|
|
406
|
+
)
|
|
407
|
+
cache_clear = cache_subp.add_parser(
|
|
408
|
+
"clear", help="Clear the cache", description="Clears everything from the cache"
|
|
409
|
+
)
|
|
410
|
+
cache_clear.set_defaults(func=clear_cache)
|
|
411
|
+
ladok3.data.add_command_options(subp)
|
|
412
|
+
ladok3.report.add_command_options(subp)
|
|
413
|
+
ladok3.student.add_command_options(subp)
|
|
414
|
+
argcomplete.autocomplete(argp)
|
|
415
|
+
args = argp.parse_args()
|
|
416
|
+
LADOK_INST, LADOK_VARS = load_credentials(args.config_file)
|
|
417
|
+
try:
|
|
418
|
+
ls = restore_ladok_session(LADOK_VARS)
|
|
419
|
+
except ValueError as error:
|
|
420
|
+
err(-1, f"Couldn't restore LADOK session: {error}")
|
|
421
|
+
if not ls:
|
|
422
|
+
ls = ladok3.LadokSession(LADOK_INST, vars=LADOK_VARS)
|
|
423
|
+
if "func" in args:
|
|
424
|
+
args.func(ls, args)
|
|
425
|
+
store_ladok_session(ls, LADOK_VARS)
|
|
426
|
+
|
|
356
427
|
|
|
357
428
|
if __name__ == "__main__":
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
429
|
+
try:
|
|
430
|
+
main()
|
|
431
|
+
sys.exit(0)
|
|
432
|
+
except Exception as e:
|
|
433
|
+
err(-1, e)
|