PyKubeGrader 0.0.9__tar.gz → 0.1.1__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {pykubegrader-0.0.9/src/PyKubeGrader.egg-info → pykubegrader-0.1.1}/PKG-INFO +1 -1
- {pykubegrader-0.0.9 → pykubegrader-0.1.1/src/PyKubeGrader.egg-info}/PKG-INFO +1 -1
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/src/PyKubeGrader.egg-info/SOURCES.txt +11 -9
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/src/pykubegrader/__init__.py +1 -2
- pykubegrader-0.1.1/src/pykubegrader/initialize.py +28 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/src/pykubegrader/telemetry.py +25 -15
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/src/pykubegrader/validate.py +17 -12
- pykubegrader-0.1.1/src/pykubegrader/widgets/__init__.py +1 -0
- pykubegrader-0.0.9/src/pykubegrader/mc_widget.py → pykubegrader-0.1.1/src/pykubegrader/widgets/multiple_choice.py +11 -2
- pykubegrader-0.0.9/src/pykubegrader/reading_widget.py → pykubegrader-0.1.1/src/pykubegrader/widgets/reading_question.py +1 -1
- pykubegrader-0.0.9/src/pykubegrader/select_many_widget.py → pykubegrader-0.1.1/src/pykubegrader/widgets/select_many.py +10 -1
- pykubegrader-0.0.9/src/pykubegrader/info_widget.py → pykubegrader-0.1.1/src/pykubegrader/widgets/student_info.py +9 -1
- pykubegrader-0.0.9/src/pykubegrader/types_widget.py → pykubegrader-0.1.1/src/pykubegrader/widgets/types_question.py +11 -2
- pykubegrader-0.1.1/src/pykubegrader/widgets_base/__init__.py +1 -0
- pykubegrader-0.0.9/src/pykubegrader/multi_select_base.py → pykubegrader-0.1.1/src/pykubegrader/widgets_base/multi_select.py +2 -2
- pykubegrader-0.0.9/src/pykubegrader/reading_base.py → pykubegrader-0.1.1/src/pykubegrader/widgets_base/reading.py +2 -2
- pykubegrader-0.0.9/src/pykubegrader/select_base.py → pykubegrader-0.1.1/src/pykubegrader/widgets_base/select.py +4 -2
- pykubegrader-0.0.9/src/pykubegrader/initialize.py +0 -18
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/.coveragerc +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/.github/workflows/main.yml +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/.gitignore +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/.readthedocs.yml +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/AUTHORS.rst +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/CHANGELOG.rst +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/CONTRIBUTING.rst +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/LICENSE.txt +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/README.rst +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/docs/Makefile +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/docs/_static/Drexel_blue_Logo_square_Dark.png +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/docs/_static/Drexel_blue_Logo_square_Light.png +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/docs/_static/custom.css +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/docs/authors.rst +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/docs/changelog.rst +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/docs/conf.py +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/docs/contributing.rst +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/docs/index.rst +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/docs/license.rst +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/docs/readme.rst +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/docs/requirements.txt +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/pyproject.toml +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/setup.cfg +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/setup.py +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/src/PyKubeGrader.egg-info/dependency_links.txt +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/src/PyKubeGrader.egg-info/not-zip-safe +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/src/PyKubeGrader.egg-info/requires.txt +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/src/PyKubeGrader.egg-info/top_level.txt +0 -0
- /pykubegrader-0.0.9/src/pykubegrader/misc.py → /pykubegrader-0.1.1/src/pykubegrader/utils.py +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/tests/conftest.py +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/tests/import_test.py +0 -0
- {pykubegrader-0.0.9 → pykubegrader-0.1.1}/tox.ini +0 -0
@@ -30,17 +30,19 @@ src/PyKubeGrader.egg-info/not-zip-safe
|
|
30
30
|
src/PyKubeGrader.egg-info/requires.txt
|
31
31
|
src/PyKubeGrader.egg-info/top_level.txt
|
32
32
|
src/pykubegrader/__init__.py
|
33
|
-
src/pykubegrader/info_widget.py
|
34
33
|
src/pykubegrader/initialize.py
|
35
|
-
src/pykubegrader/mc_widget.py
|
36
|
-
src/pykubegrader/misc.py
|
37
|
-
src/pykubegrader/multi_select_base.py
|
38
|
-
src/pykubegrader/reading_base.py
|
39
|
-
src/pykubegrader/reading_widget.py
|
40
|
-
src/pykubegrader/select_base.py
|
41
|
-
src/pykubegrader/select_many_widget.py
|
42
34
|
src/pykubegrader/telemetry.py
|
43
|
-
src/pykubegrader/
|
35
|
+
src/pykubegrader/utils.py
|
44
36
|
src/pykubegrader/validate.py
|
37
|
+
src/pykubegrader/widgets/__init__.py
|
38
|
+
src/pykubegrader/widgets/multiple_choice.py
|
39
|
+
src/pykubegrader/widgets/reading_question.py
|
40
|
+
src/pykubegrader/widgets/select_many.py
|
41
|
+
src/pykubegrader/widgets/student_info.py
|
42
|
+
src/pykubegrader/widgets/types_question.py
|
43
|
+
src/pykubegrader/widgets_base/__init__.py
|
44
|
+
src/pykubegrader/widgets_base/multi_select.py
|
45
|
+
src/pykubegrader/widgets_base/reading.py
|
46
|
+
src/pykubegrader/widgets_base/select.py
|
45
47
|
tests/conftest.py
|
46
48
|
tests/import_test.py
|
@@ -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"
|
@@ -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")
|
@@ -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,
|
@@ -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
|
-
import panel as pn
|
2
|
-
from IPython import get_ipython
|
3
|
-
|
4
|
-
from .telemetry import telemetry
|
5
|
-
|
6
|
-
# Check if in a Jupyter environment
|
7
|
-
ipython = get_ipython()
|
8
|
-
|
9
|
-
if ipython is not None:
|
10
|
-
# Initialize Panel extension
|
11
|
-
pn.extension()
|
12
|
-
|
13
|
-
# Register telemetry with pre_run_cell event
|
14
|
-
ipython.events.register("pre_run_cell", telemetry)
|
15
|
-
|
16
|
-
print("Setup completed successfully")
|
17
|
-
else:
|
18
|
-
print("Setup unsuccessful. Are you in a Jupyter environment?")
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
/pykubegrader-0.0.9/src/pykubegrader/misc.py → /pykubegrader-0.1.1/src/pykubegrader/utils.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|