PyKubeGrader 0.0.9__py3-none-any.whl → 0.1.1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- {PyKubeGrader-0.0.9.dist-info → PyKubeGrader-0.1.1.dist-info}/METADATA +1 -1
- PyKubeGrader-0.1.1.dist-info/RECORD +20 -0
- pykubegrader/__init__.py +1 -2
- pykubegrader/initialize.py +20 -10
- pykubegrader/telemetry.py +25 -15
- pykubegrader/validate.py +17 -12
- pykubegrader/widgets/__init__.py +1 -0
- pykubegrader/{mc_widget.py → widgets/multiple_choice.py} +11 -2
- pykubegrader/{reading_widget.py → widgets/reading_question.py} +1 -1
- pykubegrader/{select_many_widget.py → widgets/select_many.py} +10 -1
- pykubegrader/{info_widget.py → widgets/student_info.py} +9 -1
- pykubegrader/{types_widget.py → widgets/types_question.py} +11 -2
- pykubegrader/widgets_base/__init__.py +1 -0
- pykubegrader/{multi_select_base.py → widgets_base/multi_select.py} +2 -2
- pykubegrader/{reading_base.py → widgets_base/reading.py} +2 -2
- pykubegrader/{select_base.py → widgets_base/select.py} +4 -2
- PyKubeGrader-0.0.9.dist-info/RECORD +0 -18
- {PyKubeGrader-0.0.9.dist-info → PyKubeGrader-0.1.1.dist-info}/LICENSE.txt +0 -0
- {PyKubeGrader-0.0.9.dist-info → PyKubeGrader-0.1.1.dist-info}/WHEEL +0 -0
- {PyKubeGrader-0.0.9.dist-info → PyKubeGrader-0.1.1.dist-info}/top_level.txt +0 -0
- /pykubegrader/{misc.py → utils.py} +0 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
pykubegrader/__init__.py,sha256=AoAkdfIjDDZGWLlsIRENNq06L9h46kDGBIE8vRmsCfg,311
|
2
|
+
pykubegrader/initialize.py,sha256=PACquuxFkvOKUx51Rv6_RVu1YnYXf9L_W3hmMY5IxMw,706
|
3
|
+
pykubegrader/telemetry.py,sha256=eAzb54-lHFcgv0IsLFwsWr4nm_D7u-n-zOdR4cKGID4,3291
|
4
|
+
pykubegrader/utils.py,sha256=dKw6SyRYU3DWRgD3xER7wq-C9e1daWPkqr901LpcwiQ,642
|
5
|
+
pykubegrader/validate.py,sha256=QzdsjSS7rBzvBj_s_VHk-shjkPE-NYHQrYI3UHbq7Yo,10632
|
6
|
+
pykubegrader/widgets/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
7
|
+
pykubegrader/widgets/multiple_choice.py,sha256=NjD3-uXSnibpUQ0mO3hRp_O-rynFyl0Dz6IXE4tnCRI,2078
|
8
|
+
pykubegrader/widgets/reading_question.py,sha256=y30_swHwzH8LrT8deWTnxctAAmR8BSxTlXAqMgUrAT4,3031
|
9
|
+
pykubegrader/widgets/select_many.py,sha256=NRRs9P3Jygl9eUVW16nxMLNlHfFDuINgsFhMyXiOsXU,3826
|
10
|
+
pykubegrader/widgets/student_info.py,sha256=xhQgKehk1r5e6N_hnjAIovLdPvQju6ZqQTOiPG0aevg,3568
|
11
|
+
pykubegrader/widgets/types_question.py,sha256=kZdRRXyFzOtYTmGdC7XWb_2oaxqg1WSuLcQn_sTj6Qc,2300
|
12
|
+
pykubegrader/widgets_base/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
13
|
+
pykubegrader/widgets_base/multi_select.py,sha256=o9FwOSkfjarSZuC0wgBJLSRNtnY1-q_og1k_cdyJki8,3105
|
14
|
+
pykubegrader/widgets_base/reading.py,sha256=4uTLmlPzCwxVzufFhPjM7W19uMGguRb6y4eAV3x-zAc,5314
|
15
|
+
pykubegrader/widgets_base/select.py,sha256=QfJ72bScZbwdRcPUWLMVaaPJbAJHXnWSHtzjy7UZWkc,2255
|
16
|
+
PyKubeGrader-0.1.1.dist-info/LICENSE.txt,sha256=YTp-Ewc8Kems8PJEE27KnBPFnZSxoWvSg7nnknzPyYw,1546
|
17
|
+
PyKubeGrader-0.1.1.dist-info/METADATA,sha256=0DH4khXyZ6ibFLbqgPAvSLpu9EnkP4hClSwGi6oCXhA,2640
|
18
|
+
PyKubeGrader-0.1.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
19
|
+
PyKubeGrader-0.1.1.dist-info/top_level.txt,sha256=e550Klfze6higFxER1V62fnGOcIgiKRbsrl9CC4UdtQ,13
|
20
|
+
PyKubeGrader-0.1.1.dist-info/RECORD,,
|
pykubegrader/__init__.py
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
from importlib.metadata import PackageNotFoundError, version # pragma: no cover
|
2
2
|
|
3
3
|
try:
|
4
|
-
# Change
|
5
|
-
dist_name = "PyKubeGrader"
|
4
|
+
dist_name = "PyKubeGrader" # Change if project is renamed
|
6
5
|
__version__ = version(dist_name)
|
7
6
|
except PackageNotFoundError: # pragma: no cover
|
8
7
|
__version__ = "unknown"
|
pykubegrader/initialize.py
CHANGED
@@ -1,18 +1,28 @@
|
|
1
|
+
import json
|
2
|
+
|
1
3
|
import panel as pn
|
2
4
|
from IPython import get_ipython
|
3
5
|
|
4
|
-
from .telemetry import telemetry
|
6
|
+
from .telemetry import telemetry, update_responses
|
7
|
+
|
8
|
+
|
9
|
+
def initialize_assignment(name: str) -> None:
|
10
|
+
ipython = get_ipython()
|
11
|
+
if ipython is None:
|
12
|
+
print("Setup unsuccessful. Are you in a Jupyter environment?")
|
13
|
+
return
|
5
14
|
|
6
|
-
|
7
|
-
ipython
|
15
|
+
try:
|
16
|
+
ipython.events.register("pre_run_cell", telemetry)
|
17
|
+
except TypeError as e:
|
18
|
+
print(f"Failed to register telemetry: {e}")
|
8
19
|
|
9
|
-
if ipython is not None:
|
10
|
-
# Initialize Panel extension
|
11
20
|
pn.extension()
|
12
21
|
|
13
|
-
|
14
|
-
|
22
|
+
try:
|
23
|
+
update_responses(key="assignment", value=name)
|
24
|
+
except (TypeError, json.JSONDecodeError) as e:
|
25
|
+
print(f"Failed to initialize assignment: {e}")
|
26
|
+
return
|
15
27
|
|
16
|
-
print("
|
17
|
-
else:
|
18
|
-
print("Setup unsuccessful. Are you in a Jupyter environment?")
|
28
|
+
print("Assignment successfully initialized")
|
pykubegrader/telemetry.py
CHANGED
@@ -10,12 +10,12 @@ from IPython.core.interactiveshell import ExecutionInfo
|
|
10
10
|
from requests import Response
|
11
11
|
from requests.auth import HTTPBasicAuth
|
12
12
|
|
13
|
+
# Set logging config (`force` is important)
|
13
14
|
logging.basicConfig(filename=".output.log", level=logging.INFO, force=True)
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
log_encrypted(f"code run: {cell_content}")
|
16
|
+
#
|
17
|
+
# Local functions
|
18
|
+
#
|
19
19
|
|
20
20
|
|
21
21
|
def encrypt_to_b64(message: str) -> str:
|
@@ -34,17 +34,6 @@ def encrypt_to_b64(message: str) -> str:
|
|
34
34
|
return encrypted_b64
|
35
35
|
|
36
36
|
|
37
|
-
def log_encrypted(message: str) -> None:
|
38
|
-
encrypted_b64 = encrypt_to_b64(message)
|
39
|
-
logging.info(f"Encrypted Output: {encrypted_b64}")
|
40
|
-
|
41
|
-
|
42
|
-
def log_variable(value, info_type) -> None:
|
43
|
-
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
44
|
-
message = f"{info_type}, {value}, {timestamp}"
|
45
|
-
log_encrypted(message)
|
46
|
-
|
47
|
-
|
48
37
|
def ensure_responses() -> dict:
|
49
38
|
with open(".responses.json", "a") as _:
|
50
39
|
pass
|
@@ -61,6 +50,22 @@ def ensure_responses() -> dict:
|
|
61
50
|
return data
|
62
51
|
|
63
52
|
|
53
|
+
def log_encrypted(message: str) -> None:
|
54
|
+
encrypted_b64 = encrypt_to_b64(message)
|
55
|
+
logging.info(f"Encrypted Output: {encrypted_b64}")
|
56
|
+
|
57
|
+
|
58
|
+
def log_variable(value, info_type) -> None:
|
59
|
+
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
60
|
+
message = f"{info_type}, {value}, {timestamp}"
|
61
|
+
log_encrypted(message)
|
62
|
+
|
63
|
+
|
64
|
+
def telemetry(info: ExecutionInfo) -> None:
|
65
|
+
cell_content = info.raw_cell
|
66
|
+
log_encrypted(f"code run: {cell_content}")
|
67
|
+
|
68
|
+
|
64
69
|
def update_responses(key: str, value) -> dict:
|
65
70
|
data = ensure_responses()
|
66
71
|
data[key] = value
|
@@ -84,6 +89,11 @@ def update_responses(key: str, value) -> dict:
|
|
84
89
|
return data
|
85
90
|
|
86
91
|
|
92
|
+
#
|
93
|
+
# API request functions
|
94
|
+
#
|
95
|
+
|
96
|
+
|
87
97
|
def score_question(
|
88
98
|
student_email: str,
|
89
99
|
term: str,
|
pykubegrader/validate.py
CHANGED
@@ -139,7 +139,7 @@ def validate_logfile(
|
|
139
139
|
return entry
|
140
140
|
return ""
|
141
141
|
|
142
|
-
def
|
142
|
+
def get_entries_len(data: list[str], question_number: int) -> int:
|
143
143
|
"""function to get the unique entries by length
|
144
144
|
|
145
145
|
Args:
|
@@ -180,7 +180,7 @@ def validate_logfile(
|
|
180
180
|
# Collect entries for each question in a list.
|
181
181
|
entries = [
|
182
182
|
entry
|
183
|
-
for j in range(1,
|
183
|
+
for j in range(1, get_entries_len(data, i))
|
184
184
|
if (entry := get_last_entry(data, f"q{i}_{j}")) != ""
|
185
185
|
]
|
186
186
|
|
@@ -246,7 +246,7 @@ def validate_logfile(
|
|
246
246
|
print("Writing to results.json")
|
247
247
|
json.dump(result_structure, file, indent=4)
|
248
248
|
|
249
|
-
|
249
|
+
verify_login(login_data, login_url)
|
250
250
|
|
251
251
|
# The file to be uploaded. Ensure the path is correct.
|
252
252
|
file_path = "results.json"
|
@@ -281,15 +281,9 @@ def validate_logfile(
|
|
281
281
|
submission_message(response)
|
282
282
|
|
283
283
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
)
|
288
|
-
|
289
|
-
if login_response.status_code == 200:
|
290
|
-
print("Login successful")
|
291
|
-
else:
|
292
|
-
Exception("Login failed")
|
284
|
+
#
|
285
|
+
# Helper functions
|
286
|
+
#
|
293
287
|
|
294
288
|
|
295
289
|
def submission_message(response) -> None:
|
@@ -309,3 +303,14 @@ def submission_message(response) -> None:
|
|
309
303
|
pass
|
310
304
|
else:
|
311
305
|
print("results.json was not present")
|
306
|
+
|
307
|
+
|
308
|
+
def verify_login(login_data, login_url):
|
309
|
+
login_response = requests.post(
|
310
|
+
login_url, auth=HTTPBasicAuth(login_data["username"], login_data["password"])
|
311
|
+
)
|
312
|
+
|
313
|
+
if login_response.status_code == 200:
|
314
|
+
print("Login successful")
|
315
|
+
else:
|
316
|
+
Exception("Login failed")
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -2,8 +2,12 @@ from typing import Tuple
|
|
2
2
|
|
3
3
|
import panel as pn
|
4
4
|
|
5
|
-
from
|
6
|
-
from .
|
5
|
+
from ..utils import list_of_lists
|
6
|
+
from ..widgets_base.select import SelectQuestion
|
7
|
+
|
8
|
+
#
|
9
|
+
# Style function
|
10
|
+
#
|
7
11
|
|
8
12
|
|
9
13
|
def MCQ(
|
@@ -35,6 +39,11 @@ def MCQ(
|
|
35
39
|
return desc_widgets, radio_buttons
|
36
40
|
|
37
41
|
|
42
|
+
#
|
43
|
+
# Question class
|
44
|
+
#
|
45
|
+
|
46
|
+
|
38
47
|
class MCQuestion(SelectQuestion):
|
39
48
|
def __init__(
|
40
49
|
self,
|
@@ -1,6 +1,10 @@
|
|
1
1
|
import panel as pn
|
2
2
|
|
3
|
-
from .
|
3
|
+
from ..widgets_base.multi_select import MultiSelectQuestion
|
4
|
+
|
5
|
+
#
|
6
|
+
# Style function
|
7
|
+
#
|
4
8
|
|
5
9
|
|
6
10
|
def MultiSelect(
|
@@ -42,6 +46,11 @@ def MultiSelect(
|
|
42
46
|
return desc_widgets, checkboxes
|
43
47
|
|
44
48
|
|
49
|
+
#
|
50
|
+
# Question class
|
51
|
+
#
|
52
|
+
|
53
|
+
|
45
54
|
class SelectMany(MultiSelectQuestion):
|
46
55
|
def __init__(
|
47
56
|
self,
|
@@ -5,7 +5,11 @@ import socket
|
|
5
5
|
import numpy as np
|
6
6
|
import panel as pn
|
7
7
|
|
8
|
-
from
|
8
|
+
from ..telemetry import ensure_responses, update_responses
|
9
|
+
|
10
|
+
#
|
11
|
+
# Constants
|
12
|
+
#
|
9
13
|
|
10
14
|
EMAIL_PATTERN = re.compile(r"[a-z]+\d+@drexel\.edu")
|
11
15
|
|
@@ -20,6 +24,10 @@ KEYS = [
|
|
20
24
|
"seed",
|
21
25
|
]
|
22
26
|
|
27
|
+
#
|
28
|
+
# Form class
|
29
|
+
#
|
30
|
+
|
23
31
|
|
24
32
|
class StudentInfoForm:
|
25
33
|
def __init__(self, **kwargs) -> None:
|
@@ -2,8 +2,12 @@ from typing import Tuple
|
|
2
2
|
|
3
3
|
import panel as pn
|
4
4
|
|
5
|
-
from
|
6
|
-
from .
|
5
|
+
from ..utils import list_of_lists
|
6
|
+
from ..widgets_base.select import SelectQuestion
|
7
|
+
|
8
|
+
#
|
9
|
+
# Style function
|
10
|
+
#
|
7
11
|
|
8
12
|
|
9
13
|
def MultipleChoice(
|
@@ -31,6 +35,11 @@ def MultipleChoice(
|
|
31
35
|
return desc_widgets, dropdowns
|
32
36
|
|
33
37
|
|
38
|
+
#
|
39
|
+
# Question class
|
40
|
+
#
|
41
|
+
|
42
|
+
|
34
43
|
class TypesQuestion(SelectQuestion):
|
35
44
|
def __init__(
|
36
45
|
self,
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -2,8 +2,8 @@ from typing import Callable, Tuple
|
|
2
2
|
|
3
3
|
import panel as pn
|
4
4
|
|
5
|
-
from
|
6
|
-
from
|
5
|
+
from ..telemetry import ensure_responses, update_responses
|
6
|
+
from ..utils import shuffle_questions
|
7
7
|
|
8
8
|
|
9
9
|
class MultiSelectQuestion:
|
@@ -3,8 +3,8 @@ from typing import Optional
|
|
3
3
|
|
4
4
|
import panel as pn
|
5
5
|
|
6
|
-
from
|
7
|
-
from
|
6
|
+
from ..telemetry import ensure_responses, update_responses
|
7
|
+
from ..utils import shuffle_options
|
8
8
|
|
9
9
|
|
10
10
|
class ReadingPython:
|
@@ -2,8 +2,8 @@ from typing import Callable, Tuple
|
|
2
2
|
|
3
3
|
import panel as pn
|
4
4
|
|
5
|
-
from
|
6
|
-
from
|
5
|
+
from ..telemetry import ensure_responses, update_responses
|
6
|
+
from ..utils import shuffle_questions
|
7
7
|
|
8
8
|
|
9
9
|
class SelectQuestion:
|
@@ -57,6 +57,8 @@ class SelectQuestion:
|
|
57
57
|
)
|
58
58
|
|
59
59
|
def submit(self, _) -> None:
|
60
|
+
print("Submit button clicked...") # For debugging; remove later
|
61
|
+
|
60
62
|
selections = {key: widget.value for key, widget in zip(self.keys, self.widgets)}
|
61
63
|
|
62
64
|
for value in selections.values():
|
@@ -1,18 +0,0 @@
|
|
1
|
-
pykubegrader/__init__.py,sha256=L_TI3XcAgChNAmXaeDyMEdtgIclVl5czyC2f8Vhg_oE,355
|
2
|
-
pykubegrader/info_widget.py,sha256=x73heTqmqJQ9XkaE3eW4eLXTGJlmmTdGEkGtbIcY7uY,3532
|
3
|
-
pykubegrader/initialize.py,sha256=-s2hR8254hPz2-vKvlhHMDuNMBOZCqxxNdUpduBhYJs,444
|
4
|
-
pykubegrader/mc_widget.py,sha256=_qmJ4ZvU-KXamS726LqnCgecRg29hDd7w5WOerkLvuA,2022
|
5
|
-
pykubegrader/misc.py,sha256=dKw6SyRYU3DWRgD3xER7wq-C9e1daWPkqr901LpcwiQ,642
|
6
|
-
pykubegrader/multi_select_base.py,sha256=GJQ-xFJTErar94cFwTxsSmLKPPmywJRUQELhAbv-uFs,3102
|
7
|
-
pykubegrader/reading_base.py,sha256=vSoyThl2vFbnbgB_LDc-DDRPWCM2xGbazEptDjNn4uA,5311
|
8
|
-
pykubegrader/reading_widget.py,sha256=e8lXSzGI9dHCxW7fIvwJUU6j8qWQgHnKR06ke2W1k7w,3022
|
9
|
-
pykubegrader/select_base.py,sha256=90EnUPoNhJphkH1HXXxEgSmJDBrTWOqLNDha3DaZpGM,2178
|
10
|
-
pykubegrader/select_many_widget.py,sha256=Rmb9Es42qzSdPfhU6ypcKnw_30PwuARX2UKVwgwbQhg,3772
|
11
|
-
pykubegrader/telemetry.py,sha256=3GhItJ9dw18XcFutfQEk4z-DllCVtKXCKKLesf_jxzQ,3194
|
12
|
-
pykubegrader/types_widget.py,sha256=FOrnR2x1eNM-pcFNIC8JnQdylgSUSUzuACmiIRuFTbQ,2244
|
13
|
-
pykubegrader/validate.py,sha256=guEj0yeu9A_-Ig6S8diOfaNEWxud3k7t4tI-i4Steak,10585
|
14
|
-
PyKubeGrader-0.0.9.dist-info/LICENSE.txt,sha256=YTp-Ewc8Kems8PJEE27KnBPFnZSxoWvSg7nnknzPyYw,1546
|
15
|
-
PyKubeGrader-0.0.9.dist-info/METADATA,sha256=YifwDx2WiOdMXZTYNtSIIjEBi5AAsqUSdzOdM7szOaY,2640
|
16
|
-
PyKubeGrader-0.0.9.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
17
|
-
PyKubeGrader-0.0.9.dist-info/top_level.txt,sha256=e550Klfze6higFxER1V62fnGOcIgiKRbsrl9CC4UdtQ,13
|
18
|
-
PyKubeGrader-0.0.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|