Skip to content

Commit f98d5a1

Browse files
committed
move answer submodule to exercise submodule
* AnswerRegistry -> ExerciseRegistry * AnswerWidget -> ExerciseWidget * answer_key -> exercise_key * answer_registry -> exercise_registry
1 parent 7b4f61e commit f98d5a1

File tree

8 files changed

+139
-135
lines changed

8 files changed

+139
-135
lines changed

src/scwidgets/answer/__init__.py

-3
This file was deleted.

src/scwidgets/exercise/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from ._widget_code_exercise import CodeExercise
2+
from ._widget_exercise_registry import ExerciseRegistry, ExerciseWidget
23
from ._widget_text_exercise import TextExercise
34

4-
__all__ = ["CodeExercise", "TextExercise"]
5+
__all__ = ["CodeExercise", "TextExercise", "ExerciseWidget", "ExerciseRegistry"]

src/scwidgets/exercise/_widget_code_exercise.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from widget_code_input.utils import CodeValidationError
1212

1313
from .._utils import Formatter
14-
from ..answer import AnswerRegistry, AnswerWidget
1514
from ..check import Check, CheckableWidget, CheckRegistry, ChecksResult
1615
from ..code._widget_code_input import CodeInput
1716
from ..code._widget_parameter_panel import ParameterPanel
@@ -24,9 +23,10 @@
2423
UpdateCueBox,
2524
UpdateResetCueButton,
2625
)
26+
from ._widget_exercise_registry import ExerciseRegistry, ExerciseWidget
2727

2828

29-
class CodeExercise(VBox, CheckableWidget, AnswerWidget):
29+
class CodeExercise(VBox, CheckableWidget, ExerciseWidget):
3030
"""
3131
A widget to demonstrate code interactively in a variety of ways. It is a combination
3232
of the several widgets that allow to check check, run and visualize code.
@@ -59,8 +59,8 @@ def __init__(
5959
self,
6060
code: Union[None, WidgetCodeInput, types.FunctionType] = None,
6161
check_registry: Optional[CheckRegistry] = None,
62-
answer_registry: Optional[AnswerRegistry] = None,
63-
answer_key: Optional[str] = None,
62+
exercise_registry: Optional[ExerciseRegistry] = None,
63+
exercise_key: Optional[str] = None,
6464
parameters: Optional[
6565
Union[Dict[str, Union[Check.FunInParamT, Widget]], ParameterPanel]
6666
] = None,
@@ -90,12 +90,12 @@ def __init__(
9090
else:
9191
self._exercise_description_html = HTMLMath(self._exercise_description)
9292
if exercise_title is None:
93-
if answer_key is None:
93+
if exercise_key is None:
9494
self._exercise_title = None
9595
self._exercise_title_html = None
9696
else:
97-
self._exercise_title = answer_key
98-
self._exercise_title_html = HTML(f"<b>{answer_key}</b>")
97+
self._exercise_title = exercise_key
98+
self._exercise_title_html = HTML(f"<b>{exercise_key}</b>")
9999
else:
100100
self._exercise_title = exercise_title
101101
self._exercise_title_html = HTML(f"<b>{exercise_title}</b>")
@@ -148,8 +148,8 @@ def __init__(
148148
elif not (isinstance(cue_outputs, list)):
149149
cue_outputs = [cue_outputs]
150150

151-
CheckableWidget.__init__(self, check_registry, answer_key)
152-
AnswerWidget.__init__(self, answer_registry, answer_key)
151+
CheckableWidget.__init__(self, check_registry, exercise_key)
152+
ExerciseWidget.__init__(self, exercise_registry, exercise_key)
153153

154154
self._code = code
155155
self._output = CueOutput()
@@ -320,7 +320,7 @@ def __init__(
320320
button_tooltip=button_tooltip,
321321
)
322322

323-
if self._answer_registry is None or (
323+
if self._exercise_registry is None or (
324324
self._code is None and self._parameter_panel is None
325325
):
326326
self._save_button = None

src/scwidgets/answer/_widget_answer_registry.py renamed to src/scwidgets/exercise/_widget_exercise_registry.py

+60-55
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@
1414
from .._utils import Formatter
1515

1616

17-
class AnswerWidget:
17+
class ExerciseWidget:
1818
"""
1919
Any widget inheriting from this class can be (de)serialized
2020
:py:class:`WidgetStateRegistry`. The serialization offered by ipywidgets
2121
cannot be loaded out-of-the-box for restarted notebook since the widget IDs change
2222
23-
:param answer_registry:
24-
the answer registry that registers the answers for this widget
23+
:param exercise_registry:
24+
the exercise registry that registers the answers for this widget
2525
26-
:param answer_key:
26+
:param exercise_key:
2727
Identifier for the widget, must be unique for each regestired widget
2828
2929
Reference
@@ -34,19 +34,23 @@ class AnswerWidget:
3434

3535
def __init__(
3636
self,
37-
answer_registry: Union[None, AnswerRegistry],
38-
answer_key: Union[None, Hashable],
37+
exercise_registry: Union[None, ExerciseRegistry],
38+
exercise_key: Union[None, Hashable],
3939
):
40-
if answer_registry is not None and answer_key is None:
41-
raise ValueError("answer registry was given but no answer key was given")
42-
elif answer_registry is None and answer_key is not None:
43-
raise ValueError("answer key was given but no answer registry was given")
40+
if exercise_registry is not None and exercise_key is None:
41+
raise ValueError(
42+
"exercise registry was given but no exercise key was given"
43+
)
44+
elif exercise_registry is None and exercise_key is not None:
45+
raise ValueError(
46+
"exercise key was given but no exercise registry was given"
47+
)
4448
# we need to use a key because self is not persistent on kernel restarts
45-
self._answer_registry = answer_registry
46-
self._answer_key = answer_key
49+
self._exercise_registry = exercise_registry
50+
self._exercise_key = exercise_key
4751

48-
if self._answer_registry is not None and answer_key is not None:
49-
self._answer_registry.register_widget(self, self._answer_key)
52+
if self._exercise_registry is not None and exercise_key is not None:
53+
self._exercise_registry.register_widget(self, self._exercise_key)
5054

5155
@property
5256
def answer(self) -> dict:
@@ -77,34 +81,34 @@ def handle_load_result(self, result: Union[str, Exception]) -> None:
7781
raise NotImplementedError("handle_load_result has not been implemented")
7882

7983
def save(self) -> Union[str, Exception]:
80-
if self._answer_registry is None:
84+
if self._exercise_registry is None:
8185
raise ValueError(
82-
"No answer registry given on initialization, save cannot be used"
86+
"No exercise registry given on initialization, save cannot be used"
8387
)
84-
if self._answer_key is None:
88+
if self._exercise_key is None:
8589
raise ValueError(
86-
"No answer key given on initialization, save cannot be used"
90+
"No exercise key given on initialization, save cannot be used"
8791
)
88-
return self._answer_registry.save_answer(self._answer_key)
92+
return self._exercise_registry.save_answer(self._exercise_key)
8993

9094
def load(self) -> Union[str, Exception]:
91-
if self._answer_registry is None:
95+
if self._exercise_registry is None:
9296
raise ValueError(
93-
"No answer registry given on initialization, load cannot be used"
97+
"No exercise registry given on initialization, load cannot be used"
9498
)
95-
if self._answer_key is None:
99+
if self._exercise_key is None:
96100
raise ValueError(
97-
"No answer key given on initialization, save cannot be used"
101+
"No exercise key given on initialization, save cannot be used"
98102
)
99-
return self._answer_registry.load_answer(self._answer_key)
103+
return self._exercise_registry.load_answer(self._exercise_key)
100104

101105
@property
102-
def answer_registry(self):
103-
return self._answer_registry
106+
def exercise_registry(self):
107+
return self._exercise_registry
104108

105109
@property
106-
def answer_key(self):
107-
return self._answer_key
110+
def exercise_key(self):
111+
return self._exercise_key
108112

109113

110114
class FilenameParser:
@@ -152,7 +156,7 @@ def verify_valid_student_name(student_name: str):
152156
)
153157

154158

155-
class AnswerRegistry(VBox):
159+
class ExerciseRegistry(VBox):
156160
""" """
157161

158162
def __init__(self, filename_prefix: Optional[str] = None, *args, **kwargs):
@@ -268,15 +272,15 @@ def registered_widgets(self):
268272
def loaded_file_name(self):
269273
return self._loaded_file_name
270274

271-
def register_widget(self, widget: AnswerWidget, answer_key: Hashable):
275+
def register_widget(self, widget: ExerciseWidget, exercise_key: Hashable):
272276
"""
273277
:param widget:
274278
widget answer is save on click of save button
275-
:param answer_key:
276-
unique answer key for widget to store, so it can be reloaded persistently
279+
:param exercise_key:
280+
unique exercise key for widget to store, so it can be reloaded persistently
277281
after a restart of the python kernel
278282
"""
279-
self._widgets[answer_key] = widget
283+
self._widgets[exercise_key] = widget
280284

281285
def create_new_file(self) -> str:
282286
FilenameParser.verify_valid_student_name(self._student_name_text.value)
@@ -314,17 +318,17 @@ def create_new_file(self) -> str:
314318
self._loaded_file_name = answers_filename
315319
return f"File {answers_filename!r} created and loaded."
316320

317-
def load_answer(self, answer_key: Hashable) -> str:
321+
def load_answer(self, exercise_key: Hashable) -> str:
318322
"""
319323
Only works when file has been loaded
320324
321-
:param answer_key:
322-
unique answer key for widget to store, so it can be reloaded persistently
325+
:param exercise_key:
326+
unique exercise key for widget to store, so it can be reloaded persistently
323327
after a restart of the python kernel
324328
"""
325-
if answer_key not in self._widgets.keys():
329+
if exercise_key not in self._widgets.keys():
326330
raise KeyError(
327-
f"There is no widget registered with answer key {answer_key!r}."
331+
f"There is no widget registered with exercise key {exercise_key!r}."
328332
)
329333
if self._loaded_file_name is None:
330334
raise ValueError("No file has been selected in the dropdown list.")
@@ -340,14 +344,15 @@ def load_answer(self, answer_key: Hashable) -> str:
340344
answers_filename = self._answers_files_dropdown.value
341345
with open(answers_filename, "r") as answers_file:
342346
answers = json.load(answers_file)
343-
if answer_key not in answers.keys():
347+
if exercise_key not in answers.keys():
344348
raise KeyError(
345-
f"Your file does not contain the answer with answer key {answer_key!r}."
349+
"Your file does not contain the answer with exercise key "
350+
f"{exercise_key!r}."
346351
)
347352
else:
348-
self._widgets[answer_key].answer = answers[answer_key]
353+
self._widgets[exercise_key].answer = answers[exercise_key]
349354
self._loaded_file_name = answers_filename
350-
return f"Answer has been loaded from file {answers_filename!r}."
355+
return f"Exercise has been loaded from file {answers_filename!r}."
351356

352357
def load_file(self) -> str:
353358
"""
@@ -376,26 +381,26 @@ def load_file(self) -> str:
376381

377382
with open(answers_filename, "r") as answers_file:
378383
answers = json.load(answers_file)
379-
for answer_key, answer in answers.items():
380-
if answer_key not in self._widgets.keys():
384+
for exercise_key, answer in answers.items():
385+
if exercise_key not in self._widgets.keys():
381386
raise ValueError(
382-
f"Your file contains an answer with key {answer_key!r} "
387+
f"Your file contains an answer with key {exercise_key!r} "
383388
f"with no corresponding registered widget."
384389
)
385390
else:
386-
self._widgets[answer_key].answer = answer
391+
self._widgets[exercise_key].answer = answer
387392
self._loaded_file_name = answers_filename
388393

389394
# only notifiy all widgets when result was successful
390395
for widget in self._widgets.values():
391-
result = f"Answer has been loaded from file {self._loaded_file_name!r}."
396+
result = f"Exercise has been loaded from file {self._loaded_file_name!r}."
392397
widget.handle_load_result(result)
393398
return f"All answers loaded from file {answers_filename!r}."
394399

395-
def save_answer(self, answer_key: Hashable) -> str:
396-
if not (answer_key in self._widgets.keys()):
400+
def save_answer(self, exercise_key: Hashable) -> str:
401+
if not (exercise_key in self._widgets.keys()):
397402
raise KeyError(
398-
f"There is no widget registered with answer key {answer_key!r}."
403+
f"There is no widget registered with exercise key {exercise_key!r}."
399404
)
400405

401406
if self._loaded_file_name is None:
@@ -411,11 +416,11 @@ def save_answer(self, answer_key: Hashable) -> str:
411416
else:
412417
with open(self._loaded_file_name, "r") as answers_file:
413418
answers = json.load(answers_file)
414-
answers[answer_key] = self._widgets[answer_key].answer
419+
answers[exercise_key] = self._widgets[exercise_key].answer
415420

416421
with open(self._loaded_file_name, "w") as answers_file:
417422
json.dump(answers, answers_file)
418-
result = f"Answer has been saved in file {self._loaded_file_name!r}."
423+
result = f"Exercise has been saved in file {self._loaded_file_name!r}."
419424
return result
420425

421426
def save_all_answers(self) -> str:
@@ -434,15 +439,15 @@ def save_all_answers(self) -> str:
434439
else:
435440
with open(self._loaded_file_name, "r") as answers_file:
436441
answers = json.load(answers_file)
437-
for answer_key, widget in self._widgets.items():
438-
answers[answer_key] = widget.answer
442+
for exercise_key, widget in self._widgets.items():
443+
answers[exercise_key] = widget.answer
439444

440445
with open(self._loaded_file_name, "w") as answers_file:
441446
json.dump(answers, answers_file)
442447

443448
# only notifiy all widgets when result was successful
444449
for widget in self._widgets.values():
445-
result = f"Answer has been saved in file {self._loaded_file_name!r}."
450+
result = f"Exercise has been saved in file {self._loaded_file_name!r}."
446451
widget.handle_save_result(result)
447452

448453
return f"All answers were saved in file {self._loaded_file_name!r}."

src/scwidgets/exercise/_widget_text_exercise.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
from ipywidgets import HTML, HBox, HTMLMath, Layout, Output, Textarea, VBox
44

55
from .._utils import Formatter
6-
from ..answer import AnswerRegistry, AnswerWidget
76
from ..cue import SaveCueBox, SaveResetCueButton
7+
from ._widget_exercise_registry import ExerciseRegistry, ExerciseWidget
88

99

10-
class TextExercise(VBox, AnswerWidget):
10+
class TextExercise(VBox, ExerciseWidget):
1111
"""
1212
:param textarea:
1313
a custom textarea with custom styling, if not specified the standard parameters
@@ -19,8 +19,8 @@ class TextExercise(VBox, AnswerWidget):
1919
def __init__(
2020
self,
2121
value: Optional[str] = None,
22-
answer_key: Optional[str] = None,
23-
answer_registry: Optional[AnswerRegistry] = None,
22+
exercise_key: Optional[str] = None,
23+
exercise_registry: Optional[ExerciseRegistry] = None,
2424
exercise_description: Optional[str] = None,
2525
exercise_title: Optional[str] = None,
2626
*args,
@@ -32,12 +32,12 @@ def __init__(
3232
else:
3333
self._exercise_description_html = HTMLMath(self._exercise_description)
3434
if exercise_title is None:
35-
if answer_key is None:
35+
if exercise_key is None:
3636
self._exercise_title = None
3737
self._exercise_title_html = None
3838
else:
39-
self._exercise_title = answer_key
40-
self._exercise_title_html = HTML(f"<b>{answer_key}</b>")
39+
self._exercise_title = exercise_key
40+
self._exercise_title_html = HTML(f"<b>{exercise_key}</b>")
4141
else:
4242
self._exercise_title = exercise_title
4343
self._exercise_title_html = HTML(f"<b>{exercise_title}</b>")
@@ -52,7 +52,7 @@ def __init__(
5252
self._cue_textarea = self._textarea
5353
self._output = Output()
5454

55-
if answer_registry is None:
55+
if exercise_registry is None:
5656
self._save_button = None
5757
self._load_button = None
5858
self._button_panel = None
@@ -97,7 +97,7 @@ def __init__(
9797
layout=Layout(justify_content="flex-end"),
9898
)
9999

100-
AnswerWidget.__init__(self, answer_registry, answer_key)
100+
ExerciseWidget.__init__(self, exercise_registry, exercise_key)
101101

102102
widget_children = []
103103
if self._exercise_title_html is not None:

0 commit comments

Comments
 (0)