PyKubeGrader 0.2.26__py3-none-any.whl → 0.2.27__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: PyKubeGrader
3
- Version: 0.2.26
3
+ Version: 0.2.27
4
4
  Summary: Add a short description here!
5
5
  Home-page: https://github.com/pyscaffold/pyscaffold/
6
6
  Author: jagar2
@@ -1,11 +1,11 @@
1
1
  pykubegrader/__init__.py,sha256=AoAkdfIjDDZGWLlsIRENNq06L9h46kDGBIE8vRmsCfg,311
2
2
  pykubegrader/initialize.py,sha256=W-O1d9hy6BIYS28x3kcLG50WnNC4196eZwqwzVZdsgU,3902
3
- pykubegrader/telemetry.py,sha256=h8PPrXWGFgPWCcrChJo2woqd_XIPMFfYcxgLJ0CWpH8,5360
3
+ pykubegrader/telemetry.py,sha256=GwKcC8Y8p35L07zwTC7ijR5ux5ceb6LyC_MZp5yBbH8,5360
4
4
  pykubegrader/utils.py,sha256=T3GYnLnTL9VXjTZNPr00sUgMgobQYsNTGwynMyXdvHk,696
5
5
  pykubegrader/validate.py,sha256=2KLSB3wfFZbBh1NGgmrOV073paKAgrQz4AgA6LmCIj4,11076
6
6
  pykubegrader/build/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
7
- pykubegrader/build/api_notebook_builder.py,sha256=IzzU_jh6SYSA2wtjtlhxa4t9WM8-JqWnWsV8e3UPwdo,20305
8
- pykubegrader/build/build_folder.py,sha256=D8zNP6tFDR0S9zFIqutcPHZGw6px3vpIwnoVsl2LbvE,82949
7
+ pykubegrader/build/api_notebook_builder.py,sha256=dlcVrGgsvxnt6GlAUN3e-FrpsPNJKXSHni1fstRCBik,20311
8
+ pykubegrader/build/build_folder.py,sha256=5jl_TAnxfd9sqWvggJr4i3E7mrEf03cmFDotVrgTZWQ,82697
9
9
  pykubegrader/build/clean_folder.py,sha256=8N0KyL4eXRs0DCw-V_2jR9igtFs_mOFMQufdL6tD-38,1323
10
10
  pykubegrader/graders/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
11
11
  pykubegrader/graders/late_assignments.py,sha256=_2-rA5RqO0BWY9WAQA_mbCxxPKTOiJOl-byD2CYWaE0,1393
@@ -13,8 +13,8 @@ pykubegrader/log_parser/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YB
13
13
  pykubegrader/log_parser/parse.ipynb,sha256=5e-9dzUbJk2M8kPP55lVeksm86lSY5ocKfWOP2RSWH0,11921
14
14
  pykubegrader/log_parser/parse.py,sha256=dXzTEOTI6VTRNoHFDAjg6hZUhvB3kHtMb10_KW3NPrw,7641
15
15
  pykubegrader/submit/submit_assignment.py,sha256=UgJXKWw5b8-bRSFnba4iHAyXnujULHcWIask7hKx9ik,3421
16
- pykubegrader/tokens/tokens.py,sha256=qMUN4aCR9NB_KVfyA13pbqt0nkjDmd0_C04CI-RD1So,1260
17
- pykubegrader/tokens/validate_token.py,sha256=kzXZPCVyYAedSmnzekIdwAhjBY3ZCMUrqOHOKFKAssI,4123
16
+ pykubegrader/tokens/tokens.py,sha256=TOj0jRun1lWTztKA7KX9SjzIfNQqkwdnbczrFSPWB7Y,1089
17
+ pykubegrader/tokens/validate_token.py,sha256=c_fqE3j_4wrlXWYHnBt4X4g622BKdbWzPT9mg_xArxA,4152
18
18
  pykubegrader/widgets/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
19
19
  pykubegrader/widgets/multiple_choice.py,sha256=NjD3-uXSnibpUQ0mO3hRp_O-rynFyl0Dz6IXE4tnCRI,2078
20
20
  pykubegrader/widgets/reading_question.py,sha256=y30_swHwzH8LrT8deWTnxctAAmR8BSxTlXAqMgUrAT4,3031
@@ -25,11 +25,11 @@ pykubegrader/widgets/true_false.py,sha256=D45bjRLaAcNzsSlWPgxwTXGVZPE7PER34S30V6
25
25
  pykubegrader/widgets/types_question.py,sha256=kZdRRXyFzOtYTmGdC7XWb_2oaxqg1WSuLcQn_sTj6Qc,2300
26
26
  pykubegrader/widgets_base/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
27
27
  pykubegrader/widgets_base/multi_select.py,sha256=Cl0IN21wXLZuFu-zC65aS9tD4jMfzCRJ2DPjHao5_Ak,4044
28
- pykubegrader/widgets_base/reading.py,sha256=_vjUPynqmJe_R4vf-7hVhGnQR726S9GL6qT8bflBXBM,5383
28
+ pykubegrader/widgets_base/reading.py,sha256=xmvN1UIXwk32v9S-JhsXwDc7axPlgpvoxSeM3II8sxY,5393
29
29
  pykubegrader/widgets_base/select.py,sha256=Fw3uFNOIWo1a3CvlzSx23bvi6bSmA3TqutuRbhD4Dp8,2525
30
- PyKubeGrader-0.2.26.dist-info/LICENSE.txt,sha256=YTp-Ewc8Kems8PJEE27KnBPFnZSxoWvSg7nnknzPyYw,1546
31
- PyKubeGrader-0.2.26.dist-info/METADATA,sha256=PHetJ4aQEkMSYy0E6kMfn48RYDy6ZXmesTwd3pHmZfg,2779
32
- PyKubeGrader-0.2.26.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
33
- PyKubeGrader-0.2.26.dist-info/entry_points.txt,sha256=UPMdTT46fQwTYJWtrUwIWIbXbwyOPfNQgBFRa0frWzw,138
34
- PyKubeGrader-0.2.26.dist-info/top_level.txt,sha256=e550Klfze6higFxER1V62fnGOcIgiKRbsrl9CC4UdtQ,13
35
- PyKubeGrader-0.2.26.dist-info/RECORD,,
30
+ PyKubeGrader-0.2.27.dist-info/LICENSE.txt,sha256=YTp-Ewc8Kems8PJEE27KnBPFnZSxoWvSg7nnknzPyYw,1546
31
+ PyKubeGrader-0.2.27.dist-info/METADATA,sha256=O9OtbiAIstnf_CFiulp51aCAWlaX_rQe90LrOMudw4o,2779
32
+ PyKubeGrader-0.2.27.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
33
+ PyKubeGrader-0.2.27.dist-info/entry_points.txt,sha256=UPMdTT46fQwTYJWtrUwIWIbXbwyOPfNQgBFRa0frWzw,138
34
+ PyKubeGrader-0.2.27.dist-info/top_level.txt,sha256=e550Klfze6higFxER1V62fnGOcIgiKRbsrl9CC4UdtQ,13
35
+ PyKubeGrader-0.2.27.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.7.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -65,9 +65,6 @@ class FastAPINotebookBuilder:
65
65
  updated_cell_source.extend(
66
66
  FastAPINotebookBuilder.construct_question_info(cell_dict)
67
67
  )
68
- updated_cell_source.extend(
69
- FastAPINotebookBuilder.construct_update_responses(cell_dict)
70
- )
71
68
 
72
69
  updated_cell_source.extend(cell_source[last_import_line_ind + 1 :])
73
70
  updated_cell_source.extend(["\n"])
@@ -91,6 +88,10 @@ class FastAPINotebookBuilder:
91
88
  ["os.environ['EARNED_POINTS'] = str(earned_points)\n"]
92
89
  )
93
90
 
91
+ updated_cell_source.extend(
92
+ FastAPINotebookBuilder.construct_update_responses(cell_dict)
93
+ )
94
+
94
95
  self.replace_cell_source(cell_index, updated_cell_source)
95
96
 
96
97
  def compute_max_points_free_response(self):
@@ -142,7 +143,7 @@ class FastAPINotebookBuilder:
142
143
 
143
144
  for logging_variable in logging_variables:
144
145
  update_responses.append(
145
- f"responses = update_responses(question_id, {logging_variable})\n"
146
+ f"responses = update_responses(question_id, str({logging_variable}))\n"
146
147
  )
147
148
 
148
149
  return update_responses
@@ -189,7 +190,7 @@ class FastAPINotebookBuilder:
189
190
  question_id = cell_dict["question"] + "-" + str(cell_dict["test_number"])
190
191
 
191
192
  question_info.append(f'question_id = "{question_id}"' + "\n")
192
- question_info.append(f'max_score = {cell_dict["points"]}\n')
193
+ question_info.append(f"max_score = {cell_dict['points']}\n")
193
194
  question_info.append("score = 0\n")
194
195
 
195
196
  return question_info
@@ -791,7 +791,7 @@ class NotebookProcessor:
791
791
  data, output_file=solution_path
792
792
  )
793
793
 
794
- question_path = f"{new_notebook_path.replace(".ipynb", "")}_questions.py"
794
+ question_path = f"{new_notebook_path.replace('.ipynb', '')}_questions.py"
795
795
 
796
796
  generate_mcq_file(data, output_file=question_path)
797
797
 
@@ -831,7 +831,7 @@ class NotebookProcessor:
831
831
  data, output_file=solution_path
832
832
  )
833
833
 
834
- question_path = f"{new_notebook_path.replace(".ipynb", "")}_questions.py"
834
+ question_path = f"{new_notebook_path.replace('.ipynb', '')}_questions.py"
835
835
 
836
836
  generate_tf_file(data, output_file=question_path)
837
837
 
@@ -869,7 +869,7 @@ class NotebookProcessor:
869
869
  data, output_file=solution_path
870
870
  )
871
871
 
872
- question_path = f"{new_notebook_path.replace(".ipynb", "")}_questions.py"
872
+ question_path = f"{new_notebook_path.replace('.ipynb', '')}_questions.py"
873
873
 
874
874
  generate_select_many_file(data, output_file=question_path)
875
875
 
@@ -1809,9 +1809,7 @@ def generate_mcq_file(data_dict, output_file="mc_questions.py"):
1809
1809
  )
1810
1810
  f.write(" def __init__(self):\n")
1811
1811
  f.write(" super().__init__(\n")
1812
- f.write(
1813
- f' title=f"Question{q_value['question number']}: {q_value['title']}",\n'
1814
- )
1812
+ f.write(f' title=f"{q_value["title"]}",\n')
1815
1813
  f.write(" style=MCQ,\n")
1816
1814
  f.write(
1817
1815
  f" question_number={q_value['question number']},\n"
@@ -1882,9 +1880,7 @@ def generate_select_many_file(data_dict, output_file="select_many_questions.py")
1882
1880
  )
1883
1881
  f.write(" def __init__(self):\n")
1884
1882
  f.write(" super().__init__(\n")
1885
- f.write(
1886
- f' title=f"Question{q_value['question number']}: {q_value['title']}",\n'
1887
- )
1883
+ f.write(f' title=f"{q_value["title"]}",\n')
1888
1884
  f.write(" style=MultiSelect,\n")
1889
1885
  f.write(
1890
1886
  f" question_number={q_value['question number']},\n"
@@ -1961,9 +1957,7 @@ def generate_tf_file(data_dict, output_file="tf_questions.py"):
1961
1957
  )
1962
1958
  f.write(" def __init__(self):\n")
1963
1959
  f.write(" super().__init__(\n")
1964
- f.write(
1965
- f' title=f"Question{q_value['question number']}: {q_value['title']}",\n'
1966
- )
1960
+ f.write(f' title=f"{q_value["title"]}",\n')
1967
1961
  f.write(" style=TFStyle,\n")
1968
1962
  f.write(
1969
1963
  f" question_number={q_value['question number']},\n"
pykubegrader/telemetry.py CHANGED
@@ -134,11 +134,11 @@ def score_question(
134
134
  responses = ensure_responses()
135
135
 
136
136
  payload: dict[str, Any] = {
137
- "student_email": f'{responses["jhub_user"]}@drexel.edu',
137
+ "student_email": f"{responses['jhub_user']}@drexel.edu",
138
138
  "term": term,
139
139
  "week": responses["week"],
140
140
  "assignment": responses["assignment_type"],
141
- "question": f'_{responses["assignment"]}',
141
+ "question": f"_{responses['assignment']}",
142
142
  "responses": responses,
143
143
  }
144
144
 
@@ -1,46 +1,41 @@
1
- import json
2
1
  import os
3
2
 
4
3
  import requests
4
+ from requests.auth import HTTPBasicAuth
5
5
 
6
6
 
7
7
  def build_token_payload(token: str, duration: int) -> dict:
8
- if os.getenv("JUPYTERHUB_USER", None) is None:
8
+ jhub_user = os.getenv("JUPYTERHUB_USER")
9
+ if jhub_user is None:
9
10
  raise ValueError("JupyterHub user not found")
10
11
 
11
- # Return the extracted details as a dictionary
12
12
  return {
13
13
  "value": token,
14
- "requester": os.getenv("JUPYTERHUB_USER", None),
15
14
  "duration": duration,
15
+ "requester": jhub_user,
16
16
  }
17
17
 
18
18
 
19
- # Need to do for add token as well
20
- def add_token(token, duration=20):
19
+ def add_token(token: str, duration: int = 20) -> None:
21
20
  """
22
- Sends a POST request to add a notebook.
21
+ Sends a POST request to mint a token
23
22
  """
24
- # Define the URL
23
+
25
24
  url = "https://engr-131-api.eastus.cloudapp.azure.com/tokens"
26
25
 
27
- # Build the payload
28
26
  payload = build_token_payload(token=token, duration=duration)
29
27
 
30
- # Define HTTP Basic Authentication
31
- auth = ("user", "password")
28
+ # Dummy credentials for HTTP Basic Auth
29
+ auth = HTTPBasicAuth("user", "password")
32
30
 
33
- # Define headers
34
- headers = {"Content-Type": "application/json"}
31
+ # Add a custom header, for potential use in authorization
32
+ headers = {"x-jhub-user": payload["requester"]}
35
33
 
36
- # Serialize the payload with the custom JSON encoder
37
- serialized_payload = json.dumps(payload)
34
+ response = requests.post(url=url, json=payload, headers=headers, auth=auth)
38
35
 
39
- # Send the POST request
40
- response = requests.post(url, data=serialized_payload, headers=headers, auth=auth)
36
+ # Print response
37
+ print(f"Status code: {response.status_code}")
41
38
 
42
- # Print the response
43
- print(f"Status Code: {response.status_code}")
44
39
  try:
45
40
  print(f"Response: {response.json()}")
46
41
  except ValueError:
@@ -1,5 +1,4 @@
1
1
  import asyncio
2
- import base64
3
2
  import os
4
3
  from typing import Optional
5
4
 
@@ -15,29 +14,33 @@ class TokenValidationError(Exception):
15
14
  Custom exception raised when the token validation fails.
16
15
  """
17
16
 
18
- def __init__(self, message=None):
17
+ def __init__(self, message: Optional[str] = None):
19
18
  """
20
19
  Initialize the exception with an optional message.
21
20
 
22
21
  Args:
23
22
  message (str, optional): The error message to display. Defaults to None.
24
23
  """
24
+
25
25
  super().__init__(message)
26
26
 
27
27
 
28
- def get_credentials():
28
+ def get_credentials() -> dict[str, str]:
29
29
  """
30
30
  Fetch the username and password from environment variables.
31
31
 
32
32
  Returns:
33
33
  dict: A dictionary containing 'username' and 'password'.
34
34
  """
35
+
35
36
  username = os.getenv("user_name_student")
36
37
  password = os.getenv("keys_student")
38
+
37
39
  if not username or not password:
38
40
  raise ValueError(
39
- "Environment variables 'user_name_student' or 'keys_student' are not set."
41
+ "Environment variable 'user_name_student' or 'keys_student' not set"
40
42
  )
43
+
41
44
  return {"username": username, "password": password}
42
45
 
43
46
 
@@ -55,19 +58,22 @@ async def async_validate_token(token: Optional[str] = None) -> None:
55
58
  None: If the token is valid, the function will pass silently.
56
59
  """
57
60
 
61
+ # First, check if token is provided as an argument
58
62
  if token is not None:
59
63
  os.environ["TOKEN"] = token
60
64
 
65
+ # Next, check if token is available in environment variables
61
66
  if token is None:
62
67
  token = os.getenv("TOKEN", None)
63
68
 
69
+ # Otherwise, raise an error
64
70
  if token is None:
65
71
  raise TokenValidationError("No token provided")
66
72
 
67
73
  # Fetch the endpoint URL
68
74
  base_url = os.getenv("DB_URL")
69
75
  if not base_url:
70
- raise ValueError("Environment variable 'DB_URL' is not set.")
76
+ raise ValueError("Environment variable 'DB_URL' not set")
71
77
  endpoint = f"{base_url}validate-token/{token}"
72
78
 
73
79
  # Get credentials
@@ -75,17 +81,12 @@ async def async_validate_token(token: Optional[str] = None) -> None:
75
81
  username = credentials["username"]
76
82
  password = credentials["password"]
77
83
 
78
- # Encode credentials for Basic Authentication
79
- auth_header = (
80
- f"Basic {base64.b64encode(f'{username}:{password}'.encode()).decode()}"
81
- )
84
+ basic_auth = httpx.BasicAuth(username=username, password=password)
82
85
 
83
- # Make the GET request
86
+ # Make GET request
84
87
  async with httpx.AsyncClient() as client:
85
88
  try:
86
- response = await client.get(
87
- endpoint, headers={"Authorization": auth_header}, timeout=10
88
- )
89
+ response = await client.get(url=endpoint, auth=basic_auth, timeout=10)
89
90
 
90
91
  if response.status_code == 200:
91
92
  # If the response is 200, the token is valid
@@ -133,8 +134,9 @@ def validate_token(token: Optional[str] = None) -> None:
133
134
  # Example usage
134
135
  if __name__ == "__main__":
135
136
  token = "test"
137
+
136
138
  try:
137
139
  validate_token(token)
138
- print("Token is valid.")
140
+ print("Token is valid")
139
141
  except TokenValidationError as e:
140
142
  print(f"Token validation failed: {e}")
@@ -23,7 +23,7 @@ class ReadingPython:
23
23
 
24
24
  # Dynamically assign attributes based on keys, with default values from responses
25
25
  for num in range(len(options["lines_to_comment"]) + options["n_rows"]):
26
- key = f"q{question_number}_{num+1}"
26
+ key = f"q{question_number}_{num + 1}"
27
27
 
28
28
  # Dynamically assign the value from the responses file for persistence
29
29
  if num < len(options["lines_to_comment"]):
@@ -57,7 +57,7 @@ class ReadingPython:
57
57
  line: pn.widgets.Select(
58
58
  options=shuffle_options(options["comments_options"], seed),
59
59
  name=f"Line {line}:",
60
- value=getattr(self, f"q{question_number}_{i_comments+1}"),
60
+ value=getattr(self, f"q{question_number}_{i_comments + 1}"),
61
61
  width=600,
62
62
  )
63
63
  for i_comments, line in enumerate(options["lines_to_comment"])
@@ -97,13 +97,13 @@ class ReadingPython:
97
97
  # Function to create a row with dropdowns
98
98
  def create_row(step: int) -> pn.Row:
99
99
  widgets_list = [
100
- pn.pane.HTML(f"Step {step+1}", width=150)
100
+ pn.pane.HTML(f"Step {step + 1}", width=150)
101
101
  if i == 0
102
102
  else pn.widgets.Select(
103
103
  options=dropdown_options[i - 1],
104
104
  value=getattr(
105
105
  self,
106
- f'q{question_number}_{len(options["lines_to_comment"])+step+1}',
106
+ f"q{question_number}_{len(options['lines_to_comment']) + step + 1}",
107
107
  )[i - 1],
108
108
  width=150,
109
109
  )