Skip to content

Commit

Permalink
add abstract class for links
Browse files Browse the repository at this point in the history
  • Loading branch information
TheoPascoli committed Oct 8, 2024
1 parent 5ca372c commit 833a742
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 80 deletions.
4 changes: 3 additions & 1 deletion antarest/study/business/link_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ def update_link(self, study: RawStudy, link_creation_info: LinkInfoDTOType) -> L
command = UpdateLink(
area1=link_creation_info.area1,
area2=link_creation_info.area2,
parameters=existing_link.model_dump(exclude={"area1", "area2"}, exclude_none=True, by_alias=True),
parameters=existing_link.model_dump(
mode="json", exclude={"area1", "area2"}, exclude_none=True, by_alias=True
),
command_context=self.storage_service.variant_study_service.command_factory.command_context,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ def _revert_create_link(base_command: CreateLink, history: t.List["ICommand"], b
)
]

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

@staticmethod
def _revert_remove_link(base_command: RemoveLink, history: t.List["ICommand"], base: FileStudy) -> t.List[ICommand]:
raise NotImplementedError("The revert function for RemoveLink is not available")
Expand Down
155 changes: 87 additions & 68 deletions antarest/study/storage/variantstudy/model/command/create_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,25 +83,6 @@ class LinkProperties(LinkInfoProperties820):


class AbstractLinkCommand(ICommand, metaclass=ABCMeta):
# area1: str
# area2: str
# parameters: Optional[Dict[str, Any]] = None
# series: Optional[Union[List[List[MatrixData]], str]] = None
# direct: Optional[Union[List[List[MatrixData]], str]] = None
# indirect: Optional[Union[List[List[MatrixData]], str]] = None
pass

class CreateLink(ICommand):
"""
Command used to create a link between two areas.
"""

# Overloaded metadata
# ===================

command_name: CommandName = CommandName.CREATE_LINK
version: int = 1

# Command parameters
# ==================

Expand All @@ -120,11 +101,94 @@ def validate_series(
return validate_matrix(v, new_values) if v is not None else v

@model_validator(mode="after")
def validate_areas(self) -> "CreateLink":
def validate_areas(self) -> "AbstractLinkCommand":
if self.area1 == self.area2:
raise ValueError("Cannot create link on same node")
return self

def to_dto(self) -> CommandDTO:
args = {
"area1": self.area1,
"area2": self.area2,
"parameters": self.parameters,
}
if self.series:
args["series"] = strip_matrix_protocol(self.series)
if self.direct:
args["direct"] = strip_matrix_protocol(self.direct)
if self.indirect:
args["indirect"] = strip_matrix_protocol(self.indirect)
return CommandDTO(
action=CommandName.CREATE_LINK.value,
args=args,
)

def match(self, other: ICommand, equal: bool = False) -> bool:
if not isinstance(other, CreateLink):
return False
simple_match = self.area1 == other.area1 and self.area2 == other.area2
if not equal:
return simple_match
return (
simple_match
and self.parameters == other.parameters
and self.series == other.series
and self.direct == other.direct
and self.indirect == other.indirect
)

def _create_diff(self, other: "ICommand") -> List["ICommand"]:
other = cast(CreateLink, other)
from antarest.study.storage.variantstudy.model.command.replace_matrix import ReplaceMatrix
from antarest.study.storage.variantstudy.model.command.update_config import UpdateConfig

commands: List[ICommand] = []
area_from, area_to = sorted([self.area1, self.area2])
if self.parameters != other.parameters:
properties = LinkProperties.model_validate(other.parameters or {})
link_property = properties.model_dump(mode="json", by_alias=True, exclude_none=True)
commands.append(
UpdateConfig(
target=f"input/links/{area_from}/properties/{area_to}",
data=link_property,
command_context=self.command_context,
)
)
if self.series != other.series:
commands.append(
ReplaceMatrix(
target=f"@links_series/{area_from}/{area_to}",
matrix=strip_matrix_protocol(other.series),
command_context=self.command_context,
)
)
return commands

def get_inner_matrices(self) -> List[str]:
list_matrices = []
if self.series:
assert_this(isinstance(self.series, str))
list_matrices.append(strip_matrix_protocol(self.series))
if self.direct:
assert_this(isinstance(self.direct, str))
list_matrices.append(strip_matrix_protocol(self.direct))
if self.indirect:
assert_this(isinstance(self.indirect, str))
list_matrices.append(strip_matrix_protocol(self.indirect))
return list_matrices


class CreateLink(AbstractLinkCommand):
"""
Command used to create a link between two areas.
"""

# Overloaded metadata
# ===================

command_name: CommandName = CommandName.CREATE_LINK
version: int = 1

def _create_link_in_config(self, area_from: str, area_to: str, study_data: FileStudyTreeConfig) -> None:
self.parameters = self.parameters or {}
study_data.areas[area_from].links[area_to] = Link(
Expand Down Expand Up @@ -276,55 +340,10 @@ def match_signature(self) -> str:
)

def match(self, other: ICommand, equal: bool = False) -> bool:
if not isinstance(other, CreateLink):
return False
simple_match = self.area1 == other.area1 and self.area2 == other.area2
if not equal:
return simple_match
return (
simple_match
and self.parameters == other.parameters
and self.series == other.series
and self.direct == other.direct
and self.indirect == other.indirect
)
return super().match(other, equal)

def _create_diff(self, other: "ICommand") -> List["ICommand"]:
other = cast(CreateLink, other)
from antarest.study.storage.variantstudy.model.command.replace_matrix import ReplaceMatrix
from antarest.study.storage.variantstudy.model.command.update_config import UpdateConfig

commands: List[ICommand] = []
area_from, area_to = sorted([self.area1, self.area2])
if self.parameters != other.parameters:
properties = LinkProperties.model_validate(other.parameters or {})
link_property = properties.model_dump(mode="json", by_alias=True, exclude_none=True)
commands.append(
UpdateConfig(
target=f"input/links/{area_from}/properties/{area_to}",
data=link_property,
command_context=self.command_context,
)
)
if self.series != other.series:
commands.append(
ReplaceMatrix(
target=f"@links_series/{area_from}/{area_to}",
matrix=strip_matrix_protocol(other.series),
command_context=self.command_context,
)
)
return commands
return super()._create_diff(other)

def get_inner_matrices(self) -> List[str]:
list_matrices = []
if self.series:
assert_this(isinstance(self.series, str))
list_matrices.append(strip_matrix_protocol(self.series))
if self.direct:
assert_this(isinstance(self.direct, str))
list_matrices.append(strip_matrix_protocol(self.direct))
if self.indirect:
assert_this(isinstance(self.indirect, str))
list_matrices.append(strip_matrix_protocol(self.indirect))
return list_matrices
return super().get_inner_matrices()
63 changes: 54 additions & 9 deletions antarest/study/storage/variantstudy/model/command/update_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@
# This file is part of the Antares project.
import typing as t

from antarest.study.model import STUDY_VERSION_8_2
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.business.utils import strip_matrix_protocol
from antarest.study.storage.variantstudy.model.command.common import CommandName, CommandOutput
from antarest.study.storage.variantstudy.model.command.create_link import AbstractLinkCommand
from antarest.study.storage.variantstudy.model.command.icommand import MATCH_SIGNATURE_SEPARATOR, ICommand, OutputTuple
from antarest.study.storage.variantstudy.model.model import CommandDTO


class UpdateLink(ICommand):
class UpdateLink(AbstractLinkCommand):
"""
Command used to create a link between two areas.
"""
Expand All @@ -29,12 +32,6 @@ class UpdateLink(ICommand):
command_name: CommandName = CommandName.UPDATE_LINK
version: int = 1

# Command parameters
# ==================
area1: str
area2: str
parameters: t.Optional[t.Dict[str, t.Any]] = None

def _apply_config(self, study_data: FileStudyTreeConfig) -> OutputTuple:
area_from, area_to = sorted([self.area1, self.area2])

Expand All @@ -47,9 +44,51 @@ def _apply_config(self, study_data: FileStudyTreeConfig) -> OutputTuple:
)

def _apply(self, study_data: FileStudy) -> CommandOutput:
version = study_data.config.version
area_from, area_to = sorted([self.area1, self.area2])
study_data.tree.save(self.parameters, ["input", "links", area_from, "properties", area_to])
output, _ = self._apply_config(study_data.config)

self.series = self.series or (self.command_context.generator_matrix_constants.get_link(version=version))
self.direct = self.direct or (self.command_context.generator_matrix_constants.get_link_direct())
self.indirect = self.indirect or (self.command_context.generator_matrix_constants.get_link_indirect())

assert type(self.series) is str
if version < STUDY_VERSION_8_2:
study_data.tree.save(self.series, ["input", "links", area_from, area_to])
else:
study_data.tree.save(
self.series,
["input", "links", area_from, f"{area_to}_parameters"],
)

study_data.tree.save({}, ["input", "links", area_from, "capacities"])
if self.direct:
assert isinstance(self.direct, str)
study_data.tree.save(
self.direct,
[
"input",
"links",
area_from,
"capacities",
f"{area_to}_direct",
],
)

if self.indirect:
assert isinstance(self.indirect, str)
study_data.tree.save(
self.indirect,
[
"input",
"links",
area_from,
"capacities",
f"{area_to}_indirect",
],
)

return output

def to_dto(self) -> CommandDTO:
Expand All @@ -58,6 +97,12 @@ def to_dto(self) -> CommandDTO:
"area2": self.area2,
"parameters": self.parameters,
}
if self.series:
args["series"] = strip_matrix_protocol(self.series)
if self.direct:
args["direct"] = strip_matrix_protocol(self.direct)
if self.indirect:
args["indirect"] = strip_matrix_protocol(self.indirect)
return CommandDTO(
action=CommandName.UPDATE_LINK.value,
args=args,
Expand All @@ -69,7 +114,7 @@ def match_signature(self) -> str:
)

def _create_diff(self, other: "ICommand") -> t.List["ICommand"]:
pass
return super()._create_diff(other)

def get_inner_matrices(self) -> t.List[str]:
pass
return super().get_inner_matrices()
1 change: 0 additions & 1 deletion tests/integration/study_data_blueprint/test_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

@pytest.mark.unit_test
class TestLink:

@pytest.mark.parametrize("study_type", ["raw", "variant"])
def test_link_update(self, client: TestClient, user_access_token: str, study_type: str) -> None:
client.headers = {"Authorization": f"Bearer {user_access_token}"} # type: ignore
Expand Down
13 changes: 12 additions & 1 deletion tests/variantstudy/test_command_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@
}
],
),
CommandDTO(
action=CommandName.UPDATE_LINK.value,
args=[
{
"area1": "area1",
"area2": "area2",
"parameters": {},
"series": "series",
}
],
),
CommandDTO(
action=CommandName.REMOVE_LINK.value,
args={
Expand Down Expand Up @@ -411,7 +422,7 @@ def _get_command_classes(self) -> Set[str]:
f".{name}",
package="antarest.study.storage.variantstudy.model.command",
)
abstract_commands = {"AbstractBindingConstraintCommand"}
abstract_commands = {"AbstractBindingConstraintCommand", "AbstractLinkCommand"}
return {cmd.__name__ for cmd in ICommand.__subclasses__() if cmd.__name__ not in abstract_commands}

def test_all_commands_are_tested(self, command_factory: CommandFactory):
Expand Down

0 comments on commit 833a742

Please sign in to comment.