Skip to content

Commit

Permalink
merge with dev
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinBelthle committed Jan 17, 2025
2 parents a6a4af6 + d14454a commit 2a9a6ab
Show file tree
Hide file tree
Showing 72 changed files with 2,633 additions and 994 deletions.
40 changes: 40 additions & 0 deletions antarest/study/business/binding_constraint_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
)
from antarest.study.storage.variantstudy.model.command.icommand import ICommand
from antarest.study.storage.variantstudy.model.command.remove_binding_constraint import RemoveBindingConstraint
from antarest.study.storage.variantstudy.model.command.remove_multiple_binding_constraints import (
RemoveMultipleBindingConstraints,
)
from antarest.study.storage.variantstudy.model.command.replace_matrix import ReplaceMatrix
from antarest.study.storage.variantstudy.model.command.update_binding_constraint import (
UpdateBindingConstraint,
Expand Down Expand Up @@ -589,6 +592,18 @@ def terms_to_coeffs(terms: t.Sequence[ConstraintTerm]) -> t.Dict[str, t.List[flo
coeffs[term.id].append(term.offset)
return coeffs

def check_binding_constraints_exists(self, study: Study, bc_ids: t.List[str]) -> None:
storage_service = self.storage_service.get_storage(study)
file_study = storage_service.get_raw(study)
existing_constraints = file_study.tree.get(["input", "bindingconstraints", "bindingconstraints"])

existing_ids = {constraint["id"] for constraint in existing_constraints.values()}

missing_bc_ids = [bc_id for bc_id in bc_ids if bc_id not in existing_ids]

if missing_bc_ids:
raise BindingConstraintNotFound(f"Binding constraint(s) '{missing_bc_ids}' not found")

def get_binding_constraint(self, study: Study, bc_id: str) -> ConstraintOutput:
"""
Retrieves a binding constraint by its ID within a given study.
Expand Down Expand Up @@ -1018,6 +1033,31 @@ def remove_binding_constraint(self, study: Study, binding_constraint_id: str) ->
)
execute_or_add_commands(study, file_study, [command], self.storage_service)

def remove_multiple_binding_constraints(self, study: Study, binding_constraints_ids: t.List[str]) -> None:
"""
Removes multiple binding constraints from a study.
Args:
study: The study from which to remove the constraint.
binding_constraints_ids: The IDs of the binding constraints to remove.
Raises:
BindingConstraintNotFound: If at least one binding constraint within the specified list is not found.
"""

self.check_binding_constraints_exists(study, binding_constraints_ids)

command_context = self.storage_service.variant_study_service.command_factory.command_context
file_study = self.storage_service.get_storage(study).get_raw(study)

command = RemoveMultipleBindingConstraints(
ids=binding_constraints_ids,
command_context=command_context,
study_version=file_study.config.version,
)

execute_or_add_commands(study, file_study, [command], self.storage_service)

def _update_constraint_with_terms(
self, study: Study, bc: ConstraintOutput, terms: t.Mapping[str, ConstraintTerm]
) -> None:
Expand Down
22 changes: 20 additions & 2 deletions antarest/study/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from pathlib import Path

from antares.study.version import StudyVersion
from pydantic import BeforeValidator, PlainSerializer, field_validator
from pydantic import BeforeValidator, ConfigDict, Field, PlainSerializer, computed_field, field_validator
from sqlalchemy import ( # type: ignore
Boolean,
Column,
Expand Down Expand Up @@ -334,7 +334,7 @@ class StudyFolder:
groups: t.List[Group]


class NonStudyFolder(AntaresBaseModel):
class NonStudyFolderDTO(AntaresBaseModel):
"""
DTO used by the explorer to list directories that aren't studies directory, this will be usefull for the front
so the user can navigate in the hierarchy
Expand All @@ -343,6 +343,24 @@ class NonStudyFolder(AntaresBaseModel):
path: Path
workspace: str
name: str
has_children: bool = Field(
alias="hasChildren",
) # true when has at least one non-study-folder children

model_config = ConfigDict(populate_by_name=True)

@computed_field(alias="parentPath")
def parent_path(self) -> Path:
"""
This computed field is convenient for the front.
This field is also aliased as parentPath to match the front-end naming convention.
Returns: the parent path of the current directory. Starting with the workspace as a root directory (we want /workspafe/folder1/sub... and not workspace/folder1/fsub... ).
"""
workspace_path = Path(f"/{self.workspace}")
full_path = workspace_path.joinpath(self.path)
return full_path.parent


class WorkspaceMetadata(AntaresBaseModel):
Expand Down
39 changes: 24 additions & 15 deletions antarest/study/storage/explorer_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
from typing import List

from antarest.core.config import Config
from antarest.study.model import DEFAULT_WORKSPACE_NAME, NonStudyFolder, WorkspaceMetadata
from antarest.study.model import DEFAULT_WORKSPACE_NAME, NonStudyFolderDTO, WorkspaceMetadata
from antarest.study.storage.utils import (
get_folder_from_workspace,
get_workspace_from_config,
is_study_folder,
should_ignore_folder_for_scan,
has_non_study_folder,
is_non_study_folder,
)

logger = logging.getLogger(__name__)
Expand All @@ -33,26 +33,35 @@ def list_dir(
self,
workspace_name: str,
workspace_directory_path: str,
) -> List[NonStudyFolder]:
) -> List[NonStudyFolderDTO]:
"""
return a list of all directories under workspace_directory_path, that aren't studies.
"""
workspace = get_workspace_from_config(self.config, workspace_name, default_allowed=False)
directory_path = get_folder_from_workspace(workspace, workspace_directory_path)
directories = []
try:
# this block is skipped in case of permission error
children = list(directory_path.iterdir())
except PermissionError:
children = [] # we don't want to try to read folders we can't access
for child in children:
if (
child.is_dir()
and not is_study_folder(child)
and not should_ignore_folder_for_scan(child, workspace.filter_in, workspace.filter_out)
):
# we don't want to expose the full absolute path on the server
child_rel_path = child.relative_to(workspace.path)
directories.append(NonStudyFolder(path=child_rel_path, workspace=workspace_name, name=child.name))
for child in children:
# if we can't access one child we skip it
try:
if is_non_study_folder(child, workspace.filter_in, workspace.filter_out):
# we don't want to expose the full absolute path on the server
child_rel_path = child.relative_to(workspace.path)
has_children = has_non_study_folder(child, workspace.filter_in, workspace.filter_out)
directories.append(
NonStudyFolderDTO(
path=child_rel_path,
workspace=workspace_name,
name=child.name,
has_children=has_children,
)
)
except PermissionError as e:
logger.warning(f"Permission error while accessing {child} or one of its children: {e}")
except PermissionError as e:
logger.warning(f"Permission error while listing {directory_path}: {e}")
return directories

def list_workspaces(
Expand Down
12 changes: 12 additions & 0 deletions antarest/study/storage/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,3 +495,15 @@ def should_ignore_folder_for_scan(path: Path, filter_in: t.List[str], filter_out
and any(re.search(regex, path.name) for regex in filter_in)
and not any(re.search(regex, path.name) for regex in filter_out)
)


def has_non_study_folder(path: Path, filter_in: t.List[str], filter_out: t.List[str]) -> bool:
return any(is_non_study_folder(sub_path, filter_in, filter_out) for sub_path in path.iterdir())


def is_non_study_folder(path: Path, filter_in: t.List[str], filter_out: t.List[str]) -> bool:
if is_study_folder(path):
return False
if should_ignore_folder_for_scan(path, filter_in, filter_out):
return False
return True
11 changes: 11 additions & 0 deletions antarest/study/storage/variantstudy/business/command_reverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
from antarest.study.storage.variantstudy.model.command.remove_cluster import RemoveCluster
from antarest.study.storage.variantstudy.model.command.remove_district import RemoveDistrict
from antarest.study.storage.variantstudy.model.command.remove_link import RemoveLink
from antarest.study.storage.variantstudy.model.command.remove_multiple_binding_constraints import (
RemoveMultipleBindingConstraints,
)
from antarest.study.storage.variantstudy.model.command.remove_renewables_cluster import RemoveRenewablesCluster
from antarest.study.storage.variantstudy.model.command.remove_st_storage import RemoveSTStorage
from antarest.study.storage.variantstudy.model.command.remove_user_resource import RemoveUserResource
Expand Down Expand Up @@ -163,6 +166,14 @@ def _revert_remove_binding_constraint(
) -> t.List[ICommand]:
raise NotImplementedError("The revert function for RemoveBindingConstraint is not available")

@staticmethod
def _revert_remove_multiple_binding_constraints(
base_command: RemoveMultipleBindingConstraints,
history: t.List["ICommand"],
base: FileStudy,
) -> t.List[ICommand]:
raise NotImplementedError("The revert function for RemoveMultipleBindingConstraints is not available")

@staticmethod
def _revert_update_scenario_builder(
base_command: UpdateScenarioBuilder,
Expand Down
4 changes: 4 additions & 0 deletions antarest/study/storage/variantstudy/command_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
from antarest.study.storage.variantstudy.model.command.remove_cluster import RemoveCluster
from antarest.study.storage.variantstudy.model.command.remove_district import RemoveDistrict
from antarest.study.storage.variantstudy.model.command.remove_link import RemoveLink
from antarest.study.storage.variantstudy.model.command.remove_multiple_binding_constraints import (
RemoveMultipleBindingConstraints,
)
from antarest.study.storage.variantstudy.model.command.remove_renewables_cluster import RemoveRenewablesCluster
from antarest.study.storage.variantstudy.model.command.remove_st_storage import RemoveSTStorage
from antarest.study.storage.variantstudy.model.command.remove_user_resource import RemoveUserResource
Expand All @@ -63,6 +66,7 @@
CommandName.CREATE_BINDING_CONSTRAINT.value: CreateBindingConstraint,
CommandName.UPDATE_BINDING_CONSTRAINT.value: UpdateBindingConstraint,
CommandName.REMOVE_BINDING_CONSTRAINT.value: RemoveBindingConstraint,
CommandName.REMOVE_MULTIPLE_BINDING_CONSTRAINTS.value: RemoveMultipleBindingConstraints,
CommandName.CREATE_THERMAL_CLUSTER.value: CreateCluster,
CommandName.REMOVE_THERMAL_CLUSTER.value: RemoveCluster,
CommandName.CREATE_RENEWABLES_CLUSTER.value: CreateRenewablesCluster,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright (c) 2025, 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.
import typing as t

from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy


def remove_bc_from_scenario_builder(study_data: FileStudy, removed_groups: t.Set[str]) -> None:
"""
Update the scenario builder by removing the rows that correspond to the BC groups to remove.
NOTE: this update can be very long if the scenario builder configuration is large.
"""
if not removed_groups:
return

rulesets = study_data.tree.get(["settings", "scenariobuilder"])

for ruleset in rulesets.values():
for key in list(ruleset):
# The key is in the form "symbol,group,year"
symbol, *parts = key.split(",")
if symbol == "bc" and parts[0] in removed_groups:
del ruleset[key]

study_data.tree.save(rulesets, ["settings", "scenariobuilder"])
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class CommandName(Enum):
CREATE_BINDING_CONSTRAINT = "create_binding_constraint"
UPDATE_BINDING_CONSTRAINT = "update_binding_constraint"
REMOVE_BINDING_CONSTRAINT = "remove_binding_constraint"
REMOVE_MULTIPLE_BINDING_CONSTRAINTS = "remove_multiple_binding_constraints"
CREATE_THERMAL_CLUSTER = "create_cluster"
REMOVE_THERMAL_CLUSTER = "remove_cluster"
CREATE_RENEWABLES_CLUSTER = "create_renewables_cluster"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from antarest.study.storage.variantstudy.business.utils_binding_constraint import (
parse_bindings_coeffs_and_save_into_config,
)
from antarest.study.storage.variantstudy.model.command.binding_constraint_utils import remove_bc_from_scenario_builder
from antarest.study.storage.variantstudy.model.command.common import CommandName, CommandOutput
from antarest.study.storage.variantstudy.model.command.icommand import MATCH_SIGNATURE_SEPARATOR, ICommand
from antarest.study.storage.variantstudy.model.command_listener.command_listener import ICommandListener
Expand Down Expand Up @@ -507,24 +508,3 @@ def match(self, other: "ICommand", equal: bool = False) -> bool:
if not equal:
return self.name == other.name
return super().match(other, equal)


def remove_bc_from_scenario_builder(study_data: FileStudy, removed_groups: t.Set[str]) -> None:
"""
Update the scenario builder by removing the rows that correspond to the BC groups to remove.
NOTE: this update can be very long if the scenario builder configuration is large.
"""
if not removed_groups:
return

rulesets = study_data.tree.get(["settings", "scenariobuilder"])

for ruleset in rulesets.values():
for key in list(ruleset):
# The key is in the form "symbol,group,year"
symbol, *parts = key.split(",")
if symbol == "bc" and parts[0] in removed_groups:
del ruleset[key]

study_data.tree.save(rulesets, ["settings", "scenariobuilder"])
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import DEFAULT_GROUP
from antarest.study.storage.rawstudy.model.filesystem.config.model import FileStudyTreeConfig
from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy
from antarest.study.storage.variantstudy.model.command.binding_constraint_utils import remove_bc_from_scenario_builder
from antarest.study.storage.variantstudy.model.command.common import CommandName, CommandOutput
from antarest.study.storage.variantstudy.model.command.create_binding_constraint import remove_bc_from_scenario_builder
from antarest.study.storage.variantstudy.model.command.icommand import MATCH_SIGNATURE_SEPARATOR, ICommand
from antarest.study.storage.variantstudy.model.command_listener.command_listener import ICommandListener
from antarest.study.storage.variantstudy.model.model import CommandDTO
Expand Down
Loading

0 comments on commit 2a9a6ab

Please sign in to comment.