PyKubeGrader 0.0.8__tar.gz → 0.1.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/.github/workflows/main.yml +3 -1
  2. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/.gitignore +3 -0
  3. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/.readthedocs.yml +1 -1
  4. {pykubegrader-0.0.8/src/PyKubeGrader.egg-info → pykubegrader-0.1.0}/PKG-INFO +13 -6
  5. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/conf.py +3 -5
  6. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/setup.cfg +12 -5
  7. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/setup.py +5 -5
  8. {pykubegrader-0.0.8 → pykubegrader-0.1.0/src/PyKubeGrader.egg-info}/PKG-INFO +13 -6
  9. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/src/PyKubeGrader.egg-info/SOURCES.txt +12 -14
  10. pykubegrader-0.1.0/src/PyKubeGrader.egg-info/requires.txt +20 -0
  11. pykubegrader-0.1.0/src/pykubegrader/__init__.py +9 -0
  12. pykubegrader-0.1.0/src/pykubegrader/initialize.py +28 -0
  13. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/src/pykubegrader/telemetry.py +25 -15
  14. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/src/pykubegrader/validate.py +17 -12
  15. pykubegrader-0.1.0/src/pykubegrader/widgets/__init__.py +1 -0
  16. pykubegrader-0.0.8/src/pykubegrader/mc_widget.py → pykubegrader-0.1.0/src/pykubegrader/widgets/multiple_choice.py +11 -2
  17. pykubegrader-0.0.8/src/pykubegrader/reading_widget.py → pykubegrader-0.1.0/src/pykubegrader/widgets/reading_question.py +1 -1
  18. pykubegrader-0.0.8/src/pykubegrader/select_many_widget.py → pykubegrader-0.1.0/src/pykubegrader/widgets/select_many.py +10 -1
  19. pykubegrader-0.0.8/src/pykubegrader/info_widget.py → pykubegrader-0.1.0/src/pykubegrader/widgets/student_info.py +9 -1
  20. pykubegrader-0.0.8/src/pykubegrader/types_widget.py → pykubegrader-0.1.0/src/pykubegrader/widgets/types_question.py +11 -2
  21. pykubegrader-0.1.0/src/pykubegrader/widgets_base/__init__.py +1 -0
  22. pykubegrader-0.0.8/src/pykubegrader/multi_select_base.py → pykubegrader-0.1.0/src/pykubegrader/widgets_base/multi_select.py +2 -2
  23. pykubegrader-0.0.8/src/pykubegrader/reading_base.py → pykubegrader-0.1.0/src/pykubegrader/widgets_base/reading.py +2 -2
  24. pykubegrader-0.0.8/src/pykubegrader/select_base.py → pykubegrader-0.1.0/src/pykubegrader/widgets_base/select.py +6 -3
  25. pykubegrader-0.1.0/tests/conftest.py +10 -0
  26. pykubegrader-0.0.8/README.md +0 -3
  27. pykubegrader-0.0.8/docs/_static/.gitignore +0 -1
  28. pykubegrader-0.0.8/src/PyKubeGrader.egg-info/requires.txt +0 -13
  29. pykubegrader-0.0.8/src/pykubegrader/__init__.py +0 -16
  30. pykubegrader-0.0.8/src/pykubegrader/seed/__init__.py +0 -5
  31. pykubegrader-0.0.8/src/pykubegrader/seed/hash_seed.py +0 -25
  32. pykubegrader-0.0.8/tests/conftest.py +0 -10
  33. pykubegrader-0.0.8/uv.lock +0 -979
  34. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/.coveragerc +0 -0
  35. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/AUTHORS.rst +0 -0
  36. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/CHANGELOG.rst +0 -0
  37. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/CONTRIBUTING.rst +0 -0
  38. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/LICENSE.txt +0 -0
  39. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/README.rst +0 -0
  40. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/Makefile +0 -0
  41. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/_static/Drexel_blue_Logo_square_Dark.png +0 -0
  42. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/_static/Drexel_blue_Logo_square_Light.png +0 -0
  43. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/_static/custom.css +0 -0
  44. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/authors.rst +0 -0
  45. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/changelog.rst +0 -0
  46. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/contributing.rst +0 -0
  47. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/index.rst +0 -0
  48. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/license.rst +0 -0
  49. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/readme.rst +0 -0
  50. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/requirements.txt +0 -0
  51. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/pyproject.toml +0 -0
  52. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/src/PyKubeGrader.egg-info/dependency_links.txt +0 -0
  53. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/src/PyKubeGrader.egg-info/not-zip-safe +0 -0
  54. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/src/PyKubeGrader.egg-info/top_level.txt +0 -0
  55. /pykubegrader-0.0.8/src/pykubegrader/misc.py → /pykubegrader-0.1.0/src/pykubegrader/utils.py +0 -0
  56. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/tests/import_test.py +0 -0
  57. {pykubegrader-0.0.8 → pykubegrader-0.1.0}/tox.ini +0 -0
@@ -49,7 +49,7 @@ jobs:
49
49
 
50
50
  - name: Publish to PyPI
51
51
  if: |
52
- contains(github.event.head_commit.message, '#patch') ||
52
+ contains(github.event.head_commit.message, '#patch') ||
53
53
  contains(github.event.head_commit.message, '#minor') ||
54
54
  contains(github.event.head_commit.message, '#major')
55
55
  env:
@@ -60,6 +60,8 @@ jobs:
60
60
  twine upload dist/*
61
61
 
62
62
  - name: Deploy to GitHub Pages
63
+ env:
64
+ HOME: "${{ github.workspace }}/../" # Because self-hosted runner is root
63
65
  uses: peaceiris/actions-gh-pages@v3
64
66
  with:
65
67
  github_token: ${{ secrets.GH_TOKEN }}
@@ -52,3 +52,6 @@ MANIFEST
52
52
  .venv*/
53
53
  .conda*/
54
54
  .python-version
55
+
56
+ .mypy_cache/
57
+ .ruff_cache/
@@ -24,4 +24,4 @@ build:
24
24
  python:
25
25
  install:
26
26
  - requirements: docs/requirements.txt
27
- - {path: ., method: pip}
27
+ - { path: ., method: pip }
@@ -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"
@@ -28,10 +28,8 @@ sys.path.insert(0, os.path.join(__location__, "../src"))
28
28
  # setup.py install" in the RTD Advanced Settings.
29
29
  # Additionally it helps us to avoid running apidoc manually
30
30
 
31
- try: # for Sphinx >= 1.7
32
- from sphinx.ext import apidoc
33
- except ImportError:
34
- from sphinx import apidoc
31
+
32
+ from sphinx.ext import apidoc
35
33
 
36
34
  output_dir = os.path.join(__location__, "api")
37
35
  module_dir = os.path.join(__location__, "../src/pykubegrader")
@@ -252,7 +250,7 @@ htmlhelp_basename = "PyKubeGrader-doc"
252
250
 
253
251
  # -- Options for LaTeX output ------------------------------------------------
254
252
 
255
- latex_elements = {
253
+ latex_elements: dict[str, str] = {
256
254
  # The paper size ("letterpaper" or "a4paper").
257
255
  # "papersize": "letterpaper",
258
256
  # The font size ("10pt", "11pt" or "12pt").
@@ -23,11 +23,18 @@ package_dir =
23
23
  =src
24
24
  install_requires =
25
25
  importlib-metadata; python_version<"3.8"
26
- ipython>=8.30.0
27
- numpy>=2.1.3
28
- panel>=1.5.4
29
- pynacl>=1.5.0
30
- requests>=2.32.3
26
+ ipython
27
+ mypy
28
+ numpy
29
+ panel
30
+ pynacl
31
+ pytest
32
+ requests
33
+ ruff
34
+ setuptools
35
+ sphinx
36
+ types-requests
37
+ types-setuptools
31
38
 
32
39
  [options.packages.find]
33
40
  where = src
@@ -1,10 +1,10 @@
1
1
  """
2
- Setup file for PyKubeGrader.
3
- Use setup.cfg to configure your project.
2
+ Setup file for PyKubeGrader.
3
+ Use setup.cfg to configure your project.
4
4
 
5
- This file was generated with PyScaffold 4.6.
6
- PyScaffold helps you to put up the scaffold of your new Python project.
7
- Learn more under: https://pyscaffold.org/
5
+ This file was generated with PyScaffold 4.6.
6
+ PyScaffold helps you to put up the scaffold of your new Python project.
7
+ Learn more under: https://pyscaffold.org/
8
8
  """
9
9
 
10
10
  from setuptools import setup
@@ -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"
@@ -5,13 +5,11 @@ AUTHORS.rst
5
5
  CHANGELOG.rst
6
6
  CONTRIBUTING.rst
7
7
  LICENSE.txt
8
- README.md
9
8
  README.rst
10
9
  pyproject.toml
11
10
  setup.cfg
12
11
  setup.py
13
12
  tox.ini
14
- uv.lock
15
13
  .github/workflows/main.yml
16
14
  docs/Makefile
17
15
  docs/authors.rst
@@ -22,7 +20,6 @@ docs/index.rst
22
20
  docs/license.rst
23
21
  docs/readme.rst
24
22
  docs/requirements.txt
25
- docs/_static/.gitignore
26
23
  docs/_static/Drexel_blue_Logo_square_Dark.png
27
24
  docs/_static/Drexel_blue_Logo_square_Light.png
28
25
  docs/_static/custom.css
@@ -33,18 +30,19 @@ src/PyKubeGrader.egg-info/not-zip-safe
33
30
  src/PyKubeGrader.egg-info/requires.txt
34
31
  src/PyKubeGrader.egg-info/top_level.txt
35
32
  src/pykubegrader/__init__.py
36
- src/pykubegrader/info_widget.py
37
- src/pykubegrader/mc_widget.py
38
- src/pykubegrader/misc.py
39
- src/pykubegrader/multi_select_base.py
40
- src/pykubegrader/reading_base.py
41
- src/pykubegrader/reading_widget.py
42
- src/pykubegrader/select_base.py
43
- src/pykubegrader/select_many_widget.py
33
+ src/pykubegrader/initialize.py
44
34
  src/pykubegrader/telemetry.py
45
- src/pykubegrader/types_widget.py
35
+ src/pykubegrader/utils.py
46
36
  src/pykubegrader/validate.py
47
- src/pykubegrader/seed/__init__.py
48
- src/pykubegrader/seed/hash_seed.py
37
+ src/pykubegrader/widgets/__init__.py
38
+ src/pykubegrader/widgets/multiple_choice.py
39
+ src/pykubegrader/widgets/reading_question.py
40
+ src/pykubegrader/widgets/select_many.py
41
+ src/pykubegrader/widgets/student_info.py
42
+ src/pykubegrader/widgets/types_question.py
43
+ src/pykubegrader/widgets_base/__init__.py
44
+ src/pykubegrader/widgets_base/multi_select.py
45
+ src/pykubegrader/widgets_base/reading.py
46
+ src/pykubegrader/widgets_base/select.py
49
47
  tests/conftest.py
50
48
  tests/import_test.py
@@ -0,0 +1,20 @@
1
+ ipython
2
+ mypy
3
+ numpy
4
+ panel
5
+ pynacl
6
+ pytest
7
+ requests
8
+ ruff
9
+ setuptools
10
+ sphinx
11
+ types-requests
12
+ types-setuptools
13
+
14
+ [:python_version < "3.8"]
15
+ importlib-metadata
16
+
17
+ [testing]
18
+ setuptools
19
+ pytest
20
+ pytest-cov
@@ -0,0 +1,9 @@
1
+ from importlib.metadata import PackageNotFoundError, version # pragma: no cover
2
+
3
+ try:
4
+ dist_name = "PyKubeGrader" # Change if project is renamed
5
+ __version__ = version(dist_name)
6
+ except PackageNotFoundError: # pragma: no cover
7
+ __version__ = "unknown"
8
+ finally:
9
+ del version, PackageNotFoundError
@@ -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")
@@ -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,
@@ -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")
@@ -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,
@@ -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
 
@@ -0,0 +1,10 @@
1
+ """
2
+ Dummy conftest.py for pykubegrader.
3
+
4
+ If you don't know what this is for, just leave it empty.
5
+ Read more about conftest.py under:
6
+ - https://docs.pytest.org/en/stable/fixture.html
7
+ - https://docs.pytest.org/en/stable/writing_plugins.html
8
+ """
9
+
10
+ import pytest # noqa: F401
@@ -1,3 +0,0 @@
1
- # pygrader-utils
2
-
3
- To be documented…
@@ -1 +0,0 @@
1
- # Empty directory
@@ -1,13 +0,0 @@
1
- ipython>=8.30.0
2
- numpy>=2.1.3
3
- panel>=1.5.4
4
- pynacl>=1.5.0
5
- requests>=2.32.3
6
-
7
- [:python_version < "3.8"]
8
- importlib-metadata
9
-
10
- [testing]
11
- setuptools
12
- pytest
13
- pytest-cov
@@ -1,16 +0,0 @@
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
8
-
9
- try:
10
- # Change here if project is renamed and does not equal the package name
11
- dist_name = "PyKubeGrader"
12
- __version__ = version(dist_name)
13
- except PackageNotFoundError: # pragma: no cover
14
- __version__ = "unknown"
15
- finally:
16
- del version, PackageNotFoundError
@@ -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
@@ -1,10 +0,0 @@
1
- """
2
- Dummy conftest.py for pykubegrader.
3
-
4
- If you don't know what this is for, just leave it empty.
5
- Read more about conftest.py under:
6
- - https://docs.pytest.org/en/stable/fixture.html
7
- - https://docs.pytest.org/en/stable/writing_plugins.html
8
- """
9
-
10
- import pytest