PyKubeGrader 0.1.12__tar.gz → 0.1.13__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {pykubegrader-0.1.12/src/PyKubeGrader.egg-info → pykubegrader-0.1.13}/PKG-INFO +1 -1
- {pykubegrader-0.1.12 → pykubegrader-0.1.13/src/PyKubeGrader.egg-info}/PKG-INFO +1 -1
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/build/api_notebook_builder.py +9 -7
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/build/build_folder.py +2 -0
- pykubegrader-0.1.13/src/pykubegrader/initialize.py +106 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/telemetry.py +3 -3
- pykubegrader-0.1.12/src/pykubegrader/initialize.py +0 -65
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/.coveragerc +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/.github/workflows/main.yml +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/.gitignore +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/.readthedocs.yml +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/AUTHORS.rst +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/CHANGELOG.rst +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/CONTRIBUTING.rst +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/LICENSE.txt +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/README.rst +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/docs/Makefile +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/docs/_static/Drexel_blue_Logo_square_Dark.png +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/docs/_static/Drexel_blue_Logo_square_Light.png +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/docs/_static/custom.css +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/docs/authors.rst +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/docs/changelog.rst +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/docs/conf.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/docs/contributing.rst +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/docs/index.rst +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/docs/license.rst +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/docs/readme.rst +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/docs/requirements.txt +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/examples/.responses.json +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/examples/true_false.ipynb +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/pyproject.toml +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/setup.cfg +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/setup.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/PyKubeGrader.egg-info/SOURCES.txt +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/PyKubeGrader.egg-info/dependency_links.txt +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/PyKubeGrader.egg-info/entry_points.txt +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/PyKubeGrader.egg-info/not-zip-safe +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/PyKubeGrader.egg-info/requires.txt +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/PyKubeGrader.egg-info/top_level.txt +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/__init__.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/utils.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/validate.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/widgets/__init__.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/widgets/multiple_choice.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/widgets/reading_question.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/widgets/select_many.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/widgets/student_info.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/widgets/style.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/widgets/true_false.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/widgets/types_question.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/widgets_base/__init__.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/widgets_base/multi_select.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/widgets_base/reading.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/src/pykubegrader/widgets_base/select.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/tests/conftest.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/tests/import_test.py +0 -0
- {pykubegrader-0.1.12 → pykubegrader-0.1.13}/tox.ini +0 -0
@@ -1,12 +1,12 @@
|
|
1
|
+
import ast
|
2
|
+
import json
|
3
|
+
import re
|
4
|
+
import shutil
|
1
5
|
from dataclasses import dataclass
|
2
6
|
from pathlib import Path
|
3
7
|
from typing import Optional
|
4
|
-
|
8
|
+
|
5
9
|
import nbformat
|
6
|
-
import json
|
7
|
-
import re
|
8
|
-
import shutil
|
9
|
-
import ast
|
10
10
|
|
11
11
|
|
12
12
|
@dataclass
|
@@ -76,7 +76,9 @@ class FastAPINotebookBuilder:
|
|
76
76
|
)
|
77
77
|
updated_cell_source.extend(["earned_points += score\n"])
|
78
78
|
updated_cell_source.extend(
|
79
|
-
[
|
79
|
+
[
|
80
|
+
f'log_variable(f"{{score}}, {{max_score}}", question_id, "{self.filename.split(".")[0]}")\n'
|
81
|
+
]
|
80
82
|
)
|
81
83
|
updated_cell_source.extend(
|
82
84
|
["os.environ['EARNED_POINTS'] = str(earned_points)\n"]
|
@@ -202,7 +204,7 @@ class FastAPINotebookBuilder:
|
|
202
204
|
" ensure_responses,\n",
|
203
205
|
" log_variable,\n",
|
204
206
|
" score_question,\n",
|
205
|
-
"
|
207
|
+
" submit_question,\n",
|
206
208
|
" telemetry,\n",
|
207
209
|
" update_responses,\n",
|
208
210
|
")\n",
|
@@ -0,0 +1,106 @@
|
|
1
|
+
import os
|
2
|
+
import shutil
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
import panel as pn
|
6
|
+
import requests
|
7
|
+
from IPython import get_ipython
|
8
|
+
|
9
|
+
from .telemetry import ensure_responses, telemetry, update_responses
|
10
|
+
|
11
|
+
|
12
|
+
def initialize_assignment(
|
13
|
+
name: str,
|
14
|
+
url: str = "https://engr-131-api.eastus.cloudapp.azure.com/",
|
15
|
+
verbose: bool = False,
|
16
|
+
) -> dict:
|
17
|
+
"""
|
18
|
+
Initialize an assignment in a Jupyter environment.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
name (str): The name of the assignment.
|
22
|
+
url (str): The URL of the API server.
|
23
|
+
verbose (bool): Whether to print detailed initialization information.
|
24
|
+
|
25
|
+
Returns:
|
26
|
+
dict: The responses dictionary after initialization.
|
27
|
+
|
28
|
+
Raises:
|
29
|
+
Exception: If the environment is unsupported or initialization fails.
|
30
|
+
"""
|
31
|
+
|
32
|
+
ipython = get_ipython()
|
33
|
+
if ipython is None:
|
34
|
+
raise Exception("Setup unsuccessful. Are you in a Jupyter environment?")
|
35
|
+
|
36
|
+
try:
|
37
|
+
move_dotfiles()
|
38
|
+
ipython.events.register("pre_run_cell", telemetry)
|
39
|
+
except Exception as e:
|
40
|
+
raise Exception(f"Failed to register telemetry: {e}")
|
41
|
+
|
42
|
+
jhub_user = os.getenv("JUPYTERHUB_USER")
|
43
|
+
if jhub_user is None:
|
44
|
+
raise Exception("Setup unsuccessful. Are you on JupyterHub?")
|
45
|
+
|
46
|
+
try:
|
47
|
+
seed = hash(jhub_user) % 1000
|
48
|
+
update_responses(key="seed", value=seed)
|
49
|
+
|
50
|
+
update_responses(key="assignment", value=name)
|
51
|
+
update_responses(key="jhub_user", value=jhub_user)
|
52
|
+
|
53
|
+
responses = ensure_responses()
|
54
|
+
# TODO: Add more checks here?
|
55
|
+
assert isinstance(responses.get("seed"), int), "Seed not set"
|
56
|
+
|
57
|
+
pn.extension(silent=True)
|
58
|
+
|
59
|
+
# Check connection to API server
|
60
|
+
params = {"jhub_user": responses["jhub_user"]}
|
61
|
+
response = requests.get(url, params=params)
|
62
|
+
if verbose:
|
63
|
+
print(f"status code: {response.status_code}")
|
64
|
+
data = response.json()
|
65
|
+
for k, v in data.items():
|
66
|
+
print(f"{k}: {v}")
|
67
|
+
except Exception as e:
|
68
|
+
raise Exception(f"Failed to initialize assignment: {e}")
|
69
|
+
|
70
|
+
print("Assignment successfully initialized")
|
71
|
+
if verbose:
|
72
|
+
print(f"Assignment: {name}")
|
73
|
+
print(f"Username: {jhub_user}")
|
74
|
+
|
75
|
+
return responses
|
76
|
+
|
77
|
+
|
78
|
+
#
|
79
|
+
# Helper functions
|
80
|
+
#
|
81
|
+
|
82
|
+
|
83
|
+
def move_dotfiles():
|
84
|
+
"""
|
85
|
+
Move essential dotfiles from a fixed source directory to the current working directory.
|
86
|
+
|
87
|
+
Raises:
|
88
|
+
FileNotFoundError: If a source file is missing.
|
89
|
+
Exception: If copying fails for any other reason.
|
90
|
+
"""
|
91
|
+
source_dir = Path("/opt/dotfiles")
|
92
|
+
target_dir = Path.cwd()
|
93
|
+
|
94
|
+
files_to_copy = [".client_private_key.bin", ".server_public_key.bin"]
|
95
|
+
|
96
|
+
for file_name in files_to_copy:
|
97
|
+
source_file = source_dir / file_name
|
98
|
+
target_file = target_dir / file_name
|
99
|
+
|
100
|
+
if not source_file.exists():
|
101
|
+
raise FileNotFoundError(f"Key file not found: {source_file}")
|
102
|
+
|
103
|
+
try:
|
104
|
+
shutil.copy2(source_file, target_file)
|
105
|
+
except Exception as e:
|
106
|
+
raise Exception(f"Failed to copy {source_file} to {target_file}: {e}")
|
@@ -56,9 +56,9 @@ def log_encrypted(message: str) -> None:
|
|
56
56
|
logging.info(f"Encrypted Output: {encrypted_b64}")
|
57
57
|
|
58
58
|
|
59
|
-
def log_variable(value, info_type) -> None:
|
59
|
+
def log_variable(assignment_name, value, info_type) -> None:
|
60
60
|
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
61
|
-
message = f"{info_type}, {value}, {timestamp}"
|
61
|
+
message = f"{info_type}, {value}, {timestamp}, {assignment_name}"
|
62
62
|
log_encrypted(message)
|
63
63
|
|
64
64
|
|
@@ -119,7 +119,7 @@ def score_question(
|
|
119
119
|
return res
|
120
120
|
|
121
121
|
|
122
|
-
def
|
122
|
+
def submit_question(
|
123
123
|
student_email: str,
|
124
124
|
term: str,
|
125
125
|
assignment: str,
|
@@ -1,65 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
import os
|
3
|
-
from typing import Optional
|
4
|
-
|
5
|
-
import panel as pn
|
6
|
-
from IPython import get_ipython
|
7
|
-
import requests
|
8
|
-
from .telemetry import telemetry, update_responses, ensure_responses
|
9
|
-
|
10
|
-
|
11
|
-
def initialize_assignment(
|
12
|
-
name: str,
|
13
|
-
verbose: Optional[bool] = False,
|
14
|
-
url: Optional[str] = "https://engr-131-api.eastus.cloudapp.azure.com/",
|
15
|
-
) -> None:
|
16
|
-
ipython = get_ipython()
|
17
|
-
if ipython is None:
|
18
|
-
print("Setup unsuccessful. Are you in a Jupyter environment?")
|
19
|
-
return
|
20
|
-
|
21
|
-
try:
|
22
|
-
ipython.events.register("pre_run_cell", telemetry)
|
23
|
-
except TypeError as e:
|
24
|
-
print(f"Failed to register telemetry: {e}")
|
25
|
-
return
|
26
|
-
|
27
|
-
jhub_user = os.getenv("JUPYTERHUB_USER")
|
28
|
-
if jhub_user is None:
|
29
|
-
print("Setup unsuccessful. Are you on JupyterHub?")
|
30
|
-
return
|
31
|
-
|
32
|
-
try:
|
33
|
-
seed = hash(jhub_user) % 1000
|
34
|
-
update_responses(key="seed", value=seed)
|
35
|
-
update_responses(key="assignment", value=name)
|
36
|
-
update_responses(key="jhub_user", value=jhub_user)
|
37
|
-
|
38
|
-
except (TypeError, json.JSONDecodeError) as e:
|
39
|
-
print(f"Failed to initialize assignment: {e}")
|
40
|
-
return
|
41
|
-
|
42
|
-
# extract responses
|
43
|
-
responses = ensure_responses()
|
44
|
-
|
45
|
-
# TODO: Add more checks here??
|
46
|
-
assert isinstance(responses.get("seed"), int), "valid seed not found in responses"
|
47
|
-
|
48
|
-
pn.extension(silent=True)
|
49
|
-
|
50
|
-
if verbose:
|
51
|
-
print("Assignment successfully initialized")
|
52
|
-
print(f"Assignment: {name}")
|
53
|
-
print(f"Username: {jhub_user}")
|
54
|
-
|
55
|
-
# Checks connectivity to the API
|
56
|
-
params = {"jhub_user": responses["jhub_user"]}
|
57
|
-
response = requests.get(url, params=params)
|
58
|
-
if verbose:
|
59
|
-
print(f"status code: {response.status_code}")
|
60
|
-
data = response.json()
|
61
|
-
for k, v in data.items():
|
62
|
-
print(f"{k}: {v}")
|
63
|
-
|
64
|
-
print("Assignment successfully initialized")
|
65
|
-
return responses
|
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
|
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
|