Skip to content

Commit

Permalink
feat(api): add delete_output and delete_outputs methods (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
salemsd authored and salemsd committed Jan 9, 2025
1 parent 5679a88 commit 8d4fb3c
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/antares/craft/exceptions/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,12 @@ def __init__(self, study_id: str, message: str) -> None:
super().__init__(self.message)


class OutputDeletionError(Exception):
def __init__(self, study_id: str, output_name: str, message: str) -> None:
self.message = f"Could not delete the output {output_name} from study {study_id}: " + message
super().__init__(self.message)


class ConstraintRetrievalError(Exception):
def __init__(self, study_id: str, message: str) -> None:
self.message = f"Could not get binding constraints for {study_id}: " + message
Expand Down
8 changes: 8 additions & 0 deletions src/antares/craft/model/study.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,14 @@ def get_output(self, output_id: str) -> Output:
"""
return self._outputs[output_id]

def delete_outputs(self) -> None:
self._study_service.delete_outputs()
self._outputs.clear()

def delete_output(self, output_name: str) -> None:
self._study_service.delete_output(output_name)
self._outputs.pop(output_name)


def _verify_study_already_exists(study_directory: Path) -> None:
if study_directory.exists():
Expand Down
21 changes: 21 additions & 0 deletions src/antares/craft/service/api_services/study_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from antares.craft.exceptions.exceptions import (
APIError,
BindingConstraintDeletionError,
OutputDeletionError,
OutputsRetrievalError,
StudyDeletionError,
StudySettingsUpdateError,
Expand Down Expand Up @@ -141,3 +142,23 @@ def read_outputs(self) -> list[Output]:
]
except APIError as e:
raise OutputsRetrievalError(self.study_id, e.message)

def delete_outputs(self) -> None:
outputs_url = f"{self._base_url}/studies/{self.study_id}/outputs"
try:
response = self._wrapper.get(outputs_url)
outputs_json_list = response.json()
if not outputs_json_list:
raise OutputsRetrievalError(self.study_id, "No outputs to delete.")
for output in outputs_json_list:
output_name = output["name"]
self.delete_output(output_name)
except APIError as e:
raise OutputsRetrievalError(self.study_id, e.message)

def delete_output(self, output_name: str) -> None:
url = f"{self._base_url}/studies/{self.study_id}/outputs/{output_name}"
try:
self._wrapper.delete(url)
except APIError as e:
raise OutputDeletionError(self.study_id, output_name, e.message) from e
17 changes: 17 additions & 0 deletions src/antares/craft/service/base_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,23 @@ def read_outputs(self) -> list[Output]:
def set_output_service(self, output_service: "BaseOutputService") -> None:
pass

@abstractmethod
def delete_outputs(self) -> None:
"""
Deletes all the outputs of the study
"""
pass

@abstractmethod
def delete_output(self, output_name: str) -> None:
"""
Deletes given output from the study
Args:
output_name: To be deleted output
"""
pass


class BaseRenewableService(ABC):
@abstractmethod
Expand Down
6 changes: 6 additions & 0 deletions src/antares/craft/service/local_services/study_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,9 @@ def create_variant(self, variant_name: str) -> "Study":

def read_outputs(self) -> list[Output]:
raise NotImplementedError

def delete_outputs(self) -> None:
raise NotImplementedError

def delete_output(self, output_name: str) -> None:
raise NotImplementedError
51 changes: 51 additions & 0 deletions tests/antares/services/api_services/test_study_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
BindingConstraintCreationError,
ConstraintRetrievalError,
LinkCreationError,
OutputDeletionError,
OutputsRetrievalError,
SimulationFailedError,
SimulationTimeOutError,
Expand Down Expand Up @@ -597,3 +598,53 @@ def test_output_aggregate_values(self):
expected_matrix = pd.read_csv(StringIO(aggregate_output))
assert isinstance(aggregated_matrix, pd.DataFrame)
assert aggregated_matrix.equals(expected_matrix)

def test_delete_output(self):
output_name = "test_output"
with requests_mock.Mocker() as mocker:
outputs_url = f"https://antares.com/api/v1/studies/{self.study_id}/outputs"
delete_url = f"{outputs_url}/{output_name}"
mocker.get(outputs_url, json=[{"name": output_name, "archived": False}], status_code=200)
mocker.delete(delete_url, status_code=200)

self.study.read_outputs()
assert output_name in self.study.get_outputs()
self.study.delete_output(output_name)
assert output_name not in self.study.get_outputs()

# failing
error_message = f"Output {output_name} deletion failed"
mocker.delete(delete_url, json={"description": error_message}, status_code=404)

with pytest.raises(OutputDeletionError, match=error_message):
self.study.delete_output(output_name)

def test_delete_outputs(self):
with requests_mock.Mocker() as mocker:
outputs_url = f"https://antares.com/api/v1/studies/{self.study_id}/outputs"
outputs_json = [
{"name": "output1", "archived": False},
{"name": "output2", "archived": True},
]
mocker.get(outputs_url, json=outputs_json, status_code=200)

delete_url1 = f"https://antares.com/api/v1/studies/{self.study_id}/outputs/output1"
delete_url2 = f"https://antares.com/api/v1/studies/{self.study_id}/outputs/output2"
mocker.delete(delete_url1, status_code=200)
mocker.delete(delete_url2, status_code=200)

mocker.get(
outputs_url,
json=[{"name": "output1", "archived": False}, {"name": "output2", "archived": True}],
status_code=200,
)
assert len(self.study.read_outputs()) == 2

self.study.delete_outputs()
assert len(self.study.get_outputs()) == 0

# failing (nothing to delete)
error_message = "Outputs deletion failed"
mocker.get(outputs_url, json={"description": error_message}, status_code=404)
with pytest.raises(OutputsRetrievalError, match=error_message):
self.study.delete_outputs()
21 changes: 21 additions & 0 deletions tests/integration/test_web_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -578,3 +578,24 @@ def test_creation_lifecycle(self, antares_web: AntaresWebDesktop):
assert cluster_term.data.cluster == "cluster_test"
assert cluster_term.weight == 4.5
assert cluster_term.offset == 3

# ===== Output deletion =====

# run two new simulations for creating more outputs
study.wait_job_completion(
study.run_antares_simulation(AntaresSimulationParameters(output_suffix="2")), time_out=60
)
study.wait_job_completion(
study.run_antares_simulation(AntaresSimulationParameters(output_suffix="3")), time_out=60
)
assert len(study.read_outputs()) == 3

# delete_output
study.delete_output(output.name)
assert output.name not in study.get_outputs()
assert len(study.read_outputs()) == 2

# delete_outputs
study.delete_outputs()
assert len(study.get_outputs()) == 0
assert len(study.read_outputs()) == 0

0 comments on commit 8d4fb3c

Please sign in to comment.