Skip to content

Commit

Permalink
Add load specific answer from answer file using student name
Browse files Browse the repository at this point in the history
Adds function `load_answer_from_student_name`,
`load_answer_from_loaded_file` to ExerciseRegistry API.
`load_answer_from_loaded_file` does what `load_answer` did before.
`load_answer` requires now an additional argument an `answers_filename`.
`load_answer_from_student_name` allows to load specific answers without
loading the entire file which can conflict with entries that do not have
yet a corresponding widget.
  • Loading branch information
agoscinski committed Dec 18, 2024
1 parent 71e3f2b commit b77cb10
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 25 deletions.
66 changes: 51 additions & 15 deletions src/scwidgets/exercise/_widget_exercise_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def load(self) -> Union[str, Exception]:
raise ValueError(
"No exercise key given on initialization, save cannot be used"
)
return self._exercise_registry.load_answer(self._exercise_key)
return self._exercise_registry.load_answer_from_loaded_file(self._exercise_key)

@property
def exercise_registry(self):
Expand Down Expand Up @@ -341,26 +341,65 @@ def create_new_file_from_student_name(self, student_name: str):

self._loaded_file_name = answers_filename

def load_answer(self, exercise_key: Hashable) -> str:
def load_answer_from_student_name(
self, student_name: str, exercise_key: Union[Hashable, ExerciseWidget]
):
"""
Only works when file has been loaded
Loads the answer with key `exercise_key` from the file corresponding to
`student_name`.
:param exercise_key:
unique exercise key for widget to store, so it can be reloaded persistently
after a restart of the python kernel
:raises KeyError: Corresponding widget to exercise_key cannot be found
:raises KeyError: Corresponding key in file cannot be found
:raises FileNotFoundError: If the file cannot be found
:param student_name: The name of the student
:param exercise_key: Unique exercise key for widget to store, so it can
be reloaded persistently after a restart of the python kernel
"""
self.load_answer(self.get_answer_filename(student_name), exercise_key)

def load_answer_from_loaded_file(
self, exercise_key: Union[Hashable, ExerciseWidget]
) -> str:
"""
Loads the answer with key `exercise_key` from the currently loaded file.
:raises ValueError: No have has been loaded
:raises KeyError: Corresponding widget to exercise_key cannot be found
:raises KeyError: Corresponding key in file cannot be found
:raises FileNotFoundError: If the file cannot be found
:param exercise_key: Unique exercise key for widget to store, so it can
be reloaded persistently after a restart of the python kernel
"""
if self._loaded_file_name is None:
raise ValueError("No file has been loaded.")

self.load_answer(self._loaded_file_name, exercise_key)
return f"Exercise has been loaded from file {self._loaded_file_name!r}."

def load_answer(
self, answers_filename: str, exercise_key: Union[Hashable, ExerciseWidget]
):
"""
Loads the answer with key `exercise_key` from the `answer_filename`.
:raises KeyError: Corresponding widget to exercise_key cannot be found
:raises KeyError: Corresponding key in file cannot be found
:raises FileNotFoundError: If the file cannot be found
:param answers_filename: The file with the answer
:param exercise_key: Unique exercise key for widget to store, so it can
be reloaded persistently after a restart of the python kernel
"""
if isinstance(exercise_key, ExerciseWidget):
exercise_key = exercise_key.exercise_key

if exercise_key not in self._widgets.keys():
raise KeyError(
f"There is no widget registered with exercise key {exercise_key!r}."
)
if self._loaded_file_name is None:
raise ValueError("No file has been selected in the dropdown list.")

answers_filename = self._loaded_file_name

if not (os.path.exists(answers_filename)):
raise FileNotFoundError(
"Selected file does not exist anymore. Maybe you have renamed "
f"The file {answers_filename!r} does not exist. Maybe you have renamed "
"or deleted it? Please choose another file or create a new one."
)

Expand All @@ -375,9 +414,6 @@ def load_answer(self, exercise_key: Hashable) -> str:
else:
self._widgets[exercise_key].answer = answers[exercise_key]
self._loaded_file_name = answers_filename
return f"Exercise has been loaded from file {answers_filename!r}."

self._answers_files_dropdown.value

def load_file_from_dropdown(self) -> str:
"""
Expand Down Expand Up @@ -410,7 +446,7 @@ def load_file(self, answers_filename: str):

if not (os.path.exists(answers_filename)):
raise FileNotFoundError(
"Selected file does not exist anymore. Maybe you have renamed "
f"The file {answers_filename!r} does not exist. Maybe you have renamed "
"or deleted it? Please choose another file or create a new one."
)

Expand Down
28 changes: 18 additions & 10 deletions tests/test_answer.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,8 @@ def test_load_file_from_dropdown(self):
# Tests if error is raised on moved file
os.rename(f"{self.prefix}-{self.student_name}.json", "tmp.json")
with pytest.raises(
FileNotFoundError, match=r".*Selected file does not exist anymore.*"
FileNotFoundError,
match=r".*The file 'pytest-test-answer-registry.json' does not exist.*",
):
answer_registry.load_file_from_dropdown()
os.rename("tmp.json", f"{self.prefix}-{self.student_name}.json")
Expand All @@ -236,18 +237,26 @@ def test_load_file_from_dropdown(self):
assert answer_widget_1.answer == "update_1"
assert answer_widget_2.answer == "update_2"

def test_load_answer(self):
def test_load_answer_from_loaded_file(self):
answers = {"exercise_1": "answer_1", "exercise_2": "answer_2"}
with open(f"{self.prefix}-{self.student_name}.json", "w") as answer_file:
json.dump(answers, answer_file)

answer_registry = ExerciseRegistry(filename_prefix=self.prefix)
with pytest.raises(
ValueError,
match=r".*No file has been loaded*",
):
answer_registry.load_answer_from_loaded_file("notkey")

# To avoid the no loaded file error
answer_registry._loaded_file_name = "some"
with pytest.raises(
KeyError,
match=r".*There is no widget registered with exercise key 'notkey'.*",
):
answer_registry.load_answer("notkey")
answer_registry.load_answer_from_loaded_file("notkey")
answer_registry._loaded_file_name = None

exercise_key_1 = "exercise_1"
answer_widget_1 = mock_answer_widget(answer_registry, exercise_key_1)
Expand All @@ -259,10 +268,8 @@ def test_load_answer(self):
answer_registry._answers_files_dropdown.value = (
answer_registry._create_new_file_dropdown_option()
)
with pytest.raises(
ValueError, match=r".*No file has been selected in the dropdown list.*"
):
answer_registry.load_answer(exercise_key_1)
with pytest.raises(ValueError, match=r".*No file has been loaded.*"):
answer_registry.load_answer_from_loaded_file(exercise_key_1)
# select back file to load
answer_registry._answers_files_dropdown.value = (
f"{self.prefix}-{self.student_name}.json"
Expand All @@ -272,15 +279,16 @@ def test_load_answer(self):
# Tests if error is raised on moved file
os.rename(f"{self.prefix}-{self.student_name}.json", "tmp.json")
with pytest.raises(
FileNotFoundError, match=r".*Selected file does not exist anymore.*"
FileNotFoundError,
match=r".*The file 'pytest-test-answer-registry.json' does not exist.*",
):
answer_registry.load_answer(exercise_key_1)
answer_registry.load_answer_from_loaded_file(exercise_key_1)
os.rename("tmp.json", f"{self.prefix}-{self.student_name}.json")

# Test that file is contains only the updated answer
answer_widget_1.answer = "update_1"
answer_widget_2.answer = "update_2"
result = answer_registry.load_answer(exercise_key_1)
result = answer_registry.load_answer_from_loaded_file(exercise_key_1)
assert (
result == "Exercise has been loaded from file "
"'pytest-test-answer-registry.json'."
Expand Down

0 comments on commit b77cb10

Please sign in to comment.