PyKubeGrader 0.0.8__py3-none-any.whl → 0.1.0__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
1
  Metadata-Version: 2.1
2
2
  Name: PyKubeGrader
3
- Version: 0.0.8
3
+ Version: 0.1.0
4
4
  Summary: Add a short description here!
5
5
  Home-page: https://github.com/pyscaffold/pyscaffold/
6
6
  Author: jagar2
@@ -13,11 +13,18 @@ Classifier: Programming Language :: Python
13
13
  Description-Content-Type: text/x-rst; charset=UTF-8
14
14
  License-File: LICENSE.txt
15
15
  Requires-Dist: importlib-metadata; python_version < "3.8"
16
- Requires-Dist: ipython>=8.30.0
17
- Requires-Dist: numpy>=2.1.3
18
- Requires-Dist: panel>=1.5.4
19
- Requires-Dist: pynacl>=1.5.0
20
- Requires-Dist: requests>=2.32.3
16
+ Requires-Dist: ipython
17
+ Requires-Dist: mypy
18
+ Requires-Dist: numpy
19
+ Requires-Dist: panel
20
+ Requires-Dist: pynacl
21
+ Requires-Dist: pytest
22
+ Requires-Dist: requests
23
+ Requires-Dist: ruff
24
+ Requires-Dist: setuptools
25
+ Requires-Dist: sphinx
26
+ Requires-Dist: types-requests
27
+ Requires-Dist: types-setuptools
21
28
  Provides-Extra: testing
22
29
  Requires-Dist: setuptools; extra == "testing"
23
30
  Requires-Dist: pytest; extra == "testing"
@@ -0,0 +1,20 @@
1
+ pykubegrader/__init__.py,sha256=AoAkdfIjDDZGWLlsIRENNq06L9h46kDGBIE8vRmsCfg,311
2
+ pykubegrader/initialize.py,sha256=PACquuxFkvOKUx51Rv6_RVu1YnYXf9L_W3hmMY5IxMw,706
3
+ pykubegrader/telemetry.py,sha256=eAzb54-lHFcgv0IsLFwsWr4nm_D7u-n-zOdR4cKGID4,3291
4
+ pykubegrader/utils.py,sha256=dKw6SyRYU3DWRgD3xER7wq-C9e1daWPkqr901LpcwiQ,642
5
+ pykubegrader/validate.py,sha256=QzdsjSS7rBzvBj_s_VHk-shjkPE-NYHQrYI3UHbq7Yo,10632
6
+ pykubegrader/widgets/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
7
+ pykubegrader/widgets/multiple_choice.py,sha256=NjD3-uXSnibpUQ0mO3hRp_O-rynFyl0Dz6IXE4tnCRI,2078
8
+ pykubegrader/widgets/reading_question.py,sha256=y30_swHwzH8LrT8deWTnxctAAmR8BSxTlXAqMgUrAT4,3031
9
+ pykubegrader/widgets/select_many.py,sha256=NRRs9P3Jygl9eUVW16nxMLNlHfFDuINgsFhMyXiOsXU,3826
10
+ pykubegrader/widgets/student_info.py,sha256=xhQgKehk1r5e6N_hnjAIovLdPvQju6ZqQTOiPG0aevg,3568
11
+ pykubegrader/widgets/types_question.py,sha256=kZdRRXyFzOtYTmGdC7XWb_2oaxqg1WSuLcQn_sTj6Qc,2300
12
+ pykubegrader/widgets_base/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
13
+ pykubegrader/widgets_base/multi_select.py,sha256=o9FwOSkfjarSZuC0wgBJLSRNtnY1-q_og1k_cdyJki8,3105
14
+ pykubegrader/widgets_base/reading.py,sha256=4uTLmlPzCwxVzufFhPjM7W19uMGguRb6y4eAV3x-zAc,5314
15
+ pykubegrader/widgets_base/select.py,sha256=5k-LoD3fTZYUOubRiTeQcVN5YDGK28x4Qb2l1aRVKzo,2181
16
+ PyKubeGrader-0.1.0.dist-info/LICENSE.txt,sha256=YTp-Ewc8Kems8PJEE27KnBPFnZSxoWvSg7nnknzPyYw,1546
17
+ PyKubeGrader-0.1.0.dist-info/METADATA,sha256=t6cYjeNKJkrs5UNB5-39NgdMdfGqQ6nrKsOLiFefP6E,2640
18
+ PyKubeGrader-0.1.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
19
+ PyKubeGrader-0.1.0.dist-info/top_level.txt,sha256=e550Klfze6higFxER1V62fnGOcIgiKRbsrl9CC4UdtQ,13
20
+ PyKubeGrader-0.1.0.dist-info/RECORD,,
pykubegrader/__init__.py CHANGED
@@ -1,14 +1,7 @@
1
- import sys
2
-
3
- if sys.version_info[:2] >= (3, 8):
4
- # TODO: Import directly (no need for conditional) when `python_requires = >= 3.8`
5
- from importlib.metadata import PackageNotFoundError, version # pragma: no cover
6
- else:
7
- from importlib_metadata import PackageNotFoundError, version # pragma: no cover
1
+ from importlib.metadata import PackageNotFoundError, version # pragma: no cover
8
2
 
9
3
  try:
10
- # Change here if project is renamed and does not equal the package name
11
- dist_name = "PyKubeGrader"
4
+ dist_name = "PyKubeGrader" # Change if project is renamed
12
5
  __version__ = version(dist_name)
13
6
  except PackageNotFoundError: # pragma: no cover
14
7
  __version__ = "unknown"
@@ -0,0 +1,28 @@
1
+ import json
2
+
3
+ import panel as pn
4
+ from IPython import get_ipython
5
+
6
+ from .telemetry import telemetry, update_responses
7
+
8
+
9
+ def initialize_assignment(name: str) -> None:
10
+ ipython = get_ipython()
11
+ if ipython is None:
12
+ print("Setup unsuccessful. Are you in a Jupyter environment?")
13
+ return
14
+
15
+ try:
16
+ ipython.events.register("pre_run_cell", telemetry)
17
+ except TypeError as e:
18
+ print(f"Failed to register telemetry: {e}")
19
+
20
+ pn.extension()
21
+
22
+ try:
23
+ update_responses(key="assignment", value=name)
24
+ except (TypeError, json.JSONDecodeError) as e:
25
+ print(f"Failed to initialize assignment: {e}")
26
+ return
27
+
28
+ print("Assignment successfully initialized")
pykubegrader/telemetry.py CHANGED
@@ -10,12 +10,12 @@ from IPython.core.interactiveshell import ExecutionInfo
10
10
  from requests import Response
11
11
  from requests.auth import HTTPBasicAuth
12
12
 
13
+ # Set logging config (`force` is important)
13
14
  logging.basicConfig(filename=".output.log", level=logging.INFO, force=True)
14
15
 
15
-
16
- def telemetry(info: ExecutionInfo) -> None:
17
- cell_content = info.raw_cell
18
- log_encrypted(f"code run: {cell_content}")
16
+ #
17
+ # Local functions
18
+ #
19
19
 
20
20
 
21
21
  def encrypt_to_b64(message: str) -> str:
@@ -34,17 +34,6 @@ def encrypt_to_b64(message: str) -> str:
34
34
  return encrypted_b64
35
35
 
36
36
 
37
- def log_encrypted(message: str) -> None:
38
- encrypted_b64 = encrypt_to_b64(message)
39
- logging.info(f"Encrypted Output: {encrypted_b64}")
40
-
41
-
42
- def log_variable(value, info_type) -> None:
43
- timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
44
- message = f"{info_type}, {value}, {timestamp}"
45
- log_encrypted(message)
46
-
47
-
48
37
  def ensure_responses() -> dict:
49
38
  with open(".responses.json", "a") as _:
50
39
  pass
@@ -61,6 +50,22 @@ def ensure_responses() -> dict:
61
50
  return data
62
51
 
63
52
 
53
+ def log_encrypted(message: str) -> None:
54
+ encrypted_b64 = encrypt_to_b64(message)
55
+ logging.info(f"Encrypted Output: {encrypted_b64}")
56
+
57
+
58
+ def log_variable(value, info_type) -> None:
59
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
60
+ message = f"{info_type}, {value}, {timestamp}"
61
+ log_encrypted(message)
62
+
63
+
64
+ def telemetry(info: ExecutionInfo) -> None:
65
+ cell_content = info.raw_cell
66
+ log_encrypted(f"code run: {cell_content}")
67
+
68
+
64
69
  def update_responses(key: str, value) -> dict:
65
70
  data = ensure_responses()
66
71
  data[key] = value
@@ -84,6 +89,11 @@ def update_responses(key: str, value) -> dict:
84
89
  return data
85
90
 
86
91
 
92
+ #
93
+ # API request functions
94
+ #
95
+
96
+
87
97
  def score_question(
88
98
  student_email: str,
89
99
  term: str,
pykubegrader/validate.py CHANGED
@@ -139,7 +139,7 @@ def validate_logfile(
139
139
  return entry
140
140
  return ""
141
141
 
142
- def get_len_of_entries(data, question_number) -> int:
142
+ def get_entries_len(data: list[str], question_number: int) -> int:
143
143
  """function to get the unique entries by length
144
144
 
145
145
  Args:
@@ -180,7 +180,7 @@ def validate_logfile(
180
180
  # Collect entries for each question in a list.
181
181
  entries = [
182
182
  entry
183
- for j in range(1, get_len_of_entries(data, i))
183
+ for j in range(1, get_entries_len(data, i))
184
184
  if (entry := get_last_entry(data, f"q{i}_{j}")) != ""
185
185
  ]
186
186
 
@@ -246,7 +246,7 @@ def validate_logfile(
246
246
  print("Writing to results.json")
247
247
  json.dump(result_structure, file, indent=4)
248
248
 
249
- login_(login_data, login_url)
249
+ verify_login(login_data, login_url)
250
250
 
251
251
  # The file to be uploaded. Ensure the path is correct.
252
252
  file_path = "results.json"
@@ -281,15 +281,9 @@ def validate_logfile(
281
281
  submission_message(response)
282
282
 
283
283
 
284
- def login_(login_data, login_url):
285
- login_response = requests.post(
286
- login_url, auth=HTTPBasicAuth(login_data["username"], login_data["password"])
287
- )
288
-
289
- if login_response.status_code == 200:
290
- print("Login successful")
291
- else:
292
- Exception("Login failed")
284
+ #
285
+ # Helper functions
286
+ #
293
287
 
294
288
 
295
289
  def submission_message(response) -> None:
@@ -309,3 +303,14 @@ def submission_message(response) -> None:
309
303
  pass
310
304
  else:
311
305
  print("results.json was not present")
306
+
307
+
308
+ def verify_login(login_data, login_url):
309
+ login_response = requests.post(
310
+ login_url, auth=HTTPBasicAuth(login_data["username"], login_data["password"])
311
+ )
312
+
313
+ if login_response.status_code == 200:
314
+ print("Login successful")
315
+ else:
316
+ Exception("Login failed")
@@ -0,0 +1 @@
1
+
@@ -2,8 +2,12 @@ from typing import Tuple
2
2
 
3
3
  import panel as pn
4
4
 
5
- from .misc import list_of_lists
6
- from .select_base import SelectQuestion
5
+ from ..utils import list_of_lists
6
+ from ..widgets_base.select import SelectQuestion
7
+
8
+ #
9
+ # Style function
10
+ #
7
11
 
8
12
 
9
13
  def MCQ(
@@ -35,6 +39,11 @@ def MCQ(
35
39
  return desc_widgets, radio_buttons
36
40
 
37
41
 
42
+ #
43
+ # Question class
44
+ #
45
+
46
+
38
47
  class MCQuestion(SelectQuestion):
39
48
  def __init__(
40
49
  self,
@@ -1,4 +1,4 @@
1
- from .reading_base import ReadingPython
1
+ from ..widgets_base.reading import ReadingPython
2
2
 
3
3
 
4
4
  class ReadingPythonQuestion(ReadingPython):
@@ -1,6 +1,10 @@
1
1
  import panel as pn
2
2
 
3
- from .multi_select_base import MultiSelectQuestion
3
+ from ..widgets_base.multi_select import MultiSelectQuestion
4
+
5
+ #
6
+ # Style function
7
+ #
4
8
 
5
9
 
6
10
  def MultiSelect(
@@ -42,6 +46,11 @@ def MultiSelect(
42
46
  return desc_widgets, checkboxes
43
47
 
44
48
 
49
+ #
50
+ # Question class
51
+ #
52
+
53
+
45
54
  class SelectMany(MultiSelectQuestion):
46
55
  def __init__(
47
56
  self,
@@ -5,7 +5,11 @@ import socket
5
5
  import numpy as np
6
6
  import panel as pn
7
7
 
8
- from .telemetry import ensure_responses, update_responses
8
+ from ..telemetry import ensure_responses, update_responses
9
+
10
+ #
11
+ # Constants
12
+ #
9
13
 
10
14
  EMAIL_PATTERN = re.compile(r"[a-z]+\d+@drexel\.edu")
11
15
 
@@ -20,6 +24,10 @@ KEYS = [
20
24
  "seed",
21
25
  ]
22
26
 
27
+ #
28
+ # Form class
29
+ #
30
+
23
31
 
24
32
  class StudentInfoForm:
25
33
  def __init__(self, **kwargs) -> None:
@@ -2,8 +2,12 @@ from typing import Tuple
2
2
 
3
3
  import panel as pn
4
4
 
5
- from .misc import list_of_lists
6
- from .select_base import SelectQuestion
5
+ from ..utils import list_of_lists
6
+ from ..widgets_base.select import SelectQuestion
7
+
8
+ #
9
+ # Style function
10
+ #
7
11
 
8
12
 
9
13
  def MultipleChoice(
@@ -31,6 +35,11 @@ def MultipleChoice(
31
35
  return desc_widgets, dropdowns
32
36
 
33
37
 
38
+ #
39
+ # Question class
40
+ #
41
+
42
+
34
43
  class TypesQuestion(SelectQuestion):
35
44
  def __init__(
36
45
  self,
@@ -0,0 +1 @@
1
+
@@ -2,8 +2,8 @@ from typing import Callable, Tuple
2
2
 
3
3
  import panel as pn
4
4
 
5
- from .misc import shuffle_questions
6
- from .telemetry import ensure_responses, update_responses
5
+ from ..telemetry import ensure_responses, update_responses
6
+ from ..utils import shuffle_questions
7
7
 
8
8
 
9
9
  class MultiSelectQuestion:
@@ -3,8 +3,8 @@ from typing import Optional
3
3
 
4
4
  import panel as pn
5
5
 
6
- from .misc import shuffle_options
7
- from .telemetry import ensure_responses, update_responses
6
+ from ..telemetry import ensure_responses, update_responses
7
+ from ..utils import shuffle_options
8
8
 
9
9
 
10
10
  class ReadingPython:
@@ -2,8 +2,8 @@ from typing import Callable, Tuple
2
2
 
3
3
  import panel as pn
4
4
 
5
- from .misc import shuffle_questions
6
- from .telemetry import ensure_responses, update_responses
5
+ from ..telemetry import ensure_responses, update_responses
6
+ from ..utils import shuffle_questions
7
7
 
8
8
 
9
9
  class SelectQuestion:
@@ -49,7 +49,10 @@ class SelectQuestion:
49
49
 
50
50
  self.layout = pn.Column(
51
51
  f"# Question {self.question_number}: {title}",
52
- *(pn.Column(desc_widget, pn.Row(dropdown)) for desc_widget, dropdown in widget_pairs),
52
+ *(
53
+ pn.Column(desc_widget, pn.Row(dropdown))
54
+ for desc_widget, dropdown in widget_pairs
55
+ ),
53
56
  self.submit_button,
54
57
  )
55
58
 
@@ -1,19 +0,0 @@
1
- pykubegrader/__init__.py,sha256=JHme-EVdE2QbS9_Q2qdHiy5bEsyeQDbZ8aP2OfoSyiw,583
2
- pykubegrader/info_widget.py,sha256=x73heTqmqJQ9XkaE3eW4eLXTGJlmmTdGEkGtbIcY7uY,3532
3
- pykubegrader/mc_widget.py,sha256=_qmJ4ZvU-KXamS726LqnCgecRg29hDd7w5WOerkLvuA,2022
4
- pykubegrader/misc.py,sha256=dKw6SyRYU3DWRgD3xER7wq-C9e1daWPkqr901LpcwiQ,642
5
- pykubegrader/multi_select_base.py,sha256=GJQ-xFJTErar94cFwTxsSmLKPPmywJRUQELhAbv-uFs,3102
6
- pykubegrader/reading_base.py,sha256=vSoyThl2vFbnbgB_LDc-DDRPWCM2xGbazEptDjNn4uA,5311
7
- pykubegrader/reading_widget.py,sha256=e8lXSzGI9dHCxW7fIvwJUU6j8qWQgHnKR06ke2W1k7w,3022
8
- pykubegrader/select_base.py,sha256=Z-oAsNRBraqx3_T_hkSDq_quwQsZn8d8PRakI-_LUdQ,2132
9
- pykubegrader/select_many_widget.py,sha256=Rmb9Es42qzSdPfhU6ypcKnw_30PwuARX2UKVwgwbQhg,3772
10
- pykubegrader/telemetry.py,sha256=3GhItJ9dw18XcFutfQEk4z-DllCVtKXCKKLesf_jxzQ,3194
11
- pykubegrader/types_widget.py,sha256=FOrnR2x1eNM-pcFNIC8JnQdylgSUSUzuACmiIRuFTbQ,2244
12
- pykubegrader/validate.py,sha256=guEj0yeu9A_-Ig6S8diOfaNEWxud3k7t4tI-i4Steak,10585
13
- pykubegrader/seed/__init__.py,sha256=7Tnb4qTHPIKLirU1IJQbiiQ9FBbeGs-H5clpFS0dPEo,79
14
- pykubegrader/seed/hash_seed.py,sha256=tTSyYaMqgALZTNqc74IRjGN0Ii6gs4vYtRb9FavkfAM,651
15
- PyKubeGrader-0.0.8.dist-info/LICENSE.txt,sha256=YTp-Ewc8Kems8PJEE27KnBPFnZSxoWvSg7nnknzPyYw,1546
16
- PyKubeGrader-0.0.8.dist-info/METADATA,sha256=xv-9tAM6NhMoAUQ6AUK0Qh6C9f4LpBdg7X2R-T6PLb0,2505
17
- PyKubeGrader-0.0.8.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
18
- PyKubeGrader-0.0.8.dist-info/top_level.txt,sha256=e550Klfze6higFxER1V62fnGOcIgiKRbsrl9CC4UdtQ,13
19
- PyKubeGrader-0.0.8.dist-info/RECORD,,
@@ -1,5 +0,0 @@
1
- # Auto-generated __init__.py
2
-
3
- from . import hash_seed
4
-
5
- __all__ = ['hash_seed']
@@ -1,25 +0,0 @@
1
- import hashlib
2
-
3
- def hash_to_seed(input_string):
4
- """
5
- Hash a string into a small integer seed less than 500.
6
-
7
- Parameters:
8
- input_string (str): The string to hash.
9
-
10
- Returns:
11
- int: A seed value (0 <= seed < 500).
12
- """
13
- # Ensure the input is a string
14
- input_string = str(input_string)
15
-
16
- # Create a SHA-256 hash of the string
17
- hash_object = hashlib.sha256(input_string.encode())
18
-
19
- # Convert the hash to an integer
20
- large_number = int.from_bytes(hash_object.digest(), 'big')
21
-
22
- # Reduce the number to a value less than 500
23
- small_seed = large_number % 500
24
-
25
- return small_seed
File without changes