PyKubeGrader 0.2.9__tar.gz → 0.2.11__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. {pykubegrader-0.2.9/src/PyKubeGrader.egg-info → pykubegrader-0.2.11}/PKG-INFO +1 -1
  2. {pykubegrader-0.2.9 → pykubegrader-0.2.11/src/PyKubeGrader.egg-info}/PKG-INFO +1 -1
  3. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/build/build_folder.py +109 -1
  4. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/submit/submit_assignment.py +17 -7
  5. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/.coveragerc +0 -0
  6. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/.github/workflows/main.yml +0 -0
  7. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/.gitignore +0 -0
  8. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/.readthedocs.yml +0 -0
  9. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/AUTHORS.rst +0 -0
  10. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/CHANGELOG.rst +0 -0
  11. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/CONTRIBUTING.rst +0 -0
  12. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/LICENSE.txt +0 -0
  13. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/README.rst +0 -0
  14. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/docs/Makefile +0 -0
  15. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/docs/_static/Drexel_blue_Logo_square_Dark.png +0 -0
  16. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/docs/_static/Drexel_blue_Logo_square_Light.png +0 -0
  17. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/docs/_static/custom.css +0 -0
  18. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/docs/authors.rst +0 -0
  19. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/docs/changelog.rst +0 -0
  20. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/docs/conf.py +0 -0
  21. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/docs/contributing.rst +0 -0
  22. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/docs/index.rst +0 -0
  23. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/docs/license.rst +0 -0
  24. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/docs/readme.rst +0 -0
  25. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/docs/requirements.txt +0 -0
  26. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/examples/.responses.json +0 -0
  27. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/examples/true_false.ipynb +0 -0
  28. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/pyproject.toml +0 -0
  29. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/setup.cfg +0 -0
  30. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/setup.py +0 -0
  31. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/PyKubeGrader.egg-info/SOURCES.txt +0 -0
  32. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/PyKubeGrader.egg-info/dependency_links.txt +0 -0
  33. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/PyKubeGrader.egg-info/entry_points.txt +0 -0
  34. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/PyKubeGrader.egg-info/not-zip-safe +0 -0
  35. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/PyKubeGrader.egg-info/requires.txt +0 -0
  36. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/PyKubeGrader.egg-info/top_level.txt +0 -0
  37. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/__init__.py +0 -0
  38. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/build/__init__.py +0 -0
  39. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/build/api_notebook_builder.py +0 -0
  40. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/build/clean_folder.py +0 -0
  41. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/graders/__init__.py +0 -0
  42. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/graders/late_assignments.py +0 -0
  43. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/initialize.py +0 -0
  44. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/log_parser/__init__.py +0 -0
  45. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/log_parser/parse.ipynb +0 -0
  46. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/log_parser/parse.py +0 -0
  47. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/telemetry.py +0 -0
  48. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/utils.py +0 -0
  49. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/validate.py +0 -0
  50. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/widgets/__init__.py +0 -0
  51. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/widgets/multiple_choice.py +0 -0
  52. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/widgets/reading_question.py +0 -0
  53. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/widgets/select_many.py +0 -0
  54. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/widgets/student_info.py +0 -0
  55. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/widgets/style.py +0 -0
  56. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/widgets/true_false.py +0 -0
  57. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/widgets/types_question.py +0 -0
  58. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/widgets_base/__init__.py +0 -0
  59. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/widgets_base/multi_select.py +0 -0
  60. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/widgets_base/reading.py +0 -0
  61. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/src/pykubegrader/widgets_base/select.py +0 -0
  62. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/tests/conftest.py +0 -0
  63. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/tests/import_test.py +0 -0
  64. {pykubegrader-0.2.9 → pykubegrader-0.2.11}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyKubeGrader
3
- Version: 0.2.9
3
+ Version: 0.2.11
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.9
3
+ Version: 0.2.11
4
4
  Summary: Add a short description here!
5
5
  Home-page: https://github.com/pyscaffold/pyscaffold/
6
6
  Author: jagar2
@@ -215,6 +215,68 @@ class NotebookProcessor:
215
215
  "due_date": due_date,
216
216
  "max_score": int(self.assignment_total_points),
217
217
  }
218
+
219
+ def build_payload_notebook(self, yaml_content, notebook_title, total_points):
220
+ # Parse the YAML content
221
+ with open(yaml_content, "r") as file:
222
+ data = yaml.safe_load(file)
223
+
224
+ # Extract assignment details
225
+ assignment = data.get("assignment", {})
226
+
227
+ week_num = self.week_num
228
+ assignment_type = self.assignment_type
229
+ due_date_str = assignment.get("due_date")
230
+
231
+ # Convert due_date to a datetime object if available
232
+ due_date = None
233
+ if due_date_str:
234
+ try:
235
+ due_date = parser.parse(due_date_str) # Automatically handles timezones
236
+ except ValueError as e:
237
+ print(f"Error parsing due_date: {e}")
238
+
239
+ return {
240
+ "title": notebook_title,
241
+ "week_number": week_num,
242
+ "assignment_type": assignment_type,
243
+ "due_date": due_date,
244
+ "max_score": total_points,
245
+ }
246
+
247
+
248
+ def add_notebook(self, notebook_title, total_points):
249
+ """
250
+ Sends a POST request to add a notebook.
251
+ """
252
+ # Define the URL
253
+ url = "https://engr-131-api.eastus.cloudapp.azure.com/notebook"
254
+
255
+ # Build the payload
256
+ payload = self.build_payload_notebook(yaml_content=f"{self.root_folder}/assignment_config.yaml",
257
+ notebook_title=notebook_title,
258
+ total_points=total_points)
259
+
260
+ # Define HTTP Basic Authentication
261
+ auth = (user(), password())
262
+
263
+ # Define headers
264
+ headers = {"Content-Type": "application/json"}
265
+
266
+ # Serialize the payload with the custom JSON encoder
267
+ serialized_payload = json.dumps(payload, default=self.json_serial)
268
+
269
+ # Send the POST request
270
+ response = requests.post(
271
+ url, data=serialized_payload, headers=headers, auth=auth
272
+ )
273
+
274
+ # Print the response
275
+ print(f"Status Code: {response.status_code}")
276
+ try:
277
+ print(f"Response: {response.json()}")
278
+ except ValueError:
279
+ print(f"Response: {response.text}")
218
280
 
219
281
  def add_assignment(self):
220
282
  """
@@ -434,12 +496,58 @@ class NotebookProcessor:
434
496
  self.select_many_total_points
435
497
  + self.mcq_total_points
436
498
  + self.tf_total_points
437
- + self.otter_total_points
499
+ + self.otter_total_points
438
500
  )
501
+
502
+ # creates the assignment record in the database
503
+ self.add_notebook(notebook_name, total_points)
439
504
 
440
505
  self.assignment_total_points += total_points
441
506
 
442
507
  self.total_point_log.update({notebook_name: total_points})
508
+
509
+ student_file_path = os.path.join(self.root_folder, notebook_name + '.ipynb')
510
+ self.add_submission_cells(student_file_path, student_file_path)
511
+
512
+ def add_submission_cells(self, notebook_path: str, output_path: str) -> None:
513
+ """
514
+ Adds submission cells to the end of a Jupyter notebook.
515
+
516
+ Args:
517
+ notebook_path (str): Path to the input notebook.
518
+ output_path (str): Path to save the modified notebook.
519
+ """
520
+ # Load the notebook
521
+ with open(notebook_path, "r", encoding="utf-8") as f:
522
+ notebook = nbformat.read(f, as_version=4)
523
+
524
+ # Define the Markdown cell
525
+ markdown_cell = nbformat.v4.new_markdown_cell(
526
+ "## Submitting Assignment\n\n"
527
+ "Please run the following block of code using `shift + enter` to submit your assignment, "
528
+ "you should see your score."
529
+ )
530
+
531
+ # Define the Code cell
532
+ code_cell = nbformat.v4.new_code_cell(
533
+ "from pykubegrader.submit.submit_assignment import submit_assignment\n\n"
534
+ f'submit_assignment("week{self.week_num}-{self.assignment_type}", "{os.path.basename(notebook_path).replace(".ipynb", "")}")'
535
+ )
536
+
537
+ # Make the code cell non-editable and non-deletable
538
+ code_cell.metadata = {
539
+ "editable": False,
540
+ "deletable": False
541
+ }
542
+
543
+ # Add the cells to the notebook
544
+ notebook.cells.append(markdown_cell)
545
+ notebook.cells.append(code_cell)
546
+
547
+ # Save the modified notebook
548
+ with open(output_path, "w", encoding="utf-8") as f:
549
+ nbformat.write(notebook, f)
550
+
443
551
 
444
552
  def free_response_parser(
445
553
  self, temp_notebook_path, notebook_subfolder, notebook_name
@@ -2,6 +2,7 @@ import os
2
2
  import httpx
3
3
  import asyncio
4
4
  import nest_asyncio
5
+ import base64
5
6
 
6
7
  # Apply nest_asyncio for environments like Jupyter
7
8
  nest_asyncio.apply()
@@ -21,7 +22,7 @@ def get_credentials():
21
22
 
22
23
 
23
24
  async def call_score_assignment(
24
- assignment_title: str, file_path: str = ".output_reduced.log"
25
+ assignment_title: str, notebook_title: str, file_path: str = ".output_reduced.log"
25
26
  ) -> dict:
26
27
  """
27
28
  Submit an assignment to the scoring endpoint.
@@ -37,10 +38,17 @@ async def call_score_assignment(
37
38
  base_url = os.getenv("DB_URL")
38
39
  if not base_url:
39
40
  raise ValueError("Environment variable 'DB_URL' is not set.")
40
- url = f"{base_url}score-assignment"
41
+ url = f"{base_url}score-assignment?assignment_title={assignment_title}&notebook_title={notebook_title}"
41
42
 
42
43
  # Get credentials
43
44
  credentials = get_credentials()
45
+ username = credentials["username"]
46
+ password = credentials["password"]
47
+
48
+ # Encode credentials for Basic Authentication
49
+ auth_header = (
50
+ f"Basic {base64.b64encode(f'{username}:{password}'.encode()).decode()}"
51
+ )
44
52
 
45
53
  # Send the POST request
46
54
  async with httpx.AsyncClient() as client:
@@ -48,8 +56,8 @@ async def call_score_assignment(
48
56
  with open(file_path, "rb") as file:
49
57
  response = await client.post(
50
58
  url,
51
- data={"cred": credentials, "assignment_title": assignment_title},
52
- files={"log_file": file},
59
+ headers={"Authorization": auth_header}, # Add Authorization header
60
+ files={"log_file": file}, # Upload log file
53
61
  )
54
62
 
55
63
  # Handle the response
@@ -65,7 +73,9 @@ async def call_score_assignment(
65
73
 
66
74
 
67
75
  def submit_assignment(
68
- assignment_title: str, file_path: str = ".output_reduced.log"
76
+ assignment_title: str,
77
+ notebook_title: str,
78
+ file_path: str = ".output_reduced.log",
69
79
  ) -> None:
70
80
  """
71
81
  Synchronous wrapper for the `call_score_assignment` function.
@@ -83,11 +93,11 @@ def submit_assignment(
83
93
 
84
94
  # Run the async function in the event loop
85
95
  response = loop.run_until_complete(
86
- call_score_assignment(assignment_title, file_path)
96
+ call_score_assignment(assignment_title, notebook_title, file_path)
87
97
  )
88
98
  print("Server Response:", response.get("message", "No message in response"))
89
99
 
90
100
 
91
101
  # Example usage (remove this section if only the function needs to be importable):
92
102
  if __name__ == "__main__":
93
- submit_assignment("Week 1 Assignment", "path/to/your/log_file.txt")
103
+ submit_assignment("week1-readings", "path/to/your/log_file.txt")
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