Skip to content

Commit

Permalink
fix(storages): use command when updating matrices
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinBelthle committed Mar 5, 2024
1 parent f7f082a commit 3604026
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 95 deletions.
13 changes: 13 additions & 0 deletions antarest/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ def __str__(self) -> str:
return self.detail


class STStorageNotFoundError(HTTPException):
def __init__(self, message: str) -> None:
super().__init__(HTTPStatus.NOT_FOUND, message)


class DuplicateSTStorageId(HTTPException):
"""Exception raised when trying to create a short term storage with an already existing id."""

def __init__(self, st_storage_id: str) -> None:
msg = f"Short term storage '{st_storage_id}' already exists and could not be created"
super().__init__(HTTPStatus.CONFLICT, msg)


class UnknownModuleError(Exception):
def __init__(self, message: str) -> None:
super(UnknownModuleError, self).__init__(message)
Expand Down
63 changes: 45 additions & 18 deletions antarest/study/business/areas/st_storage_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
from typing_extensions import Literal

from antarest.core.exceptions import (
AreaNotFound,
DuplicateSTStorageId,
STStorageConfigNotFoundError,
STStorageFieldsNotFoundError,
STStorageMatrixNotFoundError,
STStorageNotFoundError,
)
from antarest.study.business.utils import AllOptionalMetaclass, camel_case_model, execute_or_add_commands
from antarest.study.model import Study
Expand All @@ -24,6 +27,7 @@
from antarest.study.storage.storage_service import StudyStorageService
from antarest.study.storage.variantstudy.model.command.create_st_storage import CreateSTStorage
from antarest.study.storage.variantstudy.model.command.remove_st_storage import RemoveSTStorage
from antarest.study.storage.variantstudy.model.command.replace_matrix import ReplaceMatrix
from antarest.study.storage.variantstudy.model.command.update_config import UpdateConfig

__all__ = (
Expand Down Expand Up @@ -258,6 +262,10 @@ def create_storage(
The ID of the newly created short-term storage.
"""
storage = form.to_config

file_study = self._get_file_study(study)
_check_creation_feasibility(file_study, area_id, storage.id)

command = CreateSTStorage(
area_id=area_id,
parameters=storage,
Expand Down Expand Up @@ -350,18 +358,11 @@ def update_storage(
"""
study_version = study.version

# review: reading the configuration poses a problem for variants,
# because it requires generating a snapshot, which takes time.
# This reading could be avoided if we don't need the previous values
# (no cross-field validation, no default values, etc.).
# In return, we won't be able to return a complete `STStorageOutput` object.
# So, we need to make sure the frontend doesn't need the missing fields.
# This missing information could also be a problem for the API users.
# The solution would be to avoid reading the configuration if the study is a variant
# (we then use the default values), otherwise, for a RAW study, we read the configuration
# and update the modified values.
# For variants, this method requires generating a snapshot, which takes time.
# But sadly, there's no other way to prevent creating wrong commands.

file_study = self._get_file_study(study)
_check_update_feasibility(file_study, area_id, storage_id)

path = STORAGE_LIST_PATH.format(area_id=area_id, storage_id=storage_id)
try:
Expand Down Expand Up @@ -408,14 +409,16 @@ def delete_storages(
area_id: The area ID of the short-term storage.
storage_ids: IDs list of short-term storages to remove.
"""
file_study = self._get_file_study(study)
_check_deletion_feasibility(file_study, area_id, storage_ids)

command_context = self.storage_service.variant_study_service.command_factory.command_context
for storage_id in storage_ids:
command = RemoveSTStorage(
area_id=area_id,
storage_id=storage_id,
command_context=command_context,
)
file_study = self._get_file_study(study)
execute_or_add_commands(study, file_study, [command], self.storage_service)

def get_matrix(
Expand Down Expand Up @@ -473,23 +476,21 @@ def update_matrix(
ts_name: Name of the time series to update.
ts: Matrix of the time series to update.
"""
matrix_object = ts.dict()
self._save_matrix_obj(study, area_id, storage_id, ts_name, matrix_object)
self._save_matrix_obj(study, area_id, storage_id, ts_name, ts.data)

def _save_matrix_obj(
self,
study: Study,
area_id: str,
storage_id: str,
ts_name: STStorageTimeSeries,
matrix_obj: t.Dict[str, t.Any],
matrix_data: t.List[t.List[float]],
) -> None:
file_study = self._get_file_study(study)
command_context = self.storage_service.variant_study_service.command_factory.command_context
path = STORAGE_SERIES_PATH.format(area_id=area_id, storage_id=storage_id, ts_name=ts_name)
try:
file_study.tree.save(matrix_obj, path.split("/"))
except KeyError:
raise STStorageMatrixNotFoundError(study.id, area_id, storage_id, ts_name) from None
command = ReplaceMatrix(target=path, matrix=matrix_data, command_context=command_context)
execute_or_add_commands(study, file_study, [command], self.storage_service)

def validate_matrices(
self,
Expand Down Expand Up @@ -534,3 +535,29 @@ def validate_matrices(

# Validation successful
return True


def _get_existing_storage_ids(file_study: FileStudy, area_id: str) -> t.List[str]:
areas = file_study.config.areas
if area_id not in areas:
raise AreaNotFound(f"Area {area_id} does not exist in study")
return [existing_storage.id for existing_storage in areas.get(area_id).st_storages] # type: ignore


def _check_deletion_feasibility(file_study: FileStudy, area_id: str, storage_ids: t.Sequence[str]) -> None:
existing_ids = _get_existing_storage_ids(file_study, area_id)
for storage_id in storage_ids:
if storage_id not in existing_ids:
raise STStorageNotFoundError(f"Short term storage {storage_id} does not exist in study")


def _check_update_feasibility(file_study: FileStudy, area_id: str, storage_id: str) -> None:
existing_ids = _get_existing_storage_ids(file_study, area_id)
if storage_id not in existing_ids:
raise STStorageNotFoundError(f"Short term storage {storage_id} does not exist in study")


def _check_creation_feasibility(file_study: FileStudy, area_id: str, storage_id: str) -> None:
existing_ids = _get_existing_storage_ids(file_study, area_id)
if storage_id in existing_ids:
raise DuplicateSTStorageId(storage_id)
Loading

0 comments on commit 3604026

Please sign in to comment.