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.
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/.github/workflows/main.yml +3 -1
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/.gitignore +3 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/.readthedocs.yml +1 -1
- {pykubegrader-0.0.8/src/PyKubeGrader.egg-info → pykubegrader-0.1.0}/PKG-INFO +13 -6
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/conf.py +3 -5
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/setup.cfg +12 -5
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/setup.py +5 -5
- {pykubegrader-0.0.8 → pykubegrader-0.1.0/src/PyKubeGrader.egg-info}/PKG-INFO +13 -6
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/src/PyKubeGrader.egg-info/SOURCES.txt +12 -14
- pykubegrader-0.1.0/src/PyKubeGrader.egg-info/requires.txt +20 -0
- pykubegrader-0.1.0/src/pykubegrader/__init__.py +9 -0
- pykubegrader-0.1.0/src/pykubegrader/initialize.py +28 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/src/pykubegrader/telemetry.py +25 -15
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/src/pykubegrader/validate.py +17 -12
- pykubegrader-0.1.0/src/pykubegrader/widgets/__init__.py +1 -0
- pykubegrader-0.0.8/src/pykubegrader/mc_widget.py → pykubegrader-0.1.0/src/pykubegrader/widgets/multiple_choice.py +11 -2
- pykubegrader-0.0.8/src/pykubegrader/reading_widget.py → pykubegrader-0.1.0/src/pykubegrader/widgets/reading_question.py +1 -1
- pykubegrader-0.0.8/src/pykubegrader/select_many_widget.py → pykubegrader-0.1.0/src/pykubegrader/widgets/select_many.py +10 -1
- pykubegrader-0.0.8/src/pykubegrader/info_widget.py → pykubegrader-0.1.0/src/pykubegrader/widgets/student_info.py +9 -1
- pykubegrader-0.0.8/src/pykubegrader/types_widget.py → pykubegrader-0.1.0/src/pykubegrader/widgets/types_question.py +11 -2
- pykubegrader-0.1.0/src/pykubegrader/widgets_base/__init__.py +1 -0
- pykubegrader-0.0.8/src/pykubegrader/multi_select_base.py → pykubegrader-0.1.0/src/pykubegrader/widgets_base/multi_select.py +2 -2
- pykubegrader-0.0.8/src/pykubegrader/reading_base.py → pykubegrader-0.1.0/src/pykubegrader/widgets_base/reading.py +2 -2
- pykubegrader-0.0.8/src/pykubegrader/select_base.py → pykubegrader-0.1.0/src/pykubegrader/widgets_base/select.py +6 -3
- pykubegrader-0.1.0/tests/conftest.py +10 -0
- pykubegrader-0.0.8/README.md +0 -3
- pykubegrader-0.0.8/docs/_static/.gitignore +0 -1
- pykubegrader-0.0.8/src/PyKubeGrader.egg-info/requires.txt +0 -13
- pykubegrader-0.0.8/src/pykubegrader/__init__.py +0 -16
- pykubegrader-0.0.8/src/pykubegrader/seed/__init__.py +0 -5
- pykubegrader-0.0.8/src/pykubegrader/seed/hash_seed.py +0 -25
- pykubegrader-0.0.8/tests/conftest.py +0 -10
- pykubegrader-0.0.8/uv.lock +0 -979
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/.coveragerc +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/AUTHORS.rst +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/CHANGELOG.rst +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/CONTRIBUTING.rst +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/LICENSE.txt +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/README.rst +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/Makefile +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/_static/Drexel_blue_Logo_square_Dark.png +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/_static/Drexel_blue_Logo_square_Light.png +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/_static/custom.css +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/authors.rst +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/changelog.rst +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/contributing.rst +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/index.rst +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/license.rst +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/readme.rst +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/docs/requirements.txt +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/pyproject.toml +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/src/PyKubeGrader.egg-info/dependency_links.txt +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/src/PyKubeGrader.egg-info/not-zip-safe +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/src/PyKubeGrader.egg-info/top_level.txt +0 -0
- /pykubegrader-0.0.8/src/pykubegrader/misc.py → /pykubegrader-0.1.0/src/pykubegrader/utils.py +0 -0
- {pykubegrader-0.0.8 → pykubegrader-0.1.0}/tests/import_test.py +0 -0
- {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 }}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: PyKubeGrader
|
3
|
-
Version: 0.0
|
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
|
17
|
-
Requires-Dist:
|
18
|
-
Requires-Dist:
|
19
|
-
Requires-Dist:
|
20
|
-
Requires-Dist:
|
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
|
-
|
32
|
-
|
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
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
3
|
-
|
2
|
+
Setup file for PyKubeGrader.
|
3
|
+
Use setup.cfg to configure your project.
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
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
|
17
|
-
Requires-Dist:
|
18
|
-
Requires-Dist:
|
19
|
-
Requires-Dist:
|
20
|
-
Requires-Dist:
|
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/
|
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/
|
35
|
+
src/pykubegrader/utils.py
|
46
36
|
src/pykubegrader/validate.py
|
47
|
-
src/pykubegrader/
|
48
|
-
src/pykubegrader/
|
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,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
|
-
|
17
|
-
|
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
|
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,
|
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
|
-
|
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
|
-
|
285
|
-
|
286
|
-
|
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
|
6
|
-
from .
|
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,6 +1,10 @@
|
|
1
1
|
import panel as pn
|
2
2
|
|
3
|
-
from .
|
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
|
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
|
6
|
-
from .
|
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
|
6
|
-
from
|
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
|
7
|
-
from
|
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
|
6
|
-
from
|
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
|
-
*(
|
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
|
pykubegrader-0.0.8/README.md
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
# Empty directory
|
@@ -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,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
|