PyKubeGrader 0.3.0__py3-none-any.whl → 0.3.1__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.3.0.dist-info → PyKubeGrader-0.3.1.dist-info}/METADATA +1 -1
- {PyKubeGrader-0.3.0.dist-info → PyKubeGrader-0.3.1.dist-info}/RECORD +8 -8
- pykubegrader/build/build_folder.py +78 -9
- pykubegrader/widgets/select_many.py +16 -5
- {PyKubeGrader-0.3.0.dist-info → PyKubeGrader-0.3.1.dist-info}/LICENSE.txt +0 -0
- {PyKubeGrader-0.3.0.dist-info → PyKubeGrader-0.3.1.dist-info}/WHEEL +0 -0
- {PyKubeGrader-0.3.0.dist-info → PyKubeGrader-0.3.1.dist-info}/entry_points.txt +0 -0
- {PyKubeGrader-0.3.0.dist-info → PyKubeGrader-0.3.1.dist-info}/top_level.txt +0 -0
@@ -5,7 +5,7 @@ pykubegrader/utils.py,sha256=FrxuZ3gtTBTm5FQeH5c0bF9kFjA_AVtE5AYeFhzwKZ0,827
|
|
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
|
-
pykubegrader/build/build_folder.py,sha256=
|
8
|
+
pykubegrader/build/build_folder.py,sha256=Ltd2skxEP1OdZA_D3Kb-WyTpDVtv4e35v6_XiMFZ9Vo,85378
|
9
9
|
pykubegrader/build/clean_folder.py,sha256=8N0KyL4eXRs0DCw-V_2jR9igtFs_mOFMQufdL6tD-38,1323
|
10
10
|
pykubegrader/build/markdown_questions.py,sha256=cSh8mkHK3hh-etJdgrZu9UQi1WPrKQtofkzLCUp1Z-w,4676
|
11
11
|
pykubegrader/graders/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
@@ -20,7 +20,7 @@ pykubegrader/widgets/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrK
|
|
20
20
|
pykubegrader/widgets/multiple_choice.py,sha256=CE7y6DPIhm4UuD8I1nwWPF2l9sKtKvYhbyPpeZ1qmQc,2686
|
21
21
|
pykubegrader/widgets/question_processor.py,sha256=59R9oBiemuVJP0qzsR1kY8MeqDq4Kh99AYRq9RGujsg,1223
|
22
22
|
pykubegrader/widgets/reading_question.py,sha256=y30_swHwzH8LrT8deWTnxctAAmR8BSxTlXAqMgUrAT4,3031
|
23
|
-
pykubegrader/widgets/select_many.py,sha256=
|
23
|
+
pykubegrader/widgets/select_many.py,sha256=wyRPCEdI6GorPX3HzSbDiT_IHr34mFOUHzUN085-d88,4621
|
24
24
|
pykubegrader/widgets/student_info.py,sha256=xhQgKehk1r5e6N_hnjAIovLdPvQju6ZqQTOiPG0aevg,3568
|
25
25
|
pykubegrader/widgets/style.py,sha256=fVBMYy_a6Yoz21avNpiORWC3f5FD-OrVpaZ3npmunvs,1656
|
26
26
|
pykubegrader/widgets/true_false.py,sha256=QllIhHuJstJft_RuShkxI_fFFTaDAlzNZOFNs00HLIM,2842
|
@@ -29,9 +29,9 @@ pykubegrader/widgets_base/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-
|
|
29
29
|
pykubegrader/widgets_base/multi_select.py,sha256=Btr09qjl2g2-wtKEtI3RYwo1Xm1dfFnHnDzw_1Yfqf4,4148
|
30
30
|
pykubegrader/widgets_base/reading.py,sha256=xmvN1UIXwk32v9S-JhsXwDc7axPlgpvoxSeM3II8sxY,5393
|
31
31
|
pykubegrader/widgets_base/select.py,sha256=RgSieRtBapAwXLd_ByJtT1L1EeUSi-9pFTuIm7zwDVE,2649
|
32
|
-
PyKubeGrader-0.3.
|
33
|
-
PyKubeGrader-0.3.
|
34
|
-
PyKubeGrader-0.3.
|
35
|
-
PyKubeGrader-0.3.
|
36
|
-
PyKubeGrader-0.3.
|
37
|
-
PyKubeGrader-0.3.
|
32
|
+
PyKubeGrader-0.3.1.dist-info/LICENSE.txt,sha256=YTp-Ewc8Kems8PJEE27KnBPFnZSxoWvSg7nnknzPyYw,1546
|
33
|
+
PyKubeGrader-0.3.1.dist-info/METADATA,sha256=fqlGOEpvYY7AyGQV7ngPA01-D00cnsja365LanEQoFE,2806
|
34
|
+
PyKubeGrader-0.3.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
35
|
+
PyKubeGrader-0.3.1.dist-info/entry_points.txt,sha256=BbLXpFZObpOXA8e3p3GcFkL-sHdUnDLUcnYmc6zx3NI,201
|
36
|
+
PyKubeGrader-0.3.1.dist-info/top_level.txt,sha256=e550Klfze6higFxER1V62fnGOcIgiKRbsrl9CC4UdtQ,13
|
37
|
+
PyKubeGrader-0.3.1.dist-info/RECORD,,
|
@@ -616,6 +616,21 @@ class NotebookProcessor:
|
|
616
616
|
shutil.copy("./keys/.client_private_key.bin", client_private_key)
|
617
617
|
shutil.copy("./keys/.server_public_key.bin", server_public_key)
|
618
618
|
|
619
|
+
# Extract the assignment config
|
620
|
+
config = extract_config_from_notebook(temp_notebook_path)
|
621
|
+
|
622
|
+
files = extract_files(config)
|
623
|
+
|
624
|
+
# print(f"Files: {files}, from {temp_notebook_path}")
|
625
|
+
|
626
|
+
if files:
|
627
|
+
for file in files:
|
628
|
+
print(f"Copying {file} to {os.path.join(notebook_subfolder, file)}")
|
629
|
+
shutil.copy(
|
630
|
+
os.path.join(self.root_folder, file),
|
631
|
+
os.path.join(notebook_subfolder, file),
|
632
|
+
)
|
633
|
+
|
619
634
|
client_private_key = os.path.join(
|
620
635
|
notebook_subfolder,
|
621
636
|
".client_private_key.bin",
|
@@ -1352,15 +1367,18 @@ def extract_SELECT_MANY(ipynb_file):
|
|
1352
1367
|
1 # Increment subquestion number for each question
|
1353
1368
|
)
|
1354
1369
|
|
1355
|
-
# Extract question text (### heading)
|
1356
|
-
question_text_match = re.search(
|
1357
|
-
|
1358
|
-
)
|
1359
|
-
question_text = (
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
)
|
1370
|
+
# # Extract question text (### heading)
|
1371
|
+
# question_text_match = re.search(
|
1372
|
+
# r"^###\s*\*\*(.+)\*\*", markdown_content, re.MULTILINE
|
1373
|
+
# )
|
1374
|
+
# question_text = (
|
1375
|
+
# question_text_match.group(1).strip()
|
1376
|
+
# if question_text_match
|
1377
|
+
# else None
|
1378
|
+
# )
|
1379
|
+
|
1380
|
+
# Extract question text enable multiple lines
|
1381
|
+
question_text = extract_question(markdown_content)
|
1364
1382
|
|
1365
1383
|
# Extract OPTIONS (lines after #### options)
|
1366
1384
|
options_match = re.search(
|
@@ -2126,6 +2144,57 @@ def update_initialize_assignment(
|
|
2126
2144
|
print(f"No matching lines found in '{notebook_path}'.")
|
2127
2145
|
|
2128
2146
|
|
2147
|
+
def extract_config_from_notebook(notebook_path):
|
2148
|
+
"""
|
2149
|
+
Extract configuration text from a Jupyter Notebook.
|
2150
|
+
|
2151
|
+
Parameters:
|
2152
|
+
notebook_path (str): Path to the Jupyter Notebook file.
|
2153
|
+
|
2154
|
+
Returns:
|
2155
|
+
str: The configuration text if found, otherwise an empty string.
|
2156
|
+
"""
|
2157
|
+
with open(notebook_path, "r", encoding="utf-8") as f:
|
2158
|
+
notebook_data = json.load(f)
|
2159
|
+
|
2160
|
+
# Iterate through cells to find the configuration text
|
2161
|
+
config_text = ""
|
2162
|
+
for cell in notebook_data.get("cells", []):
|
2163
|
+
if cell.get("cell_type") == "raw": # Check for code cells
|
2164
|
+
source = "".join(cell.get("source", []))
|
2165
|
+
if "# ASSIGNMENT CONFIG" in source:
|
2166
|
+
config_text = source
|
2167
|
+
break
|
2168
|
+
|
2169
|
+
return config_text
|
2170
|
+
|
2171
|
+
|
2172
|
+
def extract_files(config_text):
|
2173
|
+
"""
|
2174
|
+
Extract the list of files from the given configuration text, excluding .bin files.
|
2175
|
+
|
2176
|
+
Parameters:
|
2177
|
+
config_text (str): The configuration text to process.
|
2178
|
+
|
2179
|
+
Returns:
|
2180
|
+
list: A list of file names excluding .bin files.
|
2181
|
+
"""
|
2182
|
+
# Regular expression to extract files list
|
2183
|
+
file_pattern = re.search(r"files:\s*\[(.*?)\]", config_text, re.DOTALL)
|
2184
|
+
|
2185
|
+
if file_pattern:
|
2186
|
+
files = file_pattern.group(1)
|
2187
|
+
# Split the list into individual file names and exclude .bin files
|
2188
|
+
file_list = [
|
2189
|
+
file.strip()
|
2190
|
+
for file in files.split(",")
|
2191
|
+
if not file.strip().endswith(".bin")
|
2192
|
+
]
|
2193
|
+
return file_list
|
2194
|
+
else:
|
2195
|
+
return []
|
2196
|
+
|
2197
|
+
|
2129
2198
|
def main():
|
2130
2199
|
parser = argparse.ArgumentParser(
|
2131
2200
|
description="Recursively process Jupyter notebooks with '# ASSIGNMENT CONFIG', move them to a solutions folder, and run otter assign."
|
@@ -2,6 +2,7 @@ import panel as pn
|
|
2
2
|
|
3
3
|
from ..widgets.style import drexel_colors, raw_css
|
4
4
|
from ..widgets_base.multi_select import MultiSelectQuestion
|
5
|
+
from .question_processor import process_questions_and_codes
|
5
6
|
|
6
7
|
# Pass the custom CSS to Panel
|
7
8
|
pn.extension(design="material", global_css=[drexel_colors], raw_css=[raw_css])
|
@@ -24,15 +25,25 @@ def MultiSelect(
|
|
24
25
|
separator = pn.pane.HTML("<hr style='border:1px solid lightgray; width:100%;'>")
|
25
26
|
|
26
27
|
i = 0
|
28
|
+
desc_width = "500px"
|
27
29
|
|
28
30
|
for question, option_set in zip(descriptions, options):
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
# Process descriptions through `process_questions_and_codes`
|
32
|
+
processed_titles, code_blocks = process_questions_and_codes(question)
|
33
|
+
|
34
|
+
# Create an HTML pane for the title
|
35
|
+
title_pane = pn.pane.HTML(
|
33
36
|
f"<hr style='border:1px solid lightgray; width:100%;'>"
|
34
|
-
f"<div style='text-align: left; width: {desc_width};'><b>{
|
37
|
+
f"<div style='text-align: left; width: {desc_width};'><b>{processed_titles[0]}</b></div>"
|
35
38
|
)
|
39
|
+
# Add the title and code block in a row
|
40
|
+
if code_blocks[0]:
|
41
|
+
desc_widget = pn.Column(title_pane, code_blocks[0], sizing_mode="stretch_width")
|
42
|
+
else:
|
43
|
+
desc_widget = title_pane
|
44
|
+
|
45
|
+
# # Create description widget with separator
|
46
|
+
|
36
47
|
|
37
48
|
# Create checkboxes for current question
|
38
49
|
checkbox_set = [
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|