diff --git a/optuna_dashboard/_preferential_history.py b/optuna_dashboard/_preferential_history.py index dff10885..f95d57d3 100644 --- a/optuna_dashboard/_preferential_history.py +++ b/optuna_dashboard/_preferential_history.py @@ -61,7 +61,7 @@ def report_history( study_id: int, storage: BaseStorage, input_data: NewHistoryJSON, -) -> None: +) -> str: preferences = [] if input_data["mode"] == "ChooseWorst": preferences = [ @@ -95,6 +95,7 @@ def report_history( key=key, value=history.to_dict(), ) + return history_uuid def _load_preference_history(value: Any) -> History: diff --git a/python_tests/test_api.py b/python_tests/test_api.py index 7d732e37..1fc92508 100644 --- a/python_tests/test_api.py +++ b/python_tests/test_api.py @@ -8,6 +8,7 @@ from optuna import get_all_study_summaries from optuna.study import StudyDirection from optuna_dashboard._app import create_app from optuna_dashboard._app import create_new_study +from optuna_dashboard._preferential_history import serialize_preference_histories from optuna_dashboard.preferential import create_study from .wsgi_client import send_request @@ -181,6 +182,76 @@ class APITestCase(TestCase): assert best_trials[0].number == 0 assert best_trials[1].number == 2 + def test_undo_redo_history(self) -> None: + storage = optuna.storages.InMemoryStorage() + study = create_study(storage=storage) + for _ in range(3): + trial = study.ask() + study.mark_comparison_ready(trial) + + app = create_app(storage) + study_id = study._study._study_id + status, _, _ = send_request( + app, + f"/api/studies/{study_id}/preference", + "POST", + body=json.dumps( + { + "mode": "ChooseWorst", + "candidates": [0, 1, 2], + "clicked": 2, + } + ), + content_type="application/json", + ) + self.assertEqual(status, 204) + histories = serialize_preference_histories(storage.get_study_system_attrs(study_id)) + assert len(histories) == 1 + assert histories[0]["enabled"] + + history_uuid = histories[0]["uuid"] + status, _, _ = send_request( + app, + f"/api/studies/{study_id}/preference/{history_uuid}", + "PUT", + body=json.dumps( + { + "enable": False, + } + ), + content_type="application/json", + ) + self.assertEqual(status, 204) + histories = serialize_preference_histories(storage.get_study_system_attrs(study_id)) + assert len(histories) == 1 + assert not histories[0]["enabled"] + assert len(study.get_preferences()) == 0 + + status, _, _ = send_request( + app, + f"/api/studies/{study_id}/preference/{history_uuid}", + "PUT", + body=json.dumps( + { + "enable": True, + } + ), + content_type="application/json", + ) + self.assertEqual(status, 204) + histories = serialize_preference_histories(storage.get_study_system_attrs(study_id)) + assert len(histories) == 1 + assert histories[0]["enabled"] + preferences = study.get_preferences() + preferences.sort(key=lambda x: (x[0].number, x[1].number)) + assert len(preferences) == 2 + better, worse = preferences[0] + assert better.number == 0 + assert worse.number == 2 + better, worse = preferences[1] + assert better.number == 1 + assert worse.number == 2 + def test_create_study(self) -> None: for name, directions, expected_status in [ ("single-objective success", ["minimize"], 201), diff --git a/python_tests/test_preferential_history.py b/python_tests/test_preferential_history.py index 85f25374..7b288968 100644 --- a/python_tests/test_preferential_history.py +++ b/python_tests/test_preferential_history.py @@ -2,10 +2,13 @@ from __future__ import annotations from typing import Callable +from optuna_dashboard._preferential_history import load_preference_history from optuna_dashboard._preferential_history import report_history +from optuna_dashboard._preferential_history import switching_history from optuna_dashboard._serializer import serialize_preference_histories from optuna_dashboard.preferential import create_study from optuna_dashboard.preferential._system_attrs import _SYSTEM_ATTR_PREFIX_PREFERENCE +from optuna_dashboard.preferential._system_attrs import get_preference from .storage_supplier import parametrize_storages from .storage_supplier import StorageSupplier @@ -59,3 +62,58 @@ def test_report_and_get_choices(storage_supplier: Callable[[], StorageSupplier]) assert len(preferences[i]) == 2 assert preferences[i][0] == best assert preferences[i][1] == worst + + +@parametrize_storages +def test_undo_redo_history(storage_supplier: Callable[[], StorageSupplier]) -> None: + with storage_supplier() as storage: + study = create_study(storage=storage) + for _ in range(5): + trial = study.ask() + trial.suggest_float("x", 0, 1) + study.mark_comparison_ready(trial) + + study_id = study._study._study_id + + history_uuid = report_history( + study_id=study_id, + storage=storage, + input_data={ + "mode": "ChooseWorst", + "candidates": [0, 1, 2], + "clicked": 1, + }, + ) + switching_history(study_id, storage, history_uuid, False) + history = load_preference_history(history_uuid, storage.get_study_system_attrs(study_id)) + preference = get_preference(study_id, storage, history.preference_uuid) + assert history.mode == "ChooseWorst" + assert history.candidates == [0, 1, 2] + assert history.clicked == 1 + assert len(history.evacuated_preference) == 2 + assert len(preference) == 0 + + switching_history(study_id, storage, history_uuid, False) + history = load_preference_history(history_uuid, storage.get_study_system_attrs(study_id)) + preference = get_preference(study_id, storage, history.preference_uuid) + assert len(history.evacuated_preference) == 2 + assert len(preference) == 0 + + switching_history(study_id, storage, history_uuid, True) + history = load_preference_history(history_uuid, storage.get_study_system_attrs(study_id)) + preference = get_preference(study_id, storage, history.preference_uuid) + assert history.mode == "ChooseWorst" + assert history.candidates == [0, 1, 2] + assert history.clicked == 1 + assert len(history.evacuated_preference) == 0 + assert len(preference) == 2 + for i, (best, worst) in enumerate([(0, 1), (2, 1)]): + assert len(preference[i]) == 2 + assert preference[i][0] == best + assert preference[i][1] == worst + + switching_history(study_id, storage, history_uuid, True) + history = load_preference_history(history_uuid, storage.get_study_system_attrs(study_id)) + preference = get_preference(study_id, storage, history.preference_uuid) + assert len(history.evacuated_preference) == 0 + assert len(preference) == 2