PyKubeGrader 0.1.11__tar.gz → 0.1.12__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. {pykubegrader-0.1.11/src/PyKubeGrader.egg-info → pykubegrader-0.1.12}/PKG-INFO +1 -1
  2. {pykubegrader-0.1.11 → pykubegrader-0.1.12/src/PyKubeGrader.egg-info}/PKG-INFO +1 -1
  3. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/build/api_notebook_builder.py +10 -10
  4. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/build/build_folder.py +34 -6
  5. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/initialize.py +11 -14
  6. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/telemetry.py +3 -4
  7. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/validate.py +2 -2
  8. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/.coveragerc +0 -0
  9. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/.github/workflows/main.yml +0 -0
  10. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/.gitignore +0 -0
  11. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/.readthedocs.yml +0 -0
  12. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/AUTHORS.rst +0 -0
  13. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/CHANGELOG.rst +0 -0
  14. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/CONTRIBUTING.rst +0 -0
  15. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/LICENSE.txt +0 -0
  16. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/README.rst +0 -0
  17. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/docs/Makefile +0 -0
  18. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/docs/_static/Drexel_blue_Logo_square_Dark.png +0 -0
  19. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/docs/_static/Drexel_blue_Logo_square_Light.png +0 -0
  20. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/docs/_static/custom.css +0 -0
  21. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/docs/authors.rst +0 -0
  22. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/docs/changelog.rst +0 -0
  23. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/docs/conf.py +0 -0
  24. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/docs/contributing.rst +0 -0
  25. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/docs/index.rst +0 -0
  26. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/docs/license.rst +0 -0
  27. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/docs/readme.rst +0 -0
  28. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/docs/requirements.txt +0 -0
  29. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/examples/.responses.json +0 -0
  30. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/examples/true_false.ipynb +0 -0
  31. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/pyproject.toml +0 -0
  32. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/setup.cfg +0 -0
  33. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/setup.py +0 -0
  34. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/PyKubeGrader.egg-info/SOURCES.txt +0 -0
  35. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/PyKubeGrader.egg-info/dependency_links.txt +0 -0
  36. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/PyKubeGrader.egg-info/entry_points.txt +0 -0
  37. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/PyKubeGrader.egg-info/not-zip-safe +0 -0
  38. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/PyKubeGrader.egg-info/requires.txt +0 -0
  39. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/PyKubeGrader.egg-info/top_level.txt +0 -0
  40. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/__init__.py +0 -0
  41. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/utils.py +0 -0
  42. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/widgets/__init__.py +0 -0
  43. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/widgets/multiple_choice.py +0 -0
  44. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/widgets/reading_question.py +0 -0
  45. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/widgets/select_many.py +0 -0
  46. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/widgets/student_info.py +0 -0
  47. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/widgets/style.py +0 -0
  48. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/widgets/true_false.py +0 -0
  49. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/widgets/types_question.py +0 -0
  50. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/widgets_base/__init__.py +0 -0
  51. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/widgets_base/multi_select.py +0 -0
  52. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/widgets_base/reading.py +0 -0
  53. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/src/pykubegrader/widgets_base/select.py +0 -0
  54. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/tests/conftest.py +0 -0
  55. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/tests/import_test.py +0 -0
  56. {pykubegrader-0.1.11 → pykubegrader-0.1.12}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyKubeGrader
3
- Version: 0.1.11
3
+ Version: 0.1.12
4
4
  Summary: Add a short description here!
5
5
  Home-page: https://github.com/pyscaffold/pyscaffold/
6
6
  Author: jagar2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyKubeGrader
3
- Version: 0.1.11
3
+ Version: 0.1.12
4
4
  Summary: Add a short description here!
5
5
  Home-page: https://github.com/pyscaffold/pyscaffold/
6
6
  Author: jagar2
@@ -6,6 +6,7 @@ import nbformat
6
6
  import json
7
7
  import re
8
8
  import shutil
9
+ import ast
9
10
 
10
11
 
11
12
  @dataclass
@@ -20,7 +21,6 @@ class FastAPINotebookBuilder:
20
21
  self.run()
21
22
 
22
23
  def run(self):
23
-
24
24
  # here for easy debugging
25
25
  if self.temp_notebook is not None:
26
26
  shutil.copy(
@@ -34,7 +34,6 @@ class FastAPINotebookBuilder:
34
34
  self.add_api_code()
35
35
 
36
36
  def add_api_code(self):
37
-
38
37
  for i, (cell_index, cell_dict) in enumerate(self.assertion_tests_dict.items()):
39
38
  print(
40
39
  f"Processing cell {cell_index + 1}, {i} of {len(self.assertion_tests_dict)}"
@@ -105,7 +104,6 @@ class FastAPINotebookBuilder:
105
104
  @staticmethod
106
105
  def construct_update_responses(cell_dict):
107
106
  update_responses = []
108
- question_id = cell_dict["question"] + "-" + str(cell_dict["test_number"]) + "\n"
109
107
 
110
108
  logging_variables = cell_dict["logging_variables"]
111
109
 
@@ -141,7 +139,6 @@ class FastAPINotebookBuilder:
141
139
 
142
140
  @staticmethod
143
141
  def construct_graders(cell_dict):
144
-
145
142
  # Generate Python code
146
143
  added_code = [
147
144
  "if "
@@ -283,7 +280,7 @@ class FastAPINotebookBuilder:
283
280
  """
284
281
  last_import_index = -1
285
282
  is_multiline_import = False # Flag to track if we're inside a multiline import
286
-
283
+
287
284
  for i, line in enumerate(cell_source):
288
285
  stripped_line = line.strip()
289
286
 
@@ -313,11 +310,15 @@ class FastAPINotebookBuilder:
313
310
  if "source" in cell:
314
311
  for line in cell["source"]:
315
312
  # Look for the log_variables pattern
316
- match = re.search(r"log_variables:\s*\[(.*?)\]", line)
313
+ match = re.search(r"log_variables:\s*(\[.*\])", line)
317
314
  if match:
318
- # Split the variables by comma and strip whitespace
319
- log_variables = [var.strip() for var in match.group(1).split(",")]
320
- return log_variables
315
+ # Parse the list using ast.literal_eval for safety
316
+ try:
317
+ log_variables = ast.literal_eval(match.group(1))
318
+ if isinstance(log_variables, list):
319
+ return [var.strip() for var in log_variables]
320
+ except (SyntaxError, ValueError):
321
+ pass
321
322
  return []
322
323
 
323
324
  def tag_questions(cells_dict):
@@ -363,7 +364,6 @@ class FastAPINotebookBuilder:
363
364
  return cells_dict
364
365
 
365
366
  def question_dict(self):
366
-
367
367
  notebook_path = Path(self.temp_notebook)
368
368
  if not notebook_path.exists():
369
369
  raise FileNotFoundError(f"The file {notebook_path} does not exist.")
@@ -278,26 +278,38 @@ class NotebookProcessor:
278
278
  self, temp_notebook_path, notebook_subfolder, notebook_name
279
279
  ):
280
280
  if self.has_assignment(temp_notebook_path, "# ASSIGNMENT CONFIG"):
281
-
282
281
  # TODO: This is hardcoded for now, but should be in a configuration file.
283
282
  client_private_key = os.path.join(
284
283
  notebook_subfolder,
285
- "client_private_key.bin",
284
+ ".client_private_key.bin",
286
285
  )
287
286
  server_public_key = os.path.join(
288
287
  notebook_subfolder,
289
- "server_public_key.bin",
288
+ ".server_public_key.bin",
290
289
  )
291
290
 
292
- shutil.copy("./keys/client_private_key.bin", client_private_key)
293
- shutil.copy("./keys/server_public_key.bin", server_public_key)
291
+ shutil.copy("./keys/.client_private_key.bin", client_private_key)
292
+ shutil.copy("./keys/.server_public_key.bin", server_public_key)
294
293
 
295
294
  FastAPINotebookBuilder(notebook_path=temp_notebook_path)
296
295
 
296
+ debug_notebook = os.path.join(
297
+ notebook_subfolder,
298
+ "dist",
299
+ "autograder",
300
+ os.path.basename(temp_notebook_path).replace("_temp", "_debugger"),
301
+ )
302
+
297
303
  self.run_otter_assign(
298
304
  temp_notebook_path, os.path.join(notebook_subfolder, "dist")
299
305
  )
300
306
 
307
+ print(f"Copying {temp_notebook_path} to {debug_notebook}")
308
+
309
+ shutil.copy(temp_notebook_path, debug_notebook)
310
+
311
+ NotebookProcessor.remove_assignment_config_cells(debug_notebook)
312
+
301
313
  student_notebook = os.path.join(
302
314
  notebook_subfolder, "dist", "student", f"{notebook_name}.ipynb"
303
315
  )
@@ -329,6 +341,23 @@ class NotebookProcessor:
329
341
  NotebookProcessor.add_initialization_code(temp_notebook_path)
330
342
  return None
331
343
 
344
+ @staticmethod
345
+ def remove_assignment_config_cells(notebook_path):
346
+ # Read the notebook
347
+ with open(notebook_path, "r", encoding="utf-8") as f:
348
+ notebook = nbformat.read(f, as_version=nbformat.NO_CONVERT)
349
+
350
+ # Filter out cells containing "# ASSIGNMENT CONFIG"
351
+ notebook.cells = [
352
+ cell
353
+ for cell in notebook.cells
354
+ if "# ASSIGNMENT CONFIG" not in cell.get("source", "")
355
+ ]
356
+
357
+ # Save the updated notebook
358
+ with open(notebook_path, "w", encoding="utf-8") as f:
359
+ nbformat.write(notebook, f)
360
+
332
361
  @staticmethod
333
362
  def add_initialization_code(notebook_path):
334
363
  # finds the first code cell
@@ -340,7 +369,6 @@ class NotebookProcessor:
340
369
  replace_cell_source(notebook_path, index, cell)
341
370
 
342
371
  def multiple_choice_parser(self, temp_notebook_path, new_notebook_path):
343
-
344
372
  ### Parse the notebook for multiple choice questions
345
373
  if self.has_assignment(temp_notebook_path, "# BEGIN MULTIPLE CHOICE"):
346
374
  self._print_and_log(
@@ -8,11 +8,11 @@ import requests
8
8
  from .telemetry import telemetry, update_responses, ensure_responses
9
9
 
10
10
 
11
-
12
- def initialize_assignment(name: str,
13
- verbose: Optional[bool] = False,
14
- url: Optional[str] = "https://engr-131-api.eastus.cloudapp.azure.com/") -> None:
15
-
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
16
  ipython = get_ipython()
17
17
  if ipython is None:
18
18
  print("Setup unsuccessful. Are you in a Jupyter environment?")
@@ -38,13 +38,12 @@ def initialize_assignment(name: str,
38
38
  except (TypeError, json.JSONDecodeError) as e:
39
39
  print(f"Failed to initialize assignment: {e}")
40
40
  return
41
-
42
-
41
+
43
42
  # extract responses
44
43
  responses = ensure_responses()
45
-
46
- # TODO: Add more checks here??
47
- assert isinstance(responses.get('seed'), int), "valid seed not found in responses"
44
+
45
+ # TODO: Add more checks here??
46
+ assert isinstance(responses.get("seed"), int), "valid seed not found in responses"
48
47
 
49
48
  pn.extension(silent=True)
50
49
 
@@ -52,17 +51,15 @@ def initialize_assignment(name: str,
52
51
  print("Assignment successfully initialized")
53
52
  print(f"Assignment: {name}")
54
53
  print(f"Username: {jhub_user}")
55
-
56
54
 
57
-
58
55
  # Checks connectivity to the API
59
- params = { "jhub_user": responses["jhub_user"] }
56
+ params = {"jhub_user": responses["jhub_user"]}
60
57
  response = requests.get(url, params=params)
61
58
  if verbose:
62
59
  print(f"status code: {response.status_code}")
63
60
  data = response.json()
64
61
  for k, v in data.items():
65
62
  print(f"{k}: {v}")
66
-
63
+
67
64
  print("Assignment successfully initialized")
68
65
  return responses
@@ -20,11 +20,11 @@ logging.basicConfig(filename=".output.log", level=logging.INFO, force=True)
20
20
 
21
21
 
22
22
  def encrypt_to_b64(message: str) -> str:
23
- with open("server_public_key.bin", "rb") as f:
23
+ with open(".server_public_key.bin", "rb") as f:
24
24
  server_pub_key_bytes = f.read()
25
25
  server_pub_key = nacl.public.PublicKey(server_pub_key_bytes)
26
26
 
27
- with open("client_private_key.bin", "rb") as f:
27
+ with open(".client_private_key.bin", "rb") as f:
28
28
  client_private_key_bytes = f.read()
29
29
  client_priv_key = nacl.public.PrivateKey(client_private_key_bytes)
30
30
 
@@ -36,7 +36,6 @@ def encrypt_to_b64(message: str) -> str:
36
36
 
37
37
 
38
38
  def ensure_responses() -> dict:
39
-
40
39
  with open(".responses.json", "a") as _:
41
40
  pass
42
41
 
@@ -48,7 +47,7 @@ def ensure_responses() -> dict:
48
47
  except json.JSONDecodeError:
49
48
  with open(".responses.json", "w") as f:
50
49
  json.dump(responses, f)
51
-
50
+
52
51
  return responses
53
52
 
54
53
 
@@ -252,11 +252,11 @@ def validate_logfile(
252
252
 
253
253
 
254
254
  def generate_keys() -> nacl.public.Box:
255
- with open("server_private_key.bin", "rb") as priv_file:
255
+ with open(".server_private_key.bin", "rb") as priv_file:
256
256
  server_private_key_bytes = priv_file.read()
257
257
  server_priv_key = nacl.public.PrivateKey(server_private_key_bytes)
258
258
 
259
- with open("client_public_key.bin", "rb") as pub_file:
259
+ with open(".client_public_key.bin", "rb") as pub_file:
260
260
  client_public_key_bytes = pub_file.read()
261
261
  client_pub_key = nacl.public.PublicKey(client_public_key_bytes)
262
262
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes