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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: PyKubeGrader
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Add a short description here!
5
5
  Home-page: https://github.com/pyscaffold/pyscaffold/
6
6
  Author: jagar2
@@ -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=WTdPFsV1PRJ9U_730ckcWezQfMEL2oCH4ZDoflmr30M,83138
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=l7YQ8QT5k71j36KC1f5LmKIAX2bXpvMDGc6nqIJ1PeQ,4116
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.0.dist-info/LICENSE.txt,sha256=YTp-Ewc8Kems8PJEE27KnBPFnZSxoWvSg7nnknzPyYw,1546
33
- PyKubeGrader-0.3.0.dist-info/METADATA,sha256=W0PXYFxnn7vuAKI6_E7gMHfYnoftvYbYNorwwJwSfP0,2806
34
- PyKubeGrader-0.3.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
35
- PyKubeGrader-0.3.0.dist-info/entry_points.txt,sha256=BbLXpFZObpOXA8e3p3GcFkL-sHdUnDLUcnYmc6zx3NI,201
36
- PyKubeGrader-0.3.0.dist-info/top_level.txt,sha256=e550Klfze6higFxER1V62fnGOcIgiKRbsrl9CC4UdtQ,13
37
- PyKubeGrader-0.3.0.dist-info/RECORD,,
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
- r"^###\s*\*\*(.+)\*\*", markdown_content, re.MULTILINE
1358
- )
1359
- question_text = (
1360
- question_text_match.group(1).strip()
1361
- if question_text_match
1362
- else None
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
- desc_width = "500px"
30
-
31
- # Create description widget with separator
32
- desc_widget = pn.pane.HTML(
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>{question}</b></div>"
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 = [