PyKubeGrader 0.0.8__py3-none-any.whl → 0.1.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- {PyKubeGrader-0.0.8.dist-info → PyKubeGrader-0.1.0.dist-info}/METADATA +13 -6
- PyKubeGrader-0.1.0.dist-info/RECORD +20 -0
- pykubegrader/__init__.py +2 -9
- pykubegrader/initialize.py +28 -0
- 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} +6 -3
- PyKubeGrader-0.0.8.dist-info/RECORD +0 -19
- pykubegrader/seed/__init__.py +0 -5
- pykubegrader/seed/hash_seed.py +0 -25
- {PyKubeGrader-0.0.8.dist-info → PyKubeGrader-0.1.0.dist-info}/LICENSE.txt +0 -0
- {PyKubeGrader-0.0.8.dist-info → PyKubeGrader-0.1.0.dist-info}/WHEEL +0 -0
- {PyKubeGrader-0.0.8.dist-info → PyKubeGrader-0.1.0.dist-info}/top_level.txt +0 -0
- /pykubegrader/{misc.py → utils.py} +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: PyKubeGrader
|
3
|
-
Version: 0.0
|
3
|
+
Version: 0.1.0
|
4
4
|
Summary: Add a short description here!
|
5
5
|
Home-page: https://github.com/pyscaffold/pyscaffold/
|
6
6
|
Author: jagar2
|
@@ -13,11 +13,18 @@ Classifier: Programming Language :: Python
|
|
13
13
|
Description-Content-Type: text/x-rst; charset=UTF-8
|
14
14
|
License-File: LICENSE.txt
|
15
15
|
Requires-Dist: importlib-metadata; python_version < "3.8"
|
16
|
-
Requires-Dist: ipython
|
17
|
-
Requires-Dist:
|
18
|
-
Requires-Dist:
|
19
|
-
Requires-Dist:
|
20
|
-
Requires-Dist:
|
16
|
+
Requires-Dist: ipython
|
17
|
+
Requires-Dist: mypy
|
18
|
+
Requires-Dist: numpy
|
19
|
+
Requires-Dist: panel
|
20
|
+
Requires-Dist: pynacl
|
21
|
+
Requires-Dist: pytest
|
22
|
+
Requires-Dist: requests
|
23
|
+
Requires-Dist: ruff
|
24
|
+
Requires-Dist: setuptools
|
25
|
+
Requires-Dist: sphinx
|
26
|
+
Requires-Dist: types-requests
|
27
|
+
Requires-Dist: types-setuptools
|
21
28
|
Provides-Extra: testing
|
22
29
|
Requires-Dist: setuptools; extra == "testing"
|
23
30
|
Requires-Dist: pytest; extra == "testing"
|
@@ -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=5k-LoD3fTZYUOubRiTeQcVN5YDGK28x4Qb2l1aRVKzo,2181
|
16
|
+
PyKubeGrader-0.1.0.dist-info/LICENSE.txt,sha256=YTp-Ewc8Kems8PJEE27KnBPFnZSxoWvSg7nnknzPyYw,1546
|
17
|
+
PyKubeGrader-0.1.0.dist-info/METADATA,sha256=t6cYjeNKJkrs5UNB5-39NgdMdfGqQ6nrKsOLiFefP6E,2640
|
18
|
+
PyKubeGrader-0.1.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
19
|
+
PyKubeGrader-0.1.0.dist-info/top_level.txt,sha256=e550Klfze6higFxER1V62fnGOcIgiKRbsrl9CC4UdtQ,13
|
20
|
+
PyKubeGrader-0.1.0.dist-info/RECORD,,
|
pykubegrader/__init__.py
CHANGED
@@ -1,14 +1,7 @@
|
|
1
|
-
import
|
2
|
-
|
3
|
-
if sys.version_info[:2] >= (3, 8):
|
4
|
-
# TODO: Import directly (no need for conditional) when `python_requires = >= 3.8`
|
5
|
-
from importlib.metadata import PackageNotFoundError, version # pragma: no cover
|
6
|
-
else:
|
7
|
-
from importlib_metadata import PackageNotFoundError, version # pragma: no cover
|
1
|
+
from importlib.metadata import PackageNotFoundError, version # pragma: no cover
|
8
2
|
|
9
3
|
try:
|
10
|
-
# Change
|
11
|
-
dist_name = "PyKubeGrader"
|
4
|
+
dist_name = "PyKubeGrader" # Change if project is renamed
|
12
5
|
__version__ = version(dist_name)
|
13
6
|
except PackageNotFoundError: # pragma: no cover
|
14
7
|
__version__ = "unknown"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import json
|
2
|
+
|
3
|
+
import panel as pn
|
4
|
+
from IPython import get_ipython
|
5
|
+
|
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
|
14
|
+
|
15
|
+
try:
|
16
|
+
ipython.events.register("pre_run_cell", telemetry)
|
17
|
+
except TypeError as e:
|
18
|
+
print(f"Failed to register telemetry: {e}")
|
19
|
+
|
20
|
+
pn.extension()
|
21
|
+
|
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
|
27
|
+
|
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:
|
@@ -49,7 +49,10 @@ class SelectQuestion:
|
|
49
49
|
|
50
50
|
self.layout = pn.Column(
|
51
51
|
f"# Question {self.question_number}: {title}",
|
52
|
-
*(
|
52
|
+
*(
|
53
|
+
pn.Column(desc_widget, pn.Row(dropdown))
|
54
|
+
for desc_widget, dropdown in widget_pairs
|
55
|
+
),
|
53
56
|
self.submit_button,
|
54
57
|
)
|
55
58
|
|
@@ -1,19 +0,0 @@
|
|
1
|
-
pykubegrader/__init__.py,sha256=JHme-EVdE2QbS9_Q2qdHiy5bEsyeQDbZ8aP2OfoSyiw,583
|
2
|
-
pykubegrader/info_widget.py,sha256=x73heTqmqJQ9XkaE3eW4eLXTGJlmmTdGEkGtbIcY7uY,3532
|
3
|
-
pykubegrader/mc_widget.py,sha256=_qmJ4ZvU-KXamS726LqnCgecRg29hDd7w5WOerkLvuA,2022
|
4
|
-
pykubegrader/misc.py,sha256=dKw6SyRYU3DWRgD3xER7wq-C9e1daWPkqr901LpcwiQ,642
|
5
|
-
pykubegrader/multi_select_base.py,sha256=GJQ-xFJTErar94cFwTxsSmLKPPmywJRUQELhAbv-uFs,3102
|
6
|
-
pykubegrader/reading_base.py,sha256=vSoyThl2vFbnbgB_LDc-DDRPWCM2xGbazEptDjNn4uA,5311
|
7
|
-
pykubegrader/reading_widget.py,sha256=e8lXSzGI9dHCxW7fIvwJUU6j8qWQgHnKR06ke2W1k7w,3022
|
8
|
-
pykubegrader/select_base.py,sha256=Z-oAsNRBraqx3_T_hkSDq_quwQsZn8d8PRakI-_LUdQ,2132
|
9
|
-
pykubegrader/select_many_widget.py,sha256=Rmb9Es42qzSdPfhU6ypcKnw_30PwuARX2UKVwgwbQhg,3772
|
10
|
-
pykubegrader/telemetry.py,sha256=3GhItJ9dw18XcFutfQEk4z-DllCVtKXCKKLesf_jxzQ,3194
|
11
|
-
pykubegrader/types_widget.py,sha256=FOrnR2x1eNM-pcFNIC8JnQdylgSUSUzuACmiIRuFTbQ,2244
|
12
|
-
pykubegrader/validate.py,sha256=guEj0yeu9A_-Ig6S8diOfaNEWxud3k7t4tI-i4Steak,10585
|
13
|
-
pykubegrader/seed/__init__.py,sha256=7Tnb4qTHPIKLirU1IJQbiiQ9FBbeGs-H5clpFS0dPEo,79
|
14
|
-
pykubegrader/seed/hash_seed.py,sha256=tTSyYaMqgALZTNqc74IRjGN0Ii6gs4vYtRb9FavkfAM,651
|
15
|
-
PyKubeGrader-0.0.8.dist-info/LICENSE.txt,sha256=YTp-Ewc8Kems8PJEE27KnBPFnZSxoWvSg7nnknzPyYw,1546
|
16
|
-
PyKubeGrader-0.0.8.dist-info/METADATA,sha256=xv-9tAM6NhMoAUQ6AUK0Qh6C9f4LpBdg7X2R-T6PLb0,2505
|
17
|
-
PyKubeGrader-0.0.8.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
18
|
-
PyKubeGrader-0.0.8.dist-info/top_level.txt,sha256=e550Klfze6higFxER1V62fnGOcIgiKRbsrl9CC4UdtQ,13
|
19
|
-
PyKubeGrader-0.0.8.dist-info/RECORD,,
|
pykubegrader/seed/__init__.py
DELETED
pykubegrader/seed/hash_seed.py
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
import hashlib
|
2
|
-
|
3
|
-
def hash_to_seed(input_string):
|
4
|
-
"""
|
5
|
-
Hash a string into a small integer seed less than 500.
|
6
|
-
|
7
|
-
Parameters:
|
8
|
-
input_string (str): The string to hash.
|
9
|
-
|
10
|
-
Returns:
|
11
|
-
int: A seed value (0 <= seed < 500).
|
12
|
-
"""
|
13
|
-
# Ensure the input is a string
|
14
|
-
input_string = str(input_string)
|
15
|
-
|
16
|
-
# Create a SHA-256 hash of the string
|
17
|
-
hash_object = hashlib.sha256(input_string.encode())
|
18
|
-
|
19
|
-
# Convert the hash to an integer
|
20
|
-
large_number = int.from_bytes(hash_object.digest(), 'big')
|
21
|
-
|
22
|
-
# Reduce the number to a value less than 500
|
23
|
-
small_seed = large_number % 500
|
24
|
-
|
25
|
-
return small_seed
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|