From f6a4615db9ed8ccd6e7cc33f317f9841fc78d620 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Mon, 9 Oct 2023 17:52:25 +0200 Subject: [PATCH 01/13] build: prepare hotfix v2.15.2 (unreleased) --- antarest/__init__.py | 4 ++-- docs/CHANGELOG.md | 4 ++++ setup.py | 2 +- sonar-project.properties | 2 +- webapp/package-lock.json | 2 +- webapp/package.json | 2 +- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/antarest/__init__.py b/antarest/__init__.py index 4023e2ca7b..f167743c7a 100644 --- a/antarest/__init__.py +++ b/antarest/__init__.py @@ -7,9 +7,9 @@ # Standard project metadata -__version__ = "2.15.1" +__version__ = "2.15.2" __author__ = "RTE, Antares Web Team" -__date__ = "2023-10-05" +__date__ = "unreleased" # noinspection SpellCheckingInspection __credits__ = "(c) Réseau de Transport de l’Électricité (RTE)" diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index e6d96180aa..db4d73fbb9 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,10 @@ Antares Web Changelog ===================== +v2.15.2 (unreleased) +-------------------- + + v2.15.1 (2023-10-05) -------------------- diff --git a/setup.py b/setup.py index 92e4e34f98..d0b8c5deb3 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name="AntaREST", - version="2.15.1", + version="2.15.2", description="Antares Server", long_description=Path("README.md").read_text(encoding="utf-8"), long_description_content_type="text/markdown", diff --git a/sonar-project.properties b/sonar-project.properties index 9bf66c1c14..1d3a132a04 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -6,5 +6,5 @@ sonar.exclusions=antarest/gui.py,antarest/main.py sonar.python.coverage.reportPaths=coverage.xml sonar.python.version=3.8 sonar.javascript.lcov.reportPaths=webapp/coverage/lcov.info -sonar.projectVersion=2.15.1 +sonar.projectVersion=2.15.2 sonar.coverage.exclusions=antarest/gui.py,antarest/main.py,antarest/singleton_services.py,antarest/worker/archive_worker_service.py,webapp/**/* \ No newline at end of file diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 9a4f7cd401..3ab3cca0b7 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -1,6 +1,6 @@ { "name": "antares-web", - "version": "2.15.1", + "version": "2.15.2", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/webapp/package.json b/webapp/package.json index 74a8fd65f0..506c324bf2 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -1,6 +1,6 @@ { "name": "antares-web", - "version": "2.15.1", + "version": "2.15.2", "private": true, "engines": { "node": "18.16.1" From f6201976a653db19739cbc42e91ea27ac790da10 Mon Sep 17 00:00:00 2001 From: MartinBelthle <102529366+MartinBelthle@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:19:12 +0200 Subject: [PATCH 02/13] hotfix(service): user connected via tokens cannot create a study (#1757) Co-authored-by: Laurent LAPORTE --- antarest/study/service.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/antarest/study/service.py b/antarest/study/service.py index b9ec491c18..64d5c7d83c 100644 --- a/antarest/study/service.py +++ b/antarest/study/service.py @@ -641,15 +641,20 @@ def create_study( def get_user_name(self, params: RequestParameters) -> str: """ - Args: params : Request parameters + Retrieves the name of a user based on the provided request parameters. - Returns: The user's name + Args: + params: The request parameters which includes user information. + + Returns: + Returns the user's name or, if the logged user is a "bot" + (i.e., an application's token), it returns the token's author name. """ - author = "Unknown" if params.user: - if curr_user := self.user_service.get_user(params.user.id, params): - author = curr_user.to_dto().name - return author + user_id = params.user.impersonator if params.user.type == "bots" else params.user.id + if curr_user := self.user_service.get_user(user_id, params): + return curr_user.to_dto().name + return "Unknown" def get_study_synthesis(self, study_id: str, params: RequestParameters) -> FileStudyTreeConfigDTO: """ From 703351a6d8d4f70491e66c3c54a92c6d28cb92ea Mon Sep 17 00:00:00 2001 From: MartinBelthle <102529366+martinbelthle@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:16:28 +0200 Subject: [PATCH 03/13] feat(model): handling binding constraints frequency in study configuration parsing (#1702) --- .../business/binding_constraint_management.py | 5 +- .../filesystem/config/binding_constraint.py | 17 ++ .../rawstudy/model/filesystem/config/files.py | 15 +- .../rawstudy/model/filesystem/config/model.py | 9 +- .../model/filesystem/matrix/constants.py | 9 + .../bindingconstraints/bindingcontraints.py | 21 +- .../business/command_extractor.py | 5 +- .../business/utils_binding_constraint.py | 37 +-- .../variantstudy/model/command/common.py | 6 - .../command/create_binding_constraint.py | 4 +- .../command/update_binding_constraint.py | 4 +- tests/integration/assets/base_study.zip | Bin 298490 -> 311332 bytes tests/integration/assets/variant_study.zip | Bin 302041 -> 316785 bytes tests/integration/test_integration.py | 6 +- .../test_integration_variantmanager_tool.py | 236 +++++++++--------- .../filesystem/config/test_config_files.py | 16 +- tests/storage/test_model.py | 10 +- .../test_manage_binding_constraints.py | 47 ++-- .../model/command/test_remove_area.py | 5 +- .../model/command/test_remove_cluster.py | 5 +- 20 files changed, 262 insertions(+), 195 deletions(-) create mode 100644 antarest/study/storage/rawstudy/model/filesystem/config/binding_constraint.py diff --git a/antarest/study/business/binding_constraint_management.py b/antarest/study/business/binding_constraint_management.py index 0fedb98393..ca1f714750 100644 --- a/antarest/study/business/binding_constraint_management.py +++ b/antarest/study/business/binding_constraint_management.py @@ -12,8 +12,9 @@ from antarest.matrixstore.model import MatrixData from antarest.study.business.utils import execute_or_add_commands from antarest.study.model import Study +from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import BindingConstraintFrequency from antarest.study.storage.storage_service import StudyStorageService -from antarest.study.storage.variantstudy.model.command.common import BindingConstraintOperator, TimeStep +from antarest.study.storage.variantstudy.model.command.common import BindingConstraintOperator from antarest.study.storage.variantstudy.model.command.update_binding_constraint import UpdateBindingConstraint @@ -43,7 +44,7 @@ class BindingConstraintDTO(BaseModel): id: str name: str enabled: bool = True - time_step: TimeStep + time_step: BindingConstraintFrequency operator: BindingConstraintOperator values: Optional[Union[List[List[MatrixData]], str]] = None comments: Optional[str] = None diff --git a/antarest/study/storage/rawstudy/model/filesystem/config/binding_constraint.py b/antarest/study/storage/rawstudy/model/filesystem/config/binding_constraint.py new file mode 100644 index 0000000000..3648051773 --- /dev/null +++ b/antarest/study/storage/rawstudy/model/filesystem/config/binding_constraint.py @@ -0,0 +1,17 @@ +from enum import Enum +from typing import Set + +from pydantic import BaseModel + + +class BindingConstraintFrequency(str, Enum): + HOURLY = "hourly" + DAILY = "daily" + WEEKLY = "weekly" + + +class BindingConstraintDTO(BaseModel): + id: str + areas: Set[str] + clusters: Set[str] + time_step: BindingConstraintFrequency diff --git a/antarest/study/storage/rawstudy/model/filesystem/config/files.py b/antarest/study/storage/rawstudy/model/filesystem/config/files.py index 9f8111911a..8e174f3ea0 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/config/files.py +++ b/antarest/study/storage/rawstudy/model/filesystem/config/files.py @@ -10,13 +10,16 @@ from antarest.core.model import JSON from antarest.core.utils.utils import extract_file_to_tmp_dir from antarest.study.storage.rawstudy.io.reader import IniReader, MultipleSameKeysIniReader +from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import ( + BindingConstraintDTO, + BindingConstraintFrequency, +) from antarest.study.storage.rawstudy.model.filesystem.config.exceptions import ( SimulationParsingError, XpansionParsingError, ) from antarest.study.storage.rawstudy.model.filesystem.config.model import ( Area, - BindingConstraintDTO, Cluster, DistrictSet, FileStudyTreeConfig, @@ -143,8 +146,12 @@ def _parse_bindings(root: Path) -> List[BindingConstraintDTO]: area_set = set() # contains a set of strings in the following format: "area.cluster" cluster_set = set() + # Default value for time_step + time_step = BindingConstraintFrequency.HOURLY for key in bind: - if "%" in key: + if key == "type": + time_step = BindingConstraintFrequency(bind[key]) + elif "%" in key: areas = key.split("%", 1) area_set.add(areas[0]) area_set.add(areas[1]) @@ -152,7 +159,9 @@ def _parse_bindings(root: Path) -> List[BindingConstraintDTO]: cluster_set.add(key) area_set.add(key.split(".", 1)[0]) - output_list.append(BindingConstraintDTO(id=bind["id"], areas=area_set, clusters=cluster_set)) + output_list.append( + BindingConstraintDTO(id=bind["id"], areas=area_set, clusters=cluster_set, time_step=time_step) + ) return output_list diff --git a/antarest/study/storage/rawstudy/model/filesystem/config/model.py b/antarest/study/storage/rawstudy/model/filesystem/config/model.py index f688765922..774d225344 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/config/model.py +++ b/antarest/study/storage/rawstudy/model/filesystem/config/model.py @@ -1,7 +1,7 @@ import re from enum import Enum from pathlib import Path -from typing import Dict, List, Optional, Set +from typing import Dict, List, Optional from pydantic import Extra from pydantic.main import BaseModel @@ -9,6 +9,7 @@ from antarest.core.model import JSON from antarest.core.utils.utils import DTO +from .binding_constraint import BindingConstraintDTO from .st_storage import STStorageConfig @@ -106,12 +107,6 @@ def get_file(self) -> str: return f"{self.date}{modes[self.mode]}{dash}{self.name}" -class BindingConstraintDTO(BaseModel): - id: str - areas: Set[str] - clusters: Set[str] - - class FileStudyTreeConfig(DTO): """ Root object to handle all study parameters which impact tree structure diff --git a/antarest/study/storage/rawstudy/model/filesystem/matrix/constants.py b/antarest/study/storage/rawstudy/model/filesystem/matrix/constants.py index 8f2b429e17..3269f1abf6 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/matrix/constants.py +++ b/antarest/study/storage/rawstudy/model/filesystem/matrix/constants.py @@ -14,3 +14,12 @@ default_8_fixed_hourly = np.zeros((8760, 8), dtype=np.float64) default_8_fixed_hourly.flags.writeable = False + +default_binding_constraint_hourly = np.zeros((8760, 3), dtype=np.float64) +default_binding_constraint_hourly.flags.writeable = False + +default_binding_constraint_daily = np.zeros((365, 3), dtype=np.float64) +default_binding_constraint_daily.flags.writeable = False + +default_binding_constraint_weekly = np.zeros((52, 3), dtype=np.float64) +default_binding_constraint_weekly.flags.writeable = False diff --git a/antarest/study/storage/rawstudy/model/filesystem/root/input/bindingconstraints/bindingcontraints.py b/antarest/study/storage/rawstudy/model/filesystem/root/input/bindingconstraints/bindingcontraints.py index 92d5443957..849019038d 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/root/input/bindingconstraints/bindingcontraints.py +++ b/antarest/study/storage/rawstudy/model/filesystem/root/input/bindingconstraints/bindingcontraints.py @@ -1,6 +1,13 @@ +from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import BindingConstraintFrequency from antarest.study.storage.rawstudy.model.filesystem.folder_node import FolderNode from antarest.study.storage.rawstudy.model.filesystem.inode import TREE +from antarest.study.storage.rawstudy.model.filesystem.matrix.constants import ( + default_binding_constraint_daily, + default_binding_constraint_hourly, + default_binding_constraint_weekly, +) from antarest.study.storage.rawstudy.model.filesystem.matrix.input_series_matrix import InputSeriesMatrix +from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import MatrixFrequency from antarest.study.storage.rawstudy.model.filesystem.root.input.bindingconstraints.bindingconstraints_ini import ( BindingConstraintsIni, ) @@ -8,9 +15,19 @@ class BindingConstraints(FolderNode): def build(self) -> TREE: + default_matrices = { + BindingConstraintFrequency.HOURLY: default_binding_constraint_hourly, + BindingConstraintFrequency.DAILY: default_binding_constraint_daily, + BindingConstraintFrequency.WEEKLY: default_binding_constraint_weekly, + } children: TREE = { - binding.id: InputSeriesMatrix(self.context, self.config.next_file(f"{binding.id}.txt")) - # todo get the freq of binding to set the default empty matrix + binding.id: InputSeriesMatrix( + self.context, + self.config.next_file(f"{binding.id}.txt"), + freq=MatrixFrequency(binding.time_step), + nb_columns=3, + default_empty=default_matrices[binding.time_step], + ) for binding in self.config.bindings } diff --git a/antarest/study/storage/variantstudy/business/command_extractor.py b/antarest/study/storage/variantstudy/business/command_extractor.py index 0602c8b62a..ab1f8b6d42 100644 --- a/antarest/study/storage/variantstudy/business/command_extractor.py +++ b/antarest/study/storage/variantstudy/business/command_extractor.py @@ -9,12 +9,13 @@ from antarest.matrixstore.model import MatrixData from antarest.matrixstore.service import ISimpleMatrixService from antarest.study.storage.patch_service import PatchService +from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import BindingConstraintFrequency from antarest.study.storage.rawstudy.model.filesystem.config.files import get_playlist from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy from antarest.study.storage.rawstudy.model.filesystem.root.filestudytree import FileStudyTree from antarest.study.storage.variantstudy.business.matrix_constants_generator import GeneratorMatrixConstants from antarest.study.storage.variantstudy.business.utils import strip_matrix_protocol -from antarest.study.storage.variantstudy.model.command.common import BindingConstraintOperator, TimeStep +from antarest.study.storage.variantstudy.model.command.common import BindingConstraintOperator from antarest.study.storage.variantstudy.model.command.create_area import CreateArea from antarest.study.storage.variantstudy.model.command.create_binding_constraint import CreateBindingConstraint from antarest.study.storage.variantstudy.model.command.create_cluster import CreateCluster @@ -348,7 +349,7 @@ def extract_binding_constraint( binding_constraint_command = CreateBindingConstraint( name=binding["name"], enabled=binding["enabled"], - time_step=TimeStep(binding["type"]), + time_step=BindingConstraintFrequency(binding["type"]), operator=BindingConstraintOperator(binding["operator"]), coeffs={ coeff: [float(el) for el in str(value).split("%")] diff --git a/antarest/study/storage/variantstudy/business/utils_binding_constraint.py b/antarest/study/storage/variantstudy/business/utils_binding_constraint.py index 13e3617ec4..92d5170275 100644 --- a/antarest/study/storage/variantstudy/business/utils_binding_constraint.py +++ b/antarest/study/storage/variantstudy/business/utils_binding_constraint.py @@ -1,10 +1,14 @@ -from typing import Dict, List, Optional, Union +from typing import Dict, List, Literal, Mapping, Optional, Sequence, Union from antarest.core.model import JSON from antarest.matrixstore.model import MatrixData -from antarest.study.storage.rawstudy.model.filesystem.config.model import BindingConstraintDTO, FileStudyTreeConfig +from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import ( + BindingConstraintDTO, + BindingConstraintFrequency, +) +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.common import BindingConstraintOperator, CommandOutput, TimeStep +from antarest.study.storage.variantstudy.model.command.common import BindingConstraintOperator, CommandOutput def cluster_does_not_exist(study_data: FileStudy, area: str, thermal_id: str) -> bool: @@ -25,27 +29,27 @@ def apply_binding_constraint( name: str, comments: Optional[str], enabled: bool, - time_step: TimeStep, + freq: BindingConstraintFrequency, operator: BindingConstraintOperator, coeffs: Dict[str, List[float]], values: Optional[Union[List[List[MatrixData]], str]], filter_year_by_year: Optional[str] = None, filter_synthesis: Optional[str] = None, ) -> CommandOutput: - binding_constraints[str(new_key)] = { + binding_constraints[new_key] = { "name": name, "id": bd_id, "enabled": enabled, - "type": time_step.value, + "type": freq.value, "operator": operator.value, } if study_data.config.version >= 830: if filter_year_by_year: - binding_constraints[str(new_key)]["filter-year-by-year"] = filter_year_by_year + binding_constraints[new_key]["filter-year-by-year"] = filter_year_by_year if filter_synthesis: - binding_constraints[str(new_key)]["filter-synthesis"] = filter_synthesis + binding_constraints[new_key]["filter-synthesis"] = filter_synthesis if comments is not None: - binding_constraints[str(new_key)]["comments"] = comments + binding_constraints[new_key]["comments"] = comments for link_or_thermal in coeffs: if "%" in link_or_thermal: @@ -67,7 +71,7 @@ def apply_binding_constraint( if len(coeffs[link_or_thermal]) == 2: coeffs[link_or_thermal][1] = int(coeffs[link_or_thermal][1]) - binding_constraints[str(new_key)][link_or_thermal] = "%".join( + binding_constraints[new_key][link_or_thermal] = "%".join( [str(coeff_val) for coeff_val in coeffs[link_or_thermal]] ) parse_bindings_coeffs_and_save_into_config(bd_id, study_data.config, coeffs) @@ -84,19 +88,24 @@ def apply_binding_constraint( def parse_bindings_coeffs_and_save_into_config( bd_id: str, study_data_config: FileStudyTreeConfig, - coeffs: Dict[str, List[float]], + coeffs: Mapping[str, Union[Literal["hourly", "daily", "weekly"], Sequence[float]]], ) -> None: if bd_id not in [bind.id for bind in study_data_config.bindings]: areas_set = set() clusters_set = set() + # Default time_step value + time_step = BindingConstraintFrequency.HOURLY for k, v in coeffs.items(): + if k == "type": + time_step = BindingConstraintFrequency(v) if "%" in k: - areas_set.add(k.split("%")[0]) - areas_set.add(k.split("%")[1]) + areas_set |= set(k.split("%")) elif "." in k: clusters_set.add(k) areas_set.add(k.split(".")[0]) - study_data_config.bindings.append(BindingConstraintDTO(id=bd_id, areas=areas_set, clusters=clusters_set)) + study_data_config.bindings.append( + BindingConstraintDTO(id=bd_id, areas=areas_set, clusters=clusters_set, time_step=time_step) + ) def remove_area_cluster_from_binding_constraints( diff --git a/antarest/study/storage/variantstudy/model/command/common.py b/antarest/study/storage/variantstudy/model/command/common.py index 34c41402d6..a6ac905fd9 100644 --- a/antarest/study/storage/variantstudy/model/command/common.py +++ b/antarest/study/storage/variantstudy/model/command/common.py @@ -8,12 +8,6 @@ class CommandOutput: message: str = "" -class TimeStep(Enum): - HOURLY = "hourly" - DAILY = "daily" - WEEKLY = "weekly" - - class BindingConstraintOperator(Enum): BOTH = "both" EQUAL = "equal" diff --git a/antarest/study/storage/variantstudy/model/command/create_binding_constraint.py b/antarest/study/storage/variantstudy/model/command/create_binding_constraint.py index 02e888f9ac..49ddf4f308 100644 --- a/antarest/study/storage/variantstudy/model/command/create_binding_constraint.py +++ b/antarest/study/storage/variantstudy/model/command/create_binding_constraint.py @@ -4,6 +4,7 @@ from antarest.core.utils.utils import assert_this from antarest.matrixstore.model import MatrixData +from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import BindingConstraintFrequency from antarest.study.storage.rawstudy.model.filesystem.config.model import FileStudyTreeConfig, transform_name_to_id from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy from antarest.study.storage.variantstudy.business.utils import strip_matrix_protocol, validate_matrix @@ -15,7 +16,6 @@ BindingConstraintOperator, CommandName, CommandOutput, - TimeStep, ) from antarest.study.storage.variantstudy.model.command.icommand import MATCH_SIGNATURE_SEPARATOR, ICommand from antarest.study.storage.variantstudy.model.model import CommandDTO @@ -24,7 +24,7 @@ class CreateBindingConstraint(ICommand): name: str enabled: bool = True - time_step: TimeStep + time_step: BindingConstraintFrequency operator: BindingConstraintOperator coeffs: Dict[str, List[float]] values: Optional[Union[List[List[MatrixData]], str]] = None diff --git a/antarest/study/storage/variantstudy/model/command/update_binding_constraint.py b/antarest/study/storage/variantstudy/model/command/update_binding_constraint.py index dc17838f58..17f5be33bc 100644 --- a/antarest/study/storage/variantstudy/model/command/update_binding_constraint.py +++ b/antarest/study/storage/variantstudy/model/command/update_binding_constraint.py @@ -5,6 +5,7 @@ from antarest.core.model import JSON from antarest.core.utils.utils import assert_this from antarest.matrixstore.model import MatrixData +from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import BindingConstraintFrequency 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, validate_matrix @@ -13,7 +14,6 @@ BindingConstraintOperator, CommandName, CommandOutput, - TimeStep, ) from antarest.study.storage.variantstudy.model.command.icommand import MATCH_SIGNATURE_SEPARATOR, ICommand from antarest.study.storage.variantstudy.model.model import CommandDTO @@ -22,7 +22,7 @@ class UpdateBindingConstraint(ICommand): id: str enabled: bool = True - time_step: TimeStep + time_step: BindingConstraintFrequency operator: BindingConstraintOperator coeffs: Dict[str, List[float]] values: Optional[Union[List[List[MatrixData]], str]] = None diff --git a/tests/integration/assets/base_study.zip b/tests/integration/assets/base_study.zip index 28794fde9e3e60fe8ef6165a21f944e81f6dc304..712833942c0155cf0bd60d95e9800a173346dc15 100644 GIT binary patch literal 311332 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 Lv_BtCW&iho#on($ literal 298490 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 QYdyL;7CUG;yCoC*A4UO}=l}o! diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index 7fd8131a54..df4249ca47 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -529,7 +529,7 @@ def test_area_management(client: TestClient, admin_access_token: str, study_id: ], ) - client.post( + res = client.post( f"/v1/studies/{study_id}/commands", headers=admin_headers, json=[ @@ -545,8 +545,9 @@ def test_area_management(client: TestClient, admin_access_token: str, study_id: } ], ) + res.raise_for_status() - client.post( + res = client.post( f"/v1/studies/{study_id}/commands", headers=admin_headers, json=[ @@ -562,6 +563,7 @@ def test_area_management(client: TestClient, admin_access_token: str, study_id: } ], ) + res.raise_for_status() res_areas = client.get(f"/v1/studies/{study_id}/areas", headers=admin_headers) assert res_areas.json() == [ diff --git a/tests/integration/test_integration_variantmanager_tool.py b/tests/integration/test_integration_variantmanager_tool.py index d0315a31e1..a247d81c1d 100644 --- a/tests/integration/test_integration_variantmanager_tool.py +++ b/tests/integration/test_integration_variantmanager_tool.py @@ -1,19 +1,15 @@ -import os +import io import urllib.parse from pathlib import Path from typing import List, Tuple from zipfile import ZipFile +import numpy as np +import numpy.typing as npt from fastapi import FastAPI from starlette.testclient import TestClient from antarest.study.storage.rawstudy.io.reader import IniReader -from antarest.study.storage.rawstudy.model.filesystem.matrix.constants import ( - default_4_fixed_hourly, - default_8_fixed_hourly, - default_scenario_daily, - default_scenario_hourly, -) from antarest.study.storage.variantstudy.model.command.common import CommandName from antarest.study.storage.variantstudy.model.model import CommandDTO, GenerationResultInfoDTO from antarest.tools.lib import ( @@ -29,11 +25,10 @@ test_dir: Path = Path(__file__).parent -def generate_csv_string(data: List[List[float]]) -> str: - csv_str = "" - for row in data: - csv_str += "\t".join(["{:.6f}".format(v) for v in row]) + "\n" - return csv_str +def generate_csv_string(array: npt.NDArray[np.float64]) -> str: + buffer = io.StringIO() + np.savetxt(buffer, array, delimiter="\t", fmt="%.6f") + return buffer.getvalue() def generate_study_with_server( @@ -60,7 +55,7 @@ def generate_study_with_server( return generator.apply_commands(commands, matrices_dir), variant_id -def test_variant_manager(app: FastAPI, tmp_path: str): +def test_variant_manager(app: FastAPI, tmp_path: str) -> None: client = TestClient(app, raise_server_exceptions=False) commands = parse_commands(test_dir / "assets" / "commands1.json") matrix_dir = Path(tmp_path) / "empty_matrix_store" @@ -69,7 +64,7 @@ def test_variant_manager(app: FastAPI, tmp_path: str): assert res is not None and res.success -def test_parse_commands(tmp_path: str, app: FastAPI): +def test_parse_commands(tmp_path: str, app: FastAPI) -> None: base_dir = test_dir / "assets" export_path = Path(tmp_path) / "commands" study = "base_study" @@ -92,138 +87,133 @@ def test_parse_commands(tmp_path: str, app: FastAPI): assert generated_study_path.exists() and generated_study_path.is_dir() single_column_empty_items = [ - f"input{os.sep}load{os.sep}series{os.sep}load_hub w.txt", - f"input{os.sep}load{os.sep}series{os.sep}load_south.txt", - f"input{os.sep}load{os.sep}series{os.sep}load_hub n.txt", - f"input{os.sep}load{os.sep}series{os.sep}load_west.txt", - f"input{os.sep}load{os.sep}series{os.sep}load_north.txt", - f"input{os.sep}load{os.sep}series{os.sep}load_hub s.txt", - f"input{os.sep}load{os.sep}series{os.sep}load_hub e.txt", - f"input{os.sep}load{os.sep}series{os.sep}load_east.txt", - f"input{os.sep}wind{os.sep}series{os.sep}wind_east.txt", - f"input{os.sep}wind{os.sep}series{os.sep}wind_north.txt", - f"input{os.sep}wind{os.sep}series{os.sep}wind_hub n.txt", - f"input{os.sep}wind{os.sep}series{os.sep}wind_south.txt", - f"input{os.sep}wind{os.sep}series{os.sep}wind_hub w.txt", - f"input{os.sep}wind{os.sep}series{os.sep}wind_west.txt", - f"input{os.sep}wind{os.sep}series{os.sep}wind_hub e.txt", - f"input{os.sep}wind{os.sep}series{os.sep}wind_hub s.txt", - f"input{os.sep}solar{os.sep}series{os.sep}solar_east.txt", - f"input{os.sep}solar{os.sep}series{os.sep}solar_hub n.txt", - f"input{os.sep}solar{os.sep}series{os.sep}solar_south.txt", - f"input{os.sep}solar{os.sep}series{os.sep}solar_hub s.txt", - f"input{os.sep}solar{os.sep}series{os.sep}solar_north.txt", - f"input{os.sep}solar{os.sep}series{os.sep}solar_hub w.txt", - f"input{os.sep}solar{os.sep}series{os.sep}solar_hub e.txt", - f"input{os.sep}solar{os.sep}series{os.sep}solar_west.txt", - f"input{os.sep}thermal{os.sep}series{os.sep}west{os.sep}semi base{os.sep}series.txt", - f"input{os.sep}thermal{os.sep}series{os.sep}west{os.sep}peak{os.sep}series.txt", - f"input{os.sep}thermal{os.sep}series{os.sep}west{os.sep}base{os.sep}series.txt", - f"input{os.sep}thermal{os.sep}series{os.sep}north{os.sep}semi base{os.sep}series.txt", - f"input{os.sep}thermal{os.sep}series{os.sep}north{os.sep}peak{os.sep}series.txt", - f"input{os.sep}thermal{os.sep}series{os.sep}north{os.sep}base{os.sep}series.txt", - f"input{os.sep}thermal{os.sep}series{os.sep}east{os.sep}semi base{os.sep}series.txt", - f"input{os.sep}thermal{os.sep}series{os.sep}east{os.sep}peak{os.sep}series.txt", - f"input{os.sep}thermal{os.sep}series{os.sep}east{os.sep}base{os.sep}series.txt", - f"input{os.sep}thermal{os.sep}series{os.sep}south{os.sep}semi base{os.sep}series.txt", - f"input{os.sep}thermal{os.sep}series{os.sep}south{os.sep}peak{os.sep}series.txt", - f"input{os.sep}thermal{os.sep}series{os.sep}south{os.sep}base{os.sep}series.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}hub e{os.sep}ror.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}south{os.sep}ror.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}hub w{os.sep}ror.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}hub s{os.sep}ror.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}west{os.sep}ror.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}hub n{os.sep}ror.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}north{os.sep}ror.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}east{os.sep}ror.txt", + "input/load/series/load_hub w.txt", + "input/load/series/load_south.txt", + "input/load/series/load_hub n.txt", + "input/load/series/load_west.txt", + "input/load/series/load_north.txt", + "input/load/series/load_hub s.txt", + "input/load/series/load_hub e.txt", + "input/load/series/load_east.txt", + "input/wind/series/wind_east.txt", + "input/wind/series/wind_north.txt", + "input/wind/series/wind_hub n.txt", + "input/wind/series/wind_south.txt", + "input/wind/series/wind_hub w.txt", + "input/wind/series/wind_west.txt", + "input/wind/series/wind_hub e.txt", + "input/wind/series/wind_hub s.txt", + "input/solar/series/solar_east.txt", + "input/solar/series/solar_hub n.txt", + "input/solar/series/solar_south.txt", + "input/solar/series/solar_hub s.txt", + "input/solar/series/solar_north.txt", + "input/solar/series/solar_hub w.txt", + "input/solar/series/solar_hub e.txt", + "input/solar/series/solar_west.txt", + "input/thermal/series/west/semi base/series.txt", + "input/thermal/series/west/peak/series.txt", + "input/thermal/series/west/base/series.txt", + "input/thermal/series/north/semi base/series.txt", + "input/thermal/series/north/peak/series.txt", + "input/thermal/series/north/base/series.txt", + "input/thermal/series/east/semi base/series.txt", + "input/thermal/series/east/peak/series.txt", + "input/thermal/series/east/base/series.txt", + "input/thermal/series/south/semi base/series.txt", + "input/thermal/series/south/peak/series.txt", + "input/thermal/series/south/base/series.txt", + "input/hydro/series/hub e/ror.txt", + "input/hydro/series/south/ror.txt", + "input/hydro/series/hub w/ror.txt", + "input/hydro/series/hub s/ror.txt", + "input/hydro/series/west/ror.txt", + "input/hydro/series/hub n/ror.txt", + "input/hydro/series/north/ror.txt", + "input/hydro/series/east/ror.txt", ] single_column_daily_empty_items = [ - f"input{os.sep}hydro{os.sep}series{os.sep}hub e{os.sep}mod.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}south{os.sep}mod.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}hub w{os.sep}mod.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}hub s{os.sep}mod.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}west{os.sep}mod.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}hub n{os.sep}mod.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}north{os.sep}mod.txt", - f"input{os.sep}hydro{os.sep}series{os.sep}east{os.sep}mod.txt", + "input/hydro/series/hub e/mod.txt", + "input/hydro/series/south/mod.txt", + "input/hydro/series/hub w/mod.txt", + "input/hydro/series/hub s/mod.txt", + "input/hydro/series/west/mod.txt", + "input/hydro/series/hub n/mod.txt", + "input/hydro/series/north/mod.txt", + "input/hydro/series/east/mod.txt", + ] + fixed_3_cols_hourly_empty_items = [ + "input/bindingconstraints/northern mesh.txt", + "input/bindingconstraints/southern mesh.txt", ] fixed_4_cols_empty_items = [ - f"input{os.sep}reserves{os.sep}hub s.txt", - f"input{os.sep}reserves{os.sep}hub n.txt", - f"input{os.sep}reserves{os.sep}hub w.txt", - f"input{os.sep}reserves{os.sep}hub e.txt", + "input/reserves/hub s.txt", + "input/reserves/hub n.txt", + "input/reserves/hub w.txt", + "input/reserves/hub e.txt", ] fixed_8_cols_empty_items = [ - f"input{os.sep}misc-gen{os.sep}miscgen-hub w.txt", - f"input{os.sep}misc-gen{os.sep}miscgen-hub e.txt", - f"input{os.sep}misc-gen{os.sep}miscgen-hub s.txt", - f"input{os.sep}misc-gen{os.sep}miscgen-hub n.txt", + "input/misc-gen/miscgen-hub w.txt", + "input/misc-gen/miscgen-hub e.txt", + "input/misc-gen/miscgen-hub s.txt", + "input/misc-gen/miscgen-hub n.txt", ] - single_column_empty_data = generate_csv_string(default_scenario_hourly) - single_column_daily_empty_data = generate_csv_string(default_scenario_daily) - fixed_4_columns_empty_data = generate_csv_string(default_4_fixed_hourly) - fixed_8_columns_empty_data = generate_csv_string(default_8_fixed_hourly) - for root, dirs, files in os.walk(study_path): - rel_path = root[len(str(study_path)) + 1 :] - for item in files: - if item in [ - "comments.txt", - "study.antares", - "Desktop.ini", - "study.ico", - ]: - continue - elif f"{rel_path}{os.sep}{item}" in single_column_empty_items: - assert (generated_study_path / rel_path / item).read_text() == single_column_empty_data - elif f"{rel_path}{os.sep}{item}" in single_column_daily_empty_items: - assert (generated_study_path / rel_path / item).read_text() == single_column_daily_empty_data - elif f"{rel_path}{os.sep}{item}" in fixed_4_cols_empty_items: - assert (generated_study_path / rel_path / item).read_text() == fixed_4_columns_empty_data - elif f"{rel_path}{os.sep}{item}" in fixed_8_cols_empty_items: - assert (generated_study_path / rel_path / item).read_text() == fixed_8_columns_empty_data - else: - actual = (study_path / rel_path / item).read_text() - expected = (generated_study_path / rel_path / item).read_text() - assert actual.strip() == expected.strip() - - -def test_diff_local(tmp_path: Path): + single_column_empty_data = generate_csv_string(np.zeros((8760, 1), dtype=np.float64)) + single_column_daily_empty_data = generate_csv_string(np.zeros((365, 1), dtype=np.float64)) + fixed_3_cols_hourly_empty_data = generate_csv_string(np.zeros(shape=(8760, 3), dtype=np.float64)) + fixed_4_columns_empty_data = generate_csv_string(np.zeros((8760, 4), dtype=np.float64)) + fixed_8_columns_empty_data = generate_csv_string(np.zeros((8760, 8), dtype=np.float64)) + for file_path in study_path.rglob("*"): + if file_path.is_dir() or file_path.name in ["comments.txt", "study.antares", "Desktop.ini", "study.ico"]: + continue + item_relpath = file_path.relative_to(study_path).as_posix() + if item_relpath in single_column_empty_items: + assert (generated_study_path / item_relpath).read_text() == single_column_empty_data + elif item_relpath in single_column_daily_empty_items: + assert (generated_study_path / item_relpath).read_text() == single_column_daily_empty_data + elif item_relpath in fixed_3_cols_hourly_empty_items: + assert (generated_study_path / item_relpath).read_text() == fixed_3_cols_hourly_empty_data + elif item_relpath in fixed_4_cols_empty_items: + assert (generated_study_path / item_relpath).read_text() == fixed_4_columns_empty_data + elif item_relpath in fixed_8_cols_empty_items: + assert (generated_study_path / item_relpath).read_text() == fixed_8_columns_empty_data + else: + actual = (study_path / item_relpath).read_text() + expected = (generated_study_path / item_relpath).read_text() + assert actual.strip() == expected.strip() + + +def test_diff_local(tmp_path: Path) -> None: base_dir = test_dir / "assets" export_path = Path(tmp_path) / "generation_result" base_study = "base_study" variant_study = "variant_study" - output_study_commands = Path(export_path) / "output_study_commands" + output_study_commands = export_path / "output_study_commands" output_study_path = Path(tmp_path) / base_study - base_study_commands = Path(export_path) / base_study - variant_study_commands = Path(export_path) / variant_study + base_study_commands = export_path / base_study + variant_study_commands = export_path / variant_study variant_study_path = Path(tmp_path) / variant_study for study in [base_study, variant_study]: with ZipFile(base_dir / f"{study}.zip") as zip_output: zip_output.extractall(path=tmp_path) - extract_commands(Path(tmp_path) / study, Path(export_path) / study) + extract_commands(Path(tmp_path) / study, export_path / study) - res = generate_study(base_study_commands, None, str(Path(export_path) / "base_generated")) - res = generate_study( + generate_study(base_study_commands, None, str(export_path / "base_generated")) + generate_study( variant_study_commands, None, - str(Path(export_path) / "variant_generated"), + str(export_path / "variant_generated"), ) generate_diff(base_study_commands, variant_study_commands, output_study_commands) res = generate_study(output_study_commands, None, output=str(output_study_path)) assert res.success assert output_study_path.exists() and output_study_path.is_dir() - for root, dirs, files in os.walk(variant_study_path): - rel_path = root[len(str(variant_study_path)) + 1 :] - for item in files: - if item in [ - "comments.txt", - "study.antares", - "Desktop.ini", - "study.ico", - ]: - continue - actual = (variant_study_path / rel_path / item).read_text() - expected = (output_study_path / rel_path / item).read_text() - assert actual.strip() == expected.strip() + for file_path in variant_study_path.rglob("*"): + if file_path.is_dir() or file_path.name in ["comments.txt", "study.antares", "Desktop.ini", "study.ico"]: + continue + item_relpath = file_path.relative_to(variant_study_path).as_posix() + actual = (variant_study_path / item_relpath).read_text() + expected = (output_study_path / item_relpath).read_text() + assert actual.strip() == expected.strip() diff --git a/tests/storage/repository/filesystem/config/test_config_files.py b/tests/storage/repository/filesystem/config/test_config_files.py index 9b029fcea7..e0e0ccb45f 100644 --- a/tests/storage/repository/filesystem/config/test_config_files.py +++ b/tests/storage/repository/filesystem/config/test_config_files.py @@ -4,6 +4,7 @@ import pytest +from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import BindingConstraintFrequency from antarest.study.storage.rawstudy.model.filesystem.config.files import ( _parse_links, _parse_outputs, @@ -73,6 +74,7 @@ def test_parse_bindings(tmp_path: Path) -> None: [bindB] id = bindB + type = weekly """ (study_path / "input/bindingconstraints/bindingconstraints.ini").write_text(content) @@ -81,8 +83,18 @@ def test_parse_bindings(tmp_path: Path) -> None: path=study_path, version=-1, bindings=[ - BindingConstraintDTO(id="bindA", areas=[], clusters=[]), - BindingConstraintDTO(id="bindB", areas=[], clusters=[]), + BindingConstraintDTO( + id="bindA", + areas=set(), + clusters=set(), + time_step=BindingConstraintFrequency.HOURLY, + ), + BindingConstraintDTO( + id="bindB", + areas=set(), + clusters=set(), + time_step=BindingConstraintFrequency.WEEKLY, + ), ], study_id="id", output_path=study_path / "output", diff --git a/tests/storage/test_model.py b/tests/storage/test_model.py index 0b0ed0db87..4986073713 100644 --- a/tests/storage/test_model.py +++ b/tests/storage/test_model.py @@ -1,5 +1,6 @@ from pathlib import Path +from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import BindingConstraintFrequency from antarest.study.storage.rawstudy.model.filesystem.config.model import ( Area, BindingConstraintDTO, @@ -41,7 +42,14 @@ def test_file_study_tree_config_dto(): xpansion="", ) }, - bindings=[BindingConstraintDTO(id="b1", areas=[], clusters=[])], + bindings=[ + BindingConstraintDTO( + id="b1", + areas=set(), + clusters=set(), + time_step=BindingConstraintFrequency.DAILY, + ) + ], store_new_set=False, archive_input_series=["?"], enr_modelling="aggregated", diff --git a/tests/variantstudy/model/command/test_manage_binding_constraints.py b/tests/variantstudy/model/command/test_manage_binding_constraints.py index 1596ce6476..e633b490ed 100644 --- a/tests/variantstudy/model/command/test_manage_binding_constraints.py +++ b/tests/variantstudy/model/command/test_manage_binding_constraints.py @@ -1,10 +1,11 @@ from unittest.mock import Mock from antarest.study.storage.rawstudy.io.reader import IniReader +from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import BindingConstraintFrequency from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy from antarest.study.storage.variantstudy.business.command_extractor import CommandExtractor from antarest.study.storage.variantstudy.business.command_reverter import CommandReverter -from antarest.study.storage.variantstudy.model.command.common import BindingConstraintOperator, TimeStep +from antarest.study.storage.variantstudy.model.command.common import BindingConstraintOperator from antarest.study.storage.variantstudy.model.command.create_area import CreateArea from antarest.study.storage.variantstudy.model.command.create_binding_constraint import CreateBindingConstraint from antarest.study.storage.variantstudy.model.command.create_cluster import CreateCluster @@ -56,7 +57,7 @@ def test_manage_binding_constraint( bind1_cmd = CreateBindingConstraint( name="BD 1", - time_step=TimeStep.HOURLY, + time_step=BindingConstraintFrequency.HOURLY, operator=BindingConstraintOperator.LESS, coeffs={"area1%area2": [800, 30]}, comments="Hello", @@ -68,7 +69,7 @@ def test_manage_binding_constraint( bind2_cmd = CreateBindingConstraint( name="BD 2", enabled=False, - time_step=TimeStep.DAILY, + time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"area1.cluster": [50]}, command_context=command_context, @@ -104,7 +105,7 @@ def test_manage_binding_constraint( bind_update = UpdateBindingConstraint( id="bd 1", enabled=False, - time_step=TimeStep.WEEKLY, + time_step=BindingConstraintFrequency.WEEKLY, operator=BindingConstraintOperator.BOTH, coeffs={"area1%area2": [800, 30]}, values=[[0]], @@ -142,7 +143,7 @@ def test_match(command_context: CommandContext): base = CreateBindingConstraint( name="foo", enabled=False, - time_step=TimeStep.DAILY, + time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, values=[[0]], @@ -151,7 +152,7 @@ def test_match(command_context: CommandContext): other_match = CreateBindingConstraint( name="foo", enabled=False, - time_step=TimeStep.DAILY, + time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, values=[[0]], @@ -160,7 +161,7 @@ def test_match(command_context: CommandContext): other_not_match = CreateBindingConstraint( name="bar", enabled=False, - time_step=TimeStep.DAILY, + time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, command_context=command_context, @@ -177,7 +178,7 @@ def test_match(command_context: CommandContext): base = UpdateBindingConstraint( id="foo", enabled=False, - time_step=TimeStep.DAILY, + time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, values=[[0]], @@ -186,7 +187,7 @@ def test_match(command_context: CommandContext): other_match = UpdateBindingConstraint( id="foo", enabled=False, - time_step=TimeStep.DAILY, + time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, values=[[0]], @@ -195,7 +196,7 @@ def test_match(command_context: CommandContext): other_not_match = UpdateBindingConstraint( id="bar", enabled=False, - time_step=TimeStep.DAILY, + time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, command_context=command_context, @@ -224,7 +225,7 @@ def test_revert(command_context: CommandContext): base = CreateBindingConstraint( name="foo", enabled=False, - time_step=TimeStep.DAILY, + time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, values=[[0]], @@ -237,7 +238,7 @@ def test_revert(command_context: CommandContext): base = UpdateBindingConstraint( id="foo", enabled=False, - time_step=TimeStep.DAILY, + time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, values=[[0]], @@ -255,7 +256,7 @@ def test_revert(command_context: CommandContext): UpdateBindingConstraint( id="foo", enabled=True, - time_step=TimeStep.WEEKLY, + time_step=BindingConstraintFrequency.WEEKLY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, values=[[0]], @@ -264,7 +265,7 @@ def test_revert(command_context: CommandContext): UpdateBindingConstraint( id="foo", enabled=True, - time_step=TimeStep.HOURLY, + time_step=BindingConstraintFrequency.HOURLY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, values=[[0]], @@ -276,7 +277,7 @@ def test_revert(command_context: CommandContext): UpdateBindingConstraint( id="foo", enabled=True, - time_step=TimeStep.HOURLY, + time_step=BindingConstraintFrequency.HOURLY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, values=[[0]], @@ -291,7 +292,7 @@ def test_revert(command_context: CommandContext): UpdateBindingConstraint( id="foo", enabled=True, - time_step=TimeStep.WEEKLY, + time_step=BindingConstraintFrequency.WEEKLY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, values=[[0]], @@ -300,7 +301,7 @@ def test_revert(command_context: CommandContext): CreateBindingConstraint( name="foo", enabled=True, - time_step=TimeStep.HOURLY, + time_step=BindingConstraintFrequency.HOURLY, operator=BindingConstraintOperator.EQUAL, coeffs={"a": [0.3]}, values=[[0]], @@ -312,7 +313,7 @@ def test_revert(command_context: CommandContext): UpdateBindingConstraint( id="foo", enabled=True, - time_step=TimeStep.HOURLY, + time_step=BindingConstraintFrequency.HOURLY, operator=BindingConstraintOperator.EQUAL, coeffs={"a": [0.3]}, values=matrix_id, @@ -329,7 +330,7 @@ def test_create_diff(command_context: CommandContext): base = CreateBindingConstraint( name="foo", enabled=False, - time_step=TimeStep.DAILY, + time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, values="a", @@ -338,7 +339,7 @@ def test_create_diff(command_context: CommandContext): other_match = CreateBindingConstraint( name="foo", enabled=True, - time_step=TimeStep.HOURLY, + time_step=BindingConstraintFrequency.HOURLY, operator=BindingConstraintOperator.EQUAL, coeffs={"b": [0.3]}, values="b", @@ -348,7 +349,7 @@ def test_create_diff(command_context: CommandContext): UpdateBindingConstraint( id="foo", enabled=True, - time_step=TimeStep.HOURLY, + time_step=BindingConstraintFrequency.HOURLY, operator=BindingConstraintOperator.EQUAL, coeffs={"b": [0.3]}, values="b", @@ -359,7 +360,7 @@ def test_create_diff(command_context: CommandContext): base = UpdateBindingConstraint( id="foo", enabled=False, - time_step=TimeStep.DAILY, + time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, values=[[0]], @@ -368,7 +369,7 @@ def test_create_diff(command_context: CommandContext): other_match = UpdateBindingConstraint( id="foo", enabled=False, - time_step=TimeStep.DAILY, + time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, values=[[0]], diff --git a/tests/variantstudy/model/command/test_remove_area.py b/tests/variantstudy/model/command/test_remove_area.py index cb03c4f2e0..3fb77082f2 100644 --- a/tests/variantstudy/model/command/test_remove_area.py +++ b/tests/variantstudy/model/command/test_remove_area.py @@ -1,9 +1,10 @@ import pytest +from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import BindingConstraintFrequency from antarest.study.storage.rawstudy.model.filesystem.config.model import transform_name_to_id from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy from antarest.study.storage.study_upgrader import upgrade_study -from antarest.study.storage.variantstudy.model.command.common import BindingConstraintOperator, TimeStep +from antarest.study.storage.variantstudy.model.command.common import BindingConstraintOperator from antarest.study.storage.variantstudy.model.command.create_area import CreateArea from antarest.study.storage.variantstudy.model.command.create_binding_constraint import CreateBindingConstraint from antarest.study.storage.variantstudy.model.command.create_cluster import CreateCluster @@ -125,7 +126,7 @@ def test_apply( bind1_cmd = CreateBindingConstraint( name="BD 2", - time_step=TimeStep.HOURLY, + time_step=BindingConstraintFrequency.HOURLY, operator=BindingConstraintOperator.LESS, coeffs={ f"{area_id}%{area_id2}": [400, 30], diff --git a/tests/variantstudy/model/command/test_remove_cluster.py b/tests/variantstudy/model/command/test_remove_cluster.py index 0948e962f9..e4525fbc36 100644 --- a/tests/variantstudy/model/command/test_remove_cluster.py +++ b/tests/variantstudy/model/command/test_remove_cluster.py @@ -1,8 +1,9 @@ from checksumdir import dirhash +from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import BindingConstraintFrequency from antarest.study.storage.rawstudy.model.filesystem.config.model import transform_name_to_id from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy -from antarest.study.storage.variantstudy.model.command.common import BindingConstraintOperator, TimeStep +from antarest.study.storage.variantstudy.model.command.common import BindingConstraintOperator from antarest.study.storage.variantstudy.model.command.create_area import CreateArea from antarest.study.storage.variantstudy.model.command.create_binding_constraint import CreateBindingConstraint from antarest.study.storage.variantstudy.model.command.create_cluster import CreateCluster @@ -48,7 +49,7 @@ def test_apply(self, empty_study: FileStudy, command_context: CommandContext): bind1_cmd = CreateBindingConstraint( name="BD 1", - time_step=TimeStep.HOURLY, + time_step=BindingConstraintFrequency.HOURLY, operator=BindingConstraintOperator.LESS, coeffs={ f"{area_id}.{cluster_id}": [800, 30], From e00d58b203023363860cb0e849576e02ed97fd81 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Wed, 11 Oct 2023 06:28:59 +0200 Subject: [PATCH 04/13] feat(binding-constraint): add the binding constraint series in the matrix constants generator --- .../model/filesystem/matrix/constants.py | 9 -------- .../bindingconstraints/bindingcontraints.py | 10 ++++----- .../business/matrix_constants/__init__.py | 2 +- .../binding_constraint/__init__.py | 1 + .../binding_constraint/series.py | 10 +++++++++ .../business/matrix_constants_generator.py | 22 +++++++++++++++++++ .../test_matrix_constants_generator.py | 19 ++++++++++++++++ 7 files changed, 58 insertions(+), 15 deletions(-) create mode 100644 antarest/study/storage/variantstudy/business/matrix_constants/binding_constraint/__init__.py create mode 100644 antarest/study/storage/variantstudy/business/matrix_constants/binding_constraint/series.py diff --git a/antarest/study/storage/rawstudy/model/filesystem/matrix/constants.py b/antarest/study/storage/rawstudy/model/filesystem/matrix/constants.py index 3269f1abf6..8f2b429e17 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/matrix/constants.py +++ b/antarest/study/storage/rawstudy/model/filesystem/matrix/constants.py @@ -14,12 +14,3 @@ default_8_fixed_hourly = np.zeros((8760, 8), dtype=np.float64) default_8_fixed_hourly.flags.writeable = False - -default_binding_constraint_hourly = np.zeros((8760, 3), dtype=np.float64) -default_binding_constraint_hourly.flags.writeable = False - -default_binding_constraint_daily = np.zeros((365, 3), dtype=np.float64) -default_binding_constraint_daily.flags.writeable = False - -default_binding_constraint_weekly = np.zeros((52, 3), dtype=np.float64) -default_binding_constraint_weekly.flags.writeable = False diff --git a/antarest/study/storage/rawstudy/model/filesystem/root/input/bindingconstraints/bindingcontraints.py b/antarest/study/storage/rawstudy/model/filesystem/root/input/bindingconstraints/bindingcontraints.py index 849019038d..e86dedfe18 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/root/input/bindingconstraints/bindingcontraints.py +++ b/antarest/study/storage/rawstudy/model/filesystem/root/input/bindingconstraints/bindingcontraints.py @@ -1,16 +1,16 @@ from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import BindingConstraintFrequency from antarest.study.storage.rawstudy.model.filesystem.folder_node import FolderNode from antarest.study.storage.rawstudy.model.filesystem.inode import TREE -from antarest.study.storage.rawstudy.model.filesystem.matrix.constants import ( - default_binding_constraint_daily, - default_binding_constraint_hourly, - default_binding_constraint_weekly, -) from antarest.study.storage.rawstudy.model.filesystem.matrix.input_series_matrix import InputSeriesMatrix from antarest.study.storage.rawstudy.model.filesystem.matrix.matrix import MatrixFrequency from antarest.study.storage.rawstudy.model.filesystem.root.input.bindingconstraints.bindingconstraints_ini import ( BindingConstraintsIni, ) +from antarest.study.storage.variantstudy.business.matrix_constants.binding_constraint.series import ( + default_binding_constraint_daily, + default_binding_constraint_hourly, + default_binding_constraint_weekly, +) class BindingConstraints(FolderNode): diff --git a/antarest/study/storage/variantstudy/business/matrix_constants/__init__.py b/antarest/study/storage/variantstudy/business/matrix_constants/__init__.py index 0f9b1e77ca..9212a8e6c6 100644 --- a/antarest/study/storage/variantstudy/business/matrix_constants/__init__.py +++ b/antarest/study/storage/variantstudy/business/matrix_constants/__init__.py @@ -1 +1 @@ -from . import hydro, link, prepro, st_storage, thermals +from . import binding_constraint, hydro, link, prepro, st_storage, thermals diff --git a/antarest/study/storage/variantstudy/business/matrix_constants/binding_constraint/__init__.py b/antarest/study/storage/variantstudy/business/matrix_constants/binding_constraint/__init__.py new file mode 100644 index 0000000000..0a1b9046e5 --- /dev/null +++ b/antarest/study/storage/variantstudy/business/matrix_constants/binding_constraint/__init__.py @@ -0,0 +1 @@ +from . import series diff --git a/antarest/study/storage/variantstudy/business/matrix_constants/binding_constraint/series.py b/antarest/study/storage/variantstudy/business/matrix_constants/binding_constraint/series.py new file mode 100644 index 0000000000..e7b20a1137 --- /dev/null +++ b/antarest/study/storage/variantstudy/business/matrix_constants/binding_constraint/series.py @@ -0,0 +1,10 @@ +import numpy as np + +default_binding_constraint_hourly = np.zeros((8760, 3), dtype=np.float64) +default_binding_constraint_hourly.flags.writeable = False + +default_binding_constraint_daily = np.zeros((365, 3), dtype=np.float64) +default_binding_constraint_daily.flags.writeable = False + +default_binding_constraint_weekly = np.zeros((52, 3), dtype=np.float64) +default_binding_constraint_weekly.flags.writeable = False diff --git a/antarest/study/storage/variantstudy/business/matrix_constants_generator.py b/antarest/study/storage/variantstudy/business/matrix_constants_generator.py index 338506dd7c..8cb973785e 100644 --- a/antarest/study/storage/variantstudy/business/matrix_constants_generator.py +++ b/antarest/study/storage/variantstudy/business/matrix_constants_generator.py @@ -34,6 +34,10 @@ EMPTY_SCENARIO_MATRIX = "empty_scenario_matrix" ONES_SCENARIO_MATRIX = "ones_scenario_matrix" +# Binding constraint aliases +BINDING_CONSTRAINT_HOURLY = "empty_2nd_member_hourly" +BINDING_CONSTRAINT_DAILY = "empty_2nd_member_daily" +BINDING_CONSTRAINT_WEEKLY = "empty_2nd_member_daily" # Short-term storage aliases ST_STORAGE_PMAX_INJECTION = ONES_SCENARIO_MATRIX @@ -84,6 +88,12 @@ def _init(self) -> None: self.hashes[RESERVES_TS] = self.matrix_service.create(FIXED_4_COLUMNS) self.hashes[MISCGEN_TS] = self.matrix_service.create(FIXED_8_COLUMNS) + # Binding constraint matrices + series = matrix_constants.binding_constraint.series + self.hashes[BINDING_CONSTRAINT_HOURLY] = self.matrix_service.create(series.default_binding_constraint_hourly) + self.hashes[BINDING_CONSTRAINT_DAILY] = self.matrix_service.create(series.default_binding_constraint_daily) + self.hashes[BINDING_CONSTRAINT_WEEKLY] = self.matrix_service.create(series.default_binding_constraint_weekly) + # Some short-term storage matrices use np.ones((8760, 1)) self.hashes[ONES_SCENARIO_MATRIX] = self.matrix_service.create( matrix_constants.st_storage.series.pmax_injection @@ -141,6 +151,18 @@ def get_default_reserves(self) -> str: def get_default_miscgen(self) -> str: return MATRIX_PROTOCOL_PREFIX + self.hashes[MISCGEN_TS] + def get_binding_constraint_hourly(self) -> str: + """2D-matrix of shape (8760, 3), filled-in with zeros.""" + return MATRIX_PROTOCOL_PREFIX + self.hashes[BINDING_CONSTRAINT_HOURLY] + + def get_binding_constraint_daily(self) -> str: + """2D-matrix of shape (365, 3), filled-in with zeros.""" + return MATRIX_PROTOCOL_PREFIX + self.hashes[BINDING_CONSTRAINT_DAILY] + + def get_binding_constraint_weekly(self) -> str: + """2D-matrix of shape (52, 3), filled-in with zeros.""" + return MATRIX_PROTOCOL_PREFIX + self.hashes[BINDING_CONSTRAINT_WEEKLY] + def get_st_storage_pmax_injection(self) -> str: """2D-matrix of shape (8760, 1), filled-in with ones.""" return MATRIX_PROTOCOL_PREFIX + self.hashes[ST_STORAGE_PMAX_INJECTION] diff --git a/tests/study/storage/variantstudy/business/test_matrix_constants_generator.py b/tests/study/storage/variantstudy/business/test_matrix_constants_generator.py index e9689834c5..93a3262259 100644 --- a/tests/study/storage/variantstudy/business/test_matrix_constants_generator.py +++ b/tests/study/storage/variantstudy/business/test_matrix_constants_generator.py @@ -36,3 +36,22 @@ def test_get_st_storage(self, tmp_path): matrix_id5 = ref5.split(MATRIX_PROTOCOL_PREFIX)[1] matrix_dto5 = generator.matrix_service.get(matrix_id5) assert np.array(matrix_dto5.data).all() == matrix_constants.st_storage.series.inflows.all() + + def test_get_binding_constraint(self, tmp_path): + generator = GeneratorMatrixConstants(matrix_service=SimpleMatrixService(bucket_dir=tmp_path)) + series = matrix_constants.binding_constraint.series + + hourly = generator.get_binding_constraint_hourly() + hourly_matrix_id = hourly.split(MATRIX_PROTOCOL_PREFIX)[1] + hourly_matrix_dto = generator.matrix_service.get(hourly_matrix_id) + assert np.array(hourly_matrix_dto.data).all() == series.default_binding_constraint_hourly.all() + + daily = generator.get_binding_constraint_daily() + daily_matrix_id = daily.split(MATRIX_PROTOCOL_PREFIX)[1] + daily_matrix_dto = generator.matrix_service.get(daily_matrix_id) + assert np.array(daily_matrix_dto.data).all() == series.default_binding_constraint_daily.all() + + weekly = generator.get_binding_constraint_weekly() + weekly_matrix_id = weekly.split(MATRIX_PROTOCOL_PREFIX)[1] + weekly_matrix_dto = generator.matrix_service.get(weekly_matrix_id) + assert np.array(weekly_matrix_dto.data).all() == series.default_binding_constraint_weekly.all() From 68bf99f1170181f6111bc15c03ede27030f809d2 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Wed, 11 Oct 2023 07:03:29 +0200 Subject: [PATCH 05/13] feat(binding-constraint): command `create_binding_constraint` can check the matrix shape --- .../business/utils_binding_constraint.py | 3 +- .../command/create_binding_constraint.py | 67 ++++++++++++++----- .../test_manage_binding_constraints.py | 44 +++++++----- 3 files changed, 79 insertions(+), 35 deletions(-) diff --git a/antarest/study/storage/variantstudy/business/utils_binding_constraint.py b/antarest/study/storage/variantstudy/business/utils_binding_constraint.py index 92d5170275..b2eded8a6a 100644 --- a/antarest/study/storage/variantstudy/business/utils_binding_constraint.py +++ b/antarest/study/storage/variantstudy/business/utils_binding_constraint.py @@ -80,7 +80,8 @@ def apply_binding_constraint( ["input", "bindingconstraints", "bindingconstraints"], ) if values: - assert isinstance(values, str) + if not isinstance(values, str): # pragma: no cover + raise TypeError(repr(values)) study_data.tree.save(values, ["input", "bindingconstraints", bd_id]) return CommandOutput(status=True) diff --git a/antarest/study/storage/variantstudy/model/command/create_binding_constraint.py b/antarest/study/storage/variantstudy/model/command/create_binding_constraint.py index 49ddf4f308..fbc6f3e836 100644 --- a/antarest/study/storage/variantstudy/model/command/create_binding_constraint.py +++ b/antarest/study/storage/variantstudy/model/command/create_binding_constraint.py @@ -1,12 +1,13 @@ from typing import Any, Dict, List, Optional, Tuple, Union, cast -from pydantic import validator +import numpy as np +from pydantic import Field, validator -from antarest.core.utils.utils import assert_this from antarest.matrixstore.model import MatrixData from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import BindingConstraintFrequency from antarest.study.storage.rawstudy.model.filesystem.config.model import FileStudyTreeConfig, transform_name_to_id from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy +from antarest.study.storage.variantstudy.business.matrix_constants_generator import GeneratorMatrixConstants from antarest.study.storage.variantstudy.business.utils import strip_matrix_protocol, validate_matrix from antarest.study.storage.variantstudy.business.utils_binding_constraint import ( apply_binding_constraint, @@ -20,34 +21,66 @@ from antarest.study.storage.variantstudy.model.command.icommand import MATCH_SIGNATURE_SEPARATOR, ICommand from antarest.study.storage.variantstudy.model.model import CommandDTO +MatrixType = List[List[MatrixData]] + class CreateBindingConstraint(ICommand): + """ + Command used to create a binding constraint. + """ + + command_name: CommandName = CommandName.CREATE_BINDING_CONSTRAINT + version: int = 1 + + # Properties of the `CREATE_BINDING_CONSTRAINT` command: name: str enabled: bool = True time_step: BindingConstraintFrequency operator: BindingConstraintOperator coeffs: Dict[str, List[float]] - values: Optional[Union[List[List[MatrixData]], str]] = None + values: Optional[Union[MatrixType, str]] = Field(None, description="2nd member matrix") filter_year_by_year: Optional[str] = None filter_synthesis: Optional[str] = None comments: Optional[str] = None - def __init__(self, **data: Any) -> None: - super().__init__( - command_name=CommandName.CREATE_BINDING_CONSTRAINT, - version=1, - **data, - ) - @validator("values", always=True) def validate_series( - cls, v: Optional[Union[List[List[MatrixData]], str]], values: Any - ) -> Optional[Union[List[List[MatrixData]], str]]: + cls, + v: Optional[Union[MatrixType, str]], + values: Dict[str, Any], + ) -> Optional[Union[MatrixType, str]]: + constants: GeneratorMatrixConstants + constants = values["command_context"].generator_matrix_constants + time_step = values["time_step"] if v is None: - v = values["command_context"].generator_matrix_constants.get_null_matrix() - return v - else: + # Use an already-registered default matrix + methods = { + BindingConstraintFrequency.HOURLY: constants.get_binding_constraint_hourly, + BindingConstraintFrequency.DAILY: constants.get_binding_constraint_daily, + BindingConstraintFrequency.WEEKLY: constants.get_binding_constraint_weekly, + } + method = methods[time_step] + return method() + if isinstance(v, str): + # Check the matrix link + return validate_matrix(v, values) + if isinstance(v, list): + shapes = { + BindingConstraintFrequency.HOURLY: (8760, 3), + BindingConstraintFrequency.DAILY: (365, 3), + BindingConstraintFrequency.WEEKLY: (52, 3), + } + # Check the matrix values and create the corresponding matrix link + array = np.array(v, dtype=np.float64) + if array.shape != shapes[time_step]: + raise ValueError(f"Invalid matrix shape {array.shape}, expected {shapes[time_step]}") + if np.isnan(array).any(): + raise ValueError("Matrix values cannot contain NaN") + v = cast(MatrixType, array.tolist()) return validate_matrix(v, values) + # Invalid datatype + # pragma: no cover + raise TypeError(repr(v)) def _apply_config(self, study_data_config: FileStudyTreeConfig) -> Tuple[CommandOutput, Dict[str, Any]]: bd_id = transform_name_to_id(self.name) @@ -55,7 +88,6 @@ def _apply_config(self, study_data_config: FileStudyTreeConfig) -> Tuple[Command return CommandOutput(status=True), {} def _apply(self, study_data: FileStudy) -> CommandOutput: - assert_this(isinstance(self.values, str)) binding_constraints = study_data.tree.get(["input", "bindingconstraints", "bindingconstraints"]) new_key = len(binding_constraints.keys()) bd_id = transform_name_to_id(self.name) @@ -132,6 +164,7 @@ def _create_diff(self, other: "ICommand") -> List["ICommand"]: def get_inner_matrices(self) -> List[str]: if self.values is not None: - assert_this(isinstance(self.values, str)) + if not isinstance(self.values, str): # pragma: no cover + raise TypeError(repr(self.values)) return [strip_matrix_protocol(self.values)] return [] diff --git a/tests/variantstudy/model/command/test_manage_binding_constraints.py b/tests/variantstudy/model/command/test_manage_binding_constraints.py index e633b490ed..386a644444 100644 --- a/tests/variantstudy/model/command/test_manage_binding_constraints.py +++ b/tests/variantstudy/model/command/test_manage_binding_constraints.py @@ -5,6 +5,11 @@ from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy from antarest.study.storage.variantstudy.business.command_extractor import CommandExtractor from antarest.study.storage.variantstudy.business.command_reverter import CommandReverter +from antarest.study.storage.variantstudy.business.matrix_constants.binding_constraint.series import ( + default_binding_constraint_daily, + default_binding_constraint_hourly, + default_binding_constraint_weekly, +) from antarest.study.storage.variantstudy.model.command.common import BindingConstraintOperator from antarest.study.storage.variantstudy.model.command.create_area import CreateArea from antarest.study.storage.variantstudy.model.command.create_binding_constraint import CreateBindingConstraint @@ -140,13 +145,14 @@ def test_manage_binding_constraint( def test_match(command_context: CommandContext): + values = default_binding_constraint_daily.tolist() base = CreateBindingConstraint( name="foo", enabled=False, time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, - values=[[0]], + values=values, command_context=command_context, ) other_match = CreateBindingConstraint( @@ -155,7 +161,7 @@ def test_match(command_context: CommandContext): time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, - values=[[0]], + values=values, command_context=command_context, ) other_not_match = CreateBindingConstraint( @@ -172,7 +178,7 @@ def test_match(command_context: CommandContext): assert not base.match(other_other) assert base.match_signature() == "create_binding_constraint%foo" # check the matrices links - matrix_id = command_context.matrix_service.create([[0]]) + matrix_id = command_context.matrix_service.create(values) assert base.get_inner_matrices() == [matrix_id] base = UpdateBindingConstraint( @@ -181,7 +187,7 @@ def test_match(command_context: CommandContext): time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, - values=[[0]], + values=values, command_context=command_context, ) other_match = UpdateBindingConstraint( @@ -190,7 +196,7 @@ def test_match(command_context: CommandContext): time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, - values=[[0]], + values=values, command_context=command_context, ) other_not_match = UpdateBindingConstraint( @@ -207,7 +213,7 @@ def test_match(command_context: CommandContext): assert not base.match(other_other) assert base.match_signature() == "update_binding_constraint%foo" # check the matrices links - matrix_id = command_context.matrix_service.create([[0]]) + matrix_id = command_context.matrix_service.create(values) assert base.get_inner_matrices() == [matrix_id] base = RemoveBindingConstraint(id="foo", command_context=command_context) @@ -222,13 +228,16 @@ def test_match(command_context: CommandContext): def test_revert(command_context: CommandContext): + hourly_values = default_binding_constraint_hourly.tolist() + daily_values = default_binding_constraint_daily.tolist() + weekly_values = default_binding_constraint_weekly.tolist() base = CreateBindingConstraint( name="foo", enabled=False, time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, - values=[[0]], + values=daily_values, command_context=command_context, ) assert CommandReverter().revert(base, [], Mock(spec=FileStudy)) == [ @@ -241,7 +250,7 @@ def test_revert(command_context: CommandContext): time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, - values=[[0]], + values=daily_values, command_context=command_context, ) mock_command_extractor = Mock(spec=CommandExtractor) @@ -259,7 +268,7 @@ def test_revert(command_context: CommandContext): time_step=BindingConstraintFrequency.WEEKLY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, - values=[[0]], + values=weekly_values, command_context=command_context, ), UpdateBindingConstraint( @@ -268,7 +277,7 @@ def test_revert(command_context: CommandContext): time_step=BindingConstraintFrequency.HOURLY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, - values=[[0]], + values=hourly_values, command_context=command_context, ), ], @@ -280,12 +289,12 @@ def test_revert(command_context: CommandContext): time_step=BindingConstraintFrequency.HOURLY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, - values=[[0]], + values=hourly_values, command_context=command_context, ) ] # check the matrices links - matrix_id = command_context.matrix_service.create([[0]]) + hourly_matrix_id = command_context.matrix_service.create(hourly_values) assert CommandReverter().revert( base, [ @@ -295,7 +304,7 @@ def test_revert(command_context: CommandContext): time_step=BindingConstraintFrequency.WEEKLY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, - values=[[0]], + values=weekly_values, command_context=command_context, ), CreateBindingConstraint( @@ -304,7 +313,7 @@ def test_revert(command_context: CommandContext): time_step=BindingConstraintFrequency.HOURLY, operator=BindingConstraintOperator.EQUAL, coeffs={"a": [0.3]}, - values=[[0]], + values=hourly_values, command_context=command_context, ), ], @@ -316,7 +325,7 @@ def test_revert(command_context: CommandContext): time_step=BindingConstraintFrequency.HOURLY, operator=BindingConstraintOperator.EQUAL, coeffs={"a": [0.3]}, - values=matrix_id, + values=hourly_matrix_id, comments=None, command_context=command_context, ) @@ -357,13 +366,14 @@ def test_create_diff(command_context: CommandContext): ) ] + values = default_binding_constraint_daily.tolist() base = UpdateBindingConstraint( id="foo", enabled=False, time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, - values=[[0]], + values=values, command_context=command_context, ) other_match = UpdateBindingConstraint( @@ -372,7 +382,7 @@ def test_create_diff(command_context: CommandContext): time_step=BindingConstraintFrequency.DAILY, operator=BindingConstraintOperator.BOTH, coeffs={"a": [0.3]}, - values=[[0]], + values=values, command_context=command_context, ) assert base.create_diff(other_match) == [other_match] From c962f7344c7ea07c7a8c7699b2af35f90f3b853c Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Wed, 11 Oct 2023 07:49:02 +0200 Subject: [PATCH 06/13] feat(binding-constraint): command `update_binding_constraint` can check the matrix shape --- .../command/update_binding_constraint.py | 55 ++++++++++++++----- .../test_manage_binding_constraints.py | 3 +- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/antarest/study/storage/variantstudy/model/command/update_binding_constraint.py b/antarest/study/storage/variantstudy/model/command/update_binding_constraint.py index 17f5be33bc..c764106e07 100644 --- a/antarest/study/storage/variantstudy/model/command/update_binding_constraint.py +++ b/antarest/study/storage/variantstudy/model/command/update_binding_constraint.py @@ -1,6 +1,7 @@ -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict, List, Optional, Tuple, Union, cast -from pydantic import validator +import numpy as np +from pydantic import Field, validator from antarest.core.model import JSON from antarest.core.utils.utils import assert_this @@ -18,32 +19,58 @@ from antarest.study.storage.variantstudy.model.command.icommand import MATCH_SIGNATURE_SEPARATOR, ICommand from antarest.study.storage.variantstudy.model.model import CommandDTO +MatrixType = List[List[MatrixData]] + class UpdateBindingConstraint(ICommand): + """ + Command used to update a binding constraint. + """ + + command_name: CommandName = CommandName.UPDATE_BINDING_CONSTRAINT + version: int = 1 + + # Properties of the `UPDATE_BINDING_CONSTRAINT` command: id: str enabled: bool = True time_step: BindingConstraintFrequency operator: BindingConstraintOperator coeffs: Dict[str, List[float]] - values: Optional[Union[List[List[MatrixData]], str]] = None + values: Optional[Union[MatrixType, str]] = Field(None, description="2nd member matrix") filter_year_by_year: Optional[str] = None filter_synthesis: Optional[str] = None comments: Optional[str] = None - def __init__(self, **data: Any) -> None: - super().__init__( - command_name=CommandName.UPDATE_BINDING_CONSTRAINT, - version=1, - **data, - ) - @validator("values", always=True) def validate_series( - cls, v: Optional[Union[List[List[MatrixData]], str]], values: Any - ) -> Optional[Union[List[List[MatrixData]], str]]: - if v is not None: + cls, + v: Optional[Union[MatrixType, str]], + values: Dict[str, Any], + ) -> Optional[Union[MatrixType, str]]: + time_step = values["time_step"] + if v is None: + # The matrix is not updated + return None + if isinstance(v, str): + # Check the matrix link + return validate_matrix(v, values) + if isinstance(v, list): + shapes = { + BindingConstraintFrequency.HOURLY: (8760, 3), + BindingConstraintFrequency.DAILY: (365, 3), + BindingConstraintFrequency.WEEKLY: (52, 3), + } + # Check the matrix values and create the corresponding matrix link + array = np.array(v, dtype=np.float64) + if array.shape != shapes[time_step]: + raise ValueError(f"Invalid matrix shape {array.shape}, expected {shapes[time_step]}") + if np.isnan(array).any(): + raise ValueError("Matrix values cannot contain NaN") + v = cast(MatrixType, array.tolist()) return validate_matrix(v, values) - return None + # Invalid datatype + # pragma: no cover + raise TypeError(repr(v)) def _apply_config(self, study_data: FileStudyTreeConfig) -> Tuple[CommandOutput, Dict[str, Any]]: return CommandOutput(status=True), {} diff --git a/tests/variantstudy/model/command/test_manage_binding_constraints.py b/tests/variantstudy/model/command/test_manage_binding_constraints.py index 386a644444..d22e05ce1e 100644 --- a/tests/variantstudy/model/command/test_manage_binding_constraints.py +++ b/tests/variantstudy/model/command/test_manage_binding_constraints.py @@ -107,13 +107,14 @@ def test_manage_binding_constraint( "type": "daily", } + weekly_values = default_binding_constraint_weekly.tolist() bind_update = UpdateBindingConstraint( id="bd 1", enabled=False, time_step=BindingConstraintFrequency.WEEKLY, operator=BindingConstraintOperator.BOTH, coeffs={"area1%area2": [800, 30]}, - values=[[0]], + values=weekly_values, command_context=command_context, ) res = bind_update.apply(empty_study) From d277805c10d3f9c7134166e6d2f7170c7b752428 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Wed, 11 Oct 2023 07:49:50 +0200 Subject: [PATCH 07/13] docs(binding-constraint): add missing command docstring --- .../variantstudy/model/command/remove_area.py | 11 ++++++++--- .../model/command/remove_binding_constraint.py | 15 ++++++++------- .../variantstudy/model/command/remove_link.py | 11 ++++++++--- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/antarest/study/storage/variantstudy/model/command/remove_area.py b/antarest/study/storage/variantstudy/model/command/remove_area.py index 49b785869a..03b287cf9f 100644 --- a/antarest/study/storage/variantstudy/model/command/remove_area.py +++ b/antarest/study/storage/variantstudy/model/command/remove_area.py @@ -17,10 +17,15 @@ class RemoveArea(ICommand): - id: str + """ + Command used to remove an area. + """ + + command_name: CommandName = CommandName.REMOVE_AREA + version: int = 1 - def __init__(self, **data: Any) -> None: - super().__init__(command_name=CommandName.REMOVE_AREA, version=1, **data) + # Properties of the `REMOVE_AREA` command: + id: str def _remove_area_from_links_in_config(self, study_data_config: FileStudyTreeConfig) -> None: link_to_remove = [ diff --git a/antarest/study/storage/variantstudy/model/command/remove_binding_constraint.py b/antarest/study/storage/variantstudy/model/command/remove_binding_constraint.py index 4e33fd6000..97a27f5297 100644 --- a/antarest/study/storage/variantstudy/model/command/remove_binding_constraint.py +++ b/antarest/study/storage/variantstudy/model/command/remove_binding_constraint.py @@ -9,14 +9,15 @@ class RemoveBindingConstraint(ICommand): - id: str + """ + Command used to remove a binding constraint. + """ - def __init__(self, **data: Any) -> None: - super().__init__( - command_name=CommandName.REMOVE_BINDING_CONSTRAINT, - version=1, - **data, - ) + command_name: CommandName = CommandName.REMOVE_BINDING_CONSTRAINT + version: int = 1 + + # Properties of the `REMOVE_BINDING_CONSTRAINT` command: + id: str def _apply_config(self, study_data: FileStudyTreeConfig) -> Tuple[CommandOutput, Dict[str, Any]]: if self.id not in [bind.id for bind in study_data.bindings]: diff --git a/antarest/study/storage/variantstudy/model/command/remove_link.py b/antarest/study/storage/variantstudy/model/command/remove_link.py index aeb262fb14..5ec4c042cb 100644 --- a/antarest/study/storage/variantstudy/model/command/remove_link.py +++ b/antarest/study/storage/variantstudy/model/command/remove_link.py @@ -8,12 +8,17 @@ class RemoveLink(ICommand): + """ + Command used to remove a link. + """ + + command_name: CommandName = CommandName.REMOVE_LINK + version: int = 1 + + # Properties of the `REMOVE_LINK` command: area1: str area2: str - def __init__(self, **data: Any) -> None: - super().__init__(command_name=CommandName.REMOVE_LINK, version=1, **data) - def _apply_config(self, study_data: FileStudyTreeConfig) -> Tuple[CommandOutput, Dict[str, Any]]: result = self._check_link_exists(study_data) if result[0].status: From b41d957cffa6a8dde21a022f8b6c24c8de2559a2 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Wed, 11 Oct 2023 09:11:31 +0200 Subject: [PATCH 08/13] chore(binding-constraint): reduce code duplication --- .../command/create_binding_constraint.py | 112 +++++++++++------- .../command/update_binding_constraint.py | 67 +++-------- 2 files changed, 86 insertions(+), 93 deletions(-) diff --git a/antarest/study/storage/variantstudy/model/command/create_binding_constraint.py b/antarest/study/storage/variantstudy/model/command/create_binding_constraint.py index fbc6f3e836..ed53e5e31c 100644 --- a/antarest/study/storage/variantstudy/model/command/create_binding_constraint.py +++ b/antarest/study/storage/variantstudy/model/command/create_binding_constraint.py @@ -1,3 +1,4 @@ +from abc import ABCMeta from typing import Any, Dict, List, Optional, Tuple, Union, cast import numpy as np @@ -21,19 +22,43 @@ from antarest.study.storage.variantstudy.model.command.icommand import MATCH_SIGNATURE_SEPARATOR, ICommand from antarest.study.storage.variantstudy.model.model import CommandDTO +__all__ = ("AbstractBindingConstraintCommand", "CreateBindingConstraint", "check_matrix_values") + MatrixType = List[List[MatrixData]] -class CreateBindingConstraint(ICommand): - """ - Command used to create a binding constraint. +def check_matrix_values(time_step: BindingConstraintFrequency, values: MatrixType) -> None: """ + Check the binding constraint's matrix values for the specified time step. - command_name: CommandName = CommandName.CREATE_BINDING_CONSTRAINT - version: int = 1 + Args: + time_step: The frequency of the binding constraint: "hourly", "daily" or "weekly". + values: The binding constraint's 2nd member matrix. - # Properties of the `CREATE_BINDING_CONSTRAINT` command: - name: str + Raises: + ValueError: + If the matrix shape does not match the expected shape for the given time step. + If the matrix values contain NaN (Not-a-Number). + """ + shapes = { + BindingConstraintFrequency.HOURLY: (8760, 3), + BindingConstraintFrequency.DAILY: (365, 3), + BindingConstraintFrequency.WEEKLY: (52, 3), + } + # Check the matrix values and create the corresponding matrix link + array = np.array(values, dtype=np.float64) + if array.shape != shapes[time_step]: + raise ValueError(f"Invalid matrix shape {array.shape}, expected {shapes[time_step]}") + if np.isnan(array).any(): + raise ValueError("Matrix values cannot contain NaN") + + +class AbstractBindingConstraintCommand(ICommand, metaclass=ABCMeta): + """ + Abstract class for binding constraint commands. + """ + + # todo: add the `name` attribute because it should also be updated enabled: bool = True time_step: BindingConstraintFrequency operator: BindingConstraintOperator @@ -43,6 +68,42 @@ class CreateBindingConstraint(ICommand): filter_synthesis: Optional[str] = None comments: Optional[str] = None + def to_dto(self) -> CommandDTO: + args = { + "enabled": self.enabled, + "time_step": self.time_step.value, + "operator": self.operator.value, + "coeffs": self.coeffs, + "comments": self.comments, + "filter_year_by_year": self.filter_year_by_year, + "filter_synthesis": self.filter_synthesis, + } + if self.values is not None: + args["values"] = strip_matrix_protocol(self.values) + return CommandDTO( + action=self.command_name.value, + args=args, + ) + + def get_inner_matrices(self) -> List[str]: + if self.values is not None: + if not isinstance(self.values, str): # pragma: no cover + raise TypeError(repr(self.values)) + return [strip_matrix_protocol(self.values)] + return [] + + +class CreateBindingConstraint(AbstractBindingConstraintCommand): + """ + Command used to create a binding constraint. + """ + + command_name: CommandName = CommandName.CREATE_BINDING_CONSTRAINT + version: int = 1 + + # Properties of the `CREATE_BINDING_CONSTRAINT` command: + name: str + @validator("values", always=True) def validate_series( cls, @@ -65,18 +126,7 @@ def validate_series( # Check the matrix link return validate_matrix(v, values) if isinstance(v, list): - shapes = { - BindingConstraintFrequency.HOURLY: (8760, 3), - BindingConstraintFrequency.DAILY: (365, 3), - BindingConstraintFrequency.WEEKLY: (52, 3), - } - # Check the matrix values and create the corresponding matrix link - array = np.array(v, dtype=np.float64) - if array.shape != shapes[time_step]: - raise ValueError(f"Invalid matrix shape {array.shape}, expected {shapes[time_step]}") - if np.isnan(array).any(): - raise ValueError("Matrix values cannot contain NaN") - v = cast(MatrixType, array.tolist()) + check_matrix_values(time_step, v) return validate_matrix(v, values) # Invalid datatype # pragma: no cover @@ -108,20 +158,9 @@ def _apply(self, study_data: FileStudy) -> CommandOutput: ) def to_dto(self) -> CommandDTO: - return CommandDTO( - action=CommandName.CREATE_BINDING_CONSTRAINT.value, - args={ - "name": self.name, - "enabled": self.enabled, - "time_step": self.time_step.value, - "operator": self.operator.value, - "coeffs": self.coeffs, - "values": strip_matrix_protocol(self.values), - "comments": self.comments, - "filter_year_by_year": self.filter_year_by_year, - "filter_synthesis": self.filter_synthesis, - }, - ) + dto = super().to_dto() + dto.args["name"] = self.name # type: ignore + return dto def match_signature(self) -> str: return str(self.command_name.value + MATCH_SIGNATURE_SEPARATOR + self.name) @@ -161,10 +200,3 @@ def _create_diff(self, other: "ICommand") -> List["ICommand"]: command_context=other.command_context, ) ] - - def get_inner_matrices(self) -> List[str]: - if self.values is not None: - if not isinstance(self.values, str): # pragma: no cover - raise TypeError(repr(self.values)) - return [strip_matrix_protocol(self.values)] - return [] diff --git a/antarest/study/storage/variantstudy/model/command/update_binding_constraint.py b/antarest/study/storage/variantstudy/model/command/update_binding_constraint.py index c764106e07..3ec874a375 100644 --- a/antarest/study/storage/variantstudy/model/command/update_binding_constraint.py +++ b/antarest/study/storage/variantstudy/model/command/update_binding_constraint.py @@ -1,28 +1,27 @@ -from typing import Any, Dict, List, Optional, Tuple, Union, cast +from typing import Any, Dict, List, Optional, Tuple, Union -import numpy as np -from pydantic import Field, validator +from pydantic import validator from antarest.core.model import JSON -from antarest.core.utils.utils import assert_this from antarest.matrixstore.model import MatrixData -from antarest.study.storage.rawstudy.model.filesystem.config.binding_constraint import BindingConstraintFrequency 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, validate_matrix +from antarest.study.storage.variantstudy.business.utils import validate_matrix from antarest.study.storage.variantstudy.business.utils_binding_constraint import apply_binding_constraint -from antarest.study.storage.variantstudy.model.command.common import ( - BindingConstraintOperator, - CommandName, - CommandOutput, +from antarest.study.storage.variantstudy.model.command.common import CommandName, CommandOutput +from antarest.study.storage.variantstudy.model.command.create_binding_constraint import ( + AbstractBindingConstraintCommand, + check_matrix_values, ) from antarest.study.storage.variantstudy.model.command.icommand import MATCH_SIGNATURE_SEPARATOR, ICommand from antarest.study.storage.variantstudy.model.model import CommandDTO +__all__ = ("UpdateBindingConstraint",) + MatrixType = List[List[MatrixData]] -class UpdateBindingConstraint(ICommand): +class UpdateBindingConstraint(AbstractBindingConstraintCommand): """ Command used to update a binding constraint. """ @@ -32,14 +31,6 @@ class UpdateBindingConstraint(ICommand): # Properties of the `UPDATE_BINDING_CONSTRAINT` command: id: str - enabled: bool = True - time_step: BindingConstraintFrequency - operator: BindingConstraintOperator - coeffs: Dict[str, List[float]] - values: Optional[Union[MatrixType, str]] = Field(None, description="2nd member matrix") - filter_year_by_year: Optional[str] = None - filter_synthesis: Optional[str] = None - comments: Optional[str] = None @validator("values", always=True) def validate_series( @@ -55,18 +46,7 @@ def validate_series( # Check the matrix link return validate_matrix(v, values) if isinstance(v, list): - shapes = { - BindingConstraintFrequency.HOURLY: (8760, 3), - BindingConstraintFrequency.DAILY: (365, 3), - BindingConstraintFrequency.WEEKLY: (52, 3), - } - # Check the matrix values and create the corresponding matrix link - array = np.array(v, dtype=np.float64) - if array.shape != shapes[time_step]: - raise ValueError(f"Invalid matrix shape {array.shape}, expected {shapes[time_step]}") - if np.isnan(array).any(): - raise ValueError("Matrix values cannot contain NaN") - v = cast(MatrixType, array.tolist()) + check_matrix_values(time_step, v) return validate_matrix(v, values) # Invalid datatype # pragma: no cover @@ -108,22 +88,9 @@ def _apply(self, study_data: FileStudy) -> CommandOutput: ) def to_dto(self) -> CommandDTO: - args = { - "id": self.id, - "enabled": self.enabled, - "time_step": self.time_step.value, - "operator": self.operator.value, - "coeffs": self.coeffs, - "comments": self.comments, - "filter_year_by_year": self.filter_year_by_year, - "filter_synthesis": self.filter_synthesis, - } - if self.values is not None: - args["values"] = strip_matrix_protocol(self.values) - return CommandDTO( - action=CommandName.UPDATE_BINDING_CONSTRAINT.value, - args=args, - ) + dto = super().to_dto() + dto.args["id"] = self.id # type: ignore + return dto def match_signature(self) -> str: return str(self.command_name.value + MATCH_SIGNATURE_SEPARATOR + self.id) @@ -146,9 +113,3 @@ def match(self, other: ICommand, equal: bool = False) -> bool: def _create_diff(self, other: "ICommand") -> List["ICommand"]: return [other] - - def get_inner_matrices(self) -> List[str]: - if self.values is not None: - assert_this(isinstance(self.values, str)) - return [strip_matrix_protocol(self.values)] - return [] From 9cfa9f13d363ea4f73aa31ed760d525b091f04a4 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Wed, 11 Oct 2023 09:28:54 +0200 Subject: [PATCH 09/13] feat(api): add endpoint get_nb_cores (#1727) Co-authored-by: Laurent LAPORTE Co-authored-by: belthlemar (cherry picked from commit 8b04512e482f6dfb45f1b69edb602bcb04b02bcb) --- antarest/core/config.py | 469 +++-- .../adapters/slurm_launcher/slurm_launcher.py | 54 +- antarest/launcher/model.py | 6 +- antarest/launcher/service.py | 60 +- antarest/launcher/web.py | 46 +- resources/application.yaml | 19 +- resources/deploy/config.prod.yaml | 11 +- resources/deploy/config.yaml | 9 +- tests/conftest_db.py | 9 +- tests/core/assets/__init__.py | 3 + .../core/assets/config/application-2.14.yaml | 61 + .../core/assets/config/application-2.15.yaml | 66 + tests/core/test_config.py | 254 ++- tests/integration/assets/config.template.yml | 1 + .../launcher_blueprint/test_launcher_local.py | 70 + tests/launcher/test_local_launcher.py | 45 +- tests/launcher/test_service.py | 1577 +++++++++-------- tests/launcher/test_slurm_launcher.py | 248 +-- tests/study/business/conftest.py | 22 - tests/test_resources.py | 16 + 20 files changed, 1950 insertions(+), 1096 deletions(-) create mode 100644 tests/core/assets/__init__.py create mode 100644 tests/core/assets/config/application-2.14.yaml create mode 100644 tests/core/assets/config/application-2.15.yaml create mode 100644 tests/integration/launcher_blueprint/test_launcher_local.py delete mode 100644 tests/study/business/conftest.py diff --git a/antarest/core/config.py b/antarest/core/config.py index 4c232fa475..b48be8ded0 100644 --- a/antarest/core/config.py +++ b/antarest/core/config.py @@ -1,16 +1,14 @@ -import logging +import multiprocessing import tempfile -from dataclasses import dataclass, field +from dataclasses import asdict, dataclass, field from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Dict, List, Optional import yaml from antarest.core.model import JSON from antarest.core.roles import RoleType -logger = logging.getLogger(__name__) - @dataclass(frozen=True) class ExternalAuthConfig: @@ -23,13 +21,16 @@ class ExternalAuthConfig: add_ext_groups: bool = False group_mapping: Dict[str, str] = field(default_factory=dict) - @staticmethod - def from_dict(data: JSON) -> "ExternalAuthConfig": - return ExternalAuthConfig( - url=data.get("url", None), - default_group_role=RoleType(data.get("default_group_role", RoleType.READER.value)), - add_ext_groups=data.get("add_ext_groups", False), - group_mapping=data.get("group_mapping", {}), + @classmethod + def from_dict(cls, data: JSON) -> "ExternalAuthConfig": + defaults = cls() + return cls( + url=data.get("url", defaults.url), + default_group_role=( + RoleType(data["default_group_role"]) if "default_group_role" in data else defaults.default_group_role + ), + add_ext_groups=data.get("add_ext_groups", defaults.add_ext_groups), + group_mapping=data.get("group_mapping", defaults.group_mapping), ) @@ -44,13 +45,18 @@ class SecurityConfig: disabled: bool = False external_auth: ExternalAuthConfig = ExternalAuthConfig() - @staticmethod - def from_dict(data: JSON) -> "SecurityConfig": - return SecurityConfig( - jwt_key=data.get("jwt", {}).get("key", ""), - admin_pwd=data.get("login", {}).get("admin", {}).get("pwd", ""), - disabled=data.get("disabled", False), - external_auth=ExternalAuthConfig.from_dict(data.get("external_auth", {})), + @classmethod + def from_dict(cls, data: JSON) -> "SecurityConfig": + defaults = cls() + return cls( + jwt_key=data.get("jwt", {}).get("key", defaults.jwt_key), + admin_pwd=data.get("login", {}).get("admin", {}).get("pwd", defaults.admin_pwd), + disabled=data.get("disabled", defaults.disabled), + external_auth=( + ExternalAuthConfig.from_dict(data["external_auth"]) + if "external_auth" in data + else defaults.external_auth + ), ) @@ -65,13 +71,14 @@ class WorkspaceConfig: groups: List[str] = field(default_factory=lambda: []) path: Path = Path() - @staticmethod - def from_dict(data: JSON) -> "WorkspaceConfig": - return WorkspaceConfig( - path=Path(data["path"]), - groups=data.get("groups", []), - filter_in=data.get("filter_in", [".*"]), - filter_out=data.get("filter_out", []), + @classmethod + def from_dict(cls, data: JSON) -> "WorkspaceConfig": + defaults = cls() + return cls( + filter_in=data.get("filter_in", defaults.filter_in), + filter_out=data.get("filter_out", defaults.filter_out), + groups=data.get("groups", defaults.groups), + path=Path(data["path"]) if "path" in data else defaults.path, ) @@ -91,18 +98,19 @@ class DbConfig: pool_size: int = 5 pool_use_lifo: bool = False - @staticmethod - def from_dict(data: JSON) -> "DbConfig": - return DbConfig( - db_admin_url=data.get("admin_url", None), - db_url=data.get("url", ""), - db_connect_timeout=data.get("db_connect_timeout", 10), - pool_recycle=data.get("pool_recycle", None), - pool_pre_ping=data.get("pool_pre_ping", False), - pool_use_null=data.get("pool_use_null", False), - pool_max_overflow=data.get("pool_max_overflow", 10), - pool_size=data.get("pool_size", 5), - pool_use_lifo=data.get("pool_use_lifo", False), + @classmethod + def from_dict(cls, data: JSON) -> "DbConfig": + defaults = cls() + return cls( + db_admin_url=data.get("admin_url", defaults.db_admin_url), + db_url=data.get("url", defaults.db_url), + db_connect_timeout=data.get("db_connect_timeout", defaults.db_connect_timeout), + pool_recycle=data.get("pool_recycle", defaults.pool_recycle), + pool_pre_ping=data.get("pool_pre_ping", defaults.pool_pre_ping), + pool_use_null=data.get("pool_use_null", defaults.pool_use_null), + pool_max_overflow=data.get("pool_max_overflow", defaults.pool_max_overflow), + pool_size=data.get("pool_size", defaults.pool_size), + pool_use_lifo=data.get("pool_use_lifo", defaults.pool_use_lifo), ) @@ -115,7 +123,7 @@ class StorageConfig: matrixstore: Path = Path("./matrixstore") archive_dir: Path = Path("./archives") tmp_dir: Path = Path(tempfile.gettempdir()) - workspaces: Dict[str, WorkspaceConfig] = field(default_factory=lambda: {}) + workspaces: Dict[str, WorkspaceConfig] = field(default_factory=dict) allow_deletion: bool = False watcher_lock: bool = True watcher_lock_delay: int = 10 @@ -127,39 +135,112 @@ class StorageConfig: auto_archive_sleeping_time: int = 3600 auto_archive_max_parallel: int = 5 - @staticmethod - def from_dict(data: JSON) -> "StorageConfig": - return StorageConfig( - tmp_dir=Path(data.get("tmp_dir", tempfile.gettempdir())), - matrixstore=Path(data["matrixstore"]), - workspaces={n: WorkspaceConfig.from_dict(w) for n, w in data["workspaces"].items()}, - allow_deletion=data.get("allow_deletion", False), - archive_dir=Path(data["archive_dir"]), - watcher_lock=data.get("watcher_lock", True), - watcher_lock_delay=data.get("watcher_lock_delay", 10), - download_default_expiration_timeout_minutes=data.get("download_default_expiration_timeout_minutes", 1440), - matrix_gc_sleeping_time=data.get("matrix_gc_sleeping_time", 3600), - matrix_gc_dry_run=data.get("matrix_gc_dry_run", False), - auto_archive_threshold_days=data.get("auto_archive_threshold_days", 60), - auto_archive_dry_run=data.get("auto_archive_dry_run", False), - auto_archive_sleeping_time=data.get("auto_archive_sleeping_time", 3600), - auto_archive_max_parallel=data.get("auto_archive_max_parallel", 5), + @classmethod + def from_dict(cls, data: JSON) -> "StorageConfig": + defaults = cls() + workspaces = ( + {key: WorkspaceConfig.from_dict(value) for key, value in data["workspaces"].items()} + if "workspaces" in data + else defaults.workspaces + ) + return cls( + matrixstore=Path(data["matrixstore"]) if "matrixstore" in data else defaults.matrixstore, + archive_dir=Path(data["archive_dir"]) if "archive_dir" in data else defaults.archive_dir, + tmp_dir=Path(data["tmp_dir"]) if "tmp_dir" in data else defaults.tmp_dir, + workspaces=workspaces, + allow_deletion=data.get("allow_deletion", defaults.allow_deletion), + watcher_lock=data.get("watcher_lock", defaults.watcher_lock), + watcher_lock_delay=data.get("watcher_lock_delay", defaults.watcher_lock_delay), + download_default_expiration_timeout_minutes=( + data.get( + "download_default_expiration_timeout_minutes", + defaults.download_default_expiration_timeout_minutes, + ) + ), + matrix_gc_sleeping_time=data.get("matrix_gc_sleeping_time", defaults.matrix_gc_sleeping_time), + matrix_gc_dry_run=data.get("matrix_gc_dry_run", defaults.matrix_gc_dry_run), + auto_archive_threshold_days=data.get("auto_archive_threshold_days", defaults.auto_archive_threshold_days), + auto_archive_dry_run=data.get("auto_archive_dry_run", defaults.auto_archive_dry_run), + auto_archive_sleeping_time=data.get("auto_archive_sleeping_time", defaults.auto_archive_sleeping_time), + auto_archive_max_parallel=data.get("auto_archive_max_parallel", defaults.auto_archive_max_parallel), ) +@dataclass(frozen=True) +class NbCoresConfig: + """ + The NBCoresConfig class is designed to manage the configuration of the number of CPU cores + """ + + min: int = 1 + default: int = 22 + max: int = 24 + + def to_json(self) -> Dict[str, int]: + """ + Retrieves the number of cores parameters, returning a dictionary containing the values "min" + (minimum allowed value), "defaultValue" (default value), and "max" (maximum allowed value) + + Returns: + A dictionary: `{"min": min, "defaultValue": default, "max": max}`. + Because ReactJs Material UI expects "min", "defaultValue" and "max" keys. + """ + return {"min": self.min, "defaultValue": self.default, "max": self.max} + + def __post_init__(self) -> None: + """validation of CPU configuration""" + if 1 <= self.min <= self.default <= self.max: + return + msg = f"Invalid configuration: 1 <= {self.min=} <= {self.default=} <= {self.max=}" + raise ValueError(msg) + + @dataclass(frozen=True) class LocalConfig: + """Sub config object dedicated to launcher module (local)""" + binaries: Dict[str, Path] = field(default_factory=dict) + enable_nb_cores_detection: bool = True + nb_cores: NbCoresConfig = NbCoresConfig() - @staticmethod - def from_dict(data: JSON) -> Optional["LocalConfig"]: - return LocalConfig( - binaries={str(v): Path(p) for v, p in data["binaries"].items()}, + @classmethod + def from_dict(cls, data: JSON) -> "LocalConfig": + """ + Creates an instance of LocalConfig from a data dictionary + Args: + data: Parse config from dict. + Returns: object NbCoresConfig + """ + defaults = cls() + binaries = data.get("binaries", defaults.binaries) + enable_nb_cores_detection = data.get("enable_nb_cores_detection", defaults.enable_nb_cores_detection) + nb_cores = data.get("nb_cores", asdict(defaults.nb_cores)) + if enable_nb_cores_detection: + nb_cores.update(cls._autodetect_nb_cores()) + return cls( + binaries={str(v): Path(p) for v, p in binaries.items()}, + enable_nb_cores_detection=enable_nb_cores_detection, + nb_cores=NbCoresConfig(**nb_cores), ) + @classmethod + def _autodetect_nb_cores(cls) -> Dict[str, int]: + """ + Automatically detects the number of cores available on the user's machine + Returns: Instance of NbCoresConfig + """ + min_cpu = cls.nb_cores.min + max_cpu = multiprocessing.cpu_count() + default = max(min_cpu, max_cpu - 2) + return {"min": min_cpu, "max": max_cpu, "default": default} + @dataclass(frozen=True) class SlurmConfig: + """ + Sub config object dedicated to launcher module (slurm) + """ + local_workspace: Path = Path() username: str = "" hostname: str = "" @@ -169,31 +250,68 @@ class SlurmConfig: password: str = "" default_wait_time: int = 0 default_time_limit: int = 0 - default_n_cpu: int = 1 default_json_db_name: str = "" slurm_script_path: str = "" max_cores: int = 64 antares_versions_on_remote_server: List[str] = field(default_factory=list) + enable_nb_cores_detection: bool = False + nb_cores: NbCoresConfig = NbCoresConfig() - @staticmethod - def from_dict(data: JSON) -> "SlurmConfig": - return SlurmConfig( - local_workspace=Path(data["local_workspace"]), - username=data["username"], - hostname=data["hostname"], - port=data["port"], - private_key_file=data.get("private_key_file", None), - key_password=data.get("key_password", None), - password=data.get("password", None), - default_wait_time=data["default_wait_time"], - default_time_limit=data["default_time_limit"], - default_n_cpu=data["default_n_cpu"], - default_json_db_name=data["default_json_db_name"], - slurm_script_path=data["slurm_script_path"], - antares_versions_on_remote_server=data["antares_versions_on_remote_server"], - max_cores=data.get("max_cores", 64), + @classmethod + def from_dict(cls, data: JSON) -> "SlurmConfig": + """ + Creates an instance of SlurmConfig from a data dictionary + + Args: + data: Parsed config from dict. + Returns: object SlurmConfig + """ + defaults = cls() + enable_nb_cores_detection = data.get("enable_nb_cores_detection", defaults.enable_nb_cores_detection) + nb_cores = data.get("nb_cores", asdict(defaults.nb_cores)) + if "default_n_cpu" in data: + # Use the old way to configure the NB cores for backward compatibility + nb_cores["default"] = int(data["default_n_cpu"]) + nb_cores["min"] = min(nb_cores["min"], nb_cores["default"]) + nb_cores["max"] = max(nb_cores["max"], nb_cores["default"]) + if enable_nb_cores_detection: + nb_cores.update(cls._autodetect_nb_cores()) + return cls( + local_workspace=Path(data.get("local_workspace", defaults.local_workspace)), + username=data.get("username", defaults.username), + hostname=data.get("hostname", defaults.hostname), + port=data.get("port", defaults.port), + private_key_file=data.get("private_key_file", defaults.private_key_file), + key_password=data.get("key_password", defaults.key_password), + password=data.get("password", defaults.password), + default_wait_time=data.get("default_wait_time", defaults.default_wait_time), + default_time_limit=data.get("default_time_limit", defaults.default_time_limit), + default_json_db_name=data.get("default_json_db_name", defaults.default_json_db_name), + slurm_script_path=data.get("slurm_script_path", defaults.slurm_script_path), + antares_versions_on_remote_server=data.get( + "antares_versions_on_remote_server", + defaults.antares_versions_on_remote_server, + ), + max_cores=data.get("max_cores", defaults.max_cores), + enable_nb_cores_detection=enable_nb_cores_detection, + nb_cores=NbCoresConfig(**nb_cores), ) + @classmethod + def _autodetect_nb_cores(cls) -> Dict[str, int]: + raise NotImplementedError("NB Cores auto-detection is not implemented for SLURM server") + + +class InvalidConfigurationError(Exception): + """ + Exception raised when an attempt is made to retrieve the number of cores + of a launcher that doesn't exist in the configuration. + """ + + def __init__(self, launcher: str): + msg = f"Configuration is not available for the '{launcher}' launcher" + super().__init__(msg) + @dataclass(frozen=True) class LauncherConfig: @@ -202,27 +320,53 @@ class LauncherConfig: """ default: str = "local" - local: Optional[LocalConfig] = LocalConfig() - slurm: Optional[SlurmConfig] = SlurmConfig() + local: Optional[LocalConfig] = None + slurm: Optional[SlurmConfig] = None batch_size: int = 9999 - @staticmethod - def from_dict(data: JSON) -> "LauncherConfig": - local: Optional[LocalConfig] = None - if "local" in data: - local = LocalConfig.from_dict(data["local"]) - - slurm: Optional[SlurmConfig] = None - if "slurm" in data: - slurm = SlurmConfig.from_dict(data["slurm"]) - - return LauncherConfig( - default=data.get("default", "local"), + @classmethod + def from_dict(cls, data: JSON) -> "LauncherConfig": + defaults = cls() + default = data.get("default", cls.default) + local = LocalConfig.from_dict(data["local"]) if "local" in data else defaults.local + slurm = SlurmConfig.from_dict(data["slurm"]) if "slurm" in data else defaults.slurm + batch_size = data.get("batch_size", defaults.batch_size) + return cls( + default=default, local=local, slurm=slurm, - batch_size=data.get("batch_size", 9999), + batch_size=batch_size, ) + def __post_init__(self) -> None: + possible = {"local", "slurm"} + if self.default in possible: + return + msg = f"Invalid configuration: {self.default=} must be one of {possible!r}" + raise ValueError(msg) + + def get_nb_cores(self, launcher: str) -> "NbCoresConfig": + """ + Retrieve the number of cores configuration for a given launcher: "local" or "slurm". + If "default" is specified, retrieve the configuration of the default launcher. + + Args: + launcher: type of launcher "local", "slurm" or "default". + + Returns: + Number of cores of the given launcher. + + Raises: + InvalidConfigurationError: Exception raised when an attempt is made to retrieve + the number of cores of a launcher that doesn't exist in the configuration. + """ + config_map = {"local": self.local, "slurm": self.slurm} + config_map["default"] = config_map[self.default] + launcher_config = config_map.get(launcher) + if launcher_config is None: + raise InvalidConfigurationError(launcher) + return launcher_config.nb_cores + @dataclass(frozen=True) class LoggingConfig: @@ -234,14 +378,13 @@ class LoggingConfig: json: bool = False level: str = "INFO" - @staticmethod - def from_dict(data: JSON) -> "LoggingConfig": - logging_config: Dict[str, Any] = data or {} - logfile: Optional[str] = logging_config.get("logfile") - return LoggingConfig( - logfile=Path(logfile) if logfile is not None else None, - json=logging_config.get("json", False), - level=logging_config.get("level", "INFO"), + @classmethod + def from_dict(cls, data: JSON) -> "LoggingConfig": + defaults = cls() + return cls( + logfile=Path(data["logfile"]) if "logfile" in data else defaults.logfile, + json=data.get("json", defaults.json), + level=data.get("level", defaults.level), ) @@ -255,12 +398,13 @@ class RedisConfig: port: int = 6379 password: Optional[str] = None - @staticmethod - def from_dict(data: JSON) -> "RedisConfig": - return RedisConfig( - host=data["host"], - port=data["port"], - password=data.get("password", None), + @classmethod + def from_dict(cls, data: JSON) -> "RedisConfig": + defaults = cls() + return cls( + host=data.get("host", defaults.host), + port=data.get("port", defaults.port), + password=data.get("password", defaults.password), ) @@ -271,9 +415,9 @@ class EventBusConfig: """ # noinspection PyUnusedLocal - @staticmethod - def from_dict(data: JSON) -> "EventBusConfig": - return EventBusConfig() + @classmethod + def from_dict(cls, data: JSON) -> "EventBusConfig": + return cls() @dataclass(frozen=True) @@ -284,10 +428,11 @@ class CacheConfig: checker_delay: float = 0.2 # in seconds - @staticmethod - def from_dict(data: JSON) -> "CacheConfig": - return CacheConfig( - checker_delay=float(data["checker_delay"]) if "checker_delay" in data else 0.2, + @classmethod + def from_dict(cls, data: JSON) -> "CacheConfig": + defaults = cls() + return cls( + checker_delay=data.get("checker_delay", defaults.checker_delay), ) @@ -296,9 +441,13 @@ class RemoteWorkerConfig: name: str queues: List[str] = field(default_factory=list) - @staticmethod - def from_dict(data: JSON) -> "RemoteWorkerConfig": - return RemoteWorkerConfig(name=data["name"], queues=data.get("queues", [])) + @classmethod + def from_dict(cls, data: JSON) -> "RemoteWorkerConfig": + defaults = cls(name="") # `name` is mandatory + return cls( + name=data["name"], + queues=data.get("queues", defaults.queues), + ) @dataclass(frozen=True) @@ -310,16 +459,17 @@ class TaskConfig: max_workers: int = 5 remote_workers: List[RemoteWorkerConfig] = field(default_factory=list) - @staticmethod - def from_dict(data: JSON) -> "TaskConfig": - return TaskConfig( - max_workers=int(data["max_workers"]) if "max_workers" in data else 5, - remote_workers=list( - map( - lambda x: RemoteWorkerConfig.from_dict(x), - data.get("remote_workers", []), - ) - ), + @classmethod + def from_dict(cls, data: JSON) -> "TaskConfig": + defaults = cls() + remote_workers = ( + [RemoteWorkerConfig.from_dict(d) for d in data["remote_workers"]] + if "remote_workers" in data + else defaults.remote_workers + ) + return cls( + max_workers=data.get("max_workers", defaults.max_workers), + remote_workers=remote_workers, ) @@ -332,11 +482,12 @@ class ServerConfig: worker_threadpool_size: int = 5 services: List[str] = field(default_factory=list) - @staticmethod - def from_dict(data: JSON) -> "ServerConfig": - return ServerConfig( - worker_threadpool_size=int(data["worker_threadpool_size"]) if "worker_threadpool_size" in data else 5, - services=data.get("services", []), + @classmethod + def from_dict(cls, data: JSON) -> "ServerConfig": + defaults = cls() + return cls( + worker_threadpool_size=data.get("worker_threadpool_size", defaults.worker_threadpool_size), + services=data.get("services", defaults.services), ) @@ -360,36 +511,27 @@ class Config: tasks: TaskConfig = TaskConfig() root_path: str = "" - @staticmethod - def from_dict(data: JSON, res: Optional[Path] = None) -> "Config": - """ - Parse config from dict. - - Args: - data: dict struct to parse - res: resources path is not present in yaml file. - - Returns: - - """ - return Config( - security=SecurityConfig.from_dict(data.get("security", {})), - storage=StorageConfig.from_dict(data["storage"]), - launcher=LauncherConfig.from_dict(data.get("launcher", {})), - db=DbConfig.from_dict(data["db"]) if "db" in data else DbConfig(), - logging=LoggingConfig.from_dict(data.get("logging", {})), - debug=data.get("debug", False), - resources_path=res or Path(), - root_path=data.get("root_path", ""), - redis=RedisConfig.from_dict(data["redis"]) if "redis" in data else None, - eventbus=EventBusConfig.from_dict(data["eventbus"]) if "eventbus" in data else EventBusConfig(), - cache=CacheConfig.from_dict(data["cache"]) if "cache" in data else CacheConfig(), - tasks=TaskConfig.from_dict(data["tasks"]) if "tasks" in data else TaskConfig(), - server=ServerConfig.from_dict(data["server"]) if "server" in data else ServerConfig(), + @classmethod + def from_dict(cls, data: JSON) -> "Config": + defaults = cls() + return cls( + server=ServerConfig.from_dict(data["server"]) if "server" in data else defaults.server, + security=SecurityConfig.from_dict(data["security"]) if "security" in data else defaults.security, + storage=StorageConfig.from_dict(data["storage"]) if "storage" in data else defaults.storage, + launcher=LauncherConfig.from_dict(data["launcher"]) if "launcher" in data else defaults.launcher, + db=DbConfig.from_dict(data["db"]) if "db" in data else defaults.db, + logging=LoggingConfig.from_dict(data["logging"]) if "logging" in data else defaults.logging, + debug=data.get("debug", defaults.debug), + resources_path=data["resources_path"] if "resources_path" in data else defaults.resources_path, + redis=RedisConfig.from_dict(data["redis"]) if "redis" in data else defaults.redis, + eventbus=EventBusConfig.from_dict(data["eventbus"]) if "eventbus" in data else defaults.eventbus, + cache=CacheConfig.from_dict(data["cache"]) if "cache" in data else defaults.cache, + tasks=TaskConfig.from_dict(data["tasks"]) if "tasks" in data else defaults.tasks, + root_path=data.get("root_path", defaults.root_path), ) - @staticmethod - def from_yaml_file(file: Path, res: Optional[Path] = None) -> "Config": + @classmethod + def from_yaml_file(cls, file: Path, res: Optional[Path] = None) -> "Config": """ Parse config from yaml file. @@ -400,5 +542,8 @@ def from_yaml_file(file: Path, res: Optional[Path] = None) -> "Config": Returns: """ - data = yaml.safe_load(open(file)) - return Config.from_dict(data, res) + with open(file) as f: + data = yaml.safe_load(f) + if res is not None: + data["resources_path"] = res + return cls.from_dict(data) diff --git a/antarest/launcher/adapters/slurm_launcher/slurm_launcher.py b/antarest/launcher/adapters/slurm_launcher/slurm_launcher.py index 926f2b50a3..00283b9ce8 100644 --- a/antarest/launcher/adapters/slurm_launcher/slurm_launcher.py +++ b/antarest/launcher/adapters/slurm_launcher/slurm_launcher.py @@ -32,7 +32,6 @@ logger = logging.getLogger(__name__) logging.getLogger("paramiko").setLevel("WARN") -MAX_NB_CPU = 24 MAX_TIME_LIMIT = 864000 MIN_TIME_LIMIT = 3600 WORKSPACE_LOCK_FILE_NAME = ".lock" @@ -153,7 +152,7 @@ def _init_launcher_arguments(self, local_workspace: Optional[Path] = None) -> ar main_options_parameters = ParserParameters( default_wait_time=self.slurm_config.default_wait_time, default_time_limit=self.slurm_config.default_time_limit, - default_n_cpu=self.slurm_config.default_n_cpu, + default_n_cpu=self.slurm_config.nb_cores.default, studies_in_dir=str((Path(local_workspace or self.slurm_config.local_workspace) / STUDIES_INPUT_DIR_NAME)), log_dir=str((Path(self.slurm_config.local_workspace) / LOG_DIR_NAME)), finished_dir=str((Path(local_workspace or self.slurm_config.local_workspace) / STUDIES_OUTPUT_DIR_NAME)), @@ -440,7 +439,7 @@ def _run_study( _override_solver_version(study_path, version) append_log(launch_uuid, "Submitting study to slurm launcher") - launcher_args = self._check_and_apply_launcher_params(launcher_params) + launcher_args = self._apply_params(launcher_params) self._call_launcher(launcher_args, self.launcher_params) launch_success = self._check_if_study_is_in_launcher_db(launch_uuid) @@ -481,23 +480,40 @@ def _check_if_study_is_in_launcher_db(self, job_id: str) -> bool: studies = self.data_repo_tinydb.get_list_of_studies() return any(s.name == job_id for s in studies) - def _check_and_apply_launcher_params(self, launcher_params: LauncherParametersDTO) -> argparse.Namespace: + def _apply_params(self, launcher_params: LauncherParametersDTO) -> argparse.Namespace: + """ + Populate a `argparse.Namespace` object with the user parameters. + + Args: + launcher_params: + Contains the launcher parameters selected by the user. + If a parameter is not provided (`None`), the default value should be retrieved + from the configuration. + + Returns: + The `argparse.Namespace` object which is then passed to `antarestlauncher.main.run_with`, + to launch a simulation using Antares Launcher. + """ if launcher_params: launcher_args = deepcopy(self.launcher_args) - other_options = [] + if launcher_params.other_options: - options = re.split("\\s+", launcher_params.other_options) - for opt in options: - other_options.append(re.sub("[^a-zA-Z0-9_,-]", "", opt)) - if launcher_params.xpansion is not None: - launcher_args.xpansion_mode = "r" if launcher_params.xpansion_r_version else "cpp" + options = launcher_params.other_options.split() + other_options = [re.sub("[^a-zA-Z0-9_,-]", "", opt) for opt in options] + else: + other_options = [] + + # launcher_params.xpansion can be an `XpansionParametersDTO`, a bool or `None` + if launcher_params.xpansion: # not None and not False + launcher_args.xpansion_mode = {True: "r", False: "cpp"}[launcher_params.xpansion_r_version] if ( isinstance(launcher_params.xpansion, XpansionParametersDTO) and launcher_params.xpansion.sensitivity_mode ): other_options.append("xpansion_sensitivity") + time_limit = launcher_params.time_limit - if time_limit and isinstance(time_limit, int): + if time_limit is not None: if MIN_TIME_LIMIT > time_limit: logger.warning( f"Invalid slurm launcher time limit ({time_limit})," @@ -512,15 +528,23 @@ def _check_and_apply_launcher_params(self, launcher_params: LauncherParametersDT launcher_args.time_limit = MAX_TIME_LIMIT - 3600 else: launcher_args.time_limit = time_limit + post_processing = launcher_params.post_processing - if isinstance(post_processing, bool): + if post_processing is not None: launcher_args.post_processing = post_processing + nb_cpu = launcher_params.nb_cpu - if nb_cpu and isinstance(nb_cpu, int): - if 0 < nb_cpu <= MAX_NB_CPU: + if nb_cpu is not None: + nb_cores = self.slurm_config.nb_cores + if nb_cores.min <= nb_cpu <= nb_cores.max: launcher_args.n_cpu = nb_cpu else: - logger.warning(f"Invalid slurm launcher nb_cpu ({nb_cpu}), should be between 1 and 24") + logger.warning( + f"Invalid slurm launcher nb_cpu ({nb_cpu})," + f" should be between {nb_cores.min} and {nb_cores.max}" + ) + launcher_args.n_cpu = nb_cores.default + if launcher_params.adequacy_patch is not None: # the adequacy patch can be an empty object launcher_args.post_processing = True diff --git a/antarest/launcher/model.py b/antarest/launcher/model.py index 7a3c615811..a9bf0f6fde 100644 --- a/antarest/launcher/model.py +++ b/antarest/launcher/model.py @@ -17,14 +17,14 @@ class XpansionParametersDTO(BaseModel): class LauncherParametersDTO(BaseModel): - # Warning ! This class must be retrocompatible (that's the reason for the weird bool/XpansionParametersDTO union) + # Warning ! This class must be retro-compatible (that's the reason for the weird bool/XpansionParametersDTO union) # The reason is that it's stored in json format in database and deserialized using the latest class version # If compatibility is to be broken, an (alembic) data migration script should be added adequacy_patch: Optional[Dict[str, Any]] = None nb_cpu: Optional[int] = None post_processing: bool = False - time_limit: Optional[int] = None - xpansion: Union[bool, Optional[XpansionParametersDTO]] = None + time_limit: Optional[int] = None # 3600 <= time_limit < 864000 (10 days) + xpansion: Union[XpansionParametersDTO, bool, None] = None xpansion_r_version: bool = False archive_output: bool = True auto_unzip: bool = True diff --git a/antarest/launcher/service.py b/antarest/launcher/service.py index 340c00c27c..aa081e5462 100644 --- a/antarest/launcher/service.py +++ b/antarest/launcher/service.py @@ -1,4 +1,5 @@ import functools +import json import logging import os import shutil @@ -10,7 +11,7 @@ from fastapi import HTTPException -from antarest.core.config import Config +from antarest.core.config import Config, NbCoresConfig from antarest.core.exceptions import StudyNotFoundError from antarest.core.filetransfer.model import FileDownloadTaskDTO from antarest.core.filetransfer.service import FileTransferManager @@ -99,6 +100,21 @@ def _init_extensions(self) -> Dict[str, ILauncherExtension]: def get_launchers(self) -> List[str]: return list(self.launchers.keys()) + def get_nb_cores(self, launcher: str) -> NbCoresConfig: + """ + Retrieve the configuration of the launcher's nb of cores. + + Args: + launcher: name of the launcher: "default", "slurm" or "local". + + Returns: + Number of cores of the launcher + + Raises: + InvalidConfigurationError: if the launcher configuration is not available + """ + return self.config.launcher.get_nb_cores(launcher) + def _after_export_flat_hooks( self, job_id: str, @@ -586,27 +602,31 @@ def get_load(self, from_cluster: bool = False) -> Dict[str, float]: local_running_jobs.append(job) else: logger.warning(f"Unknown job launcher {job.launcher}") + load = {} - if self.config.launcher.slurm: + + slurm_config = self.config.launcher.slurm + if slurm_config is not None: if from_cluster: - raise NotImplementedError - slurm_used_cpus = functools.reduce( - lambda count, j: count - + ( - LauncherParametersDTO.parse_raw(j.launcher_params or "{}").nb_cpu - or self.config.launcher.slurm.default_n_cpu # type: ignore - ), - slurm_running_jobs, - 0, - ) - load["slurm"] = float(slurm_used_cpus) / self.config.launcher.slurm.max_cores - if self.config.launcher.local: - local_used_cpus = functools.reduce( - lambda count, j: count + (LauncherParametersDTO.parse_raw(j.launcher_params or "{}").nb_cpu or 1), - local_running_jobs, - 0, - ) - load["local"] = float(local_used_cpus) / (os.cpu_count() or 1) + raise NotImplementedError("Cluster load not implemented yet") + default_cpu = slurm_config.nb_cores.default + slurm_used_cpus = 0 + for job in slurm_running_jobs: + obj = json.loads(job.launcher_params) if job.launcher_params else {} + launch_params = LauncherParametersDTO(**obj) + slurm_used_cpus += launch_params.nb_cpu or default_cpu + load["slurm"] = slurm_used_cpus / slurm_config.max_cores + + local_config = self.config.launcher.local + if local_config is not None: + default_cpu = local_config.nb_cores.default + local_used_cpus = 0 + for job in local_running_jobs: + obj = json.loads(job.launcher_params) if job.launcher_params else {} + launch_params = LauncherParametersDTO(**obj) + local_used_cpus += launch_params.nb_cpu or default_cpu + load["local"] = local_used_cpus / local_config.nb_cores.max + return load def get_solver_versions(self, solver: str) -> List[str]: diff --git a/antarest/launcher/web.py b/antarest/launcher/web.py index ffb4cf6ccf..51b3582997 100644 --- a/antarest/launcher/web.py +++ b/antarest/launcher/web.py @@ -6,7 +6,7 @@ from fastapi import APIRouter, Depends, Query from fastapi.exceptions import HTTPException -from antarest.core.config import Config +from antarest.core.config import Config, InvalidConfigurationError from antarest.core.filetransfer.model import FileDownloadTaskDTO from antarest.core.jwt import JWTUser from antarest.core.requests import RequestParameters @@ -230,4 +230,48 @@ def get_solver_versions( raise UnknownSolverConfig(solver) return service.get_solver_versions(solver) + # noinspection SpellCheckingInspection + @bp.get( + "/launcher/nbcores", # We avoid "nb_cores" and "nb-cores" in endpoints + tags=[APITag.launcher], + summary="Retrieving Min, Default, and Max Core Count", + response_model=Dict[str, int], + ) + def get_nb_cores( + launcher: str = Query( + "default", + examples={ + "Default launcher": { + "description": "Min, Default, and Max Core Count", + "value": "default", + }, + "SLURM launcher": { + "description": "Min, Default, and Max Core Count", + "value": "slurm", + }, + "Local launcher": { + "description": "Min, Default, and Max Core Count", + "value": "local", + }, + }, + ) + ) -> Dict[str, int]: + """ + Retrieve the numer of cores of the launcher. + + Args: + - `launcher`: name of the configuration to read: "slurm" or "local". + If "default" is specified, retrieve the configuration of the default launcher. + + Returns: + - "min": min number of cores + - "defaultValue": default number of cores + - "max": max number of cores + """ + logger.info(f"Fetching the number of cores for the '{launcher}' configuration") + try: + return service.config.launcher.get_nb_cores(launcher).to_json() + except InvalidConfigurationError: + raise UnknownSolverConfig(launcher) + return bp diff --git a/resources/application.yaml b/resources/application.yaml index 75a7b8605e..a85357634f 100644 --- a/resources/application.yaml +++ b/resources/application.yaml @@ -20,9 +20,9 @@ db: #pool_recycle: storage: - tmp_dir: /tmp + tmp_dir: ./tmp matrixstore: ./matrices - archive_dir: examples/archives + archive_dir: ./examples/archives allow_deletion: false # indicate if studies found in non default workspace can be deleted by the application #matrix_gc_sleeping_time: 3600 # time in seconds to sleep between two garbage collection #matrix_gc_dry_run: False # Skip matrix effective deletion @@ -32,20 +32,23 @@ storage: #auto_archive_max_parallel: 5 # max auto archival tasks in parallel workspaces: default: # required, no filters applied, this folder is not watched - path: examples/internal_studies/ + path: ./examples/internal_studies/ # other workspaces can be added # if a directory is to be ignored by the watcher, place a file named AW_NO_SCAN inside tmp: - path: examples/studies/ + path: ./examples/studies/ # filter_in: ['.*'] # default to '.*' # filter_out: [] # default to empty # groups: [] # default empty launcher: default: local + local: binaries: 700: path/to/700 + enable_nb_cores_detection: true + # slurm: # local_workspace: path/to/workspace # username: username @@ -56,7 +59,11 @@ launcher: # password: password_is_optional_but_necessary_if_key_is_absent # default_wait_time: 900 # default_time_limit: 172800 -# default_n_cpu: 12 +# enable_nb_cores_detection: False +# nb_cores: +# min: 1 +# default: 22 +# max: 24 # default_json_db_name: launcher_db.json # slurm_script_path: /path/to/launchantares_v1.1.3.sh # db_primary_key: name @@ -70,7 +77,7 @@ launcher: debug: true -root_path: "" +root_path: "api" #tasks: # max_workers: 5 diff --git a/resources/deploy/config.prod.yaml b/resources/deploy/config.prod.yaml index 1bb5e30878..02fbb4b8bc 100644 --- a/resources/deploy/config.prod.yaml +++ b/resources/deploy/config.prod.yaml @@ -32,9 +32,12 @@ storage: launcher: default: local + local: binaries: 800: /antares_simulator/antares-8.2-solver + enable_nb_cores_detection: true + # slurm: # local_workspace: path/to/workspace # username: username @@ -45,7 +48,11 @@ launcher: # password: password_is_optional_but_necessary_if_key_is_absent # default_wait_time: 900 # default_time_limit: 172800 -# default_n_cpu: 12 +# enable_nb_cores_detection: False +# nb_cores: +# min: 1 +# default: 22 +# max: 24 # default_json_db_name: launcher_db.json # slurm_script_path: /path/to/launchantares_v1.1.3.sh # db_primary_key: name @@ -59,7 +66,7 @@ launcher: debug: false -root_path: "/api" +root_path: "api" #tasks: # max_workers: 5 diff --git a/resources/deploy/config.yaml b/resources/deploy/config.yaml index 48cea48a22..810e1f8d24 100644 --- a/resources/deploy/config.yaml +++ b/resources/deploy/config.yaml @@ -29,9 +29,12 @@ storage: launcher: default: local + local: binaries: 700: path/to/700 + enable_nb_cores_detection: true + # slurm: # local_workspace: path/to/workspace # username: username @@ -42,7 +45,11 @@ launcher: # password: password_is_optional_but_necessary_if_key_is_absent # default_wait_time: 900 # default_time_limit: 172800 -# default_n_cpu: 12 +# enable_nb_cores_detection: False +# nb_cores: +# min: 1 +# default: 22 +# max: 24 # default_json_db_name: launcher_db.json # slurm_script_path: /path/to/launchantares_v1.1.3.sh # db_primary_key: name diff --git a/tests/conftest_db.py b/tests/conftest_db.py index 877ca119d1..bcb4177766 100644 --- a/tests/conftest_db.py +++ b/tests/conftest_db.py @@ -3,7 +3,8 @@ import pytest from sqlalchemy import create_engine # type: ignore -from sqlalchemy.orm import sessionmaker +from sqlalchemy.engine.base import Engine # type: ignore +from sqlalchemy.orm import Session, sessionmaker # type: ignore from antarest.core.utils.fastapi_sqlalchemy import DBSessionMiddleware from antarest.dbmodel import Base @@ -12,7 +13,7 @@ @pytest.fixture(name="db_engine") -def db_engine_fixture() -> Generator[Any, None, None]: +def db_engine_fixture() -> Generator[Engine, None, None]: """ Fixture that creates an in-memory SQLite database engine for testing. @@ -26,7 +27,7 @@ def db_engine_fixture() -> Generator[Any, None, None]: @pytest.fixture(name="db_session") -def db_session_fixture(db_engine) -> Generator: +def db_session_fixture(db_engine: Engine) -> Generator[Session, None, None]: """ Fixture that creates a database session for testing purposes. @@ -46,7 +47,7 @@ def db_session_fixture(db_engine) -> Generator: @pytest.fixture(name="db_middleware", autouse=True) def db_middleware_fixture( - db_engine: Any, + db_engine: Engine, ) -> Generator[DBSessionMiddleware, None, None]: """ Fixture that sets up a database session middleware with custom engine settings. diff --git a/tests/core/assets/__init__.py b/tests/core/assets/__init__.py new file mode 100644 index 0000000000..773f16ec60 --- /dev/null +++ b/tests/core/assets/__init__.py @@ -0,0 +1,3 @@ +from pathlib import Path + +ASSETS_DIR = Path(__file__).parent.resolve() diff --git a/tests/core/assets/config/application-2.14.yaml b/tests/core/assets/config/application-2.14.yaml new file mode 100644 index 0000000000..650093286d --- /dev/null +++ b/tests/core/assets/config/application-2.14.yaml @@ -0,0 +1,61 @@ +security: + disabled: false + jwt: + key: super-secret + login: + admin: + pwd: admin + +db: + url: "sqlite:////home/john/antares_data/database.db" + +storage: + tmp_dir: /tmp + matrixstore: /home/john/antares_data/matrices + archive_dir: /home/john/antares_data/archives + allow_deletion: false + workspaces: + default: + path: /home/john/antares_data/internal_studies/ + studies: + path: /home/john/antares_data/studies/ + +launcher: + default: slurm + local: + binaries: + 850: /home/john/opt/antares-8.5.0-Ubuntu-20.04/antares-solver + 860: /home/john/opt/antares-8.6.0-Ubuntu-20.04/antares-8.6-solver + + slurm: + local_workspace: /home/john/antares_data/slurm_workspace + + username: antares + hostname: slurm-prod-01 + + port: 22 + private_key_file: /home/john/.ssh/id_rsa + key_password: + default_wait_time: 900 + default_time_limit: 172800 + default_n_cpu: 20 + default_json_db_name: launcher_db.json + slurm_script_path: /applis/antares/launchAntares.sh + db_primary_key: name + antares_versions_on_remote_server: + - '850' # 8.5.1/antares-8.5-solver + - '860' # 8.6.2/antares-8.6-solver + - '870' # 8.7.0/antares-8.7-solver + +debug: false + +root_path: "" + +server: + worker_threadpool_size: 12 + services: + - watcher + - matrix_gc + +logging: + level: INFO diff --git a/tests/core/assets/config/application-2.15.yaml b/tests/core/assets/config/application-2.15.yaml new file mode 100644 index 0000000000..c51d32aaae --- /dev/null +++ b/tests/core/assets/config/application-2.15.yaml @@ -0,0 +1,66 @@ +security: + disabled: false + jwt: + key: super-secret + login: + admin: + pwd: admin + +db: + url: "sqlite:////home/john/antares_data/database.db" + +storage: + tmp_dir: /tmp + matrixstore: /home/john/antares_data/matrices + archive_dir: /home/john/antares_data/archives + allow_deletion: false + workspaces: + default: + path: /home/john/antares_data/internal_studies/ + studies: + path: /home/john/antares_data/studies/ + +launcher: + default: slurm + local: + binaries: + 850: /home/john/opt/antares-8.5.0-Ubuntu-20.04/antares-solver + 860: /home/john/opt/antares-8.6.0-Ubuntu-20.04/antares-8.6-solver + enable_nb_cores_detection: True + + slurm: + local_workspace: /home/john/antares_data/slurm_workspace + + username: antares + hostname: slurm-prod-01 + + port: 22 + private_key_file: /home/john/.ssh/id_rsa + key_password: + default_wait_time: 900 + default_time_limit: 172800 + enable_nb_cores_detection: False + nb_cores: + min: 1 + default: 22 + max: 24 + default_json_db_name: launcher_db.json + slurm_script_path: /applis/antares/launchAntares.sh + db_primary_key: name + antares_versions_on_remote_server: + - '850' # 8.5.1/antares-8.5-solver + - '860' # 8.6.2/antares-8.6-solver + - '870' # 8.7.0/antares-8.7-solver + +debug: false + +root_path: "" + +server: + worker_threadpool_size: 12 + services: + - watcher + - matrix_gc + +logging: + level: INFO diff --git a/tests/core/test_config.py b/tests/core/test_config.py index 1c1c96a180..00c6f9458d 100644 --- a/tests/core/test_config.py +++ b/tests/core/test_config.py @@ -1,15 +1,253 @@ from pathlib import Path +from unittest import mock import pytest -from antarest.core.config import Config +from antarest.core.config import ( + Config, + InvalidConfigurationError, + LauncherConfig, + LocalConfig, + NbCoresConfig, + SlurmConfig, +) +from tests.core.assets import ASSETS_DIR +LAUNCHER_CONFIG = { + "default": "slurm", + "local": { + "binaries": {"860": Path("/bin/solver-860.exe")}, + "enable_nb_cores_detection": False, + "nb_cores": {"min": 2, "default": 10, "max": 20}, + }, + "slurm": { + "local_workspace": Path("/home/john/antares/workspace"), + "username": "john", + "hostname": "slurm-001", + "port": 22, + "private_key_file": Path("/home/john/.ssh/id_rsa"), + "key_password": "password", + "password": "password", + "default_wait_time": 10, + "default_time_limit": 20, + "default_json_db_name": "antares.db", + "slurm_script_path": "/path/to/slurm/launcher.sh", + "max_cores": 32, + "antares_versions_on_remote_server": ["860"], + "enable_nb_cores_detection": False, + "nb_cores": {"min": 1, "default": 34, "max": 36}, + }, + "batch_size": 100, +} -@pytest.mark.unit_test -def test_get_yaml(project_path: Path): - config = Config.from_yaml_file(file=project_path / "resources/application.yaml") - assert config.security.admin_pwd == "admin" - assert config.storage.workspaces["default"].path == Path("examples/internal_studies/") - assert not config.logging.json - assert config.logging.level == "INFO" +class TestNbCoresConfig: + def test_init__default_values(self): + config = NbCoresConfig() + assert config.min == 1 + assert config.default == 22 + assert config.max == 24 + + def test_init__invalid_values(self): + with pytest.raises(ValueError): + # default < min + NbCoresConfig(min=2, default=1, max=24) + with pytest.raises(ValueError): + # default > max + NbCoresConfig(min=1, default=25, max=24) + with pytest.raises(ValueError): + # min < 0 + NbCoresConfig(min=0, default=22, max=23) + with pytest.raises(ValueError): + # min > max + NbCoresConfig(min=22, default=22, max=21) + + def test_to_json(self): + config = NbCoresConfig() + # ReactJs Material UI expects "min", "defaultValue" and "max" keys + assert config.to_json() == {"min": 1, "defaultValue": 22, "max": 24} + + +class TestLocalConfig: + def test_init__default_values(self): + config = LocalConfig() + assert config.binaries == {}, "binaries should be empty by default" + assert config.enable_nb_cores_detection, "nb cores auto-detection should be enabled by default" + assert config.nb_cores == NbCoresConfig() + + def test_from_dict(self): + config = LocalConfig.from_dict( + { + "binaries": {"860": Path("/bin/solver-860.exe")}, + "enable_nb_cores_detection": False, + "nb_cores": {"min": 2, "default": 10, "max": 20}, + } + ) + assert config.binaries == {"860": Path("/bin/solver-860.exe")} + assert not config.enable_nb_cores_detection + assert config.nb_cores == NbCoresConfig(min=2, default=10, max=20) + + def test_from_dict__auto_detect(self): + with mock.patch("multiprocessing.cpu_count", return_value=8): + config = LocalConfig.from_dict( + { + "binaries": {"860": Path("/bin/solver-860.exe")}, + "enable_nb_cores_detection": True, + } + ) + assert config.binaries == {"860": Path("/bin/solver-860.exe")} + assert config.enable_nb_cores_detection + assert config.nb_cores == NbCoresConfig(min=1, default=6, max=8) + + +class TestSlurmConfig: + def test_init__default_values(self): + config = SlurmConfig() + assert config.local_workspace == Path() + assert config.username == "" + assert config.hostname == "" + assert config.port == 0 + assert config.private_key_file == Path() + assert config.key_password == "" + assert config.password == "" + assert config.default_wait_time == 0 + assert config.default_time_limit == 0 + assert config.default_json_db_name == "" + assert config.slurm_script_path == "" + assert config.max_cores == 64 + assert config.antares_versions_on_remote_server == [], "solver versions should be empty by default" + assert not config.enable_nb_cores_detection, "nb cores auto-detection shouldn't be enabled by default" + assert config.nb_cores == NbCoresConfig() + + def test_from_dict(self): + config = SlurmConfig.from_dict( + { + "local_workspace": Path("/home/john/antares/workspace"), + "username": "john", + "hostname": "slurm-001", + "port": 22, + "private_key_file": Path("/home/john/.ssh/id_rsa"), + "key_password": "password", + "password": "password", + "default_wait_time": 10, + "default_time_limit": 20, + "default_json_db_name": "antares.db", + "slurm_script_path": "/path/to/slurm/launcher.sh", + "max_cores": 32, + "antares_versions_on_remote_server": ["860"], + "enable_nb_cores_detection": False, + "nb_cores": {"min": 2, "default": 10, "max": 20}, + } + ) + assert config.local_workspace == Path("/home/john/antares/workspace") + assert config.username == "john" + assert config.hostname == "slurm-001" + assert config.port == 22 + assert config.private_key_file == Path("/home/john/.ssh/id_rsa") + assert config.key_password == "password" + assert config.password == "password" + assert config.default_wait_time == 10 + assert config.default_time_limit == 20 + assert config.default_json_db_name == "antares.db" + assert config.slurm_script_path == "/path/to/slurm/launcher.sh" + assert config.max_cores == 32 + assert config.antares_versions_on_remote_server == ["860"] + assert not config.enable_nb_cores_detection + assert config.nb_cores == NbCoresConfig(min=2, default=10, max=20) + + def test_from_dict__default_n_cpu__backport(self): + config = SlurmConfig.from_dict( + { + "local_workspace": Path("/home/john/antares/workspace"), + "username": "john", + "hostname": "slurm-001", + "port": 22, + "private_key_file": Path("/home/john/.ssh/id_rsa"), + "key_password": "password", + "password": "password", + "default_wait_time": 10, + "default_time_limit": 20, + "default_json_db_name": "antares.db", + "slurm_script_path": "/path/to/slurm/launcher.sh", + "max_cores": 32, + "antares_versions_on_remote_server": ["860"], + "default_n_cpu": 15, + } + ) + assert config.nb_cores == NbCoresConfig(min=1, default=15, max=24) + + def test_from_dict__auto_detect(self): + with pytest.raises(NotImplementedError): + SlurmConfig.from_dict({"enable_nb_cores_detection": True}) + + +class TestLauncherConfig: + def test_init__default_values(self): + config = LauncherConfig() + assert config.default == "local", "default launcher should be local" + assert config.local is None + assert config.slurm is None + assert config.batch_size == 9999 + + def test_from_dict(self): + config = LauncherConfig.from_dict(LAUNCHER_CONFIG) + assert config.default == "slurm" + assert config.local == LocalConfig( + binaries={"860": Path("/bin/solver-860.exe")}, + enable_nb_cores_detection=False, + nb_cores=NbCoresConfig(min=2, default=10, max=20), + ) + assert config.slurm == SlurmConfig( + local_workspace=Path("/home/john/antares/workspace"), + username="john", + hostname="slurm-001", + port=22, + private_key_file=Path("/home/john/.ssh/id_rsa"), + key_password="password", + password="password", + default_wait_time=10, + default_time_limit=20, + default_json_db_name="antares.db", + slurm_script_path="/path/to/slurm/launcher.sh", + max_cores=32, + antares_versions_on_remote_server=["860"], + enable_nb_cores_detection=False, + nb_cores=NbCoresConfig(min=1, default=34, max=36), + ) + assert config.batch_size == 100 + + def test_init__invalid_launcher(self): + with pytest.raises(ValueError): + LauncherConfig(default="invalid_launcher") + + def test_get_nb_cores__default(self): + config = LauncherConfig.from_dict(LAUNCHER_CONFIG) + # default == "slurm" + assert config.get_nb_cores(launcher="default") == NbCoresConfig(min=1, default=34, max=36) + + def test_get_nb_cores__local(self): + config = LauncherConfig.from_dict(LAUNCHER_CONFIG) + assert config.get_nb_cores(launcher="local") == NbCoresConfig(min=2, default=10, max=20) + + def test_get_nb_cores__slurm(self): + config = LauncherConfig.from_dict(LAUNCHER_CONFIG) + assert config.get_nb_cores(launcher="slurm") == NbCoresConfig(min=1, default=34, max=36) + + def test_get_nb_cores__invalid_configuration(self): + config = LauncherConfig.from_dict(LAUNCHER_CONFIG) + with pytest.raises(InvalidConfigurationError): + config.get_nb_cores("invalid_launcher") + config = LauncherConfig.from_dict({}) + with pytest.raises(InvalidConfigurationError): + config.get_nb_cores("slurm") + + +class TestConfig: + @pytest.mark.parametrize("config_name", ["application-2.14.yaml", "application-2.15.yaml"]) + def test_from_yaml_file(self, config_name: str) -> None: + yaml_path = ASSETS_DIR.joinpath("config", config_name) + config = Config.from_yaml_file(yaml_path) + assert config.security.admin_pwd == "admin" + assert config.storage.workspaces["default"].path == Path("/home/john/antares_data/internal_studies") + assert not config.logging.json + assert config.logging.level == "INFO" diff --git a/tests/integration/assets/config.template.yml b/tests/integration/assets/config.template.yml index f3ad1d256f..71c58a1ba0 100644 --- a/tests/integration/assets/config.template.yml +++ b/tests/integration/assets/config.template.yml @@ -27,6 +27,7 @@ launcher: local: binaries: 700: {{launcher_mock}} + enable_nb_cores_detection: True debug: false diff --git a/tests/integration/launcher_blueprint/test_launcher_local.py b/tests/integration/launcher_blueprint/test_launcher_local.py new file mode 100644 index 0000000000..7244fba8ee --- /dev/null +++ b/tests/integration/launcher_blueprint/test_launcher_local.py @@ -0,0 +1,70 @@ +import http + +import pytest +from starlette.testclient import TestClient + +from antarest.core.config import LocalConfig + + +# noinspection SpellCheckingInspection +@pytest.mark.integration_test +class TestLauncherNbCores: + """ + The purpose of this unit test is to check the `/v1/launcher/nbcores` endpoint. + """ + + def test_get_launcher_nb_cores( + self, + client: TestClient, + user_access_token: str, + ) -> None: + # NOTE: we have `enable_nb_cores_detection: True` in `tests/integration/assets/config.template.yml`. + local_nb_cores = LocalConfig.from_dict({"enable_nb_cores_detection": True}).nb_cores + nb_cores_expected = local_nb_cores.to_json() + res = client.get( + "/v1/launcher/nbcores", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + res.raise_for_status() + actual = res.json() + assert actual == nb_cores_expected + + res = client.get( + "/v1/launcher/nbcores?launcher=default", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + res.raise_for_status() + actual = res.json() + assert actual == nb_cores_expected + + res = client.get( + "/v1/launcher/nbcores?launcher=local", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + res.raise_for_status() + actual = res.json() + assert actual == nb_cores_expected + + # Check that the endpoint raise an exception when the "slurm" launcher is requested. + res = client.get( + "/v1/launcher/nbcores?launcher=slurm", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + assert res.status_code == http.HTTPStatus.UNPROCESSABLE_ENTITY, res.json() + actual = res.json() + assert actual == { + "description": "Unknown solver configuration: 'slurm'", + "exception": "UnknownSolverConfig", + } + + # Check that the endpoint raise an exception when an unknown launcher is requested. + res = client.get( + "/v1/launcher/nbcores?launcher=unknown", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + assert res.status_code == http.HTTPStatus.UNPROCESSABLE_ENTITY, res.json() + actual = res.json() + assert actual == { + "description": "Unknown solver configuration: 'unknown'", + "exception": "UnknownSolverConfig", + } diff --git a/tests/launcher/test_local_launcher.py b/tests/launcher/test_local_launcher.py index 04741d319a..53adc03bf0 100644 --- a/tests/launcher/test_local_launcher.py +++ b/tests/launcher/test_local_launcher.py @@ -1,19 +1,28 @@ import os import textwrap +import uuid from pathlib import Path from unittest.mock import Mock, call -from uuid import uuid4 import pytest -from sqlalchemy import create_engine from antarest.core.config import Config, LauncherConfig, LocalConfig -from antarest.core.persistence import Base -from antarest.core.utils.fastapi_sqlalchemy import DBSessionMiddleware from antarest.launcher.adapters.abstractlauncher import LauncherInitException from antarest.launcher.adapters.local_launcher.local_launcher import LocalLauncher from antarest.launcher.model import JobStatus, LauncherParametersDTO +SOLVER_NAME = "solver.bat" if os.name == "nt" else "solver.sh" + + +@pytest.fixture +def launcher_config(tmp_path: Path) -> Config: + """ + Fixture to create a launcher config with a local launcher. + """ + solver_path = tmp_path.joinpath(SOLVER_NAME) + data = {"binaries": {"700": solver_path}, "enable_nb_cores_detection": True} + return Config(launcher=LauncherConfig(local=LocalConfig.from_dict(data))) + @pytest.mark.unit_test def test_local_launcher__launcher_init_exception(): @@ -30,21 +39,12 @@ def test_local_launcher__launcher_init_exception(): @pytest.mark.unit_test -def test_compute(tmp_path: Path): - engine = create_engine("sqlite:///:memory:", echo=False) - Base.metadata.create_all(engine) - # noinspection SpellCheckingInspection - DBSessionMiddleware( - None, - custom_engine=engine, - session_args={"autocommit": False, "autoflush": False}, - ) - local_launcher = LocalLauncher(Config(), callbacks=Mock(), event_bus=Mock(), cache=Mock()) +def test_compute(tmp_path: Path, launcher_config: Config): + local_launcher = LocalLauncher(launcher_config, callbacks=Mock(), event_bus=Mock(), cache=Mock()) # prepare a dummy executable to simulate Antares Solver if os.name == "nt": - solver_name = "solver.bat" - solver_path = tmp_path.joinpath(solver_name) + solver_path = tmp_path.joinpath(SOLVER_NAME) solver_path.write_text( textwrap.dedent( """\ @@ -55,8 +55,7 @@ def test_compute(tmp_path: Path): ) ) else: - solver_name = "solver.sh" - solver_path = tmp_path.joinpath(solver_name) + solver_path = tmp_path.joinpath(SOLVER_NAME) solver_path.write_text( textwrap.dedent( """\ @@ -68,8 +67,8 @@ def test_compute(tmp_path: Path): ) solver_path.chmod(0o775) - uuid = uuid4() - local_launcher.job_id_to_study_id = {str(uuid): ("study-id", tmp_path / "run", Mock())} + study_id = uuid.uuid4() + local_launcher.job_id_to_study_id = {str(study_id): ("study-id", tmp_path / "run", Mock())} local_launcher.callbacks.import_output.return_value = "some output" launcher_parameters = LauncherParametersDTO( adequacy_patch=None, @@ -86,15 +85,15 @@ def test_compute(tmp_path: Path): local_launcher._compute( antares_solver_path=solver_path, study_uuid="study-id", - uuid=uuid, + uuid=study_id, launcher_parameters=launcher_parameters, ) # noinspection PyUnresolvedReferences local_launcher.callbacks.update_status.assert_has_calls( [ - call(str(uuid), JobStatus.RUNNING, None, None), - call(str(uuid), JobStatus.SUCCESS, None, "some output"), + call(str(study_id), JobStatus.RUNNING, None, None), + call(str(study_id), JobStatus.SUCCESS, None, "some output"), ] ) diff --git a/tests/launcher/test_service.py b/tests/launcher/test_service.py index 2c9f94d89c..a6177c5e61 100644 --- a/tests/launcher/test_service.py +++ b/tests/launcher/test_service.py @@ -3,15 +3,24 @@ import time from datetime import datetime, timedelta from pathlib import Path -from typing import Dict, List, Literal, Union +from typing import Dict, List, Union from unittest.mock import Mock, call, patch from uuid import uuid4 from zipfile import ZIP_DEFLATED, ZipFile import pytest from sqlalchemy import create_engine - -from antarest.core.config import Config, LauncherConfig, LocalConfig, SlurmConfig, StorageConfig +from typing_extensions import Literal + +from antarest.core.config import ( + Config, + InvalidConfigurationError, + LauncherConfig, + LocalConfig, + NbCoresConfig, + SlurmConfig, + StorageConfig, +) from antarest.core.exceptions import StudyNotFoundError from antarest.core.filetransfer.model import FileDownload, FileDownloadDTO, FileDownloadTaskDTO from antarest.core.interfaces.eventbus import Event, EventType @@ -20,7 +29,7 @@ from antarest.core.requests import RequestParameters, UserHasNotPermissionError from antarest.core.utils.fastapi_sqlalchemy import DBSessionMiddleware from antarest.dbmodel import Base -from antarest.launcher.model import JobLog, JobLogType, JobResult, JobStatus, LogType +from antarest.launcher.model import JobLog, JobLogType, JobResult, JobStatus, LauncherParametersDTO, LogType from antarest.launcher.service import ( EXECUTION_INFO_FILE, LAUNCHER_PARAM_NAME_SUFFIX, @@ -33,780 +42,908 @@ from antarest.study.model import OwnerInfo, PublicMode, Study, StudyMetadataDTO -@pytest.mark.unit_test -@patch.object(Auth, "get_current_user") -def test_service_run_study(get_current_user_mock): - get_current_user_mock.return_value = None - storage_service_mock = Mock() - storage_service_mock.get_study_information.return_value = StudyMetadataDTO( - id="id", - name="name", - created=1, - updated=1, - type="rawstudy", - owner=OwnerInfo(id=0, name="author"), - groups=[], - public_mode=PublicMode.NONE, - version=42, - workspace="default", - managed=True, - archived=False, - ) - storage_service_mock.get_study_path.return_value = Path("path/to/study") - - uuid = uuid4() - launcher_mock = Mock() - factory_launcher_mock = Mock() - factory_launcher_mock.build_launcher.return_value = {"local": launcher_mock} - - event_bus = Mock() - - pending = JobResult( - id=str(uuid), - study_id="study_uuid", - job_status=JobStatus.PENDING, - launcher="local", - ) - repository = Mock() - repository.save.return_value = pending - - launcher_service = LauncherService( - config=Config(), - study_service=storage_service_mock, - job_result_repository=repository, - factory_launcher=factory_launcher_mock, - event_bus=event_bus, - file_transfer_manager=Mock(), - task_service=Mock(), - cache=Mock(), - ) - launcher_service._generate_new_id = lambda: str(uuid) - - job_id = launcher_service.run_study( - "study_uuid", - "local", - None, - RequestParameters( - user=JWTUser( - id=0, - impersonator=0, - type="users", - ) - ), - ) - - assert job_id == str(uuid) - repository.save.assert_called_once_with(pending) - event_bus.push.assert_called_once_with( - Event( - type=EventType.STUDY_JOB_STARTED, - payload=pending.to_dto().dict(), - permissions=PermissionInfo(owner=0), +class TestLauncherService: + @pytest.mark.unit_test + @patch.object(Auth, "get_current_user") + def test_service_run_study(self, get_current_user_mock) -> None: + get_current_user_mock.return_value = None + storage_service_mock = Mock() + # noinspection SpellCheckingInspection + storage_service_mock.get_study_information.return_value = StudyMetadataDTO( + id="id", + name="name", + created="1", + updated="1", + type="rawstudy", + owner=OwnerInfo(id=0, name="author"), + groups=[], + public_mode=PublicMode.NONE, + version=42, + workspace="default", + managed=True, + archived=False, ) - ) + storage_service_mock.get_study_path.return_value = Path("path/to/study") + uuid = uuid4() + launcher_mock = Mock() + factory_launcher_mock = Mock() + factory_launcher_mock.build_launcher.return_value = {"local": launcher_mock} -@pytest.mark.unit_test -def test_service_get_result_from_launcher(): - launcher_mock = Mock() - fake_execution_result = JobResult( - id=str(uuid4()), - study_id="sid", - job_status=JobStatus.SUCCESS, - msg="Hello, World!", - exit_code=0, - launcher="local", - ) - factory_launcher_mock = Mock() - factory_launcher_mock.build_launcher.return_value = {"local": launcher_mock} - - repository = Mock() - repository.get.return_value = fake_execution_result - - study_service = Mock() - study_service.get_study.return_value = Mock(spec=Study, groups=[], owner=None, public_mode=PublicMode.NONE) - - launcher_service = LauncherService( - config=Config(), - study_service=study_service, - job_result_repository=repository, - factory_launcher=factory_launcher_mock, - event_bus=Mock(), - file_transfer_manager=Mock(), - task_service=Mock(), - cache=Mock(), - ) - - job_id = uuid4() - assert ( - launcher_service.get_result(job_uuid=job_id, params=RequestParameters(user=DEFAULT_ADMIN_USER)) - == fake_execution_result - ) + event_bus = Mock() + pending = JobResult( + id=str(uuid), + study_id="study_uuid", + job_status=JobStatus.PENDING, + launcher="local", + launcher_params=LauncherParametersDTO().json(), + ) + repository = Mock() + repository.save.return_value = pending + + launcher_service = LauncherService( + config=Config(), + study_service=storage_service_mock, + job_result_repository=repository, + factory_launcher=factory_launcher_mock, + event_bus=event_bus, + file_transfer_manager=Mock(), + task_service=Mock(), + cache=Mock(), + ) + launcher_service._generate_new_id = lambda: str(uuid) -@pytest.mark.unit_test -def test_service_get_result_from_database(): - launcher_mock = Mock() - fake_execution_result = JobResult( - id=str(uuid4()), - study_id="sid", - job_status=JobStatus.SUCCESS, - msg="Hello, World!", - exit_code=0, - ) - launcher_mock.get_result.return_value = None - factory_launcher_mock = Mock() - factory_launcher_mock.build_launcher.return_value = {"local": launcher_mock} - - repository = Mock() - repository.get.return_value = fake_execution_result - - study_service = Mock() - study_service.get_study.return_value = Mock(spec=Study, groups=[], owner=None, public_mode=PublicMode.NONE) - - launcher_service = LauncherService( - config=Config(), - study_service=study_service, - job_result_repository=repository, - factory_launcher=factory_launcher_mock, - event_bus=Mock(), - file_transfer_manager=Mock(), - task_service=Mock(), - cache=Mock(), - ) - - assert ( - launcher_service.get_result(job_uuid=uuid4(), params=RequestParameters(user=DEFAULT_ADMIN_USER)) - == fake_execution_result - ) + job_id = launcher_service.run_study( + "study_uuid", + "local", + LauncherParametersDTO(), + RequestParameters( + user=JWTUser( + id=0, + impersonator=0, + type="users", + ) + ), + ) + assert job_id == str(uuid) + repository.save.assert_called_once_with(pending) + event_bus.push.assert_called_once_with( + Event( + type=EventType.STUDY_JOB_STARTED, + payload=pending.to_dto().dict(), + permissions=PermissionInfo(owner=0), + ) + ) -@pytest.mark.unit_test -def test_service_get_jobs_from_database(): - launcher_mock = Mock() - now = datetime.utcnow() - fake_execution_result = [ - JobResult( + @pytest.mark.unit_test + def test_service_get_result_from_launcher(self) -> None: + launcher_mock = Mock() + fake_execution_result = JobResult( id=str(uuid4()), - study_id="a", + study_id="sid", job_status=JobStatus.SUCCESS, msg="Hello, World!", exit_code=0, + launcher="local", ) - ] - returned_faked_execution_results = [ - JobResult( - id="1", - study_id="a", - job_status=JobStatus.SUCCESS, - msg="Hello, World!", - exit_code=0, - creation_date=now, - ), - JobResult( - id="2", - study_id="b", - job_status=JobStatus.SUCCESS, - msg="Hello, World!", - exit_code=0, - creation_date=now, - ), - ] - all_faked_execution_results = returned_faked_execution_results + [ - JobResult( - id="3", - study_id="c", + factory_launcher_mock = Mock() + factory_launcher_mock.build_launcher.return_value = {"local": launcher_mock} + + repository = Mock() + repository.get.return_value = fake_execution_result + + study_service = Mock() + study_service.get_study.return_value = Mock(spec=Study, groups=[], owner=None, public_mode=PublicMode.NONE) + + launcher_service = LauncherService( + config=Config(), + study_service=study_service, + job_result_repository=repository, + factory_launcher=factory_launcher_mock, + event_bus=Mock(), + file_transfer_manager=Mock(), + task_service=Mock(), + cache=Mock(), + ) + + job_id = uuid4() + assert ( + launcher_service.get_result(job_uuid=job_id, params=RequestParameters(user=DEFAULT_ADMIN_USER)) + == fake_execution_result + ) + + @pytest.mark.unit_test + def test_service_get_result_from_database(self) -> None: + launcher_mock = Mock() + fake_execution_result = JobResult( + id=str(uuid4()), + study_id="sid", job_status=JobStatus.SUCCESS, msg="Hello, World!", exit_code=0, - creation_date=now - timedelta(days=ORPHAN_JOBS_VISIBILITY_THRESHOLD + 1), - ) - ] - launcher_mock.get_result.return_value = None - factory_launcher_mock = Mock() - factory_launcher_mock.build_launcher.return_value = {"local": launcher_mock} - - repository = Mock() - repository.find_by_study.return_value = fake_execution_result - repository.get_all.return_value = all_faked_execution_results - - study_service = Mock() - study_service.repository = Mock() - study_service.repository.get_list.return_value = [ - Mock( - spec=Study, - id="b", - groups=[], - owner=User(id=2), - public_mode=PublicMode.NONE, ) - ] - - launcher_service = LauncherService( - config=Config(), - study_service=study_service, - job_result_repository=repository, - factory_launcher=factory_launcher_mock, - event_bus=Mock(), - file_transfer_manager=Mock(), - task_service=Mock(), - cache=Mock(), - ) + launcher_mock.get_result.return_value = None + factory_launcher_mock = Mock() + factory_launcher_mock.build_launcher.return_value = {"local": launcher_mock} + + repository = Mock() + repository.get.return_value = fake_execution_result + + study_service = Mock() + study_service.get_study.return_value = Mock(spec=Study, groups=[], owner=None, public_mode=PublicMode.NONE) + + launcher_service = LauncherService( + config=Config(), + study_service=study_service, + job_result_repository=repository, + factory_launcher=factory_launcher_mock, + event_bus=Mock(), + file_transfer_manager=Mock(), + task_service=Mock(), + cache=Mock(), + ) - study_id = uuid4() - assert ( - launcher_service.get_jobs(str(study_id), params=RequestParameters(user=DEFAULT_ADMIN_USER)) - == fake_execution_result - ) - repository.find_by_study.assert_called_once_with(str(study_id)) - assert ( - launcher_service.get_jobs(None, params=RequestParameters(user=DEFAULT_ADMIN_USER)) - == all_faked_execution_results - ) - assert ( - launcher_service.get_jobs( - None, - params=RequestParameters( - user=JWTUser( - id=2, - impersonator=2, - type="users", - groups=[], - ) - ), + assert ( + launcher_service.get_result(job_uuid=uuid4(), params=RequestParameters(user=DEFAULT_ADMIN_USER)) + == fake_execution_result ) - == returned_faked_execution_results - ) - with pytest.raises(UserHasNotPermissionError): - launcher_service.remove_job( - "some job", - RequestParameters( - user=JWTUser( - id=2, - impersonator=2, - type="users", - groups=[], - ) + @pytest.mark.unit_test + def test_service_get_jobs_from_database(self) -> None: + launcher_mock = Mock() + now = datetime.utcnow() + fake_execution_result = [ + JobResult( + id=str(uuid4()), + study_id="a", + job_status=JobStatus.SUCCESS, + msg="Hello, World!", + exit_code=0, + ) + ] + returned_faked_execution_results = [ + JobResult( + id="1", + study_id="a", + job_status=JobStatus.SUCCESS, + msg="Hello, World!", + exit_code=0, + creation_date=now, + ), + JobResult( + id="2", + study_id="b", + job_status=JobStatus.SUCCESS, + msg="Hello, World!", + exit_code=0, + creation_date=now, ), + ] + all_faked_execution_results = returned_faked_execution_results + [ + JobResult( + id="3", + study_id="c", + job_status=JobStatus.SUCCESS, + msg="Hello, World!", + exit_code=0, + creation_date=now - timedelta(days=ORPHAN_JOBS_VISIBILITY_THRESHOLD + 1), + ) + ] + launcher_mock.get_result.return_value = None + factory_launcher_mock = Mock() + factory_launcher_mock.build_launcher.return_value = {"local": launcher_mock} + + repository = Mock() + repository.find_by_study.return_value = fake_execution_result + repository.get_all.return_value = all_faked_execution_results + + study_service = Mock() + study_service.repository = Mock() + study_service.repository.get_list.return_value = [ + Mock( + spec=Study, + id="b", + groups=[], + owner=User(id=2), + public_mode=PublicMode.NONE, + ) + ] + + launcher_service = LauncherService( + config=Config(), + study_service=study_service, + job_result_repository=repository, + factory_launcher=factory_launcher_mock, + event_bus=Mock(), + file_transfer_manager=Mock(), + task_service=Mock(), + cache=Mock(), ) - launcher_service.remove_job("some job", RequestParameters(user=DEFAULT_ADMIN_USER)) - repository.delete.assert_called_with("some job") + study_id = uuid4() + assert ( + launcher_service.get_jobs(str(study_id), params=RequestParameters(user=DEFAULT_ADMIN_USER)) + == fake_execution_result + ) + repository.find_by_study.assert_called_once_with(str(study_id)) + assert ( + launcher_service.get_jobs(None, params=RequestParameters(user=DEFAULT_ADMIN_USER)) + == all_faked_execution_results + ) + assert ( + launcher_service.get_jobs( + None, + params=RequestParameters( + user=JWTUser( + id=2, + impersonator=2, + type="users", + groups=[], + ) + ), + ) + == returned_faked_execution_results + ) + with pytest.raises(UserHasNotPermissionError): + launcher_service.remove_job( + "some job", + RequestParameters( + user=JWTUser( + id=2, + impersonator=2, + type="users", + groups=[], + ) + ), + ) -@pytest.mark.unit_test -@pytest.mark.parametrize( - "config, solver, expected", - [ - pytest.param( - { - "default": "local", - "local": [], - "slurm": [], - }, - "default", - [], - id="empty-config", - ), - pytest.param( - { - "default": "local", - "local": ["456", "123", "798"], - }, - "default", - ["123", "456", "798"], - id="local-config-default", - ), - pytest.param( - { - "default": "local", - "local": ["456", "123", "798"], - }, - "slurm", - [], - id="local-config-slurm", - ), - pytest.param( - { - "default": "local", - "local": ["456", "123", "798"], - }, - "unknown", - [], - id="local-config-unknown", - marks=pytest.mark.xfail( - reason="Unknown solver configuration: 'unknown'", - raises=KeyError, - strict=True, + launcher_service.remove_job("some job", RequestParameters(user=DEFAULT_ADMIN_USER)) + repository.delete.assert_called_with("some job") + + @pytest.mark.unit_test + @pytest.mark.parametrize( + "config, solver, expected", + [ + pytest.param( + { + "default": "local", + "local": [], + "slurm": [], + }, + "default", + [], + id="empty-config", ), - ), - pytest.param( - { - "default": "slurm", - "slurm": ["258", "147", "369"], - }, - "default", - ["147", "258", "369"], - id="slurm-config-default", - ), - pytest.param( - { - "default": "slurm", - "slurm": ["258", "147", "369"], - }, - "local", - [], - id="slurm-config-local", - ), - pytest.param( - { - "default": "slurm", - "slurm": ["258", "147", "369"], - }, - "unknown", - [], - id="slurm-config-unknown", - marks=pytest.mark.xfail( - reason="Unknown solver configuration: 'unknown'", - raises=KeyError, - strict=True, + pytest.param( + { + "default": "local", + "local": ["456", "123", "798"], + }, + "default", + ["123", "456", "798"], + id="local-config-default", ), - ), - pytest.param( - { - "default": "slurm", - "local": ["456", "123", "798"], - "slurm": ["258", "147", "369"], - }, - "local", - ["123", "456", "798"], - id="local+slurm-config-local", - ), - ], -) -def test_service_get_solver_versions( - config: Dict[str, Union[str, List[str]]], - solver: Literal["default", "local", "slurm", "unknown"], - expected: List[str], -) -> None: - # Prepare the configuration - default = config.get("default", "local") - local = LocalConfig(binaries={k: Path(f"solver-{k}.exe") for k in config.get("local", [])}) - slurm = SlurmConfig(antares_versions_on_remote_server=config.get("slurm", [])) - launcher_config = LauncherConfig( - default=default, - local=local if local else None, - slurm=slurm if slurm else None, - ) - config = Config(launcher=launcher_config) - launcher_service = LauncherService( - config=config, - study_service=Mock(), - job_result_repository=Mock(), - factory_launcher=Mock(), - event_bus=Mock(), - file_transfer_manager=Mock(), - task_service=Mock(), - cache=Mock(), + pytest.param( + { + "default": "local", + "local": ["456", "123", "798"], + }, + "slurm", + [], + id="local-config-slurm", + ), + pytest.param( + { + "default": "local", + "local": ["456", "123", "798"], + }, + "unknown", + [], + id="local-config-unknown", + marks=pytest.mark.xfail( + reason="Unknown solver configuration: 'unknown'", + raises=KeyError, + strict=True, + ), + ), + pytest.param( + { + "default": "slurm", + "slurm": ["258", "147", "369"], + }, + "default", + ["147", "258", "369"], + id="slurm-config-default", + ), + pytest.param( + { + "default": "slurm", + "slurm": ["258", "147", "369"], + }, + "local", + [], + id="slurm-config-local", + ), + pytest.param( + { + "default": "slurm", + "slurm": ["258", "147", "369"], + }, + "unknown", + [], + id="slurm-config-unknown", + marks=pytest.mark.xfail( + reason="Unknown solver configuration: 'unknown'", + raises=KeyError, + strict=True, + ), + ), + pytest.param( + { + "default": "slurm", + "local": ["456", "123", "798"], + "slurm": ["258", "147", "369"], + }, + "local", + ["123", "456", "798"], + id="local+slurm-config-local", + ), + ], ) + def test_service_get_solver_versions( + self, + config: Dict[str, Union[str, List[str]]], + solver: Literal["default", "local", "slurm", "unknown"], + expected: List[str], + ) -> None: + # Prepare the configuration + # the default server version from the configuration file. + # the default server is initialised to local + default = config.get("default", "local") + local = LocalConfig(binaries={k: Path(f"solver-{k}.exe") for k in config.get("local", [])}) + slurm = SlurmConfig(antares_versions_on_remote_server=config.get("slurm", [])) + launcher_config = LauncherConfig( + default=default, + local=local if local else None, + slurm=slurm if slurm else None, + ) + config = Config(launcher=launcher_config) + launcher_service = LauncherService( + config=config, + study_service=Mock(), + job_result_repository=Mock(), + factory_launcher=Mock(), + event_bus=Mock(), + file_transfer_manager=Mock(), + task_service=Mock(), + cache=Mock(), + ) - # Fetch the solver versions - actual = launcher_service.get_solver_versions(solver) + # Fetch the solver versions + actual = launcher_service.get_solver_versions(solver) + assert actual == expected - # Check the result - assert actual == expected + @pytest.mark.unit_test + @pytest.mark.parametrize( + "config_map, solver, expected", + [ + pytest.param( + {"default": "local", "local": {}, "slurm": {}}, + "default", + {}, + id="empty-config", + ), + pytest.param( + { + "default": "local", + "local": {"min": 1, "default": 11, "max": 12}, + }, + "default", + {"min": 1, "default": 11, "max": 12}, + id="local-config-default", + ), + pytest.param( + { + "default": "local", + "local": {"min": 1, "default": 11, "max": 12}, + }, + "slurm", + {}, + id="local-config-slurm", + ), + pytest.param( + { + "default": "local", + "local": {"min": 1, "default": 11, "max": 12}, + }, + "unknown", + {}, + id="local-config-unknown", + marks=pytest.mark.xfail( + reason="Configuration is not available for the 'unknown' launcher", + raises=InvalidConfigurationError, + strict=True, + ), + ), + pytest.param( + { + "default": "slurm", + "slurm": {"min": 4, "default": 8, "max": 16}, + }, + "default", + {"min": 4, "default": 8, "max": 16}, + id="slurm-config-default", + ), + pytest.param( + { + "default": "slurm", + "slurm": {"min": 4, "default": 8, "max": 16}, + }, + "local", + {}, + id="slurm-config-local", + ), + pytest.param( + { + "default": "slurm", + "slurm": {"min": 4, "default": 8, "max": 16}, + }, + "unknown", + {}, + id="slurm-config-unknown", + marks=pytest.mark.xfail( + reason="Configuration is not available for the 'unknown' launcher", + raises=InvalidConfigurationError, + strict=True, + ), + ), + pytest.param( + { + "default": "slurm", + "local": {"min": 1, "default": 11, "max": 12}, + "slurm": {"min": 4, "default": 8, "max": 16}, + }, + "local", + {"min": 1, "default": 11, "max": 12}, + id="local+slurm-config-local", + ), + ], + ) + def test_get_nb_cores( + self, + config_map: Dict[str, Union[str, Dict[str, int]]], + solver: Literal["default", "local", "slurm", "unknown"], + expected: Dict[str, int], + ) -> None: + # Prepare the configuration + default = config_map.get("default", "local") + local_nb_cores = config_map.get("local", {}) + slurm_nb_cores = config_map.get("slurm", {}) + launcher_config = LauncherConfig( + default=default, + local=LocalConfig.from_dict({"enable_nb_cores_detection": False, "nb_cores": local_nb_cores}), + slurm=SlurmConfig.from_dict({"enable_nb_cores_detection": False, "nb_cores": slurm_nb_cores}), + ) + launcher_service = LauncherService( + config=Config(launcher=launcher_config), + study_service=Mock(), + job_result_repository=Mock(), + factory_launcher=Mock(), + event_bus=Mock(), + file_transfer_manager=Mock(), + task_service=Mock(), + cache=Mock(), + ) + # Fetch the number of cores + actual = launcher_service.get_nb_cores(solver) + + # Check the result + assert actual == NbCoresConfig(**expected) + + @pytest.mark.unit_test + def test_service_kill_job(self, tmp_path: Path) -> None: + study_service = Mock() + study_service.get_study.return_value = Mock(spec=Study, groups=[], owner=None, public_mode=PublicMode.NONE) + + launcher_service = LauncherService( + config=Config(storage=StorageConfig(tmp_dir=tmp_path)), + study_service=study_service, + job_result_repository=Mock(), + event_bus=Mock(), + factory_launcher=Mock(), + file_transfer_manager=Mock(), + task_service=Mock(), + cache=Mock(), + ) + launcher = "slurm" + job_id = "job_id" + job_result_mock = Mock() + job_result_mock.id = job_id + job_result_mock.study_id = "study_id" + job_result_mock.launcher = launcher + launcher_service.job_result_repository.get.return_value = job_result_mock + launcher_service.launchers = {"slurm": Mock()} + + job_status = launcher_service.kill_job( + job_id=job_id, + params=RequestParameters(user=DEFAULT_ADMIN_USER), + ) -@pytest.mark.unit_test -def test_service_kill_job(tmp_path: Path): - study_service = Mock() - study_service.get_study.return_value = Mock(spec=Study, groups=[], owner=None, public_mode=PublicMode.NONE) + launcher_service.launchers[launcher].kill_job.assert_called_once_with(job_id=job_id) - launcher_service = LauncherService( - config=Config(storage=StorageConfig(tmp_dir=tmp_path)), - study_service=study_service, - job_result_repository=Mock(), - event_bus=Mock(), - factory_launcher=Mock(), - file_transfer_manager=Mock(), - task_service=Mock(), - cache=Mock(), - ) - launcher = "slurm" - job_id = "job_id" - job_result_mock = Mock() - job_result_mock.id = job_id - job_result_mock.study_id = "study_id" - job_result_mock.launcher = launcher - launcher_service.job_result_repository.get.return_value = job_result_mock - launcher_service.launchers = {"slurm": Mock()} - - job_status = launcher_service.kill_job( - job_id=job_id, - params=RequestParameters(user=DEFAULT_ADMIN_USER), - ) + assert job_status.job_status == JobStatus.FAILED + launcher_service.job_result_repository.save.assert_called_once_with(job_status) - launcher_service.launchers[launcher].kill_job.assert_called_once_with(job_id=job_id) + def test_append_logs(self, tmp_path: Path) -> None: + study_service = Mock() + study_service.get_study.return_value = Mock(spec=Study, groups=[], owner=None, public_mode=PublicMode.NONE) - assert job_status.job_status == JobStatus.FAILED - launcher_service.job_result_repository.save.assert_called_once_with(job_status) + launcher_service = LauncherService( + config=Config(storage=StorageConfig(tmp_dir=tmp_path)), + study_service=study_service, + job_result_repository=Mock(), + event_bus=Mock(), + factory_launcher=Mock(), + file_transfer_manager=Mock(), + task_service=Mock(), + cache=Mock(), + ) + launcher = "slurm" + job_id = "job_id" + job_result_mock = Mock() + job_result_mock.id = job_id + job_result_mock.study_id = "study_id" + job_result_mock.output_id = None + job_result_mock.launcher = launcher + job_result_mock.logs = [] + launcher_service.job_result_repository.get.return_value = job_result_mock + + engine = create_engine("sqlite:///:memory:", echo=False) + Base.metadata.create_all(engine) + # noinspection SpellCheckingInspection + DBSessionMiddleware( + None, + custom_engine=engine, + session_args={"autocommit": False, "autoflush": False}, + ) + launcher_service.append_log(job_id, "test", JobLogType.BEFORE) + launcher_service.job_result_repository.save.assert_called_with(job_result_mock) + assert job_result_mock.logs[0].message == "test" + assert job_result_mock.logs[0].job_id == "job_id" + assert job_result_mock.logs[0].log_type == str(JobLogType.BEFORE) + + def test_get_logs(self, tmp_path: Path) -> None: + study_service = Mock() + launcher_service = LauncherService( + config=Config(storage=StorageConfig(tmp_dir=tmp_path)), + study_service=study_service, + job_result_repository=Mock(), + event_bus=Mock(), + factory_launcher=Mock(), + file_transfer_manager=Mock(), + task_service=Mock(), + cache=Mock(), + ) + launcher = "slurm" + job_id = "job_id" + job_result_mock = Mock() + job_result_mock.id = job_id + job_result_mock.study_id = "study_id" + job_result_mock.output_id = None + job_result_mock.launcher = launcher + job_result_mock.logs = [ + JobLog(message="first message", log_type=str(JobLogType.BEFORE)), + JobLog(message="second message", log_type=str(JobLogType.BEFORE)), + JobLog(message="last message", log_type=str(JobLogType.AFTER)), + ] + job_result_mock.launcher_params = '{"archive_output": false}' + + launcher_service.job_result_repository.get.return_value = job_result_mock + slurm_launcher = Mock() + launcher_service.launchers = {"slurm": slurm_launcher} + slurm_launcher.get_log.return_value = "launcher logs" + + logs = launcher_service.get_log(job_id, LogType.STDOUT, RequestParameters(DEFAULT_ADMIN_USER)) + assert logs == "first message\nsecond message\nlauncher logs\nlast message" + logs = launcher_service.get_log(job_id, LogType.STDERR, RequestParameters(DEFAULT_ADMIN_USER)) + assert logs == "launcher logs" + + study_service.get_logs.side_effect = ["some sim log", "error log"] + + job_result_mock.output_id = "some id" + logs = launcher_service.get_log(job_id, LogType.STDOUT, RequestParameters(DEFAULT_ADMIN_USER)) + assert logs == "first message\nsecond message\nsome sim log\nlast message" + + logs = launcher_service.get_log(job_id, LogType.STDERR, RequestParameters(DEFAULT_ADMIN_USER)) + assert logs == "error log" + + study_service.get_logs.assert_has_calls( + [ + call( + "study_id", + "some id", + job_id, + False, + params=RequestParameters(DEFAULT_ADMIN_USER), + ), + call( + "study_id", + "some id", + job_id, + True, + params=RequestParameters(DEFAULT_ADMIN_USER), + ), + ] + ) + def test_manage_output(self, tmp_path: Path) -> None: + engine = create_engine("sqlite:///:memory:", echo=False) + Base.metadata.create_all(engine) + # noinspection SpellCheckingInspection + DBSessionMiddleware( + None, + custom_engine=engine, + session_args={"autocommit": False, "autoflush": False}, + ) -def test_append_logs(tmp_path: Path): - study_service = Mock() - study_service.get_study.return_value = Mock(spec=Study, groups=[], owner=None, public_mode=PublicMode.NONE) + study_service = Mock() + study_service.get_study.return_value = Mock(spec=Study, groups=[], owner=None, public_mode=PublicMode.NONE) + + launcher_service = LauncherService( + config=Mock(storage=StorageConfig(tmp_dir=tmp_path)), + study_service=study_service, + job_result_repository=Mock(), + event_bus=Mock(), + factory_launcher=Mock(), + file_transfer_manager=Mock(), + task_service=Mock(), + cache=Mock(), + ) - launcher_service = LauncherService( - config=Config(storage=StorageConfig(tmp_dir=tmp_path)), - study_service=study_service, - job_result_repository=Mock(), - event_bus=Mock(), - factory_launcher=Mock(), - file_transfer_manager=Mock(), - task_service=Mock(), - cache=Mock(), - ) - launcher = "slurm" - job_id = "job_id" - job_result_mock = Mock() - job_result_mock.id = job_id - job_result_mock.study_id = "study_id" - job_result_mock.output_id = None - job_result_mock.launcher = launcher - job_result_mock.logs = [] - launcher_service.job_result_repository.get.return_value = job_result_mock - - engine = create_engine("sqlite:///:memory:", echo=False) - Base.metadata.create_all(engine) - # noinspection SpellCheckingInspection - DBSessionMiddleware( - None, - custom_engine=engine, - session_args={"autocommit": False, "autoflush": False}, - ) - launcher_service.append_log(job_id, "test", JobLogType.BEFORE) - launcher_service.job_result_repository.save.assert_called_with(job_result_mock) - assert job_result_mock.logs[0].message == "test" - assert job_result_mock.logs[0].job_id == "job_id" - assert job_result_mock.logs[0].log_type == str(JobLogType.BEFORE) - - -def test_get_logs(tmp_path: Path): - study_service = Mock() - launcher_service = LauncherService( - config=Config(storage=StorageConfig(tmp_dir=tmp_path)), - study_service=study_service, - job_result_repository=Mock(), - event_bus=Mock(), - factory_launcher=Mock(), - file_transfer_manager=Mock(), - task_service=Mock(), - cache=Mock(), - ) - launcher = "slurm" - job_id = "job_id" - job_result_mock = Mock() - job_result_mock.id = job_id - job_result_mock.study_id = "study_id" - job_result_mock.output_id = None - job_result_mock.launcher = launcher - job_result_mock.logs = [ - JobLog(message="first message", log_type=str(JobLogType.BEFORE)), - JobLog(message="second message", log_type=str(JobLogType.BEFORE)), - JobLog(message="last message", log_type=str(JobLogType.AFTER)), - ] - job_result_mock.launcher_params = '{"archive_output": false}' - - launcher_service.job_result_repository.get.return_value = job_result_mock - slurm_launcher = Mock() - launcher_service.launchers = {"slurm": slurm_launcher} - slurm_launcher.get_log.return_value = "launcher logs" - - logs = launcher_service.get_log(job_id, LogType.STDOUT, RequestParameters(DEFAULT_ADMIN_USER)) - assert logs == "first message\nsecond message\nlauncher logs\nlast message" - logs = launcher_service.get_log(job_id, LogType.STDERR, RequestParameters(DEFAULT_ADMIN_USER)) - assert logs == "launcher logs" - - study_service.get_logs.side_effect = ["some sim log", "error log"] - - job_result_mock.output_id = "some id" - logs = launcher_service.get_log(job_id, LogType.STDOUT, RequestParameters(DEFAULT_ADMIN_USER)) - assert logs == "first message\nsecond message\nsome sim log\nlast message" - - logs = launcher_service.get_log(job_id, LogType.STDERR, RequestParameters(DEFAULT_ADMIN_USER)) - assert logs == "error log" - - study_service.get_logs.assert_has_calls( - [ - call( - "study_id", - "some id", - job_id, - False, - params=RequestParameters(DEFAULT_ADMIN_USER), + output_path = tmp_path / "output" + zipped_output_path = tmp_path / "zipped_output" + os.mkdir(output_path) + os.mkdir(zipped_output_path) + new_output_path = output_path / "new_output" + os.mkdir(new_output_path) + (new_output_path / "log").touch() + (new_output_path / "data").touch() + additional_log = tmp_path / "output.log" + additional_log.write_text("some log") + new_output_zipped_path = zipped_output_path / "test.zip" + with ZipFile(new_output_zipped_path, "w", ZIP_DEFLATED) as output_data: + output_data.writestr("some output", "0\n1") + job_id = "job_id" + zipped_job_id = "zipped_job_id" + study_id = "study_id" + launcher_service.job_result_repository.get.side_effect = [ + None, + JobResult(id=job_id, study_id=study_id), + JobResult(id=job_id, study_id=study_id, output_id="some id"), + JobResult(id=zipped_job_id, study_id=study_id), + JobResult( + id=job_id, + study_id=study_id, ), - call( - "study_id", - "some id", - job_id, - True, - params=RequestParameters(DEFAULT_ADMIN_USER), + JobResult( + id=job_id, + study_id=study_id, + launcher_params=json.dumps( + { + "archive_output": False, + f"{LAUNCHER_PARAM_NAME_SUFFIX}": "hello", + } + ), ), ] - ) + with pytest.raises(JobNotFound): + launcher_service._import_output(job_id, output_path, {"out.log": [additional_log]}) - -def test_manage_output(tmp_path: Path): - engine = create_engine("sqlite:///:memory:", echo=False) - Base.metadata.create_all(engine) - # noinspection SpellCheckingInspection - DBSessionMiddleware( - None, - custom_engine=engine, - session_args={"autocommit": False, "autoflush": False}, - ) - - study_service = Mock() - study_service.get_study.return_value = Mock(spec=Study, groups=[], owner=None, public_mode=PublicMode.NONE) - - launcher_service = LauncherService( - config=Mock(storage=StorageConfig(tmp_dir=tmp_path)), - study_service=study_service, - job_result_repository=Mock(), - event_bus=Mock(), - factory_launcher=Mock(), - file_transfer_manager=Mock(), - task_service=Mock(), - cache=Mock(), - ) - - output_path = tmp_path / "output" - zipped_output_path = tmp_path / "zipped_output" - os.mkdir(output_path) - os.mkdir(zipped_output_path) - new_output_path = output_path / "new_output" - os.mkdir(new_output_path) - (new_output_path / "log").touch() - (new_output_path / "data").touch() - additional_log = tmp_path / "output.log" - additional_log.write_text("some log") - new_output_zipped_path = zipped_output_path / "test.zip" - with ZipFile(new_output_zipped_path, "w", ZIP_DEFLATED) as output_data: - output_data.writestr("some output", "0\n1") - job_id = "job_id" - zipped_job_id = "zipped_job_id" - study_id = "study_id" - launcher_service.job_result_repository.get.side_effect = [ - None, - JobResult(id=job_id, study_id=study_id), - JobResult(id=job_id, study_id=study_id, output_id="some id"), - JobResult(id=zipped_job_id, study_id=study_id), - JobResult( - id=job_id, - study_id=study_id, - ), - JobResult( - id=job_id, - study_id=study_id, - launcher_params=json.dumps( - { - "archive_output": False, - f"{LAUNCHER_PARAM_NAME_SUFFIX}": "hello", - } - ), - ), - ] - with pytest.raises(JobNotFound): launcher_service._import_output(job_id, output_path, {"out.log": [additional_log]}) + assert not launcher_service._get_job_output_fallback_path(job_id).exists() + launcher_service.study_service.import_output.assert_called() - launcher_service._import_output(job_id, output_path, {"out.log": [additional_log]}) - assert not launcher_service._get_job_output_fallback_path(job_id).exists() - launcher_service.study_service.import_output.assert_called() - - launcher_service.download_output("job_id", RequestParameters(DEFAULT_ADMIN_USER)) - launcher_service.study_service.export_output.assert_called() - - launcher_service._import_output( - zipped_job_id, - zipped_output_path, - { - "out.log": [additional_log], - "antares-out": [additional_log], - "antares-err": [additional_log], - }, - ) - launcher_service.study_service.save_logs.has_calls( - [ - call(study_id, zipped_job_id, "out.log", "some log"), - call(study_id, zipped_job_id, "out", "some log"), - call(study_id, zipped_job_id, "err", "some log"), - ] - ) - - launcher_service.study_service.import_output.side_effect = [ - StudyNotFoundError(""), - StudyNotFoundError(""), - ] - - assert launcher_service._import_output(job_id, output_path, {"out.log": [additional_log]}) is None - - (new_output_path / "info.antares-output").write_text(f"[general]\nmode=eco\nname=foo\ntimestamp={time.time()}") - output_name = launcher_service._import_output(job_id, output_path, {"out.log": [additional_log]}) - assert output_name is not None - assert output_name.endswith("-hello") - assert launcher_service._get_job_output_fallback_path(job_id).exists() - assert (launcher_service._get_job_output_fallback_path(job_id) / output_name / "out.log").exists() - - launcher_service.job_result_repository.get.reset_mock() - launcher_service.job_result_repository.get.side_effect = [ - None, - JobResult(id=job_id, study_id=study_id, output_id=output_name), - ] - with pytest.raises(JobNotFound): launcher_service.download_output("job_id", RequestParameters(DEFAULT_ADMIN_USER)) + launcher_service.study_service.export_output.assert_called() - study_service.get_study.reset_mock() - study_service.get_study.side_effect = StudyNotFoundError("") + launcher_service._import_output( + zipped_job_id, + zipped_output_path, + { + "out.log": [additional_log], + "antares-out": [additional_log], + "antares-err": [additional_log], + }, + ) + launcher_service.study_service.save_logs.has_calls( + [ + call(study_id, zipped_job_id, "out.log", "some log"), + call(study_id, zipped_job_id, "out", "some log"), + call(study_id, zipped_job_id, "err", "some log"), + ] + ) - export_file = FileDownloadDTO(id="a", name="a", filename="a", ready=True) - launcher_service.file_transfer_manager.request_download.return_value = FileDownload( - id="a", name="a", filename="a", ready=True, path="a" - ) - launcher_service.task_service.add_task.return_value = "some id" + launcher_service.study_service.import_output.side_effect = [ + StudyNotFoundError(""), + StudyNotFoundError(""), + ] - assert launcher_service.download_output("job_id", RequestParameters(DEFAULT_ADMIN_USER)) == FileDownloadTaskDTO( - task="some id", file=export_file - ) + assert launcher_service._import_output(job_id, output_path, {"out.log": [additional_log]}) is None - launcher_service.remove_job(job_id, RequestParameters(user=DEFAULT_ADMIN_USER)) - assert not launcher_service._get_job_output_fallback_path(job_id).exists() + (new_output_path / "info.antares-output").write_text(f"[general]\nmode=eco\nname=foo\ntimestamp={time.time()}") + output_name = launcher_service._import_output(job_id, output_path, {"out.log": [additional_log]}) + assert output_name is not None + assert output_name.endswith("-hello") + assert launcher_service._get_job_output_fallback_path(job_id).exists() + assert (launcher_service._get_job_output_fallback_path(job_id) / output_name / "out.log").exists() + launcher_service.job_result_repository.get.reset_mock() + launcher_service.job_result_repository.get.side_effect = [ + None, + JobResult(id=job_id, study_id=study_id, output_id=output_name), + ] + with pytest.raises(JobNotFound): + launcher_service.download_output("job_id", RequestParameters(DEFAULT_ADMIN_USER)) -def test_save_stats(tmp_path: Path) -> None: - study_service = Mock() - study_service.get_study.return_value = Mock(spec=Study, groups=[], owner=None, public_mode=PublicMode.NONE) + study_service.get_study.reset_mock() + study_service.get_study.side_effect = StudyNotFoundError("") - launcher_service = LauncherService( - config=Mock(storage=StorageConfig(tmp_dir=tmp_path)), - study_service=study_service, - job_result_repository=Mock(), - event_bus=Mock(), - factory_launcher=Mock(), - file_transfer_manager=Mock(), - task_service=Mock(), - cache=Mock(), - ) + export_file = FileDownloadDTO(id="a", name="a", filename="a", ready=True) + launcher_service.file_transfer_manager.request_download.return_value = FileDownload( + id="a", name="a", filename="a", ready=True, path="a" + ) + launcher_service.task_service.add_task.return_value = "some id" - job_id = "job_id" - study_id = "study_id" - job_result = JobResult(id=job_id, study_id=study_id, job_status=JobStatus.SUCCESS) - - output_path = tmp_path / "some-output" - output_path.mkdir() - - launcher_service._save_solver_stats(job_result, output_path) - launcher_service.job_result_repository.save.assert_not_called() - - expected_saved_stats = """#item duration_ms NbOccurences -mc_years 216328 1 -study_loading 4304 1 -survey_report 158 1 -total 244581 1 -tsgen_hydro 1683 1 -tsgen_load 2702 1 -tsgen_solar 21606 1 -tsgen_thermal 407 2 -tsgen_wind 2500 1 - """ - (output_path / EXECUTION_INFO_FILE).write_text(expected_saved_stats) - - launcher_service._save_solver_stats(job_result, output_path) - launcher_service.job_result_repository.save.assert_called_with( - JobResult( - id=job_id, - study_id=study_id, - job_status=JobStatus.SUCCESS, - solver_stats=expected_saved_stats, + assert launcher_service.download_output("job_id", RequestParameters(DEFAULT_ADMIN_USER)) == FileDownloadTaskDTO( + task="some id", file=export_file ) - ) - zip_file = tmp_path / "test.zip" - with ZipFile(zip_file, "w", ZIP_DEFLATED) as output_data: - output_data.writestr(EXECUTION_INFO_FILE, "0\n1") + launcher_service.remove_job(job_id, RequestParameters(user=DEFAULT_ADMIN_USER)) + assert not launcher_service._get_job_output_fallback_path(job_id).exists() + + def test_save_solver_stats(self, tmp_path: Path) -> None: + study_service = Mock() + study_service.get_study.return_value = Mock(spec=Study, groups=[], owner=None, public_mode=PublicMode.NONE) + + launcher_service = LauncherService( + config=Mock(storage=StorageConfig(tmp_dir=tmp_path)), + study_service=study_service, + job_result_repository=Mock(), + event_bus=Mock(), + factory_launcher=Mock(), + file_transfer_manager=Mock(), + task_service=Mock(), + cache=Mock(), + ) - launcher_service._save_solver_stats(job_result, zip_file) - launcher_service.job_result_repository.save.assert_called_with( - JobResult( - id=job_id, - study_id=study_id, - job_status=JobStatus.SUCCESS, - solver_stats="0\n1", + job_id = "job_id" + study_id = "study_id" + job_result = JobResult(id=job_id, study_id=study_id, job_status=JobStatus.SUCCESS) + + output_path = tmp_path / "some-output" + output_path.mkdir() + + launcher_service._save_solver_stats(job_result, output_path) + launcher_service.job_result_repository.save.assert_not_called() + + expected_saved_stats = """#item duration_ms NbOccurences + mc_years 216328 1 + study_loading 4304 1 + survey_report 158 1 + total 244581 1 + tsgen_hydro 1683 1 + tsgen_load 2702 1 + tsgen_solar 21606 1 + tsgen_thermal 407 2 + tsgen_wind 2500 1 + """ + (output_path / EXECUTION_INFO_FILE).write_text(expected_saved_stats) + + launcher_service._save_solver_stats(job_result, output_path) + launcher_service.job_result_repository.save.assert_called_with( + JobResult( + id=job_id, + study_id=study_id, + job_status=JobStatus.SUCCESS, + solver_stats=expected_saved_stats, + ) ) - ) + zip_file = tmp_path / "test.zip" + with ZipFile(zip_file, "w", ZIP_DEFLATED) as output_data: + output_data.writestr(EXECUTION_INFO_FILE, "0\n1") + + launcher_service._save_solver_stats(job_result, zip_file) + launcher_service.job_result_repository.save.assert_called_with( + JobResult( + id=job_id, + study_id=study_id, + job_status=JobStatus.SUCCESS, + solver_stats="0\n1", + ) + ) -def test_get_load(tmp_path: Path): - study_service = Mock() - job_repository = Mock() + def test_get_load(self, tmp_path: Path) -> None: + study_service = Mock() + job_repository = Mock() - launcher_service = LauncherService( - config=Mock( + config = Config( storage=StorageConfig(tmp_dir=tmp_path), - launcher=LauncherConfig(local=LocalConfig(), slurm=SlurmConfig(default_n_cpu=12)), - ), - study_service=study_service, - job_result_repository=job_repository, - event_bus=Mock(), - factory_launcher=Mock(), - file_transfer_manager=Mock(), - task_service=Mock(), - cache=Mock(), - ) - - job_repository.get_running.side_effect = [ - [], - [], - [ - Mock( - spec=JobResult, - launcher="slurm", - launcher_params=None, - ), - ], - [ - Mock( - spec=JobResult, - launcher="slurm", - launcher_params='{"nb_cpu": 18}', - ), - Mock( - spec=JobResult, - launcher="local", - launcher_params=None, - ), - Mock( - spec=JobResult, - launcher="slurm", - launcher_params=None, + launcher=LauncherConfig( + local=LocalConfig(), + slurm=SlurmConfig(nb_cores=NbCoresConfig(min=1, default=12, max=24)), ), - Mock( - spec=JobResult, - launcher="local", - launcher_params='{"nb_cpu": 7}', - ), - ], - ] - - with pytest.raises(NotImplementedError): - launcher_service.get_load(from_cluster=True) - - load = launcher_service.get_load() - assert load["slurm"] == 0 - assert load["local"] == 0 - load = launcher_service.get_load() - assert load["slurm"] == 12.0 / 64 - assert load["local"] == 0 - load = launcher_service.get_load() - assert load["slurm"] == 30.0 / 64 - assert load["local"] == 8.0 / os.cpu_count() + ) + launcher_service = LauncherService( + config=config, + study_service=study_service, + job_result_repository=job_repository, + event_bus=Mock(), + factory_launcher=Mock(), + file_transfer_manager=Mock(), + task_service=Mock(), + cache=Mock(), + ) + + job_repository.get_running.side_effect = [ + # call #1 + [], + # call #2 + [], + # call #3 + [ + Mock( + spec=JobResult, + launcher="slurm", + launcher_params=None, + ), + ], + # call #4 + [ + Mock( + spec=JobResult, + launcher="slurm", + launcher_params='{"nb_cpu": 18}', + ), + Mock( + spec=JobResult, + launcher="local", + launcher_params=None, + ), + Mock( + spec=JobResult, + launcher="slurm", + launcher_params=None, + ), + Mock( + spec=JobResult, + launcher="local", + launcher_params='{"nb_cpu": 7}', + ), + ], + ] + + # call #1 + with pytest.raises(NotImplementedError): + launcher_service.get_load(from_cluster=True) + + # call #2 + load = launcher_service.get_load() + assert load["slurm"] == 0 + assert load["local"] == 0 + + # call #3 + load = launcher_service.get_load() + slurm_config = config.launcher.slurm + assert load["slurm"] == slurm_config.nb_cores.default / slurm_config.max_cores + assert load["local"] == 0 + + # call #4 + load = launcher_service.get_load() + local_config = config.launcher.local + assert load["slurm"] == (18 + slurm_config.nb_cores.default) / slurm_config.max_cores + assert load["local"] == (7 + local_config.nb_cores.default) / local_config.nb_cores.max diff --git a/tests/launcher/test_slurm_launcher.py b/tests/launcher/test_slurm_launcher.py index 7820abcdea..dfb6846e89 100644 --- a/tests/launcher/test_slurm_launcher.py +++ b/tests/launcher/test_slurm_launcher.py @@ -10,11 +10,9 @@ from antareslauncher.data_repo.data_repo_tinydb import DataRepoTinydb from antareslauncher.main import MainParameters from antareslauncher.study_dto import StudyDTO -from sqlalchemy import create_engine +from sqlalchemy.orm import Session # type: ignore -from antarest.core.config import Config, LauncherConfig, SlurmConfig -from antarest.core.persistence import Base -from antarest.core.utils.fastapi_sqlalchemy import DBSessionMiddleware +from antarest.core.config import Config, LauncherConfig, NbCoresConfig, SlurmConfig from antarest.launcher.adapters.abstractlauncher import LauncherInitException from antarest.launcher.adapters.slurm_launcher.slurm_launcher import ( LOG_DIR_NAME, @@ -24,32 +22,34 @@ SlurmLauncher, VersionNotSupportedError, ) -from antarest.launcher.model import JobStatus, LauncherParametersDTO +from antarest.launcher.model import JobStatus, LauncherParametersDTO, XpansionParametersDTO from antarest.tools.admin_lib import clean_locks_from_config @pytest.fixture def launcher_config(tmp_path: Path) -> Config: - return Config( - launcher=LauncherConfig( - slurm=SlurmConfig( - local_workspace=tmp_path, - default_json_db_name="default_json_db_name", - slurm_script_path="slurm_script_path", - antares_versions_on_remote_server=["42", "45"], - username="username", - hostname="hostname", - port=42, - private_key_file=Path("private_key_file"), - key_password="key_password", - password="password", - ) - ) - ) + data = { + "local_workspace": tmp_path, + "username": "john", + "hostname": "slurm-001", + "port": 22, + "private_key_file": Path("/home/john/.ssh/id_rsa"), + "key_password": "password", + "password": "password", + "default_wait_time": 10, + "default_time_limit": 20, + "default_json_db_name": "antares.db", + "slurm_script_path": "/path/to/slurm/launcher.sh", + "max_cores": 32, + "antares_versions_on_remote_server": ["840", "850", "860"], + "enable_nb_cores_detection": False, + "nb_cores": {"min": 1, "default": 34, "max": 36}, + } + return Config(launcher=LauncherConfig(slurm=SlurmConfig.from_dict(data))) @pytest.mark.unit_test -def test_slurm_launcher__launcher_init_exception(): +def test_slurm_launcher__launcher_init_exception() -> None: with pytest.raises( LauncherInitException, match="Missing parameter 'launcher.slurm'", @@ -63,13 +63,13 @@ def test_slurm_launcher__launcher_init_exception(): @pytest.mark.unit_test -def test_init_slurm_launcher_arguments(tmp_path: Path): +def test_init_slurm_launcher_arguments(tmp_path: Path) -> None: config = Config( launcher=LauncherConfig( slurm=SlurmConfig( default_wait_time=42, default_time_limit=43, - default_n_cpu=44, + nb_cores=NbCoresConfig(min=1, default=30, max=36), local_workspace=tmp_path, ) ) @@ -88,13 +88,15 @@ def test_init_slurm_launcher_arguments(tmp_path: Path): assert not arguments.xpansion_mode assert not arguments.version assert not arguments.post_processing - assert Path(arguments.studies_in) == config.launcher.slurm.local_workspace / "STUDIES_IN" - assert Path(arguments.output_dir) == config.launcher.slurm.local_workspace / "OUTPUT" - assert Path(arguments.log_dir) == config.launcher.slurm.local_workspace / "LOGS" + slurm_config = config.launcher.slurm + assert slurm_config is not None + assert Path(arguments.studies_in) == slurm_config.local_workspace / "STUDIES_IN" + assert Path(arguments.output_dir) == slurm_config.local_workspace / "OUTPUT" + assert Path(arguments.log_dir) == slurm_config.local_workspace / "LOGS" @pytest.mark.unit_test -def test_init_slurm_launcher_parameters(tmp_path: Path): +def test_init_slurm_launcher_parameters(tmp_path: Path) -> None: config = Config( launcher=LauncherConfig( slurm=SlurmConfig( @@ -115,23 +117,25 @@ def test_init_slurm_launcher_parameters(tmp_path: Path): slurm_launcher = SlurmLauncher(config=config, callbacks=Mock(), event_bus=Mock(), cache=Mock()) main_parameters = slurm_launcher._init_launcher_parameters() - assert main_parameters.json_dir == config.launcher.slurm.local_workspace - assert main_parameters.default_json_db_name == config.launcher.slurm.default_json_db_name - assert main_parameters.slurm_script_path == config.launcher.slurm.slurm_script_path - assert main_parameters.antares_versions_on_remote_server == config.launcher.slurm.antares_versions_on_remote_server + slurm_config = config.launcher.slurm + assert slurm_config is not None + assert main_parameters.json_dir == slurm_config.local_workspace + assert main_parameters.default_json_db_name == slurm_config.default_json_db_name + assert main_parameters.slurm_script_path == slurm_config.slurm_script_path + assert main_parameters.antares_versions_on_remote_server == slurm_config.antares_versions_on_remote_server assert main_parameters.default_ssh_dict == { - "username": config.launcher.slurm.username, - "hostname": config.launcher.slurm.hostname, - "port": config.launcher.slurm.port, - "private_key_file": config.launcher.slurm.private_key_file, - "key_password": config.launcher.slurm.key_password, - "password": config.launcher.slurm.password, + "username": slurm_config.username, + "hostname": slurm_config.hostname, + "port": slurm_config.port, + "private_key_file": slurm_config.private_key_file, + "key_password": slurm_config.key_password, + "password": slurm_config.password, } assert main_parameters.db_primary_key == "name" @pytest.mark.unit_test -def test_slurm_launcher_delete_function(tmp_path: str): +def test_slurm_launcher_delete_function(tmp_path: str) -> None: config = Config(launcher=LauncherConfig(slurm=SlurmConfig(local_workspace=Path(tmp_path)))) slurm_launcher = SlurmLauncher( config=config, @@ -155,64 +159,104 @@ def test_slurm_launcher_delete_function(tmp_path: str): assert not file_path.exists() -def test_extra_parameters(launcher_config: Config): +def test_extra_parameters(launcher_config: Config) -> None: + """ + The goal of this unit test is to control the protected method `_check_and_apply_launcher_params`, + which is called by the `SlurmLauncher.run_study` function, in a separate thread. + + The `_check_and_apply_launcher_params` method extract the parameters from the configuration + and populate a `argparse.Namespace` which is used to launch a simulation using Antares Launcher. + + We want to make sure all the parameters are populated correctly. + """ slurm_launcher = SlurmLauncher( config=launcher_config, callbacks=Mock(), event_bus=Mock(), cache=Mock(), ) - launcher_params = slurm_launcher._check_and_apply_launcher_params(LauncherParametersDTO()) - assert launcher_params.n_cpu == 1 - assert launcher_params.time_limit == 0 + + apply_params = slurm_launcher._apply_params + launcher_params = apply_params(LauncherParametersDTO()) + slurm_config = slurm_launcher.config.launcher.slurm + assert slurm_config is not None + assert launcher_params.n_cpu == slurm_config.nb_cores.default + assert launcher_params.time_limit == slurm_config.default_time_limit assert not launcher_params.xpansion_mode assert not launcher_params.post_processing - launcher_params = slurm_launcher._check_and_apply_launcher_params(LauncherParametersDTO(nb_cpu=12)) + launcher_params = apply_params(LauncherParametersDTO(other_options="")) + assert launcher_params.other_options == "" + + launcher_params = apply_params(LauncherParametersDTO(other_options="foo\tbar baz ")) + assert launcher_params.other_options == "foo bar baz" + + launcher_params = apply_params(LauncherParametersDTO(other_options="/foo?bar")) + assert launcher_params.other_options == "foobar" + + launcher_params = apply_params(LauncherParametersDTO(nb_cpu=12)) assert launcher_params.n_cpu == 12 - launcher_params = slurm_launcher._check_and_apply_launcher_params(LauncherParametersDTO(nb_cpu=48)) - assert launcher_params.n_cpu == 1 + launcher_params = apply_params(LauncherParametersDTO(nb_cpu=999)) + assert launcher_params.n_cpu == slurm_config.nb_cores.default # out of range - launcher_params = slurm_launcher._check_and_apply_launcher_params(LauncherParametersDTO(time_limit=10)) + launcher_params = apply_params(LauncherParametersDTO(time_limit=10)) assert launcher_params.time_limit == MIN_TIME_LIMIT - launcher_params = slurm_launcher._check_and_apply_launcher_params(LauncherParametersDTO(time_limit=999999999)) + launcher_params = apply_params(LauncherParametersDTO(time_limit=999999999)) assert launcher_params.time_limit == MAX_TIME_LIMIT - 3600 - launcher_params = slurm_launcher._check_and_apply_launcher_params(LauncherParametersDTO(time_limit=99999)) + launcher_params = apply_params(LauncherParametersDTO(time_limit=99999)) assert launcher_params.time_limit == 99999 - launcher_params = slurm_launcher._check_and_apply_launcher_params(LauncherParametersDTO(xpansion=True)) - assert launcher_params.xpansion_mode + launcher_params = apply_params(LauncherParametersDTO(xpansion=False)) + assert launcher_params.xpansion_mode is None + assert launcher_params.other_options == "" + + launcher_params = apply_params(LauncherParametersDTO(xpansion=True)) + assert launcher_params.xpansion_mode == "cpp" + assert launcher_params.other_options == "" + + launcher_params = apply_params(LauncherParametersDTO(xpansion=True, xpansion_r_version=True)) + assert launcher_params.xpansion_mode == "r" + assert launcher_params.other_options == "" + + launcher_params = apply_params(LauncherParametersDTO(xpansion=XpansionParametersDTO(sensitivity_mode=False))) + assert launcher_params.xpansion_mode == "cpp" + assert launcher_params.other_options == "" + + launcher_params = apply_params(LauncherParametersDTO(xpansion=XpansionParametersDTO(sensitivity_mode=True))) + assert launcher_params.xpansion_mode == "cpp" + assert launcher_params.other_options == "xpansion_sensitivity" + + launcher_params = apply_params(LauncherParametersDTO(post_processing=False)) + assert launcher_params.post_processing is False - launcher_params = slurm_launcher._check_and_apply_launcher_params(LauncherParametersDTO(post_processing=True)) - assert launcher_params.post_processing + launcher_params = apply_params(LauncherParametersDTO(post_processing=True)) + assert launcher_params.post_processing is True - launcher_params = slurm_launcher._check_and_apply_launcher_params(LauncherParametersDTO(adequacy_patch={})) - assert launcher_params.post_processing + launcher_params = apply_params(LauncherParametersDTO(adequacy_patch={})) + assert launcher_params.post_processing is True # noinspection PyUnresolvedReferences @pytest.mark.parametrize( - "version, job_status", - [(42, JobStatus.RUNNING), (99, JobStatus.FAILED), (45, JobStatus.FAILED)], + "version, launcher_called, job_status", + [ + (840, True, JobStatus.RUNNING), + (860, False, JobStatus.FAILED), + pytest.param( + 999, False, JobStatus.FAILED, marks=pytest.mark.xfail(raises=VersionNotSupportedError, strict=True) + ), + ], ) @pytest.mark.unit_test def test_run_study( - tmp_path: Path, launcher_config: Config, version: int, + launcher_called: bool, job_status: JobStatus, -): - engine = create_engine("sqlite:///:memory:", echo=False) - Base.metadata.create_all(engine) - # noinspection SpellCheckingInspection - DBSessionMiddleware( - None, - custom_engine=engine, - session_args={"autocommit": False, "autoflush": False}, - ) +) -> None: slurm_launcher = SlurmLauncher( config=launcher_config, callbacks=Mock(), @@ -231,7 +275,8 @@ def test_run_study( job_id = str(uuid.uuid4()) study_dir = argument.studies_in / job_id study_dir.mkdir(parents=True) - (study_dir / "study.antares").write_text( + study_antares_path = study_dir.joinpath("study.antares") + study_antares_path.write_text( textwrap.dedent( """\ [antares] @@ -242,22 +287,20 @@ def test_run_study( # noinspection PyUnusedLocal def call_launcher_mock(arguments: Namespace, parameters: MainParameters): - if version != 45: + if launcher_called: slurm_launcher.data_repo_tinydb.save_study(StudyDTO(job_id)) slurm_launcher._call_launcher = call_launcher_mock - if version == 99: - with pytest.raises(VersionNotSupportedError): - slurm_launcher._run_study(study_uuid, job_id, LauncherParametersDTO(), str(version)) - else: - slurm_launcher._run_study(study_uuid, job_id, LauncherParametersDTO(), str(version)) + # When the launcher is called + slurm_launcher._run_study(study_uuid, job_id, LauncherParametersDTO(), str(version)) + # Check the results assert ( version not in launcher_config.launcher.slurm.antares_versions_on_remote_server - or f"solver_version = {version}" in (study_dir / "study.antares").read_text(encoding="utf-8") + or f"solver_version = {version}" in study_antares_path.read_text(encoding="utf-8") ) - # slurm_launcher._clean_local_workspace.assert_called_once() + slurm_launcher.callbacks.export_study.assert_called_once() slurm_launcher.callbacks.update_status.assert_called_once_with(ANY, job_status, ANY, None) if job_status == JobStatus.RUNNING: @@ -266,7 +309,7 @@ def call_launcher_mock(arguments: Namespace, parameters: MainParameters): @pytest.mark.unit_test -def test_check_state(tmp_path: Path, launcher_config: Config): +def test_check_state(tmp_path: Path, launcher_config: Config) -> None: slurm_launcher = SlurmLauncher( config=launcher_config, callbacks=Mock(), @@ -308,16 +351,7 @@ def test_check_state(tmp_path: Path, launcher_config: Config): @pytest.mark.unit_test -def test_clean_local_workspace(tmp_path: Path, launcher_config: Config): - engine = create_engine("sqlite:///:memory:", echo=False) - Base.metadata.create_all(engine) - # noinspection SpellCheckingInspection - DBSessionMiddleware( - None, - custom_engine=engine, - session_args={"autocommit": False, "autoflush": False}, - ) - +def test_clean_local_workspace(tmp_path: Path, launcher_config: Config) -> None: slurm_launcher = SlurmLauncher( config=launcher_config, callbacks=Mock(), @@ -325,7 +359,6 @@ def test_clean_local_workspace(tmp_path: Path, launcher_config: Config): use_private_workspace=False, cache=Mock(), ) - (launcher_config.launcher.slurm.local_workspace / "machin.txt").touch() assert os.listdir(launcher_config.launcher.slurm.local_workspace) @@ -335,7 +368,7 @@ def test_clean_local_workspace(tmp_path: Path, launcher_config: Config): # noinspection PyUnresolvedReferences @pytest.mark.unit_test -def test_import_study_output(launcher_config, tmp_path): +def test_import_study_output(launcher_config, tmp_path) -> None: slurm_launcher = SlurmLauncher( config=launcher_config, callbacks=Mock(), @@ -399,7 +432,7 @@ def test_kill_job( run_with_mock, tmp_path: Path, launcher_config: Config, -): +) -> None: launch_id = "launch_id" mock_study = Mock() mock_study.name = launch_id @@ -419,35 +452,36 @@ def test_kill_job( slurm_launcher.kill_job(job_id=launch_id) + slurm_config = launcher_config.launcher.slurm launcher_arguments = Namespace( antares_version=0, check_queue=False, - job_id_to_kill=42, + job_id_to_kill=mock_study.job_id, json_ssh_config=None, log_dir=str(tmp_path / "LOGS"), - n_cpu=1, + n_cpu=slurm_config.nb_cores.default, output_dir=str(tmp_path / "OUTPUT"), post_processing=False, studies_in=str(tmp_path / "STUDIES_IN"), - time_limit=0, + time_limit=slurm_config.default_time_limit, version=False, wait_mode=False, - wait_time=0, + wait_time=slurm_config.default_wait_time, xpansion_mode=None, other_options=None, ) launcher_parameters = MainParameters( json_dir=Path(tmp_path), - default_json_db_name="default_json_db_name", - slurm_script_path="slurm_script_path", - antares_versions_on_remote_server=["42", "45"], + default_json_db_name=slurm_config.default_json_db_name, + slurm_script_path=slurm_config.slurm_script_path, + antares_versions_on_remote_server=slurm_config.antares_versions_on_remote_server, default_ssh_dict={ - "username": "username", - "hostname": "hostname", - "port": 42, - "private_key_file": Path("private_key_file"), - "key_password": "key_password", - "password": "password", + "username": slurm_config.username, + "hostname": slurm_config.hostname, + "port": slurm_config.port, + "private_key_file": slurm_config.private_key_file, + "key_password": slurm_config.key_password, + "password": slurm_config.password, }, db_primary_key="name", ) @@ -456,7 +490,7 @@ def test_kill_job( @patch("antarest.launcher.adapters.slurm_launcher.slurm_launcher.run_with") -def test_launcher_workspace_init(run_with_mock, tmp_path: Path, launcher_config: Config): +def test_launcher_workspace_init(run_with_mock, tmp_path: Path, launcher_config: Config) -> None: callbacks = Mock() (tmp_path / LOG_DIR_NAME).mkdir() @@ -474,11 +508,7 @@ def test_launcher_workspace_init(run_with_mock, tmp_path: Path, launcher_config: clean_locks_from_config(launcher_config) assert not (workspaces[0] / WORKSPACE_LOCK_FILE_NAME).exists() - slurm_launcher.data_repo_tinydb.save_study( - StudyDTO( - path="somepath", - ) - ) + slurm_launcher.data_repo_tinydb.save_study(StudyDTO(path="some_path")) run_with_mock.assert_not_called() # will use existing private workspace diff --git a/tests/study/business/conftest.py b/tests/study/business/conftest.py deleted file mode 100644 index 2638d47b3d..0000000000 --- a/tests/study/business/conftest.py +++ /dev/null @@ -1,22 +0,0 @@ -import contextlib - -import pytest -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker - -from antarest.dbmodel import Base - - -@pytest.fixture(scope="function", name="db_engine") -def db_engine_fixture(): - engine = create_engine("sqlite:///:memory:") - Base.metadata.create_all(engine) - yield engine - engine.dispose() - - -@pytest.fixture(scope="function", name="db_session") -def db_session_fixture(db_engine): - make_session = sessionmaker(bind=db_engine) - with contextlib.closing(make_session()) as session: - yield session diff --git a/tests/test_resources.py b/tests/test_resources.py index 2a0bf94677..330116e507 100644 --- a/tests/test_resources.py +++ b/tests/test_resources.py @@ -4,6 +4,8 @@ import pytest +from antarest.core.config import Config + HERE = pathlib.Path(__file__).parent.resolve() PROJECT_DIR = next(iter(p for p in HERE.parents if p.joinpath("antarest").exists())) RESOURCES_DIR = PROJECT_DIR.joinpath("resources") @@ -84,3 +86,17 @@ def test_empty_study_zip(filename: str, expected_list: Sequence[str]): with zipfile.ZipFile(resource_path) as myzip: actual = sorted(myzip.namelist()) assert actual == expected_list + + +def test_resources_config(): + """ + Check that the "resources/config.yaml" file is valid. + + The launcher section must be configured to use a local launcher + with NB Cores detection enabled. + """ + config_path = RESOURCES_DIR.joinpath("deploy/config.yaml") + config = Config.from_yaml_file(config_path, res=RESOURCES_DIR) + assert config.launcher.default == "local" + assert config.launcher.local is not None + assert config.launcher.local.enable_nb_cores_detection is True From 789c2adfc3ef3999f3779a345e0730f2f9ad906a Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Wed, 11 Oct 2023 09:50:47 +0200 Subject: [PATCH 10/13] test(binding-constraint): correct `test_command_factory` unit test to ignore abstract commands --- tests/variantstudy/test_command_factory.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/variantstudy/test_command_factory.py b/tests/variantstudy/test_command_factory.py index 4dbcb6a06f..47e3c57811 100644 --- a/tests/variantstudy/test_command_factory.py +++ b/tests/variantstudy/test_command_factory.py @@ -31,7 +31,10 @@ def setup_class(self): f".{name}", package="antarest.study.storage.variantstudy.model.command", ) - self.command_class_set = {command.__name__ for command in ICommand.__subclasses__()} + abstract_commands = {"AbstractBindingConstraintCommand"} + self.command_class_set = { + cmd.__name__ for cmd in ICommand.__subclasses__() if cmd.__name__ not in abstract_commands + } # noinspection SpellCheckingInspection @pytest.mark.parametrize( From 853cf6ba48a23d39f247a0842afac440c4ea4570 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Wed, 11 Oct 2023 10:12:52 +0200 Subject: [PATCH 11/13] feat(st-storage): allow all parameters in endpoint for short term storage creation (#1736) Co-authored-by: Laurent LAPORTE (cherry picked from commit bb6d5c6f7da808d1e651dd76560178fdd057370b) --- antarest/study/business/st_storage_manager.py | 113 +++----- antarest/study/business/utils.py | 16 + .../model/filesystem/config/st_storage.py | 37 ++- antarest/study/web/study_data_blueprint.py | 11 +- .../study_data_blueprint/test_st_storage.py | 274 ++++++++++-------- 5 files changed, 234 insertions(+), 217 deletions(-) diff --git a/antarest/study/business/st_storage_manager.py b/antarest/study/business/st_storage_manager.py index d1c040741b..f16ff680d4 100644 --- a/antarest/study/business/st_storage_manager.py +++ b/antarest/study/business/st_storage_manager.py @@ -1,10 +1,10 @@ import functools import json import operator -from typing import Any, Dict, List, Mapping, MutableMapping, Sequence +from typing import Any, Dict, List, Mapping, MutableMapping, Optional, Sequence import numpy as np -from pydantic import BaseModel, Extra, Field, root_validator, validator +from pydantic import BaseModel, Extra, root_validator, validator from typing_extensions import Literal from antarest.core.exceptions import ( @@ -12,9 +12,13 @@ STStorageFieldsNotFoundError, STStorageMatrixNotFoundError, ) -from antarest.study.business.utils import AllOptionalMetaclass, FormFieldsBaseModel, execute_or_add_commands +from antarest.study.business.utils import AllOptionalMetaclass, camel_case_model, execute_or_add_commands from antarest.study.model import Study -from antarest.study.storage.rawstudy.model.filesystem.config.st_storage import STStorageConfig, STStorageGroup +from antarest.study.storage.rawstudy.model.filesystem.config.st_storage import ( + STStorageConfig, + STStorageGroup, + STStorageProperties, +) from antarest.study.storage.storage_service import StudyStorageService from antarest.study.storage.variantstudy.model.command.create_st_storage import CreateSTStorage from antarest.study.storage.variantstudy.model.command.remove_st_storage import RemoveSTStorage @@ -23,77 +27,12 @@ _HOURS_IN_YEAR = 8760 -class FormBaseModel(FormFieldsBaseModel): - """ - A foundational model for all form-based models, providing common configurations. - """ - - class Config: - validate_assignment = True - allow_population_by_field_name = True - - -class StorageCreation(FormBaseModel): +@camel_case_model +class StorageInput(STStorageProperties, metaclass=AllOptionalMetaclass): """ - Model representing the form used to create a new short-term storage entry. + Model representing the form used to EDIT an existing short-term storage. """ - name: str = Field( - description="Name of the storage.", - regex=r"[a-zA-Z0-9_(),& -]+", - ) - group: STStorageGroup = Field( - description="Energy storage system group.", - ) - - class Config: - @staticmethod - def schema_extra(schema: MutableMapping[str, Any]) -> None: - schema["example"] = StorageCreation( - name="Siemens Battery", - group=STStorageGroup.BATTERY, - ) - - @property - def to_config(self) -> STStorageConfig: - values = self.dict(by_alias=False) - return STStorageConfig(**values) - - -class StorageUpdate(StorageCreation, metaclass=AllOptionalMetaclass): - """set name, group as optional fields""" - - -class StorageInput(StorageUpdate): - """ - Model representing the form used to edit existing short-term storage details. - """ - - injection_nominal_capacity: float = Field( - description="Injection nominal capacity (MW)", - ge=0, - ) - withdrawal_nominal_capacity: float = Field( - description="Withdrawal nominal capacity (MW)", - ge=0, - ) - reservoir_capacity: float = Field( - description="Reservoir capacity (MWh)", - ge=0, - ) - efficiency: float = Field( - description="Efficiency of the storage system", - ge=0, - le=1, - ) - initial_level: float = Field( - description="Initial level of the storage system", - ge=0, - ) - initial_level_optim: bool = Field( - description="Flag indicating if the initial level is optimized", - ) - class Config: @staticmethod def schema_extra(schema: MutableMapping[str, Any]) -> None: @@ -104,19 +43,37 @@ def schema_extra(schema: MutableMapping[str, Any]) -> None: withdrawal_nominal_capacity=150, reservoir_capacity=600, efficiency=0.94, + initial_level=0.5, initial_level_optim=True, ) -class StorageOutput(StorageInput): +class StorageCreation(StorageInput): """ - Model representing the form used to display the details of a short-term storage entry. + Model representing the form used to CREATE a new short-term storage. """ - id: str = Field( - description="Short-term storage ID", - regex=r"[a-zA-Z0-9_(),& -]+", - ) + # noinspection Pydantic + @validator("name", pre=True) + def validate_name(cls, name: Optional[str]) -> str: + """ + Validator to check if the name is not empty. + """ + if not name: + raise ValueError("'name' must not be empty") + return name + + @property + def to_config(self) -> STStorageConfig: + values = self.dict(by_alias=False, exclude_none=True) + return STStorageConfig(**values) + + +@camel_case_model +class StorageOutput(STStorageConfig): + """ + Model representing the form used to display the details of a short-term storage entry. + """ class Config: @staticmethod diff --git a/antarest/study/business/utils.py b/antarest/study/business/utils.py index b602c60138..33b62d766c 100644 --- a/antarest/study/business/utils.py +++ b/antarest/study/business/utils.py @@ -117,3 +117,19 @@ def __new__( annotations[field] = Optional[annotations[field]] namespaces["__annotations__"] = annotations return super().__new__(cls, name, bases, namespaces) + + +def camel_case_model(model: Type[BaseModel]) -> Type[BaseModel]: + """ + This decorator can be used to modify a model to use camel case aliases. + + Args: + model: The pydantic model to modify. + + Returns: + The modified model. + """ + model.__config__.alias_generator = to_camel_case + for field_name, field in model.__fields__.items(): + field.alias = to_camel_case(field_name) + return model diff --git a/antarest/study/storage/rawstudy/model/filesystem/config/st_storage.py b/antarest/study/storage/rawstudy/model/filesystem/config/st_storage.py index d2a68c4799..b82910a191 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/config/st_storage.py +++ b/antarest/study/storage/rawstudy/model/filesystem/config/st_storage.py @@ -30,22 +30,18 @@ class STStorageGroup(EnumIgnoreCase): # noinspection SpellCheckingInspection -class STStorageConfig(BaseModel): - """ - Manage the configuration files in the context of Short-Term Storage. - It provides a convenient way to read and write configuration data from/to an INI file format. +class STStorageProperties( + BaseModel, + extra=Extra.forbid, + validate_assignment=True, + allow_population_by_field_name=True, +): """ + Properties of a short-term storage system read from the configuration files. - class Config: - extra = Extra.forbid - allow_population_by_field_name = True + All aliases match the name of the corresponding field in the INI files. + """ - # The `id` field is a calculated from the `name` if not provided. - # This value must be stored in the config cache. - id: str = Field( - description="Short-term storage ID", - regex=r"[a-zA-Z0-9_(),& -]+", - ) name: str = Field( description="Short-term storage name", regex=r"[a-zA-Z0-9_(),& -]+", @@ -90,6 +86,21 @@ class Config: alias="initialleveloptim", ) + +# noinspection SpellCheckingInspection +class STStorageConfig(STStorageProperties): + """ + Manage the configuration files in the context of Short-Term Storage. + It provides a convenient way to read and write configuration data from/to an INI file format. + """ + + # The `id` field is a calculated from the `name` if not provided. + # This value must be stored in the config cache. + id: str = Field( + description="Short-term storage ID", + regex=r"[a-zA-Z0-9_(),& -]+", + ) + @root_validator(pre=True) def calculate_storage_id(cls, values: Dict[str, Any]) -> Dict[str, Any]: """ diff --git a/antarest/study/web/study_data_blueprint.py b/antarest/study/web/study_data_blueprint.py index 931816391a..610e3b7263 100644 --- a/antarest/study/web/study_data_blueprint.py +++ b/antarest/study/web/study_data_blueprint.py @@ -1577,8 +1577,15 @@ def create_st_storage( Args: - `uuid`: The UUID of the study. - `area_id`: The area ID. - - `form`: The name and the group(PSP_open, PSP_closed, Pondage, Battery, Other1, Other2, Other3, Other4, Other5) - of the storage that we want to create. + - `form`: The characteristic of the storage that we can update: + - `name`: The name of the updated storage. + - `group`: The group of the updated storage. + - `injectionNominalCapacity`: The injection Nominal Capacity of the updated storage. + - `withdrawalNominalCapacity`: The withdrawal Nominal Capacity of the updated storage. + - `reservoirCapacity`: The reservoir capacity of the updated storage. + - `efficiency`: The efficiency of the updated storage + - `initialLevel`: The initial Level of the updated storage + - `initialLevelOptim`: The initial Level Optim of the updated storage Returns: New storage with the following attributes: - `id`: The storage ID of the study. diff --git a/tests/integration/study_data_blueprint/test_st_storage.py b/tests/integration/study_data_blueprint/test_st_storage.py index cdd0b464c7..2d1035af3e 100644 --- a/tests/integration/study_data_blueprint/test_st_storage.py +++ b/tests/integration/study_data_blueprint/test_st_storage.py @@ -1,4 +1,3 @@ -import json import re import numpy as np @@ -9,6 +8,17 @@ from antarest.study.storage.rawstudy.model.filesystem.config.model import transform_name_to_id from tests.integration.utils import wait_task_completion +DEFAULT_PROPERTIES = { + # `name` field is required + "group": "Other1", + "injectionNominalCapacity": 0.0, + "withdrawalNominalCapacity": 0.0, + "reservoirCapacity": 0.0, + "efficiency": 1.0, + "initialLevel": 0.0, + "initialLevelOptim": False, +} + @pytest.mark.unit_test class TestSTStorage: @@ -61,28 +71,46 @@ def test_lifecycle__nominal( task = wait_task_completion(client, user_access_token, task_id) assert task.status == TaskStatus.COMPLETED, task - # creation with default values (only mandatory properties specified) + # ============================= + # SHORT-TERM STORAGE CREATION + # ============================= + area_id = transform_name_to_id("FR") siemens_battery = "Siemens Battery" + + # Un attempt to create a short-term storage without name + # should raise a validation error (other properties are optional). + # Un attempt to create a short-term storage with an empty name + # or an invalid name should also raise a validation error. + attempts = [{}, {"name": ""}, {"name": "!??"}] + for attempt in attempts: + res = client.post( + f"/v1/studies/{study_id}/areas/{area_id}/storages", + headers={"Authorization": f"Bearer {user_access_token}"}, + json=attempt, + ) + assert res.status_code == 422, res.json() + assert res.json()["exception"] in {"ValidationError", "RequestValidationError"}, res.json() + + # We can create a short-term storage with the following properties: + siemens_properties = { + **DEFAULT_PROPERTIES, + "name": siemens_battery, + "group": "Battery", + "injectionNominalCapacity": 1450, + "withdrawalNominalCapacity": 1350, + "reservoirCapacity": 1500, + } res = client.post( f"/v1/studies/{study_id}/areas/{area_id}/storages", headers={"Authorization": f"Bearer {user_access_token}"}, - json={"name": siemens_battery, "group": "Battery"}, + json=siemens_properties, ) assert res.status_code == 200, res.json() siemens_battery_id = res.json()["id"] assert siemens_battery_id == transform_name_to_id(siemens_battery) - assert res.json() == { - "efficiency": 1.0, - "group": "Battery", - "id": siemens_battery_id, - "initialLevel": 0.0, - "initialLevelOptim": False, - "injectionNominalCapacity": 0.0, - "name": siemens_battery, - "reservoirCapacity": 0.0, - "withdrawalNominalCapacity": 0.0, - } + siemens_config = {**siemens_properties, "id": siemens_battery_id} + assert res.json() == siemens_config # reading the properties of a short-term storage res = client.get( @@ -90,17 +118,11 @@ def test_lifecycle__nominal( headers={"Authorization": f"Bearer {user_access_token}"}, ) assert res.status_code == 200, res.json() - assert res.json() == { - "efficiency": 1.0, - "group": "Battery", - "id": siemens_battery_id, - "initialLevel": 0.0, - "initialLevelOptim": False, - "injectionNominalCapacity": 0.0, - "name": siemens_battery, - "reservoirCapacity": 0.0, - "withdrawalNominalCapacity": 0.0, - } + assert res.json() == siemens_config + + # ============================= + # SHORT-TERM STORAGE MATRICES + # ============================= # updating the matrix of a short-term storage array = np.random.rand(8760, 1) * 1000 @@ -134,25 +156,17 @@ def test_lifecycle__nominal( assert res.status_code == 200, res.json() assert res.json() is True + # ================================== + # SHORT-TERM STORAGE LIST / GROUPS + # ================================== + # Reading the list of short-term storages res = client.get( f"/v1/studies/{study_id}/areas/{area_id}/storages", headers={"Authorization": f"Bearer {user_access_token}"}, ) assert res.status_code == 200, res.json() - assert res.json() == [ - { - "efficiency": 1.0, - "group": "Battery", - "id": siemens_battery_id, - "initialLevel": 0.0, - "initialLevelOptim": False, - "injectionNominalCapacity": 0.0, - "name": siemens_battery, - "reservoirCapacity": 0.0, - "withdrawalNominalCapacity": 0.0, - } - ] + assert res.json() == [siemens_config] # updating properties res = client.patch( @@ -164,34 +178,23 @@ def test_lifecycle__nominal( }, ) assert res.status_code == 200, res.json() - assert json.loads(res.text) == { - "id": siemens_battery_id, + siemens_config = { + **siemens_config, "name": "New Siemens Battery", - "group": "Battery", - "efficiency": 1.0, - "initialLevel": 0.0, - "initialLevelOptim": False, - "injectionNominalCapacity": 0.0, - "withdrawalNominalCapacity": 0.0, "reservoirCapacity": 2500, } + assert res.json() == siemens_config res = client.get( f"/v1/studies/{study_id}/areas/{area_id}/storages/{siemens_battery_id}", headers={"Authorization": f"Bearer {user_access_token}"}, ) assert res.status_code == 200, res.json() - assert res.json() == { - "id": siemens_battery_id, - "name": "New Siemens Battery", - "group": "Battery", - "efficiency": 1.0, - "initialLevel": 0.0, - "initialLevelOptim": False, - "injectionNominalCapacity": 0.0, - "withdrawalNominalCapacity": 0.0, - "reservoirCapacity": 2500, - } + assert res.json() == siemens_config + + # =========================== + # SHORT-TERM STORAGE UPDATE + # =========================== # updating properties res = client.patch( @@ -202,37 +205,38 @@ def test_lifecycle__nominal( "reservoirCapacity": 0, }, ) - assert res.status_code == 200, res.json() - assert json.loads(res.text) == { - "id": siemens_battery_id, - "name": "New Siemens Battery", - "group": "Battery", - "efficiency": 1.0, + siemens_config = { + **siemens_config, "initialLevel": 5900, - "initialLevelOptim": False, - "injectionNominalCapacity": 0.0, - "withdrawalNominalCapacity": 0.0, "reservoirCapacity": 0, } + assert res.json() == siemens_config + # An attempt to update the `efficiency` property with an invalid value + # should raise a validation error. + # The `efficiency` property must be a float between 0 and 1. + bad_properties = {"efficiency": 2.0} + res = client.patch( + f"/v1/studies/{study_id}/areas/{area_id}/storages/{siemens_battery_id}", + headers={"Authorization": f"Bearer {user_access_token}"}, + json=bad_properties, + ) + assert res.status_code == 422, res.json() + assert res.json()["exception"] == "ValidationError", res.json() + + # The short-term storage properties should not have been updated. res = client.get( f"/v1/studies/{study_id}/areas/{area_id}/storages/{siemens_battery_id}", headers={"Authorization": f"Bearer {user_access_token}"}, ) assert res.status_code == 200, res.json() - assert res.json() == { - "id": siemens_battery_id, - "name": "New Siemens Battery", - "group": "Battery", - "efficiency": 1.0, - "initialLevel": 5900, - "initialLevelOptim": False, - "injectionNominalCapacity": 0.0, - "withdrawalNominalCapacity": 0.0, - "reservoirCapacity": 0, - } + assert res.json() == siemens_config + + # ============================= + # SHORT-TERM STORAGE DELETION + # ============================= - # deletion of short-term storages + # To delete a short-term storage, we need to provide its ID. res = client.request( "DELETE", f"/v1/studies/{study_id}/areas/{area_id}/storages", @@ -242,7 +246,7 @@ def test_lifecycle__nominal( assert res.status_code == 204, res.json() assert res.text in {"", "null"} # Old FastAPI versions return 'null'. - # deletion of short-term storages with empty list + # If the short-term storage list is empty, the deletion should be a no-op. res = client.request( "DELETE", f"/v1/studies/{study_id}/areas/{area_id}/storages", @@ -252,48 +256,79 @@ def test_lifecycle__nominal( assert res.status_code == 204, res.json() assert res.text in {"", "null"} # Old FastAPI versions return 'null'. - # deletion of short-term storages with multiple IDs + # It's possible to delete multiple short-term storages at once. + # In the following example, we will create two short-term storages: + siemens_properties = { + "name": siemens_battery, + "group": "Battery", + "injectionNominalCapacity": 1450, + "withdrawalNominalCapacity": 1350, + "reservoirCapacity": 1500, + "efficiency": 0.90, + "initialLevel": 200, + "initialLevelOptim": False, + } res = client.post( f"/v1/studies/{study_id}/areas/{area_id}/storages", headers={"Authorization": f"Bearer {user_access_token}"}, - json={"name": siemens_battery, "group": "Battery"}, + json=siemens_properties, ) assert res.status_code == 200, res.json() - siemens_battery_id1 = res.json()["id"] - - siemens_battery_del = f"{siemens_battery}del" + siemens_battery_id = res.json()["id"] + # Create another short-term storage: "Grand'Maison" + grand_maison = "Grand'Maison" + grand_maison_properties = { + "name": grand_maison, + "group": "PSP_closed", + "injectionNominalCapacity": 1500, + "withdrawalNominalCapacity": 1800, + "reservoirCapacity": 20000, + "efficiency": 0.78, + "initialLevel": 10000, + } res = client.post( f"/v1/studies/{study_id}/areas/{area_id}/storages", headers={"Authorization": f"Bearer {user_access_token}"}, - json={"name": siemens_battery_del, "group": "Battery"}, + json=grand_maison_properties, ) assert res.status_code == 200, res.json() - siemens_battery_id2 = res.json()["id"] + grand_maison_id = res.json()["id"] + # We can check that we have 2 short-term storages in the list. + # Reading the list of short-term storages + res = client.get( + f"/v1/studies/{study_id}/areas/{area_id}/storages", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + assert res.status_code == 200, res.json() + siemens_config = {**DEFAULT_PROPERTIES, **siemens_properties, "id": siemens_battery_id} + grand_maison_config = {**DEFAULT_PROPERTIES, **grand_maison_properties, "id": grand_maison_id} + assert res.json() == [siemens_config, grand_maison_config] + + # We can delete the two short-term storages at once. res = client.request( "DELETE", f"/v1/studies/{study_id}/areas/{area_id}/storages", headers={"Authorization": f"Bearer {user_access_token}"}, - json=[siemens_battery_id1, siemens_battery_id2], + json=[siemens_battery_id, grand_maison_id], ) assert res.status_code == 204, res.json() assert res.text in {"", "null"} # Old FastAPI versions return 'null'. - # Check the removal + # The list of short-term storages should be empty. res = client.get( - f"/v1/studies/{study_id}/areas/{area_id}/storages/{siemens_battery_id}", + f"/v1/studies/{study_id}/areas/{area_id}/storages", headers={"Authorization": f"Bearer {user_access_token}"}, ) - obj = res.json() - description = obj["description"] - assert siemens_battery_id in description - assert re.search(r"fields of storage", description, flags=re.IGNORECASE) - assert re.search(r"not found", description, flags=re.IGNORECASE) + assert res.status_code == 200, res.json() + assert res.json() == [] - assert res.status_code == 404, res.json() + # =========================== + # SHORT-TERM STORAGE ERRORS + # =========================== - # Check delete with the wrong value of area_id + # Check delete with the wrong value of `area_id` bad_area_id = "bad_area" res = client.request( "DELETE", @@ -311,7 +346,7 @@ def test_lifecycle__nominal( flags=re.IGNORECASE, ) - # Check delete with the wrong value of study_id + # Check delete with the wrong value of `study_id` bad_study_id = "bad_study" res = client.request( "DELETE", @@ -324,8 +359,7 @@ def test_lifecycle__nominal( assert res.status_code == 404, res.json() assert bad_study_id in description - # Check get with wrong area_id - + # Check get with wrong `area_id` res = client.get( f"/v1/studies/{study_id}/areas/{bad_area_id}/storages/{siemens_battery_id}", headers={"Authorization": f"Bearer {user_access_token}"}, @@ -335,8 +369,7 @@ def test_lifecycle__nominal( assert bad_area_id in description assert res.status_code == 404, res.json() - # Check get with wrong study_id - + # Check get with wrong `study_id` res = client.get( f"/v1/studies/{bad_study_id}/areas/{area_id}/storages/{siemens_battery_id}", headers={"Authorization": f"Bearer {user_access_token}"}, @@ -346,7 +379,7 @@ def test_lifecycle__nominal( assert res.status_code == 404, res.json() assert bad_study_id in description - # Check post with wrong study_id + # Check POST with wrong `study_id` res = client.post( f"/v1/studies/{bad_study_id}/areas/{area_id}/storages", headers={"Authorization": f"Bearer {user_access_token}"}, @@ -357,11 +390,20 @@ def test_lifecycle__nominal( assert res.status_code == 404, res.json() assert bad_study_id in description - # Check post with wrong area_id + # Check POST with wrong `area_id` res = client.post( f"/v1/studies/{study_id}/areas/{bad_area_id}/storages", headers={"Authorization": f"Bearer {user_access_token}"}, - json={"name": siemens_battery, "group": "Battery"}, + json={ + "name": siemens_battery, + "group": "Battery", + "initialLevel": 0.0, + "initialLevelOptim": False, + "injectionNominalCapacity": 0.0, + "reservoirCapacity": 0.0, + "withdrawalNominalCapacity": 0.0, + "efficiency": 1.0, + }, ) assert res.status_code == 500, res.json() obj = res.json() @@ -370,7 +412,7 @@ def test_lifecycle__nominal( assert re.search(r"Area ", description, flags=re.IGNORECASE) assert re.search(r"does not exist ", description, flags=re.IGNORECASE) - # Check post with wrong group + # Check POST with wrong `group` res = client.post( f"/v1/studies/{study_id}/areas/{bad_area_id}/storages", headers={"Authorization": f"Bearer {user_access_token}"}, @@ -381,7 +423,7 @@ def test_lifecycle__nominal( description = obj["description"] assert re.search(r"not a valid enumeration member", description, flags=re.IGNORECASE) - # Check the put with the wrong area_id + # Check PATCH with the wrong `area_id` res = client.patch( f"/v1/studies/{study_id}/areas/{bad_area_id}/storages/{siemens_battery_id}", headers={"Authorization": f"Bearer {user_access_token}"}, @@ -401,7 +443,7 @@ def test_lifecycle__nominal( assert bad_area_id in description assert re.search(r"not a child of ", description, flags=re.IGNORECASE) - # Check the put with the wrong siemens_battery_id + # Check PATCH with the wrong `siemens_battery_id` res = client.patch( f"/v1/studies/{study_id}/areas/{area_id}/storages/{siemens_battery_id}", headers={"Authorization": f"Bearer {user_access_token}"}, @@ -422,7 +464,7 @@ def test_lifecycle__nominal( assert re.search(r"fields of storage", description, flags=re.IGNORECASE) assert re.search(r"not found", description, flags=re.IGNORECASE) - # Check the put with the wrong study_id + # Check PATCH with the wrong `study_id` res = client.patch( f"/v1/studies/{bad_study_id}/areas/{area_id}/storages/{siemens_battery_id}", headers={"Authorization": f"Bearer {user_access_token}"}, @@ -440,19 +482,3 @@ def test_lifecycle__nominal( obj = res.json() description = obj["description"] assert bad_study_id in description - - # Check the put with the wrong efficiency - res = client.patch( - f"/v1/studies/{bad_study_id}/areas/{area_id}/storages/{siemens_battery_id}", - headers={"Authorization": f"Bearer {user_access_token}"}, - json={ - "efficiency": 2.0, - "initialLevel": 0.0, - "initialLevelOptim": True, - "injectionNominalCapacity": 2450, - "name": "New Siemens Battery", - "reservoirCapacity": 2500, - "withdrawalNominalCapacity": 2350, - }, - ) - assert res.status_code == 422, res.json() From 901d00df558f7e79b728e2ce7406d1bdea69f839 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Wed, 11 Oct 2023 12:07:09 +0200 Subject: [PATCH 12/13] chore(sonarcloud): correct SonarCloud issues --- antarest/core/cache/business/redis_cache.py | 1 + antarest/core/filetransfer/model.py | 13 +++- antarest/core/logging/utils.py | 8 +- antarest/core/tasks/service.py | 16 ++-- .../adapters/local_launcher/local_launcher.py | 9 ++- antarest/launcher/adapters/log_manager.py | 78 +++++++++---------- antarest/launcher/service.py | 2 +- antarest/login/web.py | 4 +- antarest/matrixstore/web.py | 4 +- .../study/business/xpansion_management.py | 2 +- .../study/web/xpansion_studies_blueprint.py | 2 +- antarest/worker/simulator_worker.py | 9 ++- 12 files changed, 81 insertions(+), 67 deletions(-) diff --git a/antarest/core/cache/business/redis_cache.py b/antarest/core/cache/business/redis_cache.py index 3c72845eb8..176e583b76 100644 --- a/antarest/core/cache/business/redis_cache.py +++ b/antarest/core/cache/business/redis_cache.py @@ -21,6 +21,7 @@ def __init__(self, redis_client: Redis): # type: ignore self.redis = redis_client def start(self) -> None: + # Assuming the Redis service is already running; no need to start it here. pass def put(self, id: str, data: JSON, duration: int = 3600) -> None: diff --git a/antarest/core/filetransfer/model.py b/antarest/core/filetransfer/model.py index 9442f051dc..bbb61c00b6 100644 --- a/antarest/core/filetransfer/model.py +++ b/antarest/core/filetransfer/model.py @@ -13,7 +13,7 @@ class FileDownloadNotFound(HTTPException): def __init__(self) -> None: super().__init__( HTTPStatus.NOT_FOUND, - f"Requested download file was not found. It must have expired", + "Requested download file was not found. It must have expired", ) @@ -21,7 +21,7 @@ class FileDownloadNotReady(HTTPException): def __init__(self) -> None: super().__init__( HTTPStatus.NOT_ACCEPTABLE, - f"Requested file is not ready for download.", + "Requested file is not ready for download.", ) @@ -70,4 +70,11 @@ def to_dto(self) -> FileDownloadDTO: ) def __repr__(self) -> str: - return f"(id={self.id},name={self.name},filename={self.filename},path={self.path},ready={self.ready},expiration_date={self.expiration_date})" + return ( + f"(id={self.id}," + f" name={self.name}," + f" filename={self.filename}," + f" path={self.path}," + f" ready={self.ready}," + f" expiration_date={self.expiration_date})" + ) diff --git a/antarest/core/logging/utils.py b/antarest/core/logging/utils.py index 9115dba45b..b0e5227ea3 100644 --- a/antarest/core/logging/utils.py +++ b/antarest/core/logging/utils.py @@ -124,12 +124,14 @@ def configure_logger(config: Config, handler_cls: str = "logging.FileHandler") - "filters": ["context"], } elif handler_cls == "logging.handlers.TimedRotatingFileHandler": + # 90 days = 3 months + # keep only 1 backup (0 means keep all) logging_config["handlers"]["default"] = { "class": handler_cls, "filename": config.logging.logfile, - "when": "D", # D = day - "interval": 90, # 90 days = 3 months - "backupCount": 1, # keep only 1 backup (0 means keep all) + "when": "D", + "interval": 90, + "backupCount": 1, "encoding": "utf-8", "delay": False, "utc": False, diff --git a/antarest/core/tasks/service.py b/antarest/core/tasks/service.py index 1c9ac3bf18..227b3fbf71 100644 --- a/antarest/core/tasks/service.py +++ b/antarest/core/tasks/service.py @@ -80,7 +80,7 @@ def await_task(self, task_id: str, timeout_sec: Optional[int] = None) -> None: # noinspection PyUnusedLocal def noop_notifier(message: str) -> None: - pass + """This function is used in tasks when no notification is required.""" DEFAULT_AWAIT_MAX_TIMEOUT = 172800 @@ -121,7 +121,7 @@ async def _await_task_end(event: Event) -> None: return _await_task_end - # todo: Is `logger_` parameter required? (consider refactoring) + # noinspection PyUnusedLocal def _send_worker_task(logger_: TaskUpdateNotifier) -> TaskResult: listener_id = self.event_bus.add_listener( _create_awaiter(task_result_wrapper), @@ -338,14 +338,18 @@ def _run_task( result.message, result.return_value, ) + event_type = {True: EventType.TASK_COMPLETED, False: EventType.TASK_FAILED}[result.success] + event_msg = {True: "completed", False: "failed"}[result.success] self.event_bus.push( Event( - type=EventType.TASK_COMPLETED if result.success else EventType.TASK_FAILED, + type=event_type, payload=TaskEventPayload( id=task_id, - message=custom_event_messages.end - if custom_event_messages is not None - else f'Task {task_id} {"completed" if result.success else "failed"}', + message=( + custom_event_messages.end + if custom_event_messages is not None + else f"Task {task_id} {event_msg}" + ), ).dict(), permissions=PermissionInfo(public_mode=PublicMode.READ), channel=EventChannelDirectory.TASK + task_id, diff --git a/antarest/launcher/adapters/local_launcher/local_launcher.py b/antarest/launcher/adapters/local_launcher/local_launcher.py index 7865f8740c..8ee598985b 100644 --- a/antarest/launcher/adapters/local_launcher/local_launcher.py +++ b/antarest/launcher/adapters/local_launcher/local_launcher.py @@ -1,3 +1,4 @@ +import io import logging import shutil import signal @@ -6,7 +7,7 @@ import threading import time from pathlib import Path -from typing import IO, Callable, Dict, Optional, Tuple, cast +from typing import Callable, Dict, Optional, Tuple, cast from uuid import UUID from antarest.core.config import Config @@ -14,7 +15,7 @@ from antarest.core.interfaces.eventbus import IEventBus from antarest.core.requests import RequestParameters from antarest.launcher.adapters.abstractlauncher import AbstractLauncher, LauncherCallbacks, LauncherInitException -from antarest.launcher.adapters.log_manager import LogTailManager +from antarest.launcher.adapters.log_manager import follow from antarest.launcher.model import JobStatus, LauncherParametersDTO, LogType logger = logging.getLogger(__name__) @@ -133,8 +134,8 @@ def stop_reading_output() -> bool: ) thread = threading.Thread( - target=lambda: LogTailManager.follow( - cast(IO[str], process.stdout), + target=lambda: follow( + cast(io.StringIO, process.stdout), self.create_update_log(str(uuid)), stop_reading_output, None, diff --git a/antarest/launcher/adapters/log_manager.py b/antarest/launcher/adapters/log_manager.py index a0bfbdfe70..eeca586f32 100644 --- a/antarest/launcher/adapters/log_manager.py +++ b/antarest/launcher/adapters/log_manager.py @@ -1,8 +1,10 @@ +import contextlib +import io import logging import time from pathlib import Path from threading import Thread -from typing import IO, Callable, Dict, Optional +from typing import Callable, Dict, Optional, cast logger = logging.getLogger(__name__) @@ -11,7 +13,7 @@ class LogTailManager: BATCH_SIZE = 10 def __init__(self, log_base_dir: Path) -> None: - logger.info(f"Initiating Log manager") + logger.info("Initiating Log manager") self.log_base_dir = log_base_dir self.tracked_logs: Dict[str, Thread] = {} @@ -47,43 +49,6 @@ def stop_tracking(self, log_path: Optional[Path]) -> None: if log_path_key in self.tracked_logs: del self.tracked_logs[log_path_key] - @staticmethod - def follow( - io: IO[str], - handler: Callable[[str], None], - stop: Callable[[], bool], - log_file: Optional[str], - ) -> None: - line = "" - line_count = 0 - - while True: - if stop(): - break - tmp = io.readline() - if not tmp: - if line: - logger.debug(f"Calling handler for {log_file}") - try: - handler(line) - except Exception as e: - logger.error("Could not handle this log line", exc_info=e) - line = "" - line_count = 0 - time.sleep(0.1) - else: - line += tmp - if line.endswith("\n"): - line_count += 1 - if line_count >= LogTailManager.BATCH_SIZE: - logger.debug(f"Calling handler for {log_file}") - try: - handler(line) - except Exception as e: - logger.error("Could not handle this log line", exc_info=e) - line = "" - line_count = 0 - def _follow( self, log_file: Optional[Path], @@ -97,4 +62,37 @@ def _follow( with open(log_file, "r") as fh: logger.info(f"Scanning {log_file}") - LogTailManager.follow(fh, handler, stop, str(log_file)) + follow(cast(io.StringIO, fh), handler, stop, str(log_file)) + + +def follow( + file: io.StringIO, + handler: Callable[[str], None], + stop: Callable[[], bool], + log_file: Optional[str], +) -> None: + line = "" + line_count = 0 + + while True: + if stop(): + break + tmp = file.readline() + if tmp: + line += tmp + if line.endswith("\n"): + line_count += 1 + if line_count >= LogTailManager.BATCH_SIZE: + logger.debug(f"Calling handler for {log_file}") + with contextlib.suppress(Exception): + handler(line) + line = "" + line_count = 0 + else: + if line: + logger.debug(f"Calling handler for {log_file}") + with contextlib.suppress(Exception): + handler(line) + line = "" + line_count = 0 + time.sleep(0.1) diff --git a/antarest/launcher/service.py b/antarest/launcher/service.py index aa081e5462..7312181dea 100644 --- a/antarest/launcher/service.py +++ b/antarest/launcher/service.py @@ -176,7 +176,7 @@ def update( channel=EventChannelDirectory.JOB_STATUS + job_result.id, ) ) - logger.info(f"Study status set") + logger.info("Study status set") def append_log(self, job_id: str, message: str, log_type: JobLogType) -> None: try: diff --git a/antarest/login/web.py b/antarest/login/web.py index 86561457ac..ab63ec16b2 100644 --- a/antarest/login/web.py +++ b/antarest/login/web.py @@ -110,7 +110,7 @@ def users_get_all( details: Optional[bool] = False, current_user: JWTUser = Depends(auth.get_current_user), ) -> Any: - logger.info(f"Fetching users list", extra={"user": current_user.id}) + logger.info("Fetching users list", extra={"user": current_user.id}) params = RequestParameters(user=current_user) return service.get_all_users(params, details) @@ -188,7 +188,7 @@ def groups_get_all( details: Optional[bool] = False, current_user: JWTUser = Depends(auth.get_current_user), ) -> Any: - logger.info(f"Fetching groups list", extra={"user": current_user.id}) + logger.info("Fetching groups list", extra={"user": current_user.id}) params = RequestParameters(user=current_user) return service.get_all_groups(params, details) diff --git a/antarest/matrixstore/web.py b/antarest/matrixstore/web.py index 4b47135b52..523176b241 100644 --- a/antarest/matrixstore/web.py +++ b/antarest/matrixstore/web.py @@ -37,7 +37,7 @@ def create( matrix: List[List[MatrixData]] = Body(description="matrix dto", default=[]), current_user: JWTUser = Depends(auth.get_current_user), ) -> Any: - logger.info(f"Creating new matrix", extra={"user": current_user.id}) + logger.info("Creating new matrix", extra={"user": current_user.id}) if current_user.id is not None: return service.create(matrix) raise UserHasNotPermissionError() @@ -60,7 +60,7 @@ def create_by_importation( @bp.get("/matrix/{id}", tags=[APITag.matrix], response_model=MatrixDTO) def get(id: str, user: JWTUser = Depends(auth.get_current_user)) -> Any: - logger.info(f"Fetching matrix", extra={"user": user.id}) + logger.info("Fetching matrix", extra={"user": user.id}) if user.id is not None: return service.get(id) raise UserHasNotPermissionError() diff --git a/antarest/study/business/xpansion_management.py b/antarest/study/business/xpansion_management.py index 4dec6e3ef6..e8c6c79f98 100644 --- a/antarest/study/business/xpansion_management.py +++ b/antarest/study/business/xpansion_management.py @@ -436,7 +436,7 @@ def _assert_candidate_is_correct( xpansion_candidate_dto: XpansionCandidateDTO, new_name: bool = False, ) -> None: - logger.info(f"Checking given candidate is correct") + logger.info("Checking given candidate is correct") self._assert_no_illegal_character_is_in_candidate_name(xpansion_candidate_dto.name) if new_name: self._assert_candidate_name_is_not_already_taken(candidates, xpansion_candidate_dto.name) diff --git a/antarest/study/web/xpansion_studies_blueprint.py b/antarest/study/web/xpansion_studies_blueprint.py index 98c11a91c2..af9496ba4c 100644 --- a/antarest/study/web/xpansion_studies_blueprint.py +++ b/antarest/study/web/xpansion_studies_blueprint.py @@ -162,7 +162,7 @@ def get_candidates( uuid: str, current_user: JWTUser = Depends(auth.get_current_user), ) -> Any: - logger.info(f"Fetching study list", extra={"user": current_user.id}) + logger.info("Fetching study list", extra={"user": current_user.id}) params = RequestParameters(user=current_user) return study_service.get_candidates(uuid, params) diff --git a/antarest/worker/simulator_worker.py b/antarest/worker/simulator_worker.py index f939d8ea61..d37a8825f5 100644 --- a/antarest/worker/simulator_worker.py +++ b/antarest/worker/simulator_worker.py @@ -1,9 +1,10 @@ +import io import logging import subprocess import threading import time from pathlib import Path -from typing import IO, cast +from typing import cast from pydantic import BaseModel @@ -12,7 +13,7 @@ from antarest.core.interfaces.eventbus import IEventBus from antarest.core.tasks.model import TaskResult from antarest.core.utils.fastapi_sqlalchemy import db -from antarest.launcher.adapters.log_manager import LogTailManager +from antarest.launcher.adapters.log_manager import follow from antarest.matrixstore.service import MatrixService from antarest.matrixstore.uri_resolver_service import UriResolverService from antarest.study.storage.rawstudy.model.filesystem.factory import StudyFactory @@ -101,8 +102,8 @@ def stop_reading() -> bool: encoding="utf-8", ) thread = threading.Thread( - target=lambda: LogTailManager.follow( - cast(IO[str], process.stdout), + target=lambda: follow( + cast(io.StringIO, process.stdout), append_output, stop_reading, None, From b7ba8b4929d7ff96a8c62fe20b5b12eddb2b5a07 Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Wed, 11 Oct 2023 13:49:05 +0200 Subject: [PATCH 13/13] build: new hotfix v2.15.2 (2023-10-11) --- antarest/__init__.py | 2 +- docs/CHANGELOG.md | 30 +++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/antarest/__init__.py b/antarest/__init__.py index f167743c7a..447ab7edd4 100644 --- a/antarest/__init__.py +++ b/antarest/__init__.py @@ -9,7 +9,7 @@ __version__ = "2.15.2" __author__ = "RTE, Antares Web Team" -__date__ = "unreleased" +__date__ = "2023-10-11" # noinspection SpellCheckingInspection __credits__ = "(c) Réseau de Transport de l’Électricité (RTE)" diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index db4d73fbb9..4fc768bc04 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,9 +1,37 @@ Antares Web Changelog ===================== -v2.15.2 (unreleased) +v2.15.2 (2023-10-11) -------------------- +### Hotfix + +* **service:** user connected via tokens cannot create a study (#1757) ([f620197](https://github.com/AntaresSimulatorTeam/AntaREST/commit/f6201976a653db19739cbc42e91ea27ac790da10)) + + +### Features + +* **binding-constraint:** handling binding constraints frequency in study configuration parsing (#1702) ([703351a](https://github.com/AntaresSimulatorTeam/AntaREST/commit/703351a6d8d4f70491e66c3c54a92c6d28cb92ea)) + - add the binding constraint series in the matrix constants generator ([e00d58b](https://github.com/AntaresSimulatorTeam/AntaREST/commit/e00d58b203023363860cb0e849576e02ed97fd81)) + - command `create_binding_constraint` can check the matrix shape ([68bf99f](https://github.com/AntaresSimulatorTeam/AntaREST/commit/68bf99f1170181f6111bc15c03ede27030f809d2)) + - command `update_binding_constraint` can check the matrix shape ([c962f73](https://github.com/AntaresSimulatorTeam/AntaREST/commit/c962f7344c7ea07c7a8c7699b2af35f90f3b853c)) + - add missing command docstring ([d277805](https://github.com/AntaresSimulatorTeam/AntaREST/commit/d277805c10d3f9c7134166e6d2f7170c7b752428)) + - reduce code duplication ([b41d957](https://github.com/AntaresSimulatorTeam/AntaREST/commit/b41d957cffa6a8dde21a022f8b6c24c8de2559a2)) + - correct `test_command_factory` unit test to ignore abstract commands ([789c2ad](https://github.com/AntaresSimulatorTeam/AntaREST/commit/789c2adfc3ef3999f3779a345e0730f2f9ad906a)) +* **api:** add endpoint get_nb_cores (#1727) ([9cfa9f1](https://github.com/AntaresSimulatorTeam/AntaREST/commit/9cfa9f13d363ea4f73aa31ed760d525b091f04a4)) +* **st-storage:** allow all parameters in endpoint for short term storage creation (#1736) ([853cf6b](https://github.com/AntaresSimulatorTeam/AntaREST/commit/853cf6ba48a23d39f247a0842afac440c4ea4570)) + + +### Chore + +* **sonarcloud:** correct SonarCloud issues ([901d00d](https://github.com/AntaresSimulatorTeam/AntaREST/commit/901d00df558f7e79b728e2ce7406d1bdea69f839)) + + +### Contributors + +laurent-laporte-pro, +MartinBelthle + v2.15.1 (2023-10-05) --------------------