PyKubeGrader 0.2.25__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.25
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,20 +1,20 @@
1
1
  pykubegrader/__init__.py,sha256=AoAkdfIjDDZGWLlsIRENNq06L9h46kDGBIE8vRmsCfg,311
2
- pykubegrader/initialize.py,sha256=lJu0h-fMUMRxZ70rFAEXix3H6j-TcRaxveC2SIKTzF4,3903
3
- pykubegrader/telemetry.py,sha256=h8PPrXWGFgPWCcrChJo2woqd_XIPMFfYcxgLJ0CWpH8,5360
2
+ pykubegrader/initialize.py,sha256=W-O1d9hy6BIYS28x3kcLG50WnNC4196eZwqwzVZdsgU,3902
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=Zd0E_R_UciK61wrUprrFSqw6Ara8EGzoa4TP2WmmOIQ,82944
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
12
12
  pykubegrader/log_parser/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
13
- pykubegrader/log_parser/parse.ipynb,sha256=H1CUuqiUSE-tiZV1IS7VG6HAEvoopGd6i_5QM5CwoE0,12230
14
- pykubegrader/log_parser/parse.py,sha256=YCs_OCnoxQKsL55MjTZWXBBBsehJL8PIB9ANnC-aE44,7379
15
- pykubegrader/submit/submit_assignment.py,sha256=p-v31479342q0AO3lJrd8Fwk7iJQUz183OeGeiNcXOY,3405
16
- pykubegrader/tokens/tokens.py,sha256=DorNwdrA5N5apB3LQjVGa2ABN5C1upkgglecaLHobr0,1260
17
- pykubegrader/tokens/validate_token.py,sha256=q5aJ0hUsFTDgrSfAszPS5RpxwTKODAguFqkZkKjkN3k,4058
13
+ pykubegrader/log_parser/parse.ipynb,sha256=5e-9dzUbJk2M8kPP55lVeksm86lSY5ocKfWOP2RSWH0,11921
14
+ pykubegrader/log_parser/parse.py,sha256=dXzTEOTI6VTRNoHFDAjg6hZUhvB3kHtMb10_KW3NPrw,7641
15
+ pykubegrader/submit/submit_assignment.py,sha256=UgJXKWw5b8-bRSFnba4iHAyXnujULHcWIask7hKx9ik,3421
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.25.dist-info/LICENSE.txt,sha256=YTp-Ewc8Kems8PJEE27KnBPFnZSxoWvSg7nnknzPyYw,1546
31
- PyKubeGrader-0.2.25.dist-info/METADATA,sha256=v_-jmszT5LaejR9v3YIDeQHgGDkRO7-jMVCC3Oi9cwg,2779
32
- PyKubeGrader-0.2.25.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
33
- PyKubeGrader-0.2.25.dist-info/entry_points.txt,sha256=UPMdTT46fQwTYJWtrUwIWIbXbwyOPfNQgBFRa0frWzw,138
34
- PyKubeGrader-0.2.25.dist-info/top_level.txt,sha256=e550Klfze6higFxER1V62fnGOcIgiKRbsrl9CC4UdtQ,13
35
- PyKubeGrader-0.2.25.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
@@ -22,13 +22,11 @@ try:
22
22
  except: # noqa: E722
23
23
  print("Passwords not found, cannot access database")
24
24
 
25
- import nbformat
26
-
27
- from .api_notebook_builder import FastAPINotebookBuilder
28
25
  from typing import Optional
29
26
 
27
+ import nbformat
30
28
 
31
- import os
29
+ from .api_notebook_builder import FastAPINotebookBuilder
32
30
 
33
31
  os.environ["JUPYTERHUB_USER"] = "jca92"
34
32
  os.environ["TOKEN"] = "token"
@@ -180,9 +178,7 @@ class NotebookProcessor:
180
178
  self.update_initialize_function()
181
179
 
182
180
  def update_initialize_function(self):
183
-
184
181
  for key, value in self.total_point_log.items():
185
-
186
182
  assignment_tag = f"week{self.week_num}-{self.assignment_type}"
187
183
 
188
184
  update_initialize_assignment(
@@ -724,6 +720,7 @@ class NotebookProcessor:
724
720
  with open(notebook_path, "w", encoding="utf-8") as f:
725
721
  nbformat.write(notebook, f)
726
722
 
723
+ @staticmethod
727
724
  def add_validate_token_cell(notebook_path: str, require_key: bool) -> None:
728
725
  """
729
726
  Adds a new code cell at the top of a Jupyter notebook if require_key is True.
@@ -794,7 +791,7 @@ class NotebookProcessor:
794
791
  data, output_file=solution_path
795
792
  )
796
793
 
797
- question_path = f"{new_notebook_path.replace(".ipynb", "")}_questions.py"
794
+ question_path = f"{new_notebook_path.replace('.ipynb', '')}_questions.py"
798
795
 
799
796
  generate_mcq_file(data, output_file=question_path)
800
797
 
@@ -834,7 +831,7 @@ class NotebookProcessor:
834
831
  data, output_file=solution_path
835
832
  )
836
833
 
837
- question_path = f"{new_notebook_path.replace(".ipynb", "")}_questions.py"
834
+ question_path = f"{new_notebook_path.replace('.ipynb', '')}_questions.py"
838
835
 
839
836
  generate_tf_file(data, output_file=question_path)
840
837
 
@@ -872,7 +869,7 @@ class NotebookProcessor:
872
869
  data, output_file=solution_path
873
870
  )
874
871
 
875
- question_path = f"{new_notebook_path.replace(".ipynb", "")}_questions.py"
872
+ question_path = f"{new_notebook_path.replace('.ipynb', '')}_questions.py"
876
873
 
877
874
  generate_select_many_file(data, output_file=question_path)
878
875
 
@@ -1812,9 +1809,7 @@ def generate_mcq_file(data_dict, output_file="mc_questions.py"):
1812
1809
  )
1813
1810
  f.write(" def __init__(self):\n")
1814
1811
  f.write(" super().__init__(\n")
1815
- f.write(
1816
- f' title=f"Question{q_value['question number']}: {q_value['title']}",\n'
1817
- )
1812
+ f.write(f' title=f"{q_value["title"]}",\n')
1818
1813
  f.write(" style=MCQ,\n")
1819
1814
  f.write(
1820
1815
  f" question_number={q_value['question number']},\n"
@@ -1885,9 +1880,7 @@ def generate_select_many_file(data_dict, output_file="select_many_questions.py")
1885
1880
  )
1886
1881
  f.write(" def __init__(self):\n")
1887
1882
  f.write(" super().__init__(\n")
1888
- f.write(
1889
- f' title=f"Question{q_value['question number']}: {q_value['title']}",\n'
1890
- )
1883
+ f.write(f' title=f"{q_value["title"]}",\n')
1891
1884
  f.write(" style=MultiSelect,\n")
1892
1885
  f.write(
1893
1886
  f" question_number={q_value['question number']},\n"
@@ -1964,9 +1957,7 @@ def generate_tf_file(data_dict, output_file="tf_questions.py"):
1964
1957
  )
1965
1958
  f.write(" def __init__(self):\n")
1966
1959
  f.write(" super().__init__(\n")
1967
- f.write(
1968
- f' title=f"Question{q_value['question number']}: {q_value['title']}",\n'
1969
- )
1960
+ f.write(f' title=f"{q_value["title"]}",\n')
1970
1961
  f.write(" style=TFStyle,\n")
1971
1962
  f.write(
1972
1963
  f" question_number={q_value['question number']},\n"
@@ -2,14 +2,16 @@ import hashlib
2
2
  import os
3
3
  import shutil
4
4
  from pathlib import Path
5
+ from typing import Optional
5
6
 
6
7
  import panel as pn
7
8
  import requests
8
9
  from IPython import get_ipython
9
- from typing import Optional
10
+
10
11
  from .telemetry import ensure_responses, log_variable, telemetry, update_responses
11
12
 
12
- #TODO: could cleanup to remove redundant imports
13
+
14
+ # TODO: could cleanup to remove redundant imports
13
15
  def initialize_assignment(
14
16
  name: str,
15
17
  week: str,
@@ -33,7 +35,7 @@ def initialize_assignment(
33
35
  Raises:
34
36
  Exception: If the environment is unsupported or initialization fails.
35
37
  """
36
-
38
+
37
39
  if assignment_tag is None:
38
40
  assignment_tag = f"{week}-{assignment_type}"
39
41
 
@@ -6,17 +6,12 @@
6
6
  "metadata": {},
7
7
  "outputs": [],
8
8
  "source": [
9
+ "# ruff: noqa\n",
10
+ "\n",
9
11
  "# Get the public/private keypair for decryption\n",
10
12
  "key_box = get_keybox()"
11
13
  ]
12
14
  },
13
- {
14
- "cell_type": "code",
15
- "execution_count": null,
16
- "metadata": {},
17
- "outputs": [],
18
- "source": []
19
- },
20
15
  {
21
16
  "cell_type": "code",
22
17
  "execution_count": null,
@@ -151,7 +146,7 @@
151
146
  "parser = LogParser(log_lines=log_lines, week_tag=\"week1-readings\")\n",
152
147
  "parser.parse_logs()\n",
153
148
  "parser.calculate_total_scores()\n",
154
- "results = parser.get_results() \n",
149
+ "results = parser.get_results()\n",
155
150
  "\n",
156
151
  "results"
157
152
  ]
@@ -192,13 +187,6 @@
192
187
  "results"
193
188
  ]
194
189
  },
195
- {
196
- "cell_type": "code",
197
- "execution_count": null,
198
- "metadata": {},
199
- "outputs": [],
200
- "source": []
201
- },
202
190
  {
203
191
  "cell_type": "code",
204
192
  "execution_count": null,
@@ -218,7 +206,7 @@
218
206
  "time_stamp = results[\"student_information\"][\"timestamp\"]\n",
219
207
  "\n",
220
208
  "week_num = results[\"week_num\"]\n",
221
- "assignment_type = results['assignment_type']\n",
209
+ "assignment_type = results[\"assignment_type\"]\n",
222
210
  "\n",
223
211
  "assignments_graded = results[\"assignment_information\"].keys()\n",
224
212
  "total_score = 0\n",
@@ -253,13 +241,6 @@
253
241
  "source": [
254
242
  "student_email"
255
243
  ]
256
- },
257
- {
258
- "cell_type": "code",
259
- "execution_count": null,
260
- "metadata": {},
261
- "outputs": [],
262
- "source": []
263
244
  }
264
245
  ],
265
246
  "metadata": {
@@ -1,5 +1,14 @@
1
1
  from dataclasses import dataclass, field
2
- from typing import Optional
2
+ from typing import Any, Optional, TypedDict
3
+
4
+
5
+ class LogParserResults(TypedDict):
6
+ student_information: dict[str, str]
7
+ week: Optional[str]
8
+ week_num: Optional[int]
9
+ assignment_type: Optional[str]
10
+ assignment_information: dict[str, Any]
11
+ assignment_scores: dict[str, Any]
3
12
 
4
13
 
5
14
  @dataclass
@@ -160,7 +169,7 @@ class LogParser:
160
169
  total_score = sum(q["score_earned"] for q in data["questions"].values())
161
170
  data["total_score"] = total_score
162
171
 
163
- def get_results(self) -> dict[str, dict]:
172
+ def get_results(self) -> LogParserResults:
164
173
  """
165
174
  Returns the parsed results as a hierarchical dictionary with three sections:
166
175
  """
@@ -1,8 +1,9 @@
1
- import os
2
- import httpx
3
1
  import asyncio
4
- import nest_asyncio
5
2
  import base64
3
+ import os
4
+
5
+ import httpx
6
+ import nest_asyncio # type: ignore
6
7
 
7
8
  # Apply nest_asyncio for environments like Jupyter
8
9
  nest_asyncio.apply()
@@ -43,7 +44,7 @@ async def call_score_assignment(
43
44
  # Get credentials
44
45
  credentials = get_credentials()
45
46
  username = credentials["username"]
46
- password = credentials["password"]
47
+ password = credentials["password"]
47
48
 
48
49
  # Encode credentials for Basic Authentication
49
50
  auth_header = (
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 requests
2
1
  import os
3
- import json
4
2
 
3
+ import requests
4
+ from requests.auth import HTTPBasicAuth
5
5
 
6
- def build_token_payload(token: str, duration: int) -> dict:
7
6
 
8
- if os.getenv("JUPYTERHUB_USER", None) is None:
7
+ def build_token_payload(token: str, duration: int) -> dict:
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,8 +1,9 @@
1
+ import asyncio
1
2
  import os
2
- import base64
3
+ from typing import Optional
4
+
3
5
  import httpx
4
- import asyncio
5
- import nest_asyncio
6
+ import nest_asyncio # type: ignore
6
7
 
7
8
  # Apply nest_asyncio for environments like Jupyter
8
9
  nest_asyncio.apply()
@@ -13,33 +14,37 @@ class TokenValidationError(Exception):
13
14
  Custom exception raised when the token validation fails.
14
15
  """
15
16
 
16
- def __init__(self, message=None):
17
+ def __init__(self, message: Optional[str] = None):
17
18
  """
18
19
  Initialize the exception with an optional message.
19
20
 
20
21
  Args:
21
22
  message (str, optional): The error message to display. Defaults to None.
22
23
  """
24
+
23
25
  super().__init__(message)
24
26
 
25
27
 
26
- def get_credentials():
28
+ def get_credentials() -> dict[str, str]:
27
29
  """
28
30
  Fetch the username and password from environment variables.
29
31
 
30
32
  Returns:
31
33
  dict: A dictionary containing 'username' and 'password'.
32
34
  """
35
+
33
36
  username = os.getenv("user_name_student")
34
37
  password = os.getenv("keys_student")
38
+
35
39
  if not username or not password:
36
40
  raise ValueError(
37
- "Environment variables 'user_name_student' or 'keys_student' are not set."
41
+ "Environment variable 'user_name_student' or 'keys_student' not set"
38
42
  )
43
+
39
44
  return {"username": username, "password": password}
40
45
 
41
46
 
42
- async def async_validate_token(token: str = None) -> None:
47
+ async def async_validate_token(token: Optional[str] = None) -> None:
43
48
  """
44
49
  Asynchronously validate a token by making a GET request to the validation endpoint.
45
50
 
@@ -53,19 +58,22 @@ async def async_validate_token(token: str = None) -> None:
53
58
  None: If the token is valid, the function will pass silently.
54
59
  """
55
60
 
61
+ # First, check if token is provided as an argument
56
62
  if token is not None:
57
63
  os.environ["TOKEN"] = token
58
64
 
65
+ # Next, check if token is available in environment variables
59
66
  if token is None:
60
67
  token = os.getenv("TOKEN", None)
61
68
 
69
+ # Otherwise, raise an error
62
70
  if token is None:
63
71
  raise TokenValidationError("No token provided")
64
72
 
65
73
  # Fetch the endpoint URL
66
74
  base_url = os.getenv("DB_URL")
67
75
  if not base_url:
68
- raise ValueError("Environment variable 'DB_URL' is not set.")
76
+ raise ValueError("Environment variable 'DB_URL' not set")
69
77
  endpoint = f"{base_url}validate-token/{token}"
70
78
 
71
79
  # Get credentials
@@ -73,17 +81,12 @@ async def async_validate_token(token: str = None) -> None:
73
81
  username = credentials["username"]
74
82
  password = credentials["password"]
75
83
 
76
- # Encode credentials for Basic Authentication
77
- auth_header = (
78
- f"Basic {base64.b64encode(f'{username}:{password}'.encode()).decode()}"
79
- )
84
+ basic_auth = httpx.BasicAuth(username=username, password=password)
80
85
 
81
- # Make the GET request
86
+ # Make GET request
82
87
  async with httpx.AsyncClient() as client:
83
88
  try:
84
- response = await client.get(
85
- endpoint, headers={"Authorization": auth_header}, timeout=10
86
- )
89
+ response = await client.get(url=endpoint, auth=basic_auth, timeout=10)
87
90
 
88
91
  if response.status_code == 200:
89
92
  # If the response is 200, the token is valid
@@ -103,7 +106,7 @@ async def async_validate_token(token: str = None) -> None:
103
106
  raise TokenValidationError(f"An unexpected error occurred: {e}")
104
107
 
105
108
 
106
- def validate_token(token: str = None) -> None:
109
+ def validate_token(token: Optional[str] = None) -> None:
107
110
  """
108
111
  Synchronous wrapper for the `async_validate_token` function.
109
112
 
@@ -131,8 +134,9 @@ def validate_token(token: str = None) -> None:
131
134
  # Example usage
132
135
  if __name__ == "__main__":
133
136
  token = "test"
137
+
134
138
  try:
135
139
  validate_token(token)
136
- print("Token is valid.")
140
+ print("Token is valid")
137
141
  except TokenValidationError as e:
138
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
  )