Skip to content

Commit

Permalink
feat(api): improve and correct the implementation of the short-term s…
Browse files Browse the repository at this point in the history
…torage manager.
  • Loading branch information
laurent-laporte-pro committed Oct 5, 2023
1 parent fe764e4 commit 27e78ab
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 264 deletions.
142 changes: 35 additions & 107 deletions antarest/study/business/st_storage_manager.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import functools
import json
import operator
import re
from typing import Any, Dict, List, Mapping, MutableMapping, Sequence, Optional
from typing import Any, Dict, List, Mapping, MutableMapping, Optional, Sequence

import numpy as np
from pydantic import BaseModel, Extra, Field, root_validator, validator
from pydantic import BaseModel, Extra, root_validator, validator
from typing_extensions import Literal

from antarest.core.exceptions import (
STStorageConfigNotFoundError,
STStorageFieldsNotFoundError,
STStorageMatrixNotFoundError,
)
from antarest.study.business.utils import AllOptionalMetaclass, FormFieldsBaseModel, execute_or_add_commands
from antarest.study.business.utils import AllOptionalMetaclass, camel_case_model, execute_or_add_commands
from antarest.study.model import Study
from antarest.study.storage.rawstudy.model.filesystem.config.st_storage import STStorageConfig, STStorageGroup
from antarest.study.storage.rawstudy.model.filesystem.config.st_storage import (
STStorageConfig,
STStorageGroup,
STStorageProperties,
)
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
Expand All @@ -24,108 +27,16 @@
_HOURS_IN_YEAR = 8760


class FormBaseModel(FormFieldsBaseModel):
"""
A foundational model for all form-based models, providing common configurations.
"""

class Config:
validate_assignment = True
allow_population_by_field_name = True


class StorageForm(FormBaseModel):
@camel_case_model
class StorageInput(STStorageProperties, metaclass=AllOptionalMetaclass):
"""
Model representing the form used to create/edit a new short-term storage entry.
Model representing the form used to EDIT an existing short-term storage.
"""

name: str = Field(
description="Name of the storage.",
)
group: STStorageGroup = Field(
STStorageGroup.OTHER1,
description="Energy storage system group.",
)
injection_nominal_capacity: float = Field(
0,
description="Injection nominal capacity (MW)",
ge=0,
)
withdrawal_nominal_capacity: float = Field(
0,
description="Withdrawal nominal capacity (MW)",
ge=0,
)
reservoir_capacity: float = Field(
0,
description="Reservoir capacity (MWh)",
ge=0,
)
efficiency: float = Field(
0,
description="Efficiency of the storage system",
ge=0,
le=1,
)
initial_level: float = Field(
0,
description="Initial level of the storage system",
ge=0,
)
initial_level_optim: bool = Field(
False,
description="Flag indicating if the initial level is optimized",
)

@validator("name")
def validate_name_st_storage(cls, value: str) -> str:
"""
Check if the field (name) is valid
:param value: name of st storage
:return: value if is correct or raise an exception
"""
pattern = r"^(?![0-9]+$).*"
if len(value) < 1 or value is None or not re.match(pattern, value):
raise ValueError(f"The field name: {value} is not valid")
return value


class StorageUpdate(StorageForm, metaclass=AllOptionalMetaclass):
"""set fields as optional"""


class StorageCreation(StorageUpdate):
"""set value's fields as optional"""

class Config:
@staticmethod
def schema_extra(schema: MutableMapping[str, Any]) -> None:
schema["example"] = StorageCreation(
name="Siemens Battery",
group=STStorageGroup.BATTERY,
injection_nominal_capacity=0,
withdrawal_nominal_capacity=0,
reservoir_capacity=0,
efficiency=0,
initial_level=0,
initial_level_optim=False,
)

@property
def to_config(self) -> STStorageConfig:
values = self.dict(by_alias=False)
return STStorageConfig(**values)


class StorageInput(StorageUpdate):
"""
Model representing the form used to edit existing short-term storage details.
"""

class Config:
@staticmethod
def schema_extra(schema: MutableMapping[str, Any]) -> None:
schema["example"] = StorageCreation(
schema["example"] = StorageInput(
name="Siemens Battery",
group=STStorageGroup.BATTERY,
injection_nominal_capacity=150,
Expand All @@ -137,15 +48,32 @@ def schema_extra(schema: MutableMapping[str, Any]) -> None:
)


class StorageOutput(StorageInput):
class StorageCreation(StorageInput):
"""
Model representing the form used to display the details of a short-term storage entry.
Model representing the form used to CREATE a new short-term storage.
"""

id: str = Field(
description="Short-term storage ID",
regex=r"[a-zA-Z0-9_(),& -]+",
)
# noinspection Pydantic
@validator("name", pre=True)
def validate_name(cls, name: Optional[str]) -> str:
"""
Validator to check if the name is not empty.
"""
if not name:
raise ValueError("'name' must not be empty")
return name

@property
def to_config(self) -> STStorageConfig:
values = self.dict(by_alias=False, exclude_none=True)
return STStorageConfig(**values)


@camel_case_model
class StorageOutput(STStorageConfig):
"""
Model representing the form used to display the details of a short-term storage entry.
"""

class Config:
@staticmethod
Expand Down
16 changes: 16 additions & 0 deletions antarest/study/business/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,19 @@ def __new__(
annotations[field] = Optional[annotations[field]]
namespaces["__annotations__"] = annotations
return super().__new__(cls, name, bases, namespaces)


def camel_case_model(model: Type[BaseModel]) -> Type[BaseModel]:
"""
This decorator can be used to modify a model to use camel case aliases.
Args:
model: The pydantic model to modify.
Returns:
The modified model.
"""
model.__config__.alias_generator = to_camel_case
for field_name, field in model.__fields__.items():
field.alias = to_camel_case(field_name)
return model
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,18 @@ class STStorageGroup(EnumIgnoreCase):


# noinspection SpellCheckingInspection
class STStorageConfig(BaseModel):
"""
Manage the configuration files in the context of Short-Term Storage.
It provides a convenient way to read and write configuration data from/to an INI file format.
class STStorageProperties(
BaseModel,
extra=Extra.forbid,
validate_assignment=True,
allow_population_by_field_name=True,
):
"""
Properties of a short-term storage system read from the configuration files.
class Config:
extra = Extra.forbid
allow_population_by_field_name = True
All aliases match the name of the corresponding field in the INI files.
"""

# The `id` field is a calculated from the `name` if not provided.
# This value must be stored in the config cache.
id: str = Field(
description="Short-term storage ID",
regex=r"[a-zA-Z0-9_(),& -]+",
)
name: str = Field(
description="Short-term storage name",
regex=r"[a-zA-Z0-9_(),& -]+",
Expand Down Expand Up @@ -90,6 +86,21 @@ class Config:
alias="initialleveloptim",
)


# noinspection SpellCheckingInspection
class STStorageConfig(STStorageProperties):
"""
Manage the configuration files in the context of Short-Term Storage.
It provides a convenient way to read and write configuration data from/to an INI file format.
"""

# The `id` field is a calculated from the `name` if not provided.
# This value must be stored in the config cache.
id: str = Field(
description="Short-term storage ID",
regex=r"[a-zA-Z0-9_(),& -]+",
)

@root_validator(pre=True)
def calculate_storage_id(cls, values: Dict[str, Any]) -> Dict[str, Any]:
"""
Expand Down
Loading

0 comments on commit 27e78ab

Please sign in to comment.