PyKubeGrader 0.2.1__tar.gz → 0.2.3__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. {pykubegrader-0.2.1/src/PyKubeGrader.egg-info → pykubegrader-0.2.3}/PKG-INFO +1 -1
  2. {pykubegrader-0.2.1 → pykubegrader-0.2.3/src/PyKubeGrader.egg-info}/PKG-INFO +1 -1
  3. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/build/build_folder.py +25 -1
  4. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/initialize.py +9 -1
  5. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/log_parser/parse.ipynb +37 -0
  6. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/log_parser/parse.py +9 -0
  7. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/telemetry.py +19 -11
  8. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/.coveragerc +0 -0
  9. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/.github/workflows/main.yml +0 -0
  10. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/.gitignore +0 -0
  11. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/.readthedocs.yml +0 -0
  12. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/AUTHORS.rst +0 -0
  13. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/CHANGELOG.rst +0 -0
  14. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/CONTRIBUTING.rst +0 -0
  15. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/LICENSE.txt +0 -0
  16. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/README.rst +0 -0
  17. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/docs/Makefile +0 -0
  18. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/docs/_static/Drexel_blue_Logo_square_Dark.png +0 -0
  19. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/docs/_static/Drexel_blue_Logo_square_Light.png +0 -0
  20. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/docs/_static/custom.css +0 -0
  21. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/docs/authors.rst +0 -0
  22. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/docs/changelog.rst +0 -0
  23. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/docs/conf.py +0 -0
  24. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/docs/contributing.rst +0 -0
  25. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/docs/index.rst +0 -0
  26. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/docs/license.rst +0 -0
  27. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/docs/readme.rst +0 -0
  28. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/docs/requirements.txt +0 -0
  29. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/examples/.responses.json +0 -0
  30. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/examples/true_false.ipynb +0 -0
  31. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/pyproject.toml +0 -0
  32. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/setup.cfg +0 -0
  33. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/setup.py +0 -0
  34. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/PyKubeGrader.egg-info/SOURCES.txt +0 -0
  35. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/PyKubeGrader.egg-info/dependency_links.txt +0 -0
  36. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/PyKubeGrader.egg-info/entry_points.txt +0 -0
  37. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/PyKubeGrader.egg-info/not-zip-safe +0 -0
  38. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/PyKubeGrader.egg-info/requires.txt +0 -0
  39. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/PyKubeGrader.egg-info/top_level.txt +0 -0
  40. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/__init__.py +0 -0
  41. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/build/__init__.py +0 -0
  42. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/build/api_notebook_builder.py +0 -0
  43. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/build/clean_folder.py +0 -0
  44. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/graders/__init__.py +0 -0
  45. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/graders/late_assignments.py +0 -0
  46. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/log_parser/__init__.py +0 -0
  47. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/utils.py +0 -0
  48. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/validate.py +0 -0
  49. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/widgets/__init__.py +0 -0
  50. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/widgets/multiple_choice.py +0 -0
  51. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/widgets/reading_question.py +0 -0
  52. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/widgets/select_many.py +0 -0
  53. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/widgets/student_info.py +0 -0
  54. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/widgets/style.py +0 -0
  55. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/widgets/true_false.py +0 -0
  56. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/widgets/types_question.py +0 -0
  57. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/widgets_base/__init__.py +0 -0
  58. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/widgets_base/multi_select.py +0 -0
  59. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/widgets_base/reading.py +0 -0
  60. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/src/pykubegrader/widgets_base/select.py +0 -0
  61. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/tests/conftest.py +0 -0
  62. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/tests/import_test.py +0 -0
  63. {pykubegrader-0.2.1 → pykubegrader-0.2.3}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyKubeGrader
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Add a short description here!
5
5
  Home-page: https://github.com/pyscaffold/pyscaffold/
6
6
  Author: jagar2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyKubeGrader
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Add a short description here!
5
5
  Home-page: https://github.com/pyscaffold/pyscaffold/
6
6
  Author: jagar2
@@ -190,6 +190,8 @@ class NotebookProcessor:
190
190
  return {
191
191
  "title": title,
192
192
  "description": str(week),
193
+ "week_number": week,
194
+ "assignment_type": assignment_type,
193
195
  "due_date": due_date,
194
196
  "max_score": int(self.assignment_total_points),
195
197
  }
@@ -503,6 +505,9 @@ class NotebookProcessor:
503
505
  NotebookProcessor.add_initialization_code(
504
506
  temp_notebook_path, self.week, self.assignment_type
505
507
  )
508
+ NotebookProcessor.replace_temp_no_otter(
509
+ temp_notebook_path, temp_notebook_path
510
+ )
506
511
  return None, 0
507
512
 
508
513
  @staticmethod
@@ -650,6 +655,22 @@ class NotebookProcessor:
650
655
  return solution_path, question_path
651
656
  else:
652
657
  return None, None
658
+
659
+ @staticmethod
660
+ def replace_temp_no_otter(input_file, output_file):
661
+ # Load the notebook
662
+ with open(input_file, "r", encoding="utf-8") as f:
663
+ notebook = nbformat.read(f, as_version=4)
664
+
665
+ # Iterate through the cells and modify `cell.source`
666
+ for cell in notebook.cells:
667
+ if cell.cell_type == "code": # Only process code cells
668
+ if 'responses = initialize_assignment(' in cell.source:
669
+ cell.source = cell.source.replace('_temp', '')
670
+
671
+ # Save the modified notebook
672
+ with open(output_file, "w", encoding="utf-8") as f:
673
+ nbformat.write(notebook, f)
653
674
 
654
675
  @staticmethod
655
676
  def replace_temp_in_notebook(input_file, output_file):
@@ -674,6 +695,7 @@ class NotebookProcessor:
674
695
  cell["source"] = [
675
696
  line.replace("_temp.ipynb", ".ipynb") for line in cell["source"]
676
697
  ]
698
+
677
699
 
678
700
  # Write the updated notebook to the output file
679
701
  with open(output_file, "w", encoding="utf-8") as f:
@@ -1398,6 +1420,8 @@ def clean_notebook(notebook_path):
1398
1420
  cell.metadata["editable"] = cell.metadata.get("editable", False)
1399
1421
  cell.metadata["deletable"] = cell.metadata.get("deletable", False)
1400
1422
  if cell.cell_type == "code":
1423
+ if "grader.check(" in cell.source:
1424
+ continue
1401
1425
  cell.metadata["tags"] = cell.metadata.get("tags", [])
1402
1426
  if "skip-execution" not in cell.metadata["tags"]:
1403
1427
  cell.metadata["tags"].append("skip-execution")
@@ -1563,7 +1587,7 @@ def generate_mcq_file(data_dict, output_file="mc_questions.py"):
1563
1587
  )
1564
1588
  f.write(" def __init__(self):\n")
1565
1589
  f.write(" super().__init__(\n")
1566
- f.write(f" title=f'{q_value['question_text']}',\n")
1590
+ f.write(f' title=f"{q_value['question_text']}",\n')
1567
1591
  f.write(" style=MCQ,\n")
1568
1592
  f.write(
1569
1593
  f" question_number={q_value['question number']},\n"
@@ -1,3 +1,4 @@
1
+ import hashlib
1
2
  import os
2
3
  import shutil
3
4
  from pathlib import Path
@@ -46,7 +47,7 @@ def initialize_assignment(
46
47
  raise Exception("Setup unsuccessful. Are you on JupyterHub?")
47
48
 
48
49
  try:
49
- seed = hash(jhub_user) % 1000
50
+ seed = username_to_seed(jhub_user) % 1000
50
51
  update_responses(key="seed", value=seed)
51
52
  update_responses(key="week", value=week)
52
53
  update_responses(key="assignment_type", value=assignment_type)
@@ -111,3 +112,10 @@ def move_dotfiles():
111
112
  shutil.copy2(source_file, target_file)
112
113
  except Exception as e:
113
114
  raise Exception(f"Failed to copy {source_file} to {target_file}: {e}")
115
+
116
+
117
+ def username_to_seed(username: str, mod: int = 1000) -> int:
118
+ hash_object = hashlib.sha256(username.encode())
119
+ hash_hex = hash_object.hexdigest()
120
+ hash_int = int(hash_hex, 16)
121
+ return hash_int % mod
@@ -182,6 +182,43 @@
182
182
  "outputs": [],
183
183
  "source": []
184
184
  },
185
+ {
186
+ "cell_type": "code",
187
+ "execution_count": null,
188
+ "metadata": {},
189
+ "outputs": [],
190
+ "source": [
191
+ "student_email = results[\"student_information\"][\"username\"]\n",
192
+ "time_stamp = results[\"student_information\"][\"timestamp\"]\n",
193
+ "assignments_graded = results[\"assignment_information\"].keys()\n",
194
+ "week_num = results[\"week_num\"]\n",
195
+ "assignment_type = results['assignment_type']\n",
196
+ "\n",
197
+ "\n",
198
+ "total_score = 0\n",
199
+ "for assignment in assignments_graded:\n",
200
+ " max_points = results[\"assignment_information\"][assignment][\"max_points\"]\n",
201
+ " total_score = results[\"assignment_information\"][assignment][\"total_score\"]\n",
202
+ "\n"
203
+ ]
204
+ },
205
+ {
206
+ "cell_type": "code",
207
+ "execution_count": null,
208
+ "metadata": {},
209
+ "outputs": [],
210
+ "source": [
211
+ "def extract_score_from_dict(results):\n",
212
+ " student_email = results[\"student_information\"][\"username\"]"
213
+ ]
214
+ },
215
+ {
216
+ "cell_type": "code",
217
+ "execution_count": null,
218
+ "metadata": {},
219
+ "outputs": [],
220
+ "source": []
221
+ },
185
222
  {
186
223
  "cell_type": "code",
187
224
  "execution_count": null,
@@ -166,6 +166,15 @@ class LogParser:
166
166
  """
167
167
  return {
168
168
  "student_information": self.student_info,
169
+ "week": self.week_tag,
170
+ "week_num": (
171
+ int(self.week_tag.split("-")[0].strip().replace("week", ""))
172
+ if self.week_tag
173
+ else None
174
+ ),
175
+ "assignment_type": (
176
+ self.week_tag.split("-")[1].strip() if self.week_tag else None
177
+ ),
169
178
  "assignment_information": {
170
179
  assignment: {
171
180
  "latest_timestamp": data["latest_timestamp"],
@@ -10,6 +10,7 @@ import requests
10
10
  from IPython.core.interactiveshell import ExecutionInfo
11
11
  from requests import Response
12
12
  from requests.auth import HTTPBasicAuth
13
+ from requests.exceptions import RequestException
13
14
 
14
15
  #
15
16
  # Logging setup
@@ -122,7 +123,6 @@ def update_responses(key: str, value) -> dict:
122
123
  #
123
124
 
124
125
 
125
- # TODO: Improve error handling
126
126
  def score_question(
127
127
  term: str = "winter_2025",
128
128
  base_url: str = "https://engr-131-api.eastus.cloudapp.azure.com",
@@ -140,16 +140,24 @@ def score_question(
140
140
  "responses": responses,
141
141
  }
142
142
 
143
- res = requests.post(url, json=payload, auth=HTTPBasicAuth("student", "capture"))
144
-
145
- res_data: dict[str, tuple[float, float]] = res.json()
146
-
147
- for question, (points_earned, max_points) in res_data.items():
148
- log_variable(
149
- assignment_name=responses["assignment"],
150
- value=f"{points_earned}, {max_points}",
151
- info_type=question,
152
- )
143
+ try:
144
+ res = requests.post(url, json=payload, auth=HTTPBasicAuth("student", "capture"))
145
+ res.raise_for_status()
146
+
147
+ res_data: dict[str, tuple[float, float]] = res.json()
148
+
149
+ for question, (points_earned, max_points) in res_data.items():
150
+ log_variable(
151
+ assignment_name=responses["assignment"],
152
+ value=f"{points_earned}, {max_points}",
153
+ info_type=question,
154
+ )
155
+ except RequestException as e:
156
+ raise RuntimeError("Failed to access question-scoring endpoint") from e
157
+ except ValueError as e:
158
+ raise ValueError("Failed to parse question-scoring JSON response") from e
159
+ except Exception as e:
160
+ raise RuntimeError("Failed to score question") from e
153
161
 
154
162
 
155
163
  def submit_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
File without changes
File without changes