PyKubeGrader 0.2.38__tar.gz → 0.2.40__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. {pykubegrader-0.2.38/src/PyKubeGrader.egg-info → pykubegrader-0.2.40}/PKG-INFO +1 -1
  2. {pykubegrader-0.2.38 → pykubegrader-0.2.40/src/PyKubeGrader.egg-info}/PKG-INFO +1 -1
  3. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/build/markdown_questions.py +47 -2
  4. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/utils.py +3 -1
  5. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/widgets/question_processor.py +14 -10
  6. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/widgets/true_false.py +1 -0
  7. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/widgets_base/multi_select.py +6 -1
  8. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/widgets_base/select.py +5 -1
  9. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/.coveragerc +0 -0
  10. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/.github/workflows/main.yml +0 -0
  11. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/.gitignore +0 -0
  12. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/.readthedocs.yml +0 -0
  13. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/AUTHORS.rst +0 -0
  14. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/CHANGELOG.rst +0 -0
  15. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/CONTRIBUTING.rst +0 -0
  16. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/LICENSE.txt +0 -0
  17. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/README.rst +0 -0
  18. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/docs/Makefile +0 -0
  19. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/docs/_static/Drexel_blue_Logo_square_Dark.png +0 -0
  20. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/docs/_static/Drexel_blue_Logo_square_Light.png +0 -0
  21. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/docs/_static/custom.css +0 -0
  22. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/docs/authors.rst +0 -0
  23. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/docs/changelog.rst +0 -0
  24. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/docs/conf.py +0 -0
  25. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/docs/contributing.rst +0 -0
  26. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/docs/index.rst +0 -0
  27. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/docs/license.rst +0 -0
  28. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/docs/readme.rst +0 -0
  29. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/docs/requirements.txt +0 -0
  30. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/examples/.responses.json +0 -0
  31. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/examples/true_false.ipynb +0 -0
  32. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/pyproject.toml +0 -0
  33. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/setup.cfg +0 -0
  34. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/setup.py +0 -0
  35. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/PyKubeGrader.egg-info/SOURCES.txt +0 -0
  36. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/PyKubeGrader.egg-info/dependency_links.txt +0 -0
  37. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/PyKubeGrader.egg-info/entry_points.txt +0 -0
  38. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/PyKubeGrader.egg-info/not-zip-safe +0 -0
  39. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/PyKubeGrader.egg-info/requires.txt +0 -0
  40. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/PyKubeGrader.egg-info/top_level.txt +0 -0
  41. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/__init__.py +0 -0
  42. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/build/__init__.py +0 -0
  43. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/build/api_notebook_builder.py +0 -0
  44. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/build/build_folder.py +0 -0
  45. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/build/clean_folder.py +0 -0
  46. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/graders/__init__.py +0 -0
  47. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/graders/late_assignments.py +0 -0
  48. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/initialize.py +0 -0
  49. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/log_parser/__init__.py +0 -0
  50. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/log_parser/parse.ipynb +0 -0
  51. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/log_parser/parse.py +0 -0
  52. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/submit/submit_assignment.py +0 -0
  53. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/telemetry.py +0 -0
  54. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/tokens/tokens.py +0 -0
  55. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/tokens/validate_token.py +0 -0
  56. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/validate.py +0 -0
  57. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/widgets/__init__.py +0 -0
  58. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/widgets/multiple_choice.py +0 -0
  59. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/widgets/reading_question.py +0 -0
  60. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/widgets/select_many.py +0 -0
  61. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/widgets/student_info.py +0 -0
  62. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/widgets/style.py +0 -0
  63. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/widgets/types_question.py +0 -0
  64. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/widgets_base/__init__.py +0 -0
  65. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/src/pykubegrader/widgets_base/reading.py +0 -0
  66. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/tests/conftest.py +0 -0
  67. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/tests/import_test.py +0 -0
  68. {pykubegrader-0.2.38 → pykubegrader-0.2.40}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: PyKubeGrader
3
- Version: 0.2.38
3
+ Version: 0.2.40
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.2
2
2
  Name: PyKubeGrader
3
- Version: 0.2.38
3
+ Version: 0.2.40
4
4
  Summary: Add a short description here!
5
5
  Home-page: https://github.com/pyscaffold/pyscaffold/
6
6
  Author: jagar2
@@ -47,7 +47,11 @@ class MarkdownToNotebook:
47
47
  current_cell = ""
48
48
  if "[markdown]" in line:
49
49
  current_type = "markdown"
50
- elif "# BEGIN" in line or "# END" in line:
50
+ elif (
51
+ "# BEGIN" in line
52
+ or "# END" in line
53
+ or "# ASSIGNMENT CONFIG" in line
54
+ ):
51
55
  current_type = "raw"
52
56
  else:
53
57
  current_type = "code"
@@ -65,7 +69,10 @@ class MarkdownToNotebook:
65
69
  elif current_type == "code":
66
70
  cells.append(nbf.v4.new_code_cell(current_cell.strip()))
67
71
  elif current_type == "raw":
68
- cells.append(nbf.v4.new_raw_cell(current_cell.strip()))
72
+ cell = nbf.v4.new_raw_cell(current_cell.strip())
73
+ cell["metadata"]["languageId"] = "raw"
74
+ cell["metadata"]["cell_type"] = "raw"
75
+ cells.append(cell)
69
76
 
70
77
  nb["cells"] = cells
71
78
 
@@ -73,8 +80,46 @@ class MarkdownToNotebook:
73
80
  with open(notebook_name, "w") as f:
74
81
  nbf.write(nb, f)
75
82
 
83
+ self.modify_notebook(notebook_name)
84
+
76
85
  print(f"Notebook saved as: {notebook_name}")
77
86
 
87
+ @staticmethod
88
+ def modify_notebook(notebook_path: str):
89
+ """
90
+ Modifies an existing Jupyter Notebook by converting cells containing specific markers
91
+ ("# BEGIN T", "# END", "# ASSIGNMENT CONFIG") into raw cells.
92
+
93
+ Args:
94
+ notebook_path (str): Path to the existing Jupyter Notebook to modify.
95
+ """
96
+ if not os.path.exists(notebook_path):
97
+ print(f"Error: Notebook '{notebook_path}' does not exist.")
98
+ return
99
+
100
+ with open(notebook_path, "r") as f:
101
+ nb = nbf.read(f, as_version=4)
102
+
103
+ for cell in nb["cells"]:
104
+ if cell["cell_type"] == "code" and cell["source"].startswith(
105
+ (
106
+ "# BEGIN TESTS",
107
+ "# END TESTS",
108
+ "# END SOLUTION",
109
+ "# BEGIN QUESTION",
110
+ "# ASSIGNMENT CONFIG",
111
+ "# END TF",
112
+ "# BEGIN TF",
113
+ )
114
+ ):
115
+ cell["cell_type"] = "raw"
116
+ if "metadata" not in cell:
117
+ cell["metadata"] = {}
118
+ cell["metadata"] = {"vscode": {"languageId": "raw"}}
119
+
120
+ with open(notebook_path, "w") as f:
121
+ nbf.write(nb, f)
122
+
78
123
 
79
124
  def main():
80
125
  parser = argparse.ArgumentParser(
@@ -15,7 +15,9 @@ def list_of_lists(options: list) -> bool:
15
15
 
16
16
  def shuffle_options(options: list[Optional[str]], seed: int) -> list[Optional[str]]:
17
17
  random.seed(seed)
18
- random.shuffle(options)
18
+
19
+ for inner_list in options:
20
+ random.shuffle(inner_list)
19
21
 
20
22
  return options
21
23
 
@@ -6,29 +6,33 @@ def process_questions_and_codes(titles):
6
6
  # Ensure titles is a list
7
7
  if isinstance(titles, str):
8
8
  titles = [titles]
9
-
9
+
10
10
  processed_titles = []
11
11
  code_blocks = []
12
-
12
+
13
13
  for title in titles:
14
14
  # Split the title at the "```python" delimiter
15
15
  parts = title.split("```python", maxsplit=1)
16
-
16
+
17
17
  # First part is the title, stripped of leading/trailing whitespace
18
18
  title_without_code = parts[0].strip()
19
-
19
+
20
+ # Remove aberrant ** from the beginning or end of the title
21
+ if title_without_code.startswith("**"):
22
+ title_without_code = title_without_code[2:]
23
+ if title_without_code.endswith("**"):
24
+ title_without_code = title_without_code[:-2]
25
+
20
26
  # Second part (if exists) contains the code block; split at closing ```
21
27
  code = parts[1].split("```", maxsplit=1)[0].strip() if len(parts) > 1 else ""
22
-
28
+
23
29
  # Append processed title
24
30
  processed_titles.append(title_without_code)
25
-
31
+
26
32
  # Append code block as Markdown if it exists
27
33
  if code:
28
- code_blocks.append(
29
- pn.pane.Markdown(f"```python\n{code}\n```")
30
- )
34
+ code_blocks.append(pn.pane.Markdown(f"```python\n{code}\n```"))
31
35
  else:
32
36
  code_blocks.append(None)
33
-
37
+
34
38
  return processed_titles, code_blocks
@@ -91,4 +91,5 @@ class TFQuestion(SelectQuestion):
91
91
  options=[["True", "False"] for _ in range(len(keys))],
92
92
  descriptions=descriptions,
93
93
  points=points,
94
+ shuffle_answers=False,
94
95
  )
@@ -4,7 +4,7 @@ from typing import Callable, Tuple
4
4
  import panel as pn
5
5
 
6
6
  from ..telemetry import ensure_responses, score_question, update_responses
7
- from ..utils import shuffle_questions
7
+ from ..utils import shuffle_questions, shuffle_options
8
8
  from ..widgets.style import drexel_colors, raw_css
9
9
 
10
10
  # Pass the custom CSS to Panel
@@ -56,6 +56,9 @@ class MultiSelectQuestion:
56
56
 
57
57
  self.initial_vals = [getattr(self, key) for key in self.keys]
58
58
 
59
+ # add shuffle options to multi_select.py
60
+ options = shuffle_options(options, seed)
61
+
59
62
  description_widgets, self.widgets = style(
60
63
  descriptions, options, self.initial_vals
61
64
  )
@@ -69,12 +72,14 @@ class MultiSelectQuestion:
69
72
  question_header = pn.pane.HTML(
70
73
  f"<h2>Question {self.question_number}: {title}</h2>"
71
74
  )
75
+
72
76
  question_body = pn.Column(
73
77
  *[
74
78
  pn.Row(desc_widget, checkbox_set)
75
79
  for desc_widget, checkbox_set in widget_pairs
76
80
  ]
77
81
  )
82
+
78
83
 
79
84
  self.layout = pn.Column(question_header, question_body, self.submit_button)
80
85
 
@@ -4,7 +4,7 @@ from typing import Callable, Tuple
4
4
  import panel as pn
5
5
 
6
6
  from ..telemetry import ensure_responses, score_question, update_responses
7
- from ..utils import shuffle_questions
7
+ from ..utils import shuffle_questions, shuffle_options
8
8
  from ..widgets.style import drexel_colors
9
9
 
10
10
  # Pass custom CSS to Panel
@@ -24,6 +24,7 @@ class SelectQuestion:
24
24
  options: list,
25
25
  descriptions: list[str],
26
26
  points: int,
27
+ shuffle_answers: bool = True,
27
28
  ):
28
29
  responses = ensure_responses()
29
30
 
@@ -45,6 +46,9 @@ class SelectQuestion:
45
46
 
46
47
  self.initial_vals: list = [getattr(self, key) for key in self.keys]
47
48
 
49
+ if shuffle_answers:
50
+ options = shuffle_options(options, seed)
51
+
48
52
  desc_widgets, self.widgets = style(descriptions, options, self.initial_vals)
49
53
 
50
54
  self.submit_button = pn.widgets.Button(name="Submit", button_type="primary")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes