Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(links): endpoint links create #2146

Closed
wants to merge 83 commits into from
Closed
Show file tree
Hide file tree
Changes from 73 commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
3663b60
v2.16.8
skamril Apr 19, 2024
d9bac0e
v2.17
laurent-laporte-pro May 15, 2024
55a245b
v2.17.1
skamril Jun 10, 2024
23027df
v2.17.2
skamril Jun 19, 2024
4150708
v2.17.3
skamril Jul 18, 2024
caa9cb8
v2.17.4
skamril Jul 26, 2024
b491c80
v2.17.5
skamril Aug 2, 2024
130d76c
WIP : first commit
TheoPascoli Sep 17, 2024
2d2027b
isort and black
TheoPascoli Sep 17, 2024
5cc7cea
add versioning
TheoPascoli Sep 18, 2024
f3a9464
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Sep 18, 2024
0cf0ff5
add versioning
TheoPascoli Sep 18, 2024
d6ecfb1
add versioning
TheoPascoli Sep 18, 2024
e3e2158
add versioning
TheoPascoli Sep 18, 2024
2c37fff
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Sep 18, 2024
e03678a
correct typing issue
TheoPascoli Sep 19, 2024
2f17d71
correct typing issue
TheoPascoli Sep 19, 2024
22e2eda
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Sep 19, 2024
26cdb3a
correct typing issue
TheoPascoli Sep 19, 2024
76188de
fix tests
TheoPascoli Sep 19, 2024
9dff527
fix tests
TheoPascoli Sep 19, 2024
cac1349
fix tests
TheoPascoli Sep 19, 2024
d9716e3
refactor code
TheoPascoli Sep 19, 2024
0a540b1
refactor code
TheoPascoli Sep 19, 2024
40cb85b
fix mypy
TheoPascoli Sep 20, 2024
0d17337
fix mypy
TheoPascoli Sep 20, 2024
827fef2
fix mypy
TheoPascoli Sep 20, 2024
1e7004f
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Sep 23, 2024
3eb93dd
change ui handling
TheoPascoli Sep 23, 2024
a04fbe2
black fixing
TheoPascoli Sep 23, 2024
ca0898f
fix errors
TheoPascoli Sep 24, 2024
30cf961
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Sep 24, 2024
6a79ad6
fix errors
TheoPascoli Sep 24, 2024
b6c0053
fix errors
TheoPascoli Sep 24, 2024
a047c30
refactor code
MartinBelthle Sep 24, 2024
60c0c52
isort and black
TheoPascoli Sep 24, 2024
d520b83
refactor classes
TheoPascoli Sep 24, 2024
40c42d0
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Sep 25, 2024
0c002d9
add integration tests
TheoPascoli Sep 25, 2024
26d382e
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Sep 25, 2024
c7728db
add integration tests
TheoPascoli Sep 25, 2024
779dc39
add integration tests
TheoPascoli Sep 25, 2024
6ff5520
v2.17.6
skamril Sep 25, 2024
3853e45
add integration tests
TheoPascoli Sep 26, 2024
05e4ddf
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Sep 27, 2024
647dfbd
add integration tests
TheoPascoli Sep 27, 2024
e057c4b
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Sep 27, 2024
1355def
fix: fix tests
TheoPascoli Sep 30, 2024
c07e789
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Sep 30, 2024
8b4ad08
Merge remote-tracking branch 'origin/master' into fix/fix-endpoint-li…
TheoPascoli Sep 30, 2024
18f2d77
build(ui): fix issue with build result not working (#2163)
skamril Sep 30, 2024
b6359ff
Merge branch 'fix/fix-endpoint-links-creation' of https://github.com/…
TheoPascoli Sep 30, 2024
ede7b3e
fix: fix tests
TheoPascoli Sep 30, 2024
3a6a7c8
fix: remove optional typing in models
TheoPascoli Sep 30, 2024
fcace50
fix: remove optional typing in models
TheoPascoli Sep 30, 2024
0f4d6e0
fix: remove optional typing in models
TheoPascoli Sep 30, 2024
a4d0cd7
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Oct 7, 2024
4d5c8ea
conflict: merge dev
TheoPascoli Oct 7, 2024
c4772db
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Oct 8, 2024
d4c0830
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Oct 9, 2024
a56337c
adapt to new handling of study_versions
TheoPascoli Oct 9, 2024
fe6b602
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Oct 9, 2024
b429307
Merge branch 'dev' into fix/fix-endpoint-links-creation
TheoPascoli Oct 10, 2024
650bc59
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Oct 11, 2024
1543472
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Oct 14, 2024
b219b0f
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Oct 16, 2024
2bdaa94
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Oct 17, 2024
b40ccf2
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Oct 18, 2024
f7ef6c6
Merge branch 'dev' into fix/fix-endpoint-links-creation
TheoPascoli Oct 23, 2024
3e361b3
Merge branch 'dev' into fix/fix-endpoint-links-creation
TheoPascoli Oct 23, 2024
90fcc21
Merge branch 'dev' into fix/fix-endpoint-links-creation
TheoPascoli Oct 24, 2024
fe34e97
Merge branch 'dev' into fix/fix-endpoint-links-creation
TheoPascoli Oct 24, 2024
a11c7c7
Merge branch 'dev' into fix/fix-endpoint-links-creation
sylvlecl Oct 25, 2024
6d0814b
Refactor to include camelcase to the return object
TheoPascoli Oct 26, 2024
eb366de
Refactor link DTOs and validation logic
TheoPascoli Oct 28, 2024
dea6710
Refactor filter values to use a constant list
TheoPascoli Oct 28, 2024
0ff40b9
Refactor filter fields to use enumerations
TheoPascoli Oct 28, 2024
b8a52e4
Add a missing newline before LinkInfoProperties820 class
TheoPascoli Oct 28, 2024
53bf23c
Remove unused import and update type annotations
TheoPascoli Oct 28, 2024
99c7d63
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Oct 29, 2024
be9450e
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
TheoPascoli Oct 30, 2024
fa5287a
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
skamril Nov 7, 2024
5924a10
Merge remote-tracking branch 'origin/dev' into fix/fix-endpoint-links…
skamril Nov 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions antarest/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,11 @@ def __init__(self, message: str) -> None:
super().__init__(HTTPStatus.UNPROCESSABLE_ENTITY, message)


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


class VariantStudyParentNotValid(HTTPException):
def __init__(self, message: str) -> None:
super().__init__(HTTPStatus.UNPROCESSABLE_ENTITY, message)
Expand Down
4 changes: 4 additions & 0 deletions antarest/core/utils/string.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ def to_pascal_case(value: str) -> str:
def to_camel_case(value: str) -> str:
v = to_pascal_case(value)
return v[0].lower() + v[1:] if len(v) > 0 else ""


def to_kebab_case(string: str) -> str:
return string.replace("_", "-")
100 changes: 66 additions & 34 deletions antarest/study/business/link_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,37 @@

import typing as t

from antarest.core.exceptions import ConfigFileNotFound
from antares.study.version import StudyVersion

from antarest.core.exceptions import ConfigFileNotFound, LinkValidationError
from antarest.core.model import JSON
from antarest.core.serialization import AntaresBaseModel
from antarest.study.business.all_optional_meta import all_optional_model, camel_case_model
from antarest.study.business.utils import execute_or_add_commands
from antarest.study.model import RawStudy
from antarest.study.model import STUDY_VERSION_8_2, RawStudy
from antarest.study.storage.rawstudy.model.filesystem.config.links import LinkProperties
from antarest.study.storage.storage_service import StudyStorageService
from antarest.study.storage.variantstudy.model.command.create_link import CreateLink
from antarest.study.storage.variantstudy.model.command.common import FilteringOptions
from antarest.study.storage.variantstudy.model.command.create_link import (
AreaInfo,
CreateLink,
LinkInfoProperties,
LinkInfoProperties820,
)
from antarest.study.storage.variantstudy.model.command.remove_link import RemoveLink
from antarest.study.storage.variantstudy.model.command.update_config import UpdateConfig

_ALL_LINKS_PATH = "input/links"


class LinkUIDTO(AntaresBaseModel):
color: str
width: float
style: str
class LinkInfoDTOBase(AreaInfo, LinkInfoProperties):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class AreaInfo seems not used elsewhere, it should be defined here.
Not a big fan of using inheritance that much, areas could also got just in the definition of those 2 classes. But Ok to leave it this way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had troubles with some tests when removing the area value, especially commands tests. I left it that way

sylvlecl marked this conversation as resolved.
Show resolved Hide resolved
pass


class LinkInfoDTO820(AreaInfo, LinkInfoProperties820):
pass


class LinkInfoDTO(AntaresBaseModel):
area1: str
area2: str
ui: t.Optional[LinkUIDTO] = None
LinkInfoDTOType = t.Union[LinkInfoDTO820, LinkInfoDTOBase]


@all_optional_model
Expand All @@ -51,38 +57,62 @@ class LinkManager:
def __init__(self, storage_service: StudyStorageService) -> None:
self.storage_service = storage_service

def get_all_links(self, study: RawStudy, with_ui: bool = False) -> t.List[LinkInfoDTO]:
def get_all_links(self, study: RawStudy) -> t.List[LinkInfoDTOType]:
file_study = self.storage_service.get_storage(study).get_raw(study)
result = []
result: t.List[LinkInfoDTOType] = []

for area_id, area in file_study.config.areas.items():
links_config: t.Optional[t.Dict[str, t.Any]] = None
if with_ui:
links_config = file_study.tree.get(["input", "links", area_id, "properties"])
links_config = file_study.tree.get(["input", "links", area_id, "properties"])

for link in area.links:
ui_info: t.Optional[LinkUIDTO] = None
if with_ui and links_config and link in links_config:
ui_info = LinkUIDTO(
color=f"{links_config[link].get('colorr', '163')},{links_config[link].get('colorg', '163')},{links_config[link].get('colorb', '163')}",
width=links_config[link].get("link-width", 1),
style=links_config[link].get("link-style", "plain"),
)
result.append(LinkInfoDTO(area1=area_id, area2=link, ui=ui_info))
link_properties = links_config[link]

link_creation_data: t.Dict[str, t.Any] = {"area1": area_id, "area2": link}
link_creation_data.update(link_properties)

link_data: LinkInfoDTOType
if StudyVersion.parse(study.version) < STUDY_VERSION_8_2:
link_data = LinkInfoDTOBase(**link_creation_data)
else:
link_data = LinkInfoDTO820(**link_creation_data)

result.append(link_data)

return result

def create_link(self, study: RawStudy, link_creation_info: LinkInfoDTO) -> LinkInfoDTO:
def create_link(self, study: RawStudy, link_creation_info: LinkInfoDTOType) -> LinkInfoDTOType:
if link_creation_info.area1 == link_creation_info.area2:
raise LinkValidationError(f"Cannot create link on same area: {link_creation_info.area1}")

if StudyVersion.parse(study.version) < STUDY_VERSION_8_2 and isinstance(link_creation_info, LinkInfoDTO820):
if link_creation_info.filter_synthesis is not None or link_creation_info.filter_year_by_year is not None:
TheoPascoli marked this conversation as resolved.
Show resolved Hide resolved
raise LinkValidationError("Cannot specify a filter value for study's version earlier than v8.2")

link_info = link_creation_info.model_dump(exclude_none=True, by_alias=True)

link_data: LinkInfoDTOType
if StudyVersion.parse(study.version) < STUDY_VERSION_8_2:
link_data = LinkInfoDTOBase(**link_info)
else:
link_data = LinkInfoDTO820(**link_info)
if isinstance(link_data, LinkInfoDTO820) and link_data.filter_synthesis is None:
TheoPascoli marked this conversation as resolved.
Show resolved Hide resolved
link_data.filter_synthesis = FilteringOptions.FILTER_SYNTHESIS
TheoPascoli marked this conversation as resolved.
Show resolved Hide resolved
if isinstance(link_data, LinkInfoDTO820) and link_data.filter_year_by_year is None:
TheoPascoli marked this conversation as resolved.
Show resolved Hide resolved
link_data.filter_year_by_year = FilteringOptions.FILTER_YEAR_BY_YEAR

storage_service = self.storage_service.get_storage(study)
file_study = storage_service.get_raw(study)

command = CreateLink(
area1=link_creation_info.area1,
area2=link_creation_info.area2,
parameters=link_data.model_dump(exclude={"area1", "area2"}, exclude_none=True, by_alias=True),
command_context=self.storage_service.variant_study_service.command_factory.command_context,
)

execute_or_add_commands(study, file_study, [command], self.storage_service)
return LinkInfoDTO(
area1=link_creation_info.area1,
area2=link_creation_info.area2,
)

return link_data

def delete_link(self, study: RawStudy, area1_id: str, area2_id: str) -> None:
file_study = self.storage_service.get_storage(study).get_raw(study)
Expand Down Expand Up @@ -137,15 +167,17 @@ def update_links_props(
for (area1, area2), update_link_dto in update_links_by_ids.items():
# Update the link properties.
old_link_dto = old_links_by_ids[(area1, area2)]
new_link_dto = old_link_dto.copy(update=update_link_dto.model_dump(by_alias=False, exclude_none=True))
new_links_by_ids[(area1, area2)] = new_link_dto
updated_link_properties = old_link_dto.copy(
update=update_link_dto.model_dump(by_alias=False, exclude_none=True)
)
new_links_by_ids[(area1, area2)] = updated_link_properties

# Convert the DTO to a configuration object and update the configuration file.
properties = LinkProperties(**new_link_dto.model_dump(by_alias=False))
properties = LinkProperties(**updated_link_properties.model_dump(by_alias=False))
path = f"{_ALL_LINKS_PATH}/{area1}/properties/{area2}"
cmd = UpdateConfig(
target=path,
data=properties.to_config(),
data=properties.to_ini(int(study.version)),
command_context=self.storage_service.variant_study_service.command_factory.command_context,
)
commands.append(cmd)
Expand Down
6 changes: 4 additions & 2 deletions antarest/study/business/table_mode_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ def _get_table_data_unsafe(self, study: RawStudy, table_type: TableModeType) ->
data = {area_id: area.model_dump(by_alias=True) for area_id, area in areas_map.items()}
elif table_type == TableModeType.LINK:
links_map = self._link_manager.get_all_links_props(study)
excludes = set() if int(study.version) >= 820 else {"filter_synthesis", "filter_year_by_year"}
data = {
f"{area1_id} / {area2_id}": link.model_dump(by_alias=True)
f"{area1_id} / {area2_id}": link.model_dump(by_alias=True, exclude=excludes)
for (area1_id, area2_id), link in links_map.items()
}
elif table_type == TableModeType.THERMAL:
Expand Down Expand Up @@ -195,8 +196,9 @@ def update_table_data(
elif table_type == TableModeType.LINK:
links_map = {tuple(key.split(" / ")): LinkOutput(**values) for key, values in data.items()}
updated_map = self._link_manager.update_links_props(study, links_map) # type: ignore
excludes = set() if int(study.version) >= 820 else {"filter_synthesis", "filter_year_by_year"}
data = {
f"{area1_id} / {area2_id}": link.model_dump(by_alias=True)
f"{area1_id} / {area2_id}": link.model_dump(by_alias=True, exclude=excludes)
for (area1_id, area2_id), link in updated_map.items()
}
return data
Expand Down
11 changes: 5 additions & 6 deletions antarest/study/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
from antarest.study.business.correlation_management import CorrelationManager
from antarest.study.business.district_manager import DistrictManager
from antarest.study.business.general_management import GeneralManager
from antarest.study.business.link_management import LinkInfoDTO, LinkManager
from antarest.study.business.link_management import LinkInfoDTOType, LinkManager
from antarest.study.business.matrix_management import MatrixManager, MatrixManagerError
from antarest.study.business.optimization_management import OptimizationManager
from antarest.study.business.playlist_management import PlaylistManager
Expand Down Expand Up @@ -1825,12 +1825,11 @@ def get_all_areas(
def get_all_links(
self,
uuid: str,
with_ui: bool,
params: RequestParameters,
) -> t.List[LinkInfoDTO]:
) -> t.List[LinkInfoDTOType]:
study = self.get_study(uuid)
assert_permission(params.user, study, StudyPermissionType.READ)
return self.links.get_all_links(study, with_ui)
return self.links.get_all_links(study)

def create_area(
self,
Expand All @@ -1854,9 +1853,9 @@ def create_area(
def create_link(
self,
uuid: str,
link_creation_dto: LinkInfoDTO,
link_creation_dto: LinkInfoDTOType,
params: RequestParameters,
) -> LinkInfoDTO:
) -> LinkInfoDTOType:
study = self.get_study(uuid)
assert_permission(params.user, study, StudyPermissionType.WRITE)
self._assert_study_unarchived(study)
Expand Down
42 changes: 40 additions & 2 deletions antarest/study/storage/rawstudy/model/filesystem/config/links.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,43 @@ class TransmissionCapacity(EnumIgnoreCase):
ENABLED = "enabled"


class LinkStyle(EnumIgnoreCase):
"""
Enum representing the style of a link in a network visualization.

Attributes:
DOT: Represents a dotted line style.
PLAIN: Represents a solid line style.
DASH: Represents a dashed line style.
DOT_DASH: Represents a line style with alternating dots and dashes.
"""

DOT = "dot"
PLAIN = "plain"
DASH = "dash"
DOT_DASH = "dotdash"
OTHER = "other"


class FilterOption(EnumIgnoreCase):
TheoPascoli marked this conversation as resolved.
Show resolved Hide resolved
"""
Enum representing the time filter options for data visualization or analysis in Antares Web.

Attributes:
HOURLY: Represents filtering data by the hour.
DAILY: Represents filtering data by the day.
WEEKLY: Represents filtering data by the week.
MONTHLY: Represents filtering data by the month.
ANNUAL: Represents filtering data by the year.
"""

HOURLY = "hourly"
DAILY = "daily"
WEEKLY = "weekly"
MONTHLY = "monthly"
ANNUAL = "annual"


class LinkProperties(IniProperties):
"""
Configuration read from a section in the `input/links/<area1>/properties.ini` file.
Expand Down Expand Up @@ -159,11 +196,12 @@ def _validate_colors(cls, values: t.MutableMapping[str, t.Any]) -> t.Mapping[str
return validate_colors(values)

# noinspection SpellCheckingInspection
def to_config(self) -> t.Dict[str, t.Any]:
def to_ini(self, version: int) -> t.Dict[str, t.Any]:
"""
Convert the object to a dictionary for writing to a configuration file.
"""
obj = dict(super().to_config())
excludes = set() if version >= 820 else {"filter_synthesis", "filter_year_by_year"}
obj = self.model_dump(mode="json", exclude_none=True, by_alias=True, exclude=excludes)
color_rgb = obj.pop("colorRgb", "#707070")
return {
"colorr": int(color_rgb[1:3], 16),
Expand Down
Loading
Loading