diff --git a/src/antares/craft/model/binding_constraint.py b/src/antares/craft/model/binding_constraint.py index f87375ac..a90eb8f9 100644 --- a/src/antares/craft/model/binding_constraint.py +++ b/src/antares/craft/model/binding_constraint.py @@ -9,17 +9,15 @@ # SPDX-License-Identifier: MPL-2.0 # # This file is part of the Antares project. - +from dataclasses import dataclass from enum import Enum from typing import Any, Optional, Union import pandas as pd from antares.craft.service.base_services import BaseBindingConstraintService -from antares.craft.tools.all_optional_meta import all_optional_model from antares.craft.tools.contents_tool import EnumIgnoreCase, transform_name_to_id from pydantic import BaseModel, Field, model_validator -from pydantic.alias_generators import to_camel class BindingConstraintFrequency(EnumIgnoreCase): @@ -92,20 +90,19 @@ def generate_id(cls, data: Union[dict[str, str], LinkData, ClusterData]) -> str: return ".".join((data.area.lower(), data.cluster.lower())) -class DefaultBindingConstraintProperties(BaseModel, extra="forbid", populate_by_name=True, alias_generator=to_camel): - """Default properties for binding constraints - - Attributes: - enabled (bool): True - time_step (BindingConstraintFrequency): BindingConstraintFrequency.HOURLY - operator (BindingConstraintOperator): BindingConstraintOperator.LESS - comments (str): None - filter_year_by_year (str): "hourly" - filter_synthesis (str): "hourly" - group (str): "default" +@dataclass +class BindingConstraintPropertiesUpdate: + enabled: Optional[bool] = None + time_step: Optional[BindingConstraintFrequency] = None + operator: Optional[BindingConstraintOperator] = None + comments: Optional[str] = None + filter_year_by_year: Optional[str] = None + filter_synthesis: Optional[str] = None + group: Optional[str] = None - """ +@dataclass +class BindingConstraintProperties: enabled: bool = True time_step: BindingConstraintFrequency = BindingConstraintFrequency.HOURLY operator: BindingConstraintOperator = BindingConstraintOperator.LESS @@ -115,11 +112,6 @@ class DefaultBindingConstraintProperties(BaseModel, extra="forbid", populate_by_ group: str = "default" -@all_optional_model -class BindingConstraintProperties(DefaultBindingConstraintProperties): - pass - - class BindingConstraint: def __init__( self, @@ -162,7 +154,7 @@ def delete_term(self, term: ConstraintTerm) -> None: self._binding_constraint_service.delete_binding_constraint_term(self.id, term.id) self._terms.pop(term.id) - def update_properties(self, properties: BindingConstraintProperties) -> None: + def update_properties(self, properties: BindingConstraintPropertiesUpdate) -> None: new_properties = self._binding_constraint_service.update_binding_constraint_properties(self, properties) self._properties = new_properties diff --git a/src/antares/craft/service/api_services/models/binding_constraint.py b/src/antares/craft/service/api_services/models/binding_constraint.py new file mode 100644 index 00000000..924f6e3d --- /dev/null +++ b/src/antares/craft/service/api_services/models/binding_constraint.py @@ -0,0 +1,52 @@ +# Copyright (c) 2024, RTE (https://www.rte-france.com) +# +# See AUTHORS.txt +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# SPDX-License-Identifier: MPL-2.0 +# +# This file is part of the Antares project. + +from dataclasses import asdict +from typing import Union + +from antares.craft.model.binding_constraint import ( + BindingConstraintFrequency, + BindingConstraintOperator, + BindingConstraintProperties, + BindingConstraintPropertiesUpdate, +) +from antares.craft.service.api_services.models import APIBaseModel +from antares.craft.tools.all_optional_meta import all_optional_model + +BindingConstraintPropertiesType = Union[BindingConstraintProperties, BindingConstraintPropertiesUpdate] + + +@all_optional_model +class BindingConstraintPropertiesAPI(APIBaseModel): + enabled: bool + time_step: BindingConstraintFrequency + operator: BindingConstraintOperator + comments: str + filter_year_by_year: str + filter_synthesis: str + group: str + + @staticmethod + def from_user_model(user_class: BindingConstraintPropertiesType) -> "BindingConstraintPropertiesAPI": + user_dict = asdict(user_class) + return BindingConstraintPropertiesAPI.model_validate(user_dict) + + def to_user_model(self) -> BindingConstraintProperties: + return BindingConstraintProperties( + enabled=self.enabled, + time_step=self.time_step, + operator=self.operator, + comments=self.comments, + filter_year_by_year=self.filter_year_by_year, + filter_synthesis=self.filter_synthesis, + group=self.group, + ) diff --git a/src/antares/craft/service/api_services/binding_constraint_api.py b/src/antares/craft/service/api_services/services/binding_constraint.py similarity index 90% rename from src/antares/craft/service/api_services/binding_constraint_api.py rename to src/antares/craft/service/api_services/services/binding_constraint.py index a0fd83bc..fc7d3bfa 100644 --- a/src/antares/craft/service/api_services/binding_constraint_api.py +++ b/src/antares/craft/service/api_services/services/binding_constraint.py @@ -30,9 +30,11 @@ from antares.craft.model.binding_constraint import ( BindingConstraint, BindingConstraintProperties, + BindingConstraintPropertiesUpdate, ConstraintMatrixName, ConstraintTerm, ) +from antares.craft.service.api_services.models.binding_constraint import BindingConstraintPropertiesAPI from antares.craft.service.api_services.utils import get_matrix from antares.craft.service.base_services import BaseBindingConstraintService from typing_extensions import override @@ -77,7 +79,8 @@ def create_binding_constraint( try: body = {"name": name} if properties: - camel_properties = properties.model_dump(mode="json", by_alias=True, exclude_none=True) + api_model = BindingConstraintPropertiesAPI.from_user_model(properties) + camel_properties = api_model.model_dump(mode="json", by_alias=True, exclude_none=True) body = {**body, **camel_properties} for matrix, matrix_name in zip( [less_term_matrix, equal_term_matrix, greater_term_matrix], @@ -90,7 +93,8 @@ def create_binding_constraint( bc_id = created_properties["id"] for key in ["terms", "id", "name"]: del created_properties[key] - bc_properties = BindingConstraintProperties.model_validate(created_properties) + api_properties = BindingConstraintPropertiesAPI.model_validate(created_properties) + bc_properties = api_properties.to_user_model() bc_terms: list[ConstraintTerm] = [] if terms: @@ -120,11 +124,12 @@ def delete_binding_constraint_term(self, constraint_id: str, term_id: str) -> No @override def update_binding_constraint_properties( - self, binding_constraint: BindingConstraint, properties: BindingConstraintProperties + self, binding_constraint: BindingConstraint, properties: BindingConstraintPropertiesUpdate ) -> BindingConstraintProperties: url = f"{self._base_url}/studies/{self.study_id}/bindingconstraints/{binding_constraint.id}" try: - body = properties.model_dump(mode="json", by_alias=True, exclude_none=True) + api_model = BindingConstraintPropertiesAPI.from_user_model(properties) + body = api_model.model_dump(mode="json", by_alias=True, exclude_none=True) if not body: return binding_constraint.properties @@ -132,7 +137,8 @@ def update_binding_constraint_properties( json_response = response.json() for key in ["terms", "id", "name"]: del json_response[key] - new_properties = BindingConstraintProperties.model_validate(json_response) + new_api_properties = BindingConstraintPropertiesAPI.model_validate(json_response) + new_properties = new_api_properties.to_user_model() except APIError as e: raise ConstraintPropertiesUpdateError(binding_constraint.id, e.message) from e diff --git a/src/antares/craft/service/base_services.py b/src/antares/craft/service/base_services.py index 5ae6c668..3ff31475 100644 --- a/src/antares/craft/service/base_services.py +++ b/src/antares/craft/service/base_services.py @@ -25,6 +25,7 @@ from antares.craft.model.binding_constraint import ( BindingConstraint, BindingConstraintProperties, + BindingConstraintPropertiesUpdate, ConstraintMatrixName, ConstraintTerm, ) @@ -491,7 +492,7 @@ def delete_binding_constraint_term(self, constraint_id: str, term_id: str) -> No @abstractmethod def update_binding_constraint_properties( - self, binding_constraint: "BindingConstraint", properties: "BindingConstraintProperties" + self, binding_constraint: "BindingConstraint", properties: "BindingConstraintPropertiesUpdate" ) -> "BindingConstraintProperties": """ Args: diff --git a/src/antares/craft/service/local_services/models/binding_constraint.py b/src/antares/craft/service/local_services/models/binding_constraint.py new file mode 100644 index 00000000..812dcf91 --- /dev/null +++ b/src/antares/craft/service/local_services/models/binding_constraint.py @@ -0,0 +1,52 @@ +# Copyright (c) 2024, RTE (https://www.rte-france.com) +# +# See AUTHORS.txt +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# SPDX-License-Identifier: MPL-2.0 +# +# This file is part of the Antares project. + +from dataclasses import asdict +from typing import Union + +from antares.craft.model.binding_constraint import ( + BindingConstraintFrequency, + BindingConstraintOperator, + BindingConstraintProperties, + BindingConstraintPropertiesUpdate, +) +from antares.craft.service.local_services.models import LocalBaseModel +from antares.craft.tools.all_optional_meta import all_optional_model + +BindingConstraintPropertiesType = Union[BindingConstraintProperties, BindingConstraintPropertiesUpdate] + + +@all_optional_model +class BindingConstraintPropertiesLocal(LocalBaseModel): + enabled: bool + time_step: BindingConstraintFrequency + operator: BindingConstraintOperator + comments: str + filter_year_by_year: str + filter_synthesis: str + group: str + + @staticmethod + def from_user_model(user_class: BindingConstraintPropertiesType) -> "BindingConstraintPropertiesLocal": + user_dict = asdict(user_class) + return BindingConstraintPropertiesLocal.model_validate(user_dict) + + def to_user_model(self) -> BindingConstraintProperties: + return BindingConstraintProperties( + enabled=self.enabled, + time_step=self.time_step, + operator=self.operator, + comments=self.comments, + filter_year_by_year=self.filter_year_by_year, + filter_synthesis=self.filter_synthesis, + group=self.group, + ) diff --git a/src/antares/craft/service/local_services/binding_constraint_local.py b/src/antares/craft/service/local_services/services/binding_constraint.py similarity index 83% rename from src/antares/craft/service/local_services/binding_constraint_local.py rename to src/antares/craft/service/local_services/services/binding_constraint.py index fc73c965..80887611 100644 --- a/src/antares/craft/service/local_services/binding_constraint_local.py +++ b/src/antares/craft/service/local_services/services/binding_constraint.py @@ -21,58 +21,18 @@ BindingConstraintFrequency, BindingConstraintOperator, BindingConstraintProperties, + BindingConstraintPropertiesUpdate, ConstraintMatrixName, ConstraintTerm, - DefaultBindingConstraintProperties, ) from antares.craft.service.base_services import BaseBindingConstraintService +from antares.craft.service.local_services.models.binding_constraint import BindingConstraintPropertiesLocal from antares.craft.tools.ini_tool import IniFile, InitializationFilesTypes from antares.craft.tools.matrix_tool import df_read, df_save from antares.craft.tools.time_series_tool import TimeSeriesFileType -from pydantic import Field from typing_extensions import override -class BindingConstraintPropertiesLocal(DefaultBindingConstraintProperties): - """ - Used to create the entries for the bindingconstraints.ini file - - Attributes: - constraint_name: The constraint name - constraint_id: The constraint id - properties (BindingConstraintProperties): The BindingConstraintProperties to set - terms (dict[str, ConstraintTerm]]): The terms applying to the binding constraint - """ - - constraint_name: str - constraint_id: str - terms: dict[str, ConstraintTerm] = Field(default_factory=dict[str, ConstraintTerm]) - - @property - def list_ini_fields(self) -> dict[str, str]: - ini_dict = { - "name": self.constraint_name, - "id": self.constraint_id, - "enabled": f"{self.enabled}".lower(), - "type": self.time_step.value, - "operator": self.operator.value, - "comments": self.comments, - "filter-year-by-year": self.filter_year_by_year, - "filter-synthesis": self.filter_synthesis, - "group": self.group, - } | {term_id: term.weight_offset() for term_id, term in self.terms.items()} - return {key: value for key, value in ini_dict.items() if value not in [None, ""]} - - def yield_binding_constraint_properties(self) -> BindingConstraintProperties: - excludes = { - "constraint_name", - "constraint_id", - "terms", - "list_ini_fields", - } - return BindingConstraintProperties(**self.model_dump(mode="json", exclude=excludes)) - - class BindingConstraintLocalService(BaseBindingConstraintService): def __init__(self, config: LocalConfiguration, study_name: str, **kwargs: Any) -> None: super().__init__(**kwargs) @@ -232,7 +192,7 @@ def delete_binding_constraint_term(self, constraint_id: str, term_id: str) -> No @override def update_binding_constraint_properties( - self, binding_constraint: BindingConstraint, properties: BindingConstraintProperties + self, binding_constraint: BindingConstraint, properties: BindingConstraintPropertiesUpdate ) -> BindingConstraintProperties: raise NotImplementedError diff --git a/src/antares/craft/service/service_factory.py b/src/antares/craft/service/service_factory.py index 2083095d..8f9aa277 100644 --- a/src/antares/craft/service/service_factory.py +++ b/src/antares/craft/service/service_factory.py @@ -14,9 +14,9 @@ from antares.craft.config.base_configuration import BaseConfiguration from antares.craft.config.local_configuration import LocalConfiguration from antares.craft.service.api_services.area_api import AreaApiService -from antares.craft.service.api_services.binding_constraint_api import BindingConstraintApiService from antares.craft.service.api_services.hydro_api import HydroApiService from antares.craft.service.api_services.link_api import LinkApiService +from antares.craft.service.api_services.services.binding_constraint import BindingConstraintApiService from antares.craft.service.api_services.services.output import OutputApiService from antares.craft.service.api_services.services.renewable import RenewableApiService from antares.craft.service.api_services.services.run import RunApiService @@ -38,9 +38,9 @@ BaseThermalService, ) from antares.craft.service.local_services.area_local import AreaLocalService -from antares.craft.service.local_services.binding_constraint_local import BindingConstraintLocalService from antares.craft.service.local_services.hydro_local import HydroLocalService from antares.craft.service.local_services.link_local import LinkLocalService +from antares.craft.service.local_services.services.binding_constraint import BindingConstraintLocalService from antares.craft.service.local_services.services.output import OutputLocalService from antares.craft.service.local_services.services.renewable import RenewableLocalService from antares.craft.service.local_services.services.run import RunLocalService