diff --git a/antarest/__init__.py b/antarest/__init__.py index 29e8eb67b6..f3685ed893 100644 --- a/antarest/__init__.py +++ b/antarest/__init__.py @@ -7,9 +7,9 @@ # Standard project metadata -__version__ = "2.14.4" +__version__ = "2.14.5" __author__ = "RTE, Antares Web Team" -__date__ = "2023-06-28" +__date__ = "2023-08-11" # noinspection SpellCheckingInspection __credits__ = "(c) Réseau de Transport de l’Électricité (RTE)" diff --git a/antarest/launcher/web.py b/antarest/launcher/web.py index b1377b167f..c82fcf000b 100644 --- a/antarest/launcher/web.py +++ b/antarest/launcher/web.py @@ -199,7 +199,7 @@ def remove_result( response_model=LauncherEnginesDTO, ) def get_engines() -> Any: - logger.info(f"Listing launch engines") + logger.info("Listing launch engines") return LauncherEnginesDTO(engines=service.get_launchers()) @bp.get( @@ -223,7 +223,7 @@ def get_load( ) def get_solver_versions( solver: str = Query( - "local", + "default", examples={ "Default solver": { "description": "Get the solver versions of the default configuration", diff --git a/antarest/study/storage/rawstudy/model/filesystem/ini_file_node.py b/antarest/study/storage/rawstudy/model/filesystem/ini_file_node.py index bf65180824..a6ef0c3a23 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/ini_file_node.py +++ b/antarest/study/storage/rawstudy/model/filesystem/ini_file_node.py @@ -8,25 +8,20 @@ import zipfile from json import JSONDecodeError from pathlib import Path -from typing import List, Optional, cast, Dict, Any, Union, Callable - -from filelock import FileLock +from typing import Any, Callable, Dict, List, Optional, Union, cast from antarest.core.model import JSON, SUB_JSON from antarest.study.storage.rawstudy.io.reader import IniReader from antarest.study.storage.rawstudy.io.reader.ini_reader import IReader -from antarest.study.storage.rawstudy.io.writer.ini_writer import ( - IniWriter, -) +from antarest.study.storage.rawstudy.io.writer.ini_writer import IniWriter from antarest.study.storage.rawstudy.model.filesystem.config.model import ( FileStudyTreeConfig, ) from antarest.study.storage.rawstudy.model.filesystem.context import ( ContextServer, ) -from antarest.study.storage.rawstudy.model.filesystem.inode import ( - INode, -) +from antarest.study.storage.rawstudy.model.filesystem.inode import INode +from filelock import FileLock class IniFileNodeWarning(UserWarning): diff --git a/antarest/study/storage/rawstudy/model/filesystem/matrix/input_series_matrix.py b/antarest/study/storage/rawstudy/model/filesystem/matrix/input_series_matrix.py index 07cf23e85f..bcb1569d89 100644 --- a/antarest/study/storage/rawstudy/model/filesystem/matrix/input_series_matrix.py +++ b/antarest/study/storage/rawstudy/model/filesystem/matrix/input_series_matrix.py @@ -4,6 +4,7 @@ import numpy as np import pandas as pd + from antarest.core.model import JSON from antarest.core.utils.utils import StopWatch from antarest.study.storage.rawstudy.model.filesystem.config.model import ( diff --git a/antarest/study/storage/variantstudy/model/command/icommand.py b/antarest/study/storage/variantstudy/model/command/icommand.py index 85b1877cd8..8da2092efc 100644 --- a/antarest/study/storage/variantstudy/model/command/icommand.py +++ b/antarest/study/storage/variantstudy/model/command/icommand.py @@ -91,13 +91,11 @@ def apply(self, study_data: FileStudy) -> CommandOutput: f"Failed to execute variant command {self.command_name}", exc_info=e, ) - return CommandOutput( - status=False, - message=( - f"Unexpected exception occurred when trying" - f" to apply command {self.command_name}: {e}" - ), + message = ( + f"Unexpected exception occurred when trying" + f" to apply command {self.command_name}: {e}" ) + return CommandOutput(status=False, message=message) @abstractmethod def to_dto(self) -> CommandDTO: diff --git a/antarest/study/storage/variantstudy/model/command/remove_renewables_cluster.py b/antarest/study/storage/variantstudy/model/command/remove_renewables_cluster.py index 96e3323c9f..28d47a3e33 100644 --- a/antarest/study/storage/variantstudy/model/command/remove_renewables_cluster.py +++ b/antarest/study/storage/variantstudy/model/command/remove_renewables_cluster.py @@ -1,20 +1,20 @@ -from typing import Any, List, Tuple, Dict +from typing import Any, Dict, List, Tuple from antarest.study.storage.rawstudy.model.filesystem.config.model import ( - FileStudyTreeConfig, Area, + FileStudyTreeConfig, ) from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy from antarest.study.storage.variantstudy.business.utils_binding_constraint import ( remove_area_cluster_from_binding_constraints, ) from antarest.study.storage.variantstudy.model.command.common import ( - CommandOutput, CommandName, + CommandOutput, ) from antarest.study.storage.variantstudy.model.command.icommand import ( - ICommand, MATCH_SIGNATURE_SEPARATOR, + ICommand, ) from antarest.study.storage.variantstudy.model.model import CommandDTO diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index e9779659c9..86ade2895a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,50 @@ Antares Web Changelog ===================== +v2.14.5 (2023-08-11) +-------------------- + +### Features + +* **ui-i18n:** add missing adequacy patch translations (#1680) ([8a06461](https://github.com/AntaresSimulatorTeam/AntaREST/commit/8a06461f4118227b94be7f587d37ea2430c70505)) +* **ui:** removed the "patch" number from the list of versions in the simulation launch dialog when it's equal to 0 (#1698) ([1bc0156](https://github.com/AntaresSimulatorTeam/AntaREST/commit/1bc0156c3e08e321e9ccc396b95cadeabf1c1fc7)) + + +### Bug Fixes + +* **web:** modified API response model to prevent Watcher's ValidationError (#1526) ([b0e48d1](https://github.com/AntaresSimulatorTeam/AntaREST/commit/b0e48d1bd31463cb6ce5e9aefeff761c016d0b35)) +* **xpansion:** corrected field types for Xpansion parameters (sensitivity analysis) ([3e481b9](https://github.com/AntaresSimulatorTeam/AntaREST/commit/3e481b9c8866ecc3dc42e351552e1ded036f62ad)) +* **variant:** fixed implementation of the method for extracting the difference between two studies ([c534785](https://github.com/AntaresSimulatorTeam/AntaREST/commit/c5347851da867a19b990e05c6516bedc7508c8ce)) +* **api:** added missing `use_leeway` field and validation rules in the hydro configuration form (#1650) ([27e46e5](https://github.com/AntaresSimulatorTeam/AntaREST/commit/27e46e5bda77aed65c84e82931d426b4b69a43bd)) +* **export:** ZIP outputs are no longer compressed before export (used by Xpansion) (#1656) ([cba6261](https://github.com/AntaresSimulatorTeam/AntaREST/commit/cba62613e19712240f74f417854e95bd588ba95d)) +* **log-parser:** simplified analysis and improved accuracy in displaying simulation progress for a study (#1682) ([2442674](https://github.com/AntaresSimulatorTeam/AntaREST/commit/24426749e9b6100eb3ab4b7159f615444242b95a)) +* **table-mode:** corrected reading of UI information when the study has only one area (#1674) ([55c4181](https://github.com/AntaresSimulatorTeam/AntaREST/commit/55c4181b64959c5e191fed2256437fc95787199f)) +* **table-mode:** issue to read area information in the case where the study has only one area (#1690) ([87d9617](https://github.com/AntaresSimulatorTeam/AntaREST/commit/87d961703cebdc037671fe73988903eb14dd9547)) +* **command:** improve INI reader to support API PUT `/v1/studies/{uuid}/raw` (#1461) ([9e5cf25](https://github.com/AntaresSimulatorTeam/AntaREST/commit/9e5cf25b2f69890016ea36f3be0e9ac03c7695b6)) +* **variant:** fixed time series deletion of renewable clusters (#1693) ([4ba1b17](https://github.com/AntaresSimulatorTeam/AntaREST/commit/4ba1b17dd3c1b8ea62a5a02f39d15e94a4b9a331)) +* **launcher:** fixing launcher versions display and creation of the endpoint `/v1/launcher/versions` ([410afc2](https://github.com/AntaresSimulatorTeam/AntaREST/commit/410afc2e4ecbb296878985839ee27f84bc70d9d8)) + and (#1672) ([a76f3a9](https://github.com/AntaresSimulatorTeam/AntaREST/commit/a76f3a9f01df0225d7fb54b20ba3ff599d749138)) +* **launcher:** set the default number of cores to 22 (instead of 12) (#1695) ([2c89799](https://github.com/AntaresSimulatorTeam/AntaREST/commit/2c8979916d46a0ed46a67bc75ac9a2e365e3f164)) + + +### Continuous Integration + +* upgrade mypy to v1.4.1 and Black to v23.7.0 for improved typing and formatting (#1685) ([7cff8c5](https://github.com/AntaresSimulatorTeam/AntaREST/commit/7cff8c56c38728a1b29eae0221bcc8226e9ca80c)) + + +### Tests + +* enhanced integration tests: refactored fixtures and resources ([70af9b1](https://github.com/AntaresSimulatorTeam/AntaREST/commit/70af9b167bf54d534696da8b781edda56ccee788)) + + +### Contributors + +laurent-laporte-pro, +MartinBelthle, +hdinia, +skamril + + v2.14.4 (2023-06-28) -------------------- diff --git a/setup.py b/setup.py index 610a0ca972..9a7fd1295c 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="AntaREST", - version="2.14.4", + version="2.14.5", description="Antares Server", long_description=long_description, long_description_content_type="text/markdown", diff --git a/sonar-project.properties b/sonar-project.properties index 2e78a84c9b..1156c995fd 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.14.4 +sonar.projectVersion=2.14.5 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/tests/integration/launcher_blueprint/__init__.py b/tests/integration/launcher_blueprint/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/integration/launcher_blueprint/test_solver_versions.py b/tests/integration/launcher_blueprint/test_solver_versions.py new file mode 100644 index 0000000000..34cf6737a9 --- /dev/null +++ b/tests/integration/launcher_blueprint/test_solver_versions.py @@ -0,0 +1,79 @@ +from typing import Sequence, Any +from unittest.mock import patch + +import pytest +from starlette.testclient import TestClient + + +@pytest.mark.integration_test +class TestSolverVersions: + """ + The purpose of this unit test is to check the `/v1/launcher/versions` endpoint. + """ + + def test_get_solver_versions( + self, + client: TestClient, + user_access_token: str, + ) -> None: + # Fetch the default server version from the configuration file. + # NOTE: the value is defined in `tests/integration/assets/config.template.yml`. + res = client.get( + "/v1/launcher/versions", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + res.raise_for_status() + actual = res.json() + assert actual == ["700"] + + res = client.get( + "/v1/launcher/versions?solver=default", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + res.raise_for_status() + actual = res.json() + assert actual == ["700"] + + res = client.get( + "/v1/launcher/versions?solver=local", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + res.raise_for_status() + actual = res.json() + assert actual == ["700"] + + res = client.get( + "/v1/launcher/versions?solver=slurm", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + res.raise_for_status() + actual = res.json() + assert actual == [] + + def test_get_solver_versions__default( + self, + client: TestClient, + user_access_token: str, + ) -> None: + """ + This unit test checks that the default value + of the `solver` parameter is indeed "default". + """ + + def get_versions(_: Any, solver: str) -> Sequence[str]: + # To distinguish between the default value and the local value, + # we use a different value for each `solver` value. + versions = {"default": ["123"], "local": ["456"], "slurm": ["798"]} + return versions[solver] + + with patch( + "antarest.launcher.service.LauncherService.get_solver_versions", + new=get_versions, + ): + res = client.get( + "/v1/launcher/versions", + headers={"Authorization": f"Bearer {user_access_token}"}, + ) + res.raise_for_status() + actual = res.json() + assert actual == ["123"] diff --git a/tests/integration/test_integration_variantmanager_tool.py b/tests/integration/test_integration_variantmanager_tool.py index cd7b3b3168..0dd9c02fb0 100644 --- a/tests/integration/test_integration_variantmanager_tool.py +++ b/tests/integration/test_integration_variantmanager_tool.py @@ -1,15 +1,16 @@ import os import urllib.parse -import zipfile from pathlib import Path from typing import List, Tuple - -import numpy as np -from fastapi import FastAPI -from starlette.testclient import TestClient +from zipfile import ZipFile from antarest.study.storage.rawstudy.io.reader import IniReader -from antarest.study.storage.study_upgrader import get_current_version +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, ) @@ -26,8 +27,17 @@ generate_study, parse_commands, ) +from fastapi import FastAPI +from starlette.testclient import TestClient -TEST_DIR: Path = Path(__file__).parent +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_study_with_server( @@ -64,7 +74,7 @@ def generate_study_with_server( def test_variant_manager(app: FastAPI, tmp_path: str): client = TestClient(app, raise_server_exceptions=False) - commands = parse_commands(TEST_DIR / "assets" / "commands1.json") + commands = parse_commands(test_dir / "assets" / "commands1.json") matrix_dir = Path(tmp_path) / "empty_matrix_store" matrix_dir.mkdir(parents=True, exist_ok=True) res, study_id = generate_study_with_server( @@ -74,12 +84,11 @@ def test_variant_manager(app: FastAPI, tmp_path: str): def test_parse_commands(tmp_path: str, app: FastAPI): - # sourcery skip: low-code-quality - base_dir = TEST_DIR / "assets" + base_dir = test_dir / "assets" export_path = Path(tmp_path) / "commands" study = "base_study" study_path = Path(tmp_path) / study - with zipfile.ZipFile(base_dir / "base_study.zip") as zip_output: + with ZipFile(base_dir / "base_study.zip") as zip_output: zip_output.extractall(path=tmp_path) output_dir = Path(export_path) / study study_info = IniReader().read(study_path / "study.antares") @@ -102,213 +111,157 @@ def test_parse_commands(tmp_path: str, app: FastAPI): ) assert generated_study_path.exists() and generated_study_path.is_dir() - single_column_empty_items = { - "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 = { - "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_4_cols_empty_items = { - "input/reserves/hub s.txt", - "input/reserves/hub n.txt", - "input/reserves/hub w.txt", - "input/reserves/hub e.txt", - } - # noinspection SpellCheckingInspection - fixed_8_cols_empty_items = { - "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", - } - matrix_items = ( - single_column_empty_items - | single_column_daily_empty_items - | fixed_4_cols_empty_items - | fixed_8_cols_empty_items + 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", + ] + 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", + ] + 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", + ] + 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", + ] + single_column_empty_data = generate_csv_string(default_scenario_hourly) + single_column_daily_empty_data = generate_csv_string( + default_scenario_daily ) - - ignorable_items = { - "Desktop.ini", - "study.antares", - "settings/comments.txt", - "settings/resources/study.ico", - } - - # fmt: off - single_column_empty_matrix = np.zeros((8760, 1), dtype=np.float64) - single_column_daily_empty_matrix = np.zeros((365, 1), dtype=np.float64) - fixed_4_columns_empty_matrix = np.zeros((8760, 4), dtype=np.float64) - fixed_8_columns_empty_matrix = np.zeros((8760, 8), dtype=np.float64) - # fmt: on - + 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: - cfg_path = Path(root).joinpath(item) - cfg_relpath = cfg_path.relative_to(study_path).as_posix() - - if cfg_relpath in ignorable_items: + if item in [ + "comments.txt", + "study.antares", + "Desktop.ini", + "study.ico", + ]: continue - - # print(f"Reading config data '{cfg_relpath}'...", file=sys.stderr) - actual_cfg_path = generated_study_path / cfg_relpath - if cfg_relpath in matrix_items: - data = np.loadtxt( - actual_cfg_path, - delimiter="\t", - dtype=np.float64, - ndmin=2, - ) - else: - data = actual_cfg_path.read_text() - - if cfg_relpath in single_column_empty_items: + elif f"{rel_path}{os.sep}{item}" in single_column_empty_items: assert ( - data.all() == single_column_empty_matrix.all(), - f"Invalid single_column_empty_matrix '{actual_cfg_path}'", - ) - elif cfg_relpath in single_column_daily_empty_items: + 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 ( - data.all() == single_column_daily_empty_matrix.all(), - f"Invalid single_column_daily_empty_matrix '{actual_cfg_path}'", - ) - elif cfg_relpath in fixed_4_cols_empty_items: + 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 ( - data.all() == fixed_4_columns_empty_matrix.all(), - f"Invalid fixed_4_columns_empty_matrix '{actual_cfg_path}'", - ) - elif cfg_relpath in fixed_8_cols_empty_items: + 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 ( - data.all() == fixed_8_columns_empty_matrix.all(), - f"Invalid fixed_8_columns_empty_matrix '{actual_cfg_path}'", - ) + generated_study_path / rel_path / item + ).read_text() == fixed_8_columns_empty_data else: - assert ( - data == cfg_path.read_text(), - f"Invalid config '{actual_cfg_path}'", - ) + 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): - # Extract resources in `assets` - assets_dir = tmp_path.joinpath("assets") - assets_dir.mkdir() - raw_study_dir = assets_dir.joinpath("raw_study") - variant_study_dir = assets_dir.joinpath("variant_study") - for src in [ - TEST_DIR.joinpath("assets/base_study.zip"), - TEST_DIR.joinpath("assets/variant_study.zip"), - ]: - with zipfile.ZipFile(src) as zip_output: - zip_output.extractall(path=assets_dir) - assets_dir.joinpath("base_study").replace(raw_study_dir) - - # Extract the commands used to "regenerate" the studies - results_dir = tmp_path.joinpath("results") - commands_dir = results_dir.joinpath("commands") - raw_study_commands = commands_dir.joinpath("raw_study") - variant_study_commands = commands_dir.joinpath("variant_study") - extract_commands(raw_study_dir, raw_study_commands) - extract_commands(variant_study_dir, variant_study_commands), + 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_path = Path(tmp_path) / base_study + base_study_commands = Path(export_path) / base_study + variant_study_commands = Path(export_path) / variant_study + variant_study_path = Path(tmp_path) / variant_study - study_version = get_current_version(raw_study_dir) + 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) - raw_generated_dir = results_dir.joinpath("raw_generated") res = generate_study( - raw_study_commands, - "raw1", - output=str(raw_generated_dir), - study_version=study_version, + base_study_commands, None, str(Path(export_path) / "base_generated") ) - assert res.success - variant_generated_dir = results_dir.joinpath("variant_generated") res = generate_study( variant_study_commands, - "variant1", - output=str(variant_generated_dir), - study_version=study_version, + None, + str(Path(export_path) / "variant_generated"), ) - assert res.success - # Calculates the differences between the RAW study and the variant study. generate_diff( - raw_study_commands, - variant_study_commands, - commands_dir, - study_version=study_version, + base_study_commands, variant_study_commands, output_study_commands ) - # After calculating the differences, the `generate_diff` function generates a list - # of commands that only contains the differences between the two studies. - # These differences are then applied to the original RAW study to regenerate - # a study in the final state. res = generate_study( - commands_dir, - "diff1", - output=str(raw_generated_dir), - study_version=study_version, + output_study_commands, None, output=str(output_study_path) ) assert res.success - # Quick compare generated raw and variant - for raw_path in raw_generated_dir.glob("**/*"): - if raw_path.is_dir() or raw_path.name in [ - "comments.txt", - "study.antares", - "Desktop.ini", - "study.ico", - ]: - continue - relpath = raw_path.relative_to(raw_generated_dir) - variant_path = variant_generated_dir.joinpath(relpath) - raw_text = raw_path.read_text(encoding="utf-8") - variant_text = variant_path.read_text(encoding="utf-8") - assert raw_text == variant_text, f"Invalid path '{relpath}'" + 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() diff --git a/tests/storage/repository/antares_io/reader/test_ini_reader.py b/tests/storage/repository/antares_io/reader/test_ini_reader.py index 0782447f6f..a24c1373e4 100644 --- a/tests/storage/repository/antares_io/reader/test_ini_reader.py +++ b/tests/storage/repository/antares_io/reader/test_ini_reader.py @@ -2,7 +2,6 @@ from pathlib import Path import pytest - from antarest.study.storage.rawstudy.io.reader import IniReader from antarest.study.storage.rawstudy.io.reader.ini_reader import ( MultipleSameKeysIniReader, diff --git a/tests/variantstudy/model/command/test_create_area.py b/tests/variantstudy/model/command/test_create_area.py index 58fecacbdf..0118c0bb71 100644 --- a/tests/variantstudy/model/command/test_create_area.py +++ b/tests/variantstudy/model/command/test_create_area.py @@ -4,6 +4,7 @@ import pytest from antarest.study.storage.rawstudy.model.filesystem.config.model import ( + ENR_MODELLING, transform_name_to_id, ENR_MODELLING, ) diff --git a/tests/variantstudy/model/command/test_remove_renewables_cluster.py b/tests/variantstudy/model/command/test_remove_renewables_cluster.py index 562c6d72ad..9e0e7efb61 100644 --- a/tests/variantstudy/model/command/test_remove_renewables_cluster.py +++ b/tests/variantstudy/model/command/test_remove_renewables_cluster.py @@ -1,8 +1,8 @@ from checksumdir import dirhash from antarest.study.storage.rawstudy.model.filesystem.config.model import ( - transform_name_to_id, ENR_MODELLING, + transform_name_to_id, ) from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy from antarest.study.storage.variantstudy.model.command.create_area import ( diff --git a/webapp/package.json b/webapp/package.json index a7bb313af9..4d5db51074 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -1,6 +1,6 @@ { "name": "antares-web", - "version": "2.14.4", + "version": "2.14.5", "private": true, "engines": { "node": "18.16.1" diff --git a/webapp/src/components/App/Studies/LauncherDialog.tsx b/webapp/src/components/App/Studies/LauncherDialog.tsx index 9698a79efa..136c6b5ca7 100644 --- a/webapp/src/components/App/Studies/LauncherDialog.tsx +++ b/webapp/src/components/App/Studies/LauncherDialog.tsx @@ -47,7 +47,7 @@ import CheckBoxFE from "../../common/fieldEditors/CheckBoxFE"; import { convertVersions } from "../../../services/utils"; const LAUNCH_DURATION_MAX_HOURS = 240; -const LAUNCH_LOAD_DEFAULT = 12; +const LAUNCH_LOAD_DEFAULT = 22; const LAUNCH_LOAD_SLIDER = { step: 1, min: 1, max: 24 }; interface Props { diff --git a/webapp/src/services/utils/index.ts b/webapp/src/services/utils/index.ts index be007f5836..5dc7a3fa70 100644 --- a/webapp/src/services/utils/index.ts +++ b/webapp/src/services/utils/index.ts @@ -148,8 +148,17 @@ export const exportText = (fileData: string, filename: string): void => { link.remove(); }; -export const displayVersionName = (version: string): string => - version.split("").join("."); +/** + * Gets the appropriate version name to display. + * The patch version is not displayed because it is not relevant. + * Its value is always 0 from the server. + * + * Ex: '820' -> '8.2' + * + * @param v Version in format '[major][minor]0' (ex: '820'). + * @returns Version in format '[major].[minor]' (ex: '8.2'). + */ +export const displayVersionName = (v: string): string => `${v[0]}.${v[1]}`; export const convertVersions = (versions: Array): Array => versions.map((version) => ({