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.
- {PyKubeGrader-0.2.38.dist-info → PyKubeGrader-0.2.40.dist-info}/METADATA +1 -1
- {PyKubeGrader-0.2.38.dist-info → PyKubeGrader-0.2.40.dist-info}/RECORD +12 -12
- pykubegrader/build/markdown_questions.py +47 -2
- pykubegrader/utils.py +3 -1
- pykubegrader/widgets/question_processor.py +14 -10
- pykubegrader/widgets/true_false.py +1 -0
- pykubegrader/widgets_base/multi_select.py +6 -1
- pykubegrader/widgets_base/select.py +5 -1
- {PyKubeGrader-0.2.38.dist-info → PyKubeGrader-0.2.40.dist-info}/LICENSE.txt +0 -0
- {PyKubeGrader-0.2.38.dist-info → PyKubeGrader-0.2.40.dist-info}/WHEEL +0 -0
- {PyKubeGrader-0.2.38.dist-info → PyKubeGrader-0.2.40.dist-info}/entry_points.txt +0 -0
- {PyKubeGrader-0.2.38.dist-info → PyKubeGrader-0.2.40.dist-info}/top_level.txt +0 -0
@@ -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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
32
|
-
PyKubeGrader-0.2.
|
33
|
-
PyKubeGrader-0.2.
|
34
|
-
PyKubeGrader-0.2.
|
35
|
-
PyKubeGrader-0.2.
|
36
|
-
PyKubeGrader-0.2.
|
37
|
-
PyKubeGrader-0.2.
|
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
|
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
|
-
|
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
|
-
|
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
|
@@ -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
|