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

feat: add dao layer #2276

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
106 changes: 9 additions & 97 deletions antarest/study/business/link_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,123 +11,35 @@
# This file is part of the Antares project.

import typing as t
from typing import Any

from antares.study.version import StudyVersion

from antarest.core.exceptions import LinkNotFound
from antarest.core.model import JSON
from antarest.study.business.model.link_model import LinkBaseDTO, LinkDTO, LinkInternal
from antarest.study.business.utils import execute_or_add_commands
from antarest.study.business.model.link_model import LinkBaseDTO, LinkDTO
from antarest.study.dao.dao_factory import DAOFactory
from antarest.study.model import RawStudy, Study
from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy
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.remove_link import RemoveLink
from antarest.study.storage.variantstudy.model.command.update_link import UpdateLink


class LinkManager:
def __init__(self, storage_service: StudyStorageService) -> None:
self.storage_service = storage_service
def __init__(self, dao_factory: DAOFactory) -> None:
self.composite_dao = dao_factory.create_link_dao()

def get_all_links(self, study: Study) -> t.List[LinkDTO]:
file_study = self.storage_service.get_storage(study).get_raw(study)
result: t.List[LinkDTO] = []

for area_id, area in file_study.config.areas.items():
links_config = file_study.tree.get(["input", "links", area_id, "properties"])

for link in area.links:
link_tree_config: t.Dict[str, t.Any] = links_config[link]
link_tree_config.update({"area1": area_id, "area2": link})

link_internal = LinkInternal.model_validate(link_tree_config)

result.append(link_internal.to_dto())

return result

def get_link(self, study: RawStudy, link: LinkInternal) -> LinkInternal:
file_study = self.storage_service.get_storage(study).get_raw(study)

link_properties = self._get_link_if_exists(file_study, link)

link_properties.update({"area1": link.area1, "area2": link.area2})

updated_link = LinkInternal.model_validate(link_properties)

return updated_link
return self.composite_dao.get_links(study)

def create_link(self, study: Study, link_creation_dto: LinkDTO) -> LinkDTO:
link = link_creation_dto.to_internal(StudyVersion.parse(study.version))

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

command = CreateLink(
area1=link.area1,
area2=link.area2,
parameters=link.model_dump(exclude_none=True),
command_context=self.storage_service.variant_study_service.command_factory.command_context,
study_version=file_study.config.version,
)

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

return link_creation_dto
return self.composite_dao.create_link(study, link_creation_dto)

def update_link(self, study: RawStudy, area_from: str, area_to: str, link_update_dto: LinkBaseDTO) -> LinkDTO:
link_dto = LinkDTO(area1=area_from, area2=area_to, **link_update_dto.model_dump(exclude_unset=True))

link = link_dto.to_internal(StudyVersion.parse(study.version))
file_study = self.storage_service.get_storage(study).get_raw(study)

self._get_link_if_exists(file_study, link)

command = UpdateLink(
area1=link.area1,
area2=link.area2,
parameters=link.model_dump(
include=link_update_dto.model_fields_set, exclude={"area1", "area2"}, exclude_none=True
),
command_context=self.storage_service.variant_study_service.command_factory.command_context,
study_version=file_study.config.version,
)

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

updated_link = self.get_link(study, link)

return updated_link.to_dto()
return self.composite_dao.update_link(study, area_from, area_to, link_update_dto)

def update_links(
self,
study: RawStudy,
update_links_by_ids: t.Mapping[t.Tuple[str, str], LinkBaseDTO],
) -> t.Mapping[t.Tuple[str, str], LinkBaseDTO]:
new_links_by_ids = {}
for (area1, area2), update_link_dto in update_links_by_ids.items():
updated_link = self.update_link(study, area1, area2, update_link_dto)
new_links_by_ids[(area1, area2)] = updated_link

return new_links_by_ids
return self.composite_dao.update_links(study, update_links_by_ids)

def delete_link(self, study: RawStudy, area1_id: str, area2_id: str) -> None:
file_study = self.storage_service.get_storage(study).get_raw(study)
command = RemoveLink(
area1=area1_id,
area2=area2_id,
command_context=self.storage_service.variant_study_service.command_factory.command_context,
study_version=file_study.config.version,
)
execute_or_add_commands(study, file_study, [command], self.storage_service)

def _get_link_if_exists(self, file_study: FileStudy, link: LinkInternal) -> dict[str, Any]:
try:
return file_study.tree.get(["input", "links", link.area1, "properties", link.area2])
except KeyError:
raise LinkNotFound(f"The link {link.area1} -> {link.area2} is not present in the study")
self.composite_dao.delete_link(study, area1_id, area2_id)

@staticmethod
def get_table_schema() -> JSON:
Expand Down
11 changes: 11 additions & 0 deletions antarest/study/dao/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 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.
24 changes: 24 additions & 0 deletions antarest/study/dao/dao_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# 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 antarest.core.interfaces.cache import ICache
from antarest.study.dao.link.composite_link_dao import CompositeLinkDAO
from antarest.study.storage.storage_service import StudyStorageService


class DAOFactory:
def __init__(self, storage_service: StudyStorageService, cache_service: ICache):
self.storage_service = storage_service
self.cache_service = cache_service

def create_link_dao(self) -> CompositeLinkDAO:
return CompositeLinkDAO(self.storage_service, self.cache_service)
11 changes: 11 additions & 0 deletions antarest/study/dao/link/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 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.
58 changes: 58 additions & 0 deletions antarest/study/dao/link/cache_link_dao.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# 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.

import typing as t

from antarest.core.interfaces.cache import ICache
from antarest.study.business.model.link_model import LinkDTO


class CacheLinkDao:
def __init__(self, cache: ICache):
self.cache = cache

def get_links(self, study_id: str) -> t.Optional[t.List[LinkDTO]]:
cache_key = self._get_cache_key(study_id)
cached_data = self.cache.get(cache_key)
if cached_data is None:
return None
return [LinkDTO.model_validate(link) for link in cached_data["links"]]

def put(self, study_id: str, link: LinkDTO, timeout: int = 600000) -> None:
try:
cache_key = self._get_cache_key(study_id)
cached_links = self.cache.get(cache_key)

if cached_links is None:
cached_links = {"links": []}

existing_links = cached_links.get("links", [])

if isinstance(existing_links, dict):
existing_links = list(existing_links.values())
elif not isinstance(existing_links, list):
existing_links = []

link_data = link.model_dump()
existing_links.append(link_data)

cached_links["links"] = existing_links
self.cache.put(cache_key, cached_links, timeout)
except:
raise

def invalidate(self, study_id: str) -> None:
cache_key = self._get_cache_key(study_id)
self.cache.invalidate(cache_key)

def _get_cache_key(self, study_id: str) -> str:
return f"links:{study_id}"
57 changes: 57 additions & 0 deletions antarest/study/dao/link/composite_link_dao.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# 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.

import typing as t

from antarest.core.interfaces.cache import ICache
from antarest.study.business.model.link_model import LinkBaseDTO, LinkDTO
from antarest.study.dao.link.cache_link_dao import CacheLinkDao
from antarest.study.dao.link.storage_link_dao import StorageLinkDao
from antarest.study.model import Study
from antarest.study.storage.storage_service import StudyStorageService


class CompositeLinkDAO:
def __init__(self, storage_service: StudyStorageService, cache_service: ICache):
self.storage_dao = StorageLinkDao(storage_service)
self.cache_dao = CacheLinkDao(cache_service)

def create_link(self, study: Study, link: LinkDTO) -> LinkDTO:
self.cache_dao.invalidate(study.id)
return self.storage_dao.create_link(study, link)

def get_links(self, study: Study) -> t.List[LinkDTO]:
links = self.cache_dao.get_links(study.id)
if links is not None:
return links

links = self.storage_dao.get_links(study)

for link in links:
self.cache_dao.put(study.id, link)
return links

def update_link(self, study: Study, area_from: str, area_to: str, link_update_dto: LinkBaseDTO) -> LinkDTO:
self.cache_dao.invalidate(study.id)
return self.storage_dao.update_link(study, area_from, area_to, link_update_dto)

def update_links(
self,
study: Study,
update_links_by_ids: t.Mapping[t.Tuple[str, str], LinkBaseDTO],
) -> t.Mapping[t.Tuple[str, str], LinkBaseDTO]:
self.cache_dao.invalidate(study.id)
return self.storage_dao.update_links(study, update_links_by_ids)

def delete_link(self, study: Study, area_from: str, area_to: str) -> None:
self.cache_dao.invalidate(study.id)
self.storage_dao.delete_link(study, area_from, area_to)
Loading
Loading