PyKubeGrader 0.2.38__py3-none-any.whl → 0.2.40__py3-none-any.whl

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.
@@ -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,13 +1,13 @@
1
1
  pykubegrader/__init__.py,sha256=AoAkdfIjDDZGWLlsIRENNq06L9h46kDGBIE8vRmsCfg,311
2
2
  pykubegrader/initialize.py,sha256=Bwu1q18l18FB9lGppvt-L41D5gzr3S8t6zC0_UbrASw,3994
3
3
  pykubegrader/telemetry.py,sha256=-XiWKMlwneS4zD8-hPcMVZFY4b6gK4jHKTwhjUBw8gw,6556
4
- pykubegrader/utils.py,sha256=FrxuZ3gtTBTm5FQeH5c0bF9kFjA_AVtE5AYeFhzwKZ0,827
4
+ pykubegrader/utils.py,sha256=eqQSf2xOAtqB9pWSf6RI-WwwdEowVe8yr2XIiNpc5Rc,870
5
5
  pykubegrader/validate.py,sha256=OKnItGyd-L8QPKcsE0KRuwBI_IxKiJzMLJKZiA2j3II,11184
6
6
  pykubegrader/build/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
7
7
  pykubegrader/build/api_notebook_builder.py,sha256=dlcVrGgsvxnt6GlAUN3e-FrpsPNJKXSHni1fstRCBik,20311
8
8
  pykubegrader/build/build_folder.py,sha256=WTdPFsV1PRJ9U_730ckcWezQfMEL2oCH4ZDoflmr30M,83138
9
9
  pykubegrader/build/clean_folder.py,sha256=dfs9NuZ-EP6q_xYQnZKH74aEafEB6hpTAZcSkY14UWI,1328
10
- pykubegrader/build/markdown_questions.py,sha256=GwU47GyXEXVqD9XDMRZ7VWesmY2kvWV5PLAw21Pgkhk,3118
10
+ pykubegrader/build/markdown_questions.py,sha256=OEEFoM5L4Ptax0dm8Tz-gM01akJEoDvsPNgixE1Vk1s,4675
11
11
  pykubegrader/graders/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
12
12
  pykubegrader/graders/late_assignments.py,sha256=_2-rA5RqO0BWY9WAQA_mbCxxPKTOiJOl-byD2CYWaE0,1393
13
13
  pykubegrader/log_parser/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
@@ -18,20 +18,20 @@ pykubegrader/tokens/tokens.py,sha256=X9f3SzrGCrAJp_BXhr6VJn5f0LxtgQ7HLPBw7zEF2BY
18
18
  pykubegrader/tokens/validate_token.py,sha256=MQtgz_USvSZ9JahJ48ybjp74F5aYz64lhtvuwVc4kQw,2712
19
19
  pykubegrader/widgets/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
20
20
  pykubegrader/widgets/multiple_choice.py,sha256=oH1NjL2cuQDhn-9Ds8yBNAWDHr2anGS__F8uel7IsiI,2657
21
- pykubegrader/widgets/question_processor.py,sha256=Kcm73lXyLNNqeiwmeBdPxdKDYTmAuX65RlqOZGo221Q,1031
21
+ pykubegrader/widgets/question_processor.py,sha256=iWlurO1IRHY94NXI8ToZcBolGkP9lj9C9qZdE8nuH_Q,1233
22
22
  pykubegrader/widgets/reading_question.py,sha256=y30_swHwzH8LrT8deWTnxctAAmR8BSxTlXAqMgUrAT4,3031
23
23
  pykubegrader/widgets/select_many.py,sha256=l7YQ8QT5k71j36KC1f5LmKIAX2bXpvMDGc6nqIJ1PeQ,4116
24
24
  pykubegrader/widgets/student_info.py,sha256=xhQgKehk1r5e6N_hnjAIovLdPvQju6ZqQTOiPG0aevg,3568
25
25
  pykubegrader/widgets/style.py,sha256=fVBMYy_a6Yoz21avNpiORWC3f5FD-OrVpaZ3npmunvs,1656
26
- pykubegrader/widgets/true_false.py,sha256=D45bjRLaAcNzsSlWPgxwTXGVZPE7PER34S30V6PjEXU,2807
26
+ pykubegrader/widgets/true_false.py,sha256=QllIhHuJstJft_RuShkxI_fFFTaDAlzNZOFNs00HLIM,2842
27
27
  pykubegrader/widgets/types_question.py,sha256=kZdRRXyFzOtYTmGdC7XWb_2oaxqg1WSuLcQn_sTj6Qc,2300
28
28
  pykubegrader/widgets_base/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
29
- pykubegrader/widgets_base/multi_select.py,sha256=Cl0IN21wXLZuFu-zC65aS9tD4jMfzCRJ2DPjHao5_Ak,4044
29
+ pykubegrader/widgets_base/multi_select.py,sha256=nSK_2hDCG67Nbf00nJfabcahNnNWt8W_4Pec8tg4xAM,4182
30
30
  pykubegrader/widgets_base/reading.py,sha256=xmvN1UIXwk32v9S-JhsXwDc7axPlgpvoxSeM3II8sxY,5393
31
- pykubegrader/widgets_base/select.py,sha256=Fw3uFNOIWo1a3CvlzSx23bvi6bSmA3TqutuRbhD4Dp8,2525
32
- PyKubeGrader-0.2.38.dist-info/LICENSE.txt,sha256=YTp-Ewc8Kems8PJEE27KnBPFnZSxoWvSg7nnknzPyYw,1546
33
- PyKubeGrader-0.2.38.dist-info/METADATA,sha256=EB80QMUnx_Rfwz8FAOSk1suNMIkeWnGvm0CdDV20_aM,2779
34
- PyKubeGrader-0.2.38.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
35
- PyKubeGrader-0.2.38.dist-info/entry_points.txt,sha256=BbLXpFZObpOXA8e3p3GcFkL-sHdUnDLUcnYmc6zx3NI,201
36
- PyKubeGrader-0.2.38.dist-info/top_level.txt,sha256=e550Klfze6higFxER1V62fnGOcIgiKRbsrl9CC4UdtQ,13
37
- PyKubeGrader-0.2.38.dist-info/RECORD,,
31
+ pykubegrader/widgets_base/select.py,sha256=PJhBRDpAYkFnmDzvm7jxiGW1E5VcEtZBe_wEtDKs-ZQ,2662
32
+ PyKubeGrader-0.2.40.dist-info/LICENSE.txt,sha256=YTp-Ewc8Kems8PJEE27KnBPFnZSxoWvSg7nnknzPyYw,1546
33
+ PyKubeGrader-0.2.40.dist-info/METADATA,sha256=pqjfN003R-98Q4nSu8gjaiLI0Vu8EIZI2SZrk5BZZ-k,2779
34
+ PyKubeGrader-0.2.40.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
35
+ PyKubeGrader-0.2.40.dist-info/entry_points.txt,sha256=BbLXpFZObpOXA8e3p3GcFkL-sHdUnDLUcnYmc6zx3NI,201
36
+ PyKubeGrader-0.2.40.dist-info/top_level.txt,sha256=e550Klfze6higFxER1V62fnGOcIgiKRbsrl9CC4UdtQ,13
37
+ PyKubeGrader-0.2.40.dist-info/RECORD,,
@@ -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(
pykubegrader/utils.py CHANGED
@@ -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")