PyKubeGrader 0.1.4__tar.gz → 0.1.6__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. {pykubegrader-0.1.4/src/PyKubeGrader.egg-info → pykubegrader-0.1.6}/PKG-INFO +2 -1
  2. pykubegrader-0.1.6/examples/.responses.json +1 -0
  3. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/examples/true_false.ipynb +1 -6
  4. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/setup.cfg +1 -0
  5. {pykubegrader-0.1.4 → pykubegrader-0.1.6/src/PyKubeGrader.egg-info}/PKG-INFO +2 -1
  6. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/PyKubeGrader.egg-info/requires.txt +1 -0
  7. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/build/build_folder.py +31 -70
  8. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/telemetry.py +2 -2
  9. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/validate.py +2 -2
  10. pykubegrader-0.1.6/src/pykubegrader/widgets/__init__.py +19 -0
  11. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/widgets/style.py +0 -2
  12. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/widgets/true_false.py +3 -10
  13. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/widgets_base/select.py +2 -3
  14. pykubegrader-0.1.4/examples/.responses.json +0 -1
  15. pykubegrader-0.1.4/src/pykubegrader/widgets/__init__.py +0 -10
  16. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/.coveragerc +0 -0
  17. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/.github/workflows/main.yml +0 -0
  18. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/.gitignore +0 -0
  19. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/.readthedocs.yml +0 -0
  20. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/AUTHORS.rst +0 -0
  21. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/CHANGELOG.rst +0 -0
  22. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/CONTRIBUTING.rst +0 -0
  23. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/LICENSE.txt +0 -0
  24. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/README.rst +0 -0
  25. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/docs/Makefile +0 -0
  26. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/docs/_static/Drexel_blue_Logo_square_Dark.png +0 -0
  27. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/docs/_static/Drexel_blue_Logo_square_Light.png +0 -0
  28. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/docs/_static/custom.css +0 -0
  29. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/docs/authors.rst +0 -0
  30. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/docs/changelog.rst +0 -0
  31. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/docs/conf.py +0 -0
  32. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/docs/contributing.rst +0 -0
  33. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/docs/index.rst +0 -0
  34. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/docs/license.rst +0 -0
  35. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/docs/readme.rst +0 -0
  36. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/docs/requirements.txt +0 -0
  37. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/pyproject.toml +0 -0
  38. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/setup.py +0 -0
  39. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/PyKubeGrader.egg-info/SOURCES.txt +0 -0
  40. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/PyKubeGrader.egg-info/dependency_links.txt +0 -0
  41. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/PyKubeGrader.egg-info/entry_points.txt +0 -0
  42. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/PyKubeGrader.egg-info/not-zip-safe +0 -0
  43. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/PyKubeGrader.egg-info/top_level.txt +0 -0
  44. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/__init__.py +0 -0
  45. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/initialize.py +0 -0
  46. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/utils.py +0 -0
  47. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/widgets/multiple_choice.py +0 -0
  48. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/widgets/reading_question.py +0 -0
  49. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/widgets/select_many.py +1 -1
  50. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/widgets/student_info.py +0 -0
  51. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/widgets/types_question.py +0 -0
  52. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/widgets_base/__init__.py +0 -0
  53. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/widgets_base/multi_select.py +1 -1
  54. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/src/pykubegrader/widgets_base/reading.py +0 -0
  55. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/tests/conftest.py +0 -0
  56. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/tests/import_test.py +0 -0
  57. {pykubegrader-0.1.4 → pykubegrader-0.1.6}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyKubeGrader
3
- Version: 0.1.4
3
+ Version: 0.1.6
4
4
  Summary: Add a short description here!
5
5
  Home-page: https://github.com/pyscaffold/pyscaffold/
6
6
  Author: jagar2
@@ -15,6 +15,7 @@ License-File: LICENSE.txt
15
15
  Requires-Dist: importlib-metadata; python_version < "3.8"
16
16
  Requires-Dist: ipython
17
17
  Requires-Dist: mypy
18
+ Requires-Dist: nbformat
18
19
  Requires-Dist: numpy
19
20
  Requires-Dist: panel
20
21
  Requires-Dist: pynacl
@@ -0,0 +1 @@
1
+ { "seed": 30, "MC1": "True", "MC2": "True", "MC3": "False", "MC4": "False" }
@@ -16,7 +16,6 @@
16
16
  "outputs": [],
17
17
  "source": [
18
18
  "class Question1(TFQuestion):\n",
19
- "\n",
20
19
  " def __init__(\n",
21
20
  " self,\n",
22
21
  " title=\"Respond with True or False\",\n",
@@ -56,11 +55,7 @@
56
55
  "execution_count": null,
57
56
  "metadata": {},
58
57
  "outputs": [],
59
- "source": [
60
- "import time\n",
61
- "from IPython.display import display, update_display\n",
62
- "\n"
63
- ]
58
+ "source": []
64
59
  },
65
60
  {
66
61
  "cell_type": "code",
@@ -25,6 +25,7 @@ install_requires =
25
25
  importlib-metadata; python_version<"3.8"
26
26
  ipython
27
27
  mypy
28
+ nbformat
28
29
  numpy
29
30
  panel
30
31
  pynacl
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyKubeGrader
3
- Version: 0.1.4
3
+ Version: 0.1.6
4
4
  Summary: Add a short description here!
5
5
  Home-page: https://github.com/pyscaffold/pyscaffold/
6
6
  Author: jagar2
@@ -15,6 +15,7 @@ License-File: LICENSE.txt
15
15
  Requires-Dist: importlib-metadata; python_version < "3.8"
16
16
  Requires-Dist: ipython
17
17
  Requires-Dist: mypy
18
+ Requires-Dist: nbformat
18
19
  Requires-Dist: numpy
19
20
  Requires-Dist: panel
20
21
  Requires-Dist: pynacl
@@ -1,5 +1,6 @@
1
1
  ipython
2
2
  mypy
3
+ nbformat
3
4
  numpy
4
5
  panel
5
6
  pynacl
@@ -1,14 +1,15 @@
1
- from dataclasses import dataclass, field
1
+ import argparse
2
+ import importlib.util
3
+ import json
4
+ import logging
2
5
  import os
6
+ import re
3
7
  import shutil
4
- import nbformat
5
8
  import subprocess
6
9
  import sys
7
- import argparse
8
- import logging
9
- import json
10
- import re
11
- import importlib.util
10
+ from dataclasses import dataclass, field
11
+
12
+ import nbformat
12
13
 
13
14
 
14
15
  @dataclass
@@ -182,11 +183,11 @@ class NotebookProcessor:
182
183
  shutil.copy(notebook_path, temp_notebook_path)
183
184
 
184
185
  # Determine the path to the autograder folder
185
- autograder_path = os.path.join(notebook_subfolder, f"dist/autograder/")
186
+ autograder_path = os.path.join(notebook_subfolder, "dist/autograder/")
186
187
  os.makedirs(autograder_path, exist_ok=True)
187
188
 
188
189
  # Determine the path to the student folder
189
- student_path = os.path.join(notebook_subfolder, f"dist/student/")
190
+ student_path = os.path.join(notebook_subfolder, "dist/student/")
190
191
  os.makedirs(student_path, exist_ok=True)
191
192
 
192
193
  if os.path.abspath(notebook_path) != os.path.abspath(new_notebook_path):
@@ -230,7 +231,6 @@ class NotebookProcessor:
230
231
 
231
232
  ### Parse the notebook for TF questions
232
233
  if self.has_assignment(temp_notebook_path, "# BEGIN TF"):
233
-
234
234
  markers = ("# BEGIN TF", "# END TF")
235
235
 
236
236
  self._print_and_log(
@@ -262,7 +262,6 @@ class NotebookProcessor:
262
262
 
263
263
  ### Parse the notebook for select_many questions
264
264
  if self.has_assignment(temp_notebook_path, "# BEGIN SELECT MANY"):
265
-
266
265
  markers = ("# BEGIN SELECT MANY", "# END SELECT MANY")
267
266
 
268
267
  self._print_and_log(
@@ -347,23 +346,31 @@ class NotebookProcessor:
347
346
 
348
347
  ### CODE TO ENSURE THAT STUDENT NOTEBOOK IS IMPORTABLE
349
348
  if "question_path" in locals():
350
-
351
349
  # question_root_path = os.path.dirname(question_path)
352
350
  question_file_name = os.path.basename(question_path)
353
- question_file_name_sanitized = sanitize_string(question_file_name.replace("_questions", ""))
351
+ question_file_name_sanitized = sanitize_string(
352
+ question_file_name.replace("_questions", "")
353
+ )
354
354
  if question_file_name_sanitized.endswith("_py"):
355
355
  question_file_name_sanitized = question_file_name_sanitized[:-3] + ".py"
356
-
356
+
357
357
  # Rename the file
358
- os.rename(os.path.join(student_path, question_file_name.replace("_questions", "")), os.path.join(student_path, question_file_name_sanitized))
359
-
358
+ os.rename(
359
+ os.path.join(
360
+ student_path, question_file_name.replace("_questions", "")
361
+ ),
362
+ os.path.join(student_path, question_file_name_sanitized),
363
+ )
364
+
360
365
  # Ensure the "questions" folder exists
361
366
  questions_folder_jbook = os.path.join(self.root_folder, "questions")
362
367
  os.makedirs(questions_folder_jbook, exist_ok=True)
363
-
364
- # Copy the renamed file to the "questions" folder
365
- shutil.copy(os.path.join(student_path, question_file_name_sanitized), os.path.join(questions_folder_jbook, question_file_name_sanitized))
366
368
 
369
+ # Copy the renamed file to the "questions" folder
370
+ shutil.copy(
371
+ os.path.join(student_path, question_file_name_sanitized),
372
+ os.path.join(questions_folder_jbook, question_file_name_sanitized),
373
+ )
367
374
 
368
375
  @staticmethod
369
376
  def replace_temp_in_notebook(input_file, output_file):
@@ -434,7 +441,7 @@ class NotebookProcessor:
434
441
  # {"Q2": {"question_text": "What is 3+3?", "points": 3.0}}
435
442
  # ]
436
443
  """
437
- merged_data = []
444
+ # merged_data = []
438
445
 
439
446
  # Loop through each question set in the data
440
447
  for i, _data in enumerate(data):
@@ -454,7 +461,7 @@ class NotebookProcessor:
454
461
  grade_ = [raw[i]["grade"]]
455
462
 
456
463
  # Merge each question's metadata with corresponding raw metadata
457
- for j, (key, value) in enumerate(_data.items()):
464
+ for j, (key, _) in enumerate(_data.items()):
458
465
  # Combine raw metadata with question data
459
466
  data[i][key] = data[i][key] | raw[i]
460
467
  # Assign the correct point value to the question
@@ -576,52 +583,6 @@ class NotebookProcessor:
576
583
  f.write(f' "{key}": {repr(solution)},\n')
577
584
  f.write("}\n")
578
585
 
579
- @staticmethod
580
- def generate_solution_MCQ(data_list, output_file="output.py"):
581
- """
582
- Generates a Python file with solutions and total points based on the input data.
583
- If the file already exists, it appends new solutions to the existing solution dictionary.
584
-
585
- Args:
586
- data_list (list): A list of dictionaries containing question metadata.
587
- output_file (str): Path to the output Python file.
588
- """
589
-
590
- solutions = {}
591
- total_points = 0.0
592
-
593
- # If the output file exists, load the existing solutions and total_points
594
- if os.path.exists(output_file):
595
- spec = importlib.util.spec_from_file_location(
596
- "existing_module", output_file
597
- )
598
- existing_module = importlib.util.module_from_spec(spec)
599
- spec.loader.exec_module(existing_module) # Load the module dynamically
600
-
601
- # Attempt to read existing solutions and total_points
602
- if hasattr(existing_module, "solutions"):
603
- solutions.update(existing_module.solutions)
604
- if hasattr(existing_module, "total_points"):
605
- total_points += existing_module.total_points
606
-
607
- # Process new question data and update solutions and total_points
608
- for question_set in data_list:
609
- for key, question_data in question_set.items():
610
- solution_key = f"q{question_data['question number']}-{question_data['subquestion_number']}-{key}"
611
- solutions[solution_key] = question_data["solution"]
612
- total_points += question_data["points"]
613
-
614
- # Write updated total_points and solutions back to the file
615
- with open(output_file, "w", encoding="utf-8") as f:
616
- f.write("from typing import Any\n\n")
617
- f.write(f"total_points: float = {total_points}\n\n")
618
-
619
- f.write("solutions: dict[str, Any] = {\n")
620
- for key, solution in solutions.items():
621
- # For safety, we assume solutions are strings, but if not, repr would be safer
622
- f.write(f' "{key}": {repr(solution)},\n')
623
- f.write("}\n")
624
-
625
586
  def extract_MCQ(ipynb_file):
626
587
  """
627
588
  Extracts questions from markdown cells and organizes them as a nested dictionary,
@@ -1314,7 +1275,7 @@ def generate_mcq_file(data_dict, output_file="mc_questions.py"):
1314
1275
  ]
1315
1276
 
1316
1277
  # Ensure header lines are present
1317
- existing_content = ensure_imports(output_file, header_lines)
1278
+ _existing_content = ensure_imports(output_file, header_lines)
1318
1279
 
1319
1280
  for question_dict in data_dict:
1320
1281
  with open(output_file, "a", encoding="utf-8") as f:
@@ -1383,7 +1344,7 @@ def generate_select_many_file(data_dict, output_file="select_many_questions.py")
1383
1344
  ]
1384
1345
 
1385
1346
  # Ensure header lines are present
1386
- existing_content = ensure_imports(output_file, header_lines)
1347
+ _existing_content = ensure_imports(output_file, header_lines)
1387
1348
 
1388
1349
  for question_dict in data_dict:
1389
1350
  with open(output_file, "a", encoding="utf-8") as f:
@@ -1458,7 +1419,7 @@ def generate_tf_file(data_dict, output_file="tf_questions.py"):
1458
1419
  ]
1459
1420
 
1460
1421
  # Ensure header lines are present
1461
- existing_content = ensure_imports(output_file, header_lines)
1422
+ _existing_content = ensure_imports(output_file, header_lines)
1462
1423
 
1463
1424
  for question_dict in data_dict:
1464
1425
  with open(output_file, "a", encoding="utf-8") as f:
@@ -100,7 +100,7 @@ def score_question(
100
100
  assignment: str,
101
101
  question: str,
102
102
  submission: str,
103
- base_url: str = "http://localhost:8000",
103
+ base_url: str = "https://engr-131-api.t6e.dev/",
104
104
  ) -> Response:
105
105
  url = base_url + "/live-scorer"
106
106
 
@@ -124,7 +124,7 @@ def submit_question_new(
124
124
  question: str,
125
125
  responses: dict,
126
126
  score: dict,
127
- base_url: str = "http://localhost:8000",
127
+ base_url: str = "https://engr-131-api.t6e.dev/",
128
128
  ):
129
129
  url = base_url + "/submit-question"
130
130
 
@@ -18,8 +18,8 @@ def validate_logfile(
18
18
  free_response_questions=0,
19
19
  username="student",
20
20
  password="capture",
21
- post_url="http://localhost:8000/upload-score",
22
- login_url="http://localhost:8000/login",
21
+ post_url="https://engr-131-api.t6e.dev/upload-score",
22
+ login_url="https://engr-131-api.t6e.dev/login",
23
23
  ) -> None:
24
24
  login_data = {
25
25
  "username": username,
@@ -0,0 +1,19 @@
1
+ # Auto-generated __init__.py
2
+
3
+ from . import (
4
+ multiple_choice,
5
+ reading_question,
6
+ select_many,
7
+ student_info,
8
+ true_false,
9
+ types_question,
10
+ )
11
+
12
+ __all__ = [
13
+ "select_many",
14
+ "multiple_choice",
15
+ "true_false",
16
+ "reading_question",
17
+ "student_info",
18
+ "types_question",
19
+ ]
@@ -1,5 +1,3 @@
1
- import panel as pn
2
-
3
1
  # Extend the Material Design with custom Drexel colors
4
2
  drexel_colors = """
5
3
  :root {
@@ -1,13 +1,10 @@
1
- from typing import Tuple
1
+ from typing import List, Tuple
2
2
 
3
3
  import panel as pn
4
4
 
5
- from ..utils import list_of_lists
6
5
  from ..widgets_base.select import SelectQuestion
7
6
  from .style import drexel_colors, raw_css
8
7
 
9
- import panel as pn
10
-
11
8
  # Pass the custom CSS to Panel
12
9
  pn.extension(design="material", global_css=[drexel_colors], raw_css=[raw_css])
13
10
 
@@ -16,10 +13,6 @@ pn.extension(design="material", global_css=[drexel_colors], raw_css=[raw_css])
16
13
  #
17
14
 
18
15
 
19
- import panel as pn
20
- from typing import List, Tuple
21
-
22
-
23
16
  def TrueFalse_style(
24
17
  descriptions: List[str],
25
18
  options: List[str] | List[List[str]],
@@ -37,7 +30,7 @@ def TrueFalse_style(
37
30
  Tuple[List[pn.pane.HTML], List[pn.widgets.RadioBoxGroup]]: Styled description panes and radio button groups.
38
31
  """
39
32
  desc_width = "100%" # Responsive width for descriptions
40
- button_width = "100%" # Responsive width for radio buttons
33
+ # button_width = "100%" # Responsive width for radio buttons
41
34
 
42
35
  # Create description widgets
43
36
  desc_widgets = [
@@ -73,8 +66,8 @@ def TrueFalse_style(
73
66
  # Question class
74
67
  #
75
68
 
76
- class TFQuestion(SelectQuestion):
77
69
 
70
+ class TFQuestion(SelectQuestion):
78
71
  def __init__(
79
72
  self,
80
73
  title="Select if the statement is True or False",
@@ -1,3 +1,4 @@
1
+ import time
1
2
  from typing import Callable, Tuple
2
3
 
3
4
  import panel as pn
@@ -5,8 +6,6 @@ import panel as pn
5
6
  from ..telemetry import ensure_responses, update_responses
6
7
  from ..utils import shuffle_questions
7
8
  from ..widgets.style import drexel_colors
8
- import time
9
- from IPython.display import update_display, display
10
9
 
11
10
  # Pass the custom CSS to Panel
12
11
  pn.extension(design="material", global_css=[drexel_colors])
@@ -75,7 +74,7 @@ class SelectQuestion:
75
74
  self.submit_button.name = "Responses Submitted"
76
75
  time.sleep(1)
77
76
  self.submit_button.name = "Submit"
78
-
77
+
79
78
  # # Display the message with a unique display_id
80
79
  # display_id = "temp_message"
81
80
  # display("Responses recorded successfully", display_id=display_id)
@@ -1 +0,0 @@
1
- {"seed": 30, "MC1": "True", "MC2": "True", "MC3": "False", "MC4": "False"}
@@ -1,10 +0,0 @@
1
- # Auto-generated __init__.py
2
-
3
- from . import select_many
4
- from . import multiple_choice
5
- from . import true_false
6
- from . import reading_question
7
- from . import student_info
8
- from . import types_question
9
-
10
- __all__ = ['select_many', 'multiple_choice', 'true_false', 'reading_question', 'student_info', 'types_question']
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,7 +1,7 @@
1
1
  import panel as pn
2
2
 
3
- from ..widgets_base.multi_select import MultiSelectQuestion
4
3
  from ..widgets.style import drexel_colors, raw_css
4
+ from ..widgets_base.multi_select import MultiSelectQuestion
5
5
 
6
6
  # Pass the custom CSS to Panel
7
7
  pn.extension(design="material", global_css=[drexel_colors], raw_css=[raw_css])
@@ -1,7 +1,7 @@
1
+ import time
1
2
  from typing import Callable, Tuple
2
3
 
3
4
  import panel as pn
4
- import time
5
5
 
6
6
  from ..telemetry import ensure_responses, update_responses
7
7
  from ..utils import shuffle_questions
File without changes