diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index afff97ee35..bc5713a1f5 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -49,6 +49,7 @@ jobs:
python -m pip install --upgrade pip
pip install pydantic --no-binary pydantic
pip install -r requirements-dev.txt
+ pip install -r installer/requirements.txt
- name: 🐍 Install Windows dependencies
if: matrix.os == 'windows-latest'
diff --git a/antarest/core/exceptions.py b/antarest/core/exceptions.py
index 6fce8f0213..8358cc385d 100644
--- a/antarest/core/exceptions.py
+++ b/antarest/core/exceptions.py
@@ -361,11 +361,8 @@ def __str__(self) -> str:
class UnsupportedStudyVersion(HTTPException):
- def __init__(self, version: str) -> None:
- super().__init__(
- HTTPStatus.BAD_REQUEST,
- f"Study version {version} is not supported",
- )
+ def __init__(self, message: str) -> None:
+ super().__init__(HTTPStatus.BAD_REQUEST, message)
class UnsupportedOperationOnArchivedStudy(HTTPException):
diff --git a/antarest/study/business/adequacy_patch_management.py b/antarest/study/business/adequacy_patch_management.py
index f9ce0f01f3..7d837df7b6 100644
--- a/antarest/study/business/adequacy_patch_management.py
+++ b/antarest/study/business/adequacy_patch_management.py
@@ -1,6 +1,6 @@
from typing import Any, Dict, List, Optional
-from pydantic.types import StrictBool, confloat
+from pydantic.types import StrictBool, confloat, conint
from antarest.study.business.enum_ignore_case import EnumIgnoreCase
from antarest.study.business.utils import GENERAL_DATA_PATH, FieldInfo, FormFieldsBaseModel, execute_or_add_commands
@@ -28,7 +28,7 @@ class AdequacyPatchFormFields(FormFieldsBaseModel):
check_csr_cost_function: Optional[StrictBool]
threshold_initiate_curtailment_sharing_rule: Optional[ThresholdType] # type: ignore
threshold_display_local_matching_rule_violations: Optional[ThresholdType] # type: ignore
- threshold_csr_variable_bounds_relaxation: Optional[ThresholdType] # type: ignore
+ threshold_csr_variable_bounds_relaxation: Optional[conint(ge=0, strict=True)] # type: ignore
ADEQUACY_PATCH_PATH = f"{GENERAL_DATA_PATH}/adequacy patch"
diff --git a/antarest/study/service.py b/antarest/study/service.py
index 5a2bfcda2a..d270e8daa3 100644
--- a/antarest/study/service.py
+++ b/antarest/study/service.py
@@ -32,7 +32,6 @@
StudyVariantUpgradeError,
TaskAlreadyRunning,
UnsupportedOperationOnArchivedStudy,
- UnsupportedStudyVersion,
)
from antarest.core.filetransfer.model import FileDownloadTaskDTO
from antarest.core.filetransfer.service import FileTransferManager
@@ -116,12 +115,7 @@
from antarest.study.storage.rawstudy.raw_study_service import RawStudyService
from antarest.study.storage.storage_service import StudyStorageService
from antarest.study.storage.study_download_utils import StudyDownloader, get_output_variables_information
-from antarest.study.storage.study_upgrader import (
- find_next_version,
- get_current_version,
- should_study_be_denormalized,
- upgrade_study,
-)
+from antarest.study.storage.study_upgrader import StudyUpgrader, check_versions_coherence, find_next_version
from antarest.study.storage.utils import assert_permission, get_start_date, is_managed, remove_from_cache
from antarest.study.storage.variantstudy.business.utils import transform_command_to_dto
from antarest.study.storage.variantstudy.model.command.icommand import ICommand
@@ -192,13 +186,13 @@ def _upgrade_study(self) -> None:
self.storage_service.variant_study_service.clear_snapshot(study_to_upgrade)
else:
study_path = Path(study_to_upgrade.path)
- current_version = get_current_version(study_path)
- if is_managed(study_to_upgrade) and should_study_be_denormalized(current_version, target_version):
+ study_upgrader = StudyUpgrader(study_path, target_version)
+ if is_managed(study_to_upgrade) and study_upgrader.should_denormalize_study():
# We have to denormalize the study because the upgrade impacts study matrices
file_study = self.storage_service.get_storage(study_to_upgrade).get_raw(study_to_upgrade)
file_study.tree.denormalize()
is_study_denormalized = True
- upgrade_study(study_path, target_version)
+ study_upgrader.upgrade()
remove_from_cache(self.cache_service, study_to_upgrade.id)
study_to_upgrade.version = target_version
self.repository.save(study_to_upgrade)
@@ -2426,13 +2420,15 @@ def upgrade_study(
# First check if the study is a variant study, if so throw an error
if isinstance(study, VariantStudy):
raise StudyVariantUpgradeError(True)
- # If the study is a parent raw study, throw an error
+ # If the study is a parent raw study and has variants, throw an error
elif self.repository.has_children(study_id):
raise StudyVariantUpgradeError(False)
- target_version = target_version or find_next_version(study.version)
+ # Checks versions coherence before launching the task
if not target_version:
- raise UnsupportedStudyVersion(study.version)
+ target_version = find_next_version(study.version)
+ else:
+ check_versions_coherence(study.version, target_version)
task_name = f"Upgrade study {study.name} ({study.id}) to version {target_version}"
study_tasks = self.task_service.list_tasks(
diff --git a/antarest/study/storage/study_upgrader/__init__.py b/antarest/study/storage/study_upgrader/__init__.py
index 6bfbf712be..c2a8427c94 100644
--- a/antarest/study/storage/study_upgrader/__init__.py
+++ b/antarest/study/storage/study_upgrader/__init__.py
@@ -1,59 +1,14 @@
-import logging
-import re
-import shutil
-import tempfile
-import time
-import typing as t
from http import HTTPStatus
from http.client import HTTPException
from pathlib import Path
-from antarest.core.exceptions import StudyValidationError
+from antares.study.version.exceptions import ApplicationError
+from antares.study.version.model.study_version import StudyVersion
+from antares.study.version.upgrade_app import UpgradeApp
-from .upgrader_710 import upgrade_710
-from .upgrader_720 import upgrade_720
-from .upgrader_800 import upgrade_800
-from .upgrader_810 import upgrade_810
-from .upgrader_820 import upgrade_820
-from .upgrader_830 import upgrade_830
-from .upgrader_840 import upgrade_840
-from .upgrader_850 import upgrade_850
-from .upgrader_860 import upgrade_860
-from .upgrader_870 import upgrade_870
-from .upgrader_880 import upgrade_880
+from antarest.core.exceptions import UnsupportedStudyVersion
-STUDY_ANTARES = "study.antares"
-"""
-Main file of an Antares study containing the caption, the version, the creation date, etc.
-"""
-
-logger = logging.getLogger(__name__)
-
-
-class UpgradeMethod(t.NamedTuple):
- """Raw study upgrade method (old version, new version, upgrade function)."""
-
- old: str
- new: str
- method: t.Callable[[Path], None]
- files: t.List[Path]
-
-
-_GENERAL_DATA_PATH = Path("settings/generaldata.ini")
-
-UPGRADE_METHODS = [
- UpgradeMethod("700", "710", upgrade_710, [_GENERAL_DATA_PATH]),
- UpgradeMethod("710", "720", upgrade_720, []),
- UpgradeMethod("720", "800", upgrade_800, [_GENERAL_DATA_PATH]),
- UpgradeMethod("800", "810", upgrade_810, [_GENERAL_DATA_PATH, Path("input")]),
- UpgradeMethod("810", "820", upgrade_820, [Path("input/links")]),
- UpgradeMethod("820", "830", upgrade_830, [_GENERAL_DATA_PATH, Path("input/areas")]),
- UpgradeMethod("830", "840", upgrade_840, [_GENERAL_DATA_PATH]),
- UpgradeMethod("840", "850", upgrade_850, [_GENERAL_DATA_PATH]),
- UpgradeMethod("850", "860", upgrade_860, [Path("input"), _GENERAL_DATA_PATH]),
- UpgradeMethod("860", "870", upgrade_870, [Path("input/thermal"), Path("input/bindingconstraints")]),
- UpgradeMethod("870", "880", upgrade_880, [Path("input/st-storage/clusters")]),
-]
+AVAILABLE_VERSIONS = ["700", "710", "720", "800", "810", "820", "830", "840", "850", "860", "870", "880"]
class InvalidUpgrade(HTTPException):
@@ -61,229 +16,55 @@ def __init__(self, message: str) -> None:
super().__init__(HTTPStatus.UNPROCESSABLE_ENTITY, message)
-def find_next_version(from_version: str) -> str:
- """
- Find the next study version from the given version.
-
- Args:
- from_version: The current version as a string.
-
- Returns:
- The next version as a string.
- If no next version was found, returns an empty string.
- """
- return next(
- (meth.new for meth in UPGRADE_METHODS if from_version == meth.old),
- "",
- )
-
-
-def upgrade_study(study_path: Path, target_version: str) -> None:
- with tempfile.TemporaryDirectory(suffix=".upgrade.tmp", prefix="~", dir=study_path.parent) as path:
- tmp_dir = Path(path)
+class StudyUpgrader:
+ def __init__(self, study_path: Path, target_version: str):
try:
- src_version = get_current_version(study_path)
- files_to_upgrade = can_upgrade_version(src_version, target_version)
- files_to_retrieve = _copies_only_necessary_files(files_to_upgrade, study_path, tmp_dir)
- _do_upgrade(tmp_dir, src_version, target_version)
- except (StudyValidationError, InvalidUpgrade) as e:
- logger.warning(str(e))
- raise
- except Exception as e:
- logger.error(f"Unhandled exception : {e}", exc_info=True)
- raise
+ version = StudyVersion.parse(target_version)
+ except ValueError as e:
+ raise InvalidUpgrade(str(e)) from e
else:
- _replace_safely_original_files(files_to_retrieve, study_path, tmp_dir)
-
+ self.app = UpgradeApp(study_path, version=version)
-def get_current_version(study_path: Path) -> str:
- """
- Get the current version of a study.
+ def upgrade(self) -> None:
+ try:
+ self.app()
+ except ApplicationError as e:
+ raise InvalidUpgrade(str(e)) from e
- Args:
- study_path: Path to the study.
+ def should_denormalize_study(self) -> bool:
+ return self.app.should_denormalize
- Returns:
- The current version of the study.
- Raises:
- StudyValidationError: If the version number is not found in the
- `study.antares` file or does not match the expected format.
- """
-
- antares_path = study_path / STUDY_ANTARES
- pattern = r"version\s*=\s*([\w.-]+)\s*"
- with antares_path.open(encoding="utf-8") as lines:
- for line in lines:
- if match := re.fullmatch(pattern, line):
- return match[1].rstrip()
- raise StudyValidationError(
- f"File parsing error: the version number is not found in '{antares_path}'"
- f" or does not match the expected '{pattern}' format."
- )
+def _get_version_index(version: str) -> int:
+ try:
+ return AVAILABLE_VERSIONS.index(version)
+ except ValueError:
+ raise UnsupportedStudyVersion(f"Version '{version}' isn't among supported versions: {AVAILABLE_VERSIONS}")
-def can_upgrade_version(from_version: str, to_version: str) -> t.List[Path]:
+def find_next_version(from_version: str) -> str:
"""
- Checks if upgrading from one version to another is possible.
+ Find the next study version from the given version.
Args:
- from_version: The current version of the study.
- to_version: The target version of the study.
+ from_version: The current version as a string.
Returns:
- If the upgrade is possible, the list of concerned folders and files
+ The next version as a string.
Raises:
- InvalidUpgrade: If the upgrade is not possible.
- """
- list_versions = []
- if from_version == to_version:
- raise InvalidUpgrade(f"Your study is already in version '{to_version}'")
-
- sources = [u.old for u in UPGRADE_METHODS]
- if from_version not in sources:
- raise InvalidUpgrade(f"Version '{from_version}' unknown: possible versions are {', '.join(sources)}")
-
- targets = [u.new for u in UPGRADE_METHODS]
- if to_version not in targets:
- raise InvalidUpgrade(f"Version '{to_version}' unknown: possible versions are {', '.join(targets)}")
-
- files = [u.files for u in UPGRADE_METHODS]
- curr_version = from_version
- for src, dst, file in zip(sources, targets, files):
- if curr_version == src:
- for path in file:
- if path not in list_versions:
- list_versions.append(path)
- curr_version = dst
- if curr_version == to_version:
- return list_versions
-
- # This code must be unreachable!
- raise InvalidUpgrade(
- f"Impossible to upgrade from version '{from_version}'"
- f" to version '{to_version}':"
- f" missing value in `UPGRADE_METHODS`."
- )
-
-
-def _update_study_antares_file(target_version: str, study_path: Path) -> None:
- antares_path = study_path / STUDY_ANTARES
- content = antares_path.read_text(encoding="utf-8")
- content = re.sub(
- r"^version\s*=.*$",
- f"version = {target_version}",
- content,
- flags=re.MULTILINE,
- )
- content = re.sub(
- r"^lastsave\s*=.*$",
- f"lastsave = {int(time.time())}",
- content,
- flags=re.MULTILINE,
- )
- antares_path.write_text(content, encoding="utf-8")
-
-
-def _copies_only_necessary_files(files_to_upgrade: t.List[Path], study_path: Path, tmp_path: Path) -> t.List[Path]:
- """
- Copies files concerned by the version upgrader into a temporary directory.
- Args:
- study_path: Path to the study.
- tmp_path: Path to the temporary directory where the file modification will be performed.
- files_to_upgrade: List[Path]: List of the files and folders concerned by the upgrade.
- Returns:
- The list of files and folders that were really copied. It's the same as files_to_upgrade but
- without any children that has parents already in the list.
+ UnsupportedStudyVersion if the current version is not supported or if the study is already in last version.
"""
- files_to_copy = _filters_out_children_files(files_to_upgrade)
- files_to_copy.append(Path(STUDY_ANTARES))
- files_to_retrieve = []
- for path in files_to_copy:
- entire_path = study_path / path
- if not entire_path.exists():
- # This can happen when upgrading a study to v8.8.
- continue
- if entire_path.is_dir():
- if not (tmp_path / path).exists():
- shutil.copytree(entire_path, tmp_path / path, dirs_exist_ok=True)
- files_to_retrieve.append(path)
- elif len(path.parts) == 1:
- shutil.copy(entire_path, tmp_path / path)
- files_to_retrieve.append(path)
- else:
- parent_path = path.parent
- (tmp_path / parent_path).mkdir(parents=True, exist_ok=True)
- shutil.copy(entire_path, tmp_path / parent_path)
- files_to_retrieve.append(path)
- return files_to_retrieve
-
-
-def _filters_out_children_files(files_to_upgrade: t.List[Path]) -> t.List[Path]:
- """
- Filters out children paths of "input" if "input" is already in the list.
- Args:
- files_to_upgrade: List[Path]: List of the files and folders concerned by the upgrade.
- Returns:
- The list of files filtered
- """
- is_input_in_files_to_upgrade = Path("input") in files_to_upgrade
- if is_input_in_files_to_upgrade:
- files_to_keep = [Path("input")]
- files_to_keep.extend(path for path in files_to_upgrade if "input" not in path.parts)
- return files_to_keep
- return files_to_upgrade
-
-
-def _replace_safely_original_files(files_to_replace: t.List[Path], study_path: Path, tmp_path: Path) -> None:
- """
- Replace files/folders of the study that should be upgraded by their copy already upgraded in the tmp directory.
- It uses Path.rename() and an intermediary tmp directory to swap the folders safely.
- In the end, all tmp directories are removed.
- Args:
- study_path: Path to the study.
- tmp_path: Path to the temporary directory where the file modification will be performed.
- files_to_replace: List[Path]: List of files and folders that were really copied
- (cf. _copies_only_necessary_files's doc just above)
- """
- for k, path in enumerate(files_to_replace):
- backup_dir = Path(
- tempfile.mkdtemp(
- suffix=f".backup_{k}.tmp",
- prefix="~",
- dir=study_path.parent,
- )
- )
- backup_dir.rmdir()
- original_path = study_path / path
- original_path.rename(backup_dir)
- (tmp_path / path).rename(original_path)
- if backup_dir.is_dir():
- shutil.rmtree(backup_dir)
- else:
- backup_dir.unlink()
-
-
-def _do_upgrade(study_path: Path, src_version: str, target_version: str) -> None:
- _update_study_antares_file(target_version, study_path)
- curr_version = src_version
- for old, new, method, _ in UPGRADE_METHODS:
- if curr_version == old and curr_version != target_version:
- method(study_path)
- curr_version = new
-
-
-def should_study_be_denormalized(src_version: str, target_version: str) -> bool:
- try:
- can_upgrade_version(src_version, target_version)
- except InvalidUpgrade:
- return False
- curr_version = src_version
- list_of_upgrades = []
- for old, new, method, _ in UPGRADE_METHODS:
- if curr_version == old and curr_version != target_version:
- list_of_upgrades.append(new)
- curr_version = new
- # These upgrades alter matrices so the study needs to be denormalized
- return "820" in list_of_upgrades or "870" in list_of_upgrades
+ start_pos = _get_version_index(from_version)
+ if start_pos == len(AVAILABLE_VERSIONS) - 1:
+ raise UnsupportedStudyVersion(f"Your study is already in the latest supported version: '{from_version}'")
+ return AVAILABLE_VERSIONS[start_pos + 1]
+
+
+def check_versions_coherence(from_version: str, target_version: str) -> None:
+ start_pos = _get_version_index(from_version)
+ final_pos = _get_version_index(target_version)
+ if final_pos == start_pos:
+ raise InvalidUpgrade(f"Your study is already in the version you asked: {from_version}")
+ elif final_pos < start_pos:
+ raise InvalidUpgrade(f"Cannot downgrade your study version : from {from_version} to {target_version}")
diff --git a/antarest/study/storage/study_upgrader/upgrader_710.py b/antarest/study/storage/study_upgrader/upgrader_710.py
deleted file mode 100644
index b1679b3342..0000000000
--- a/antarest/study/storage/study_upgrader/upgrader_710.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from pathlib import Path
-
-from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.rawstudy.ini_writer import IniWriter
-from antarest.study.storage.rawstudy.model.filesystem.root.settings.generaldata import DUPLICATE_KEYS
-
-GENERAL_DATA_PATH = "settings/generaldata.ini"
-
-
-def upgrade_710(study_path: Path) -> None:
- """
- Upgrade the study configuration to version 710.
-
- NOTE:
- The file `study.antares` is not upgraded here.
-
- Args:
- study_path: path to the study directory.
- """
-
- reader = IniReader(DUPLICATE_KEYS)
- data = reader.read(study_path / GENERAL_DATA_PATH)
- data["general"]["geographic-trimming"] = data["general"]["filtering"]
- data["general"]["thematic-trimming"] = False
- data["optimization"]["link-type"] = "local"
- data["other preferences"]["hydro-pricing-mode"] = "fast"
- del data["general"]["filtering"]
- writer = IniWriter(special_keys=DUPLICATE_KEYS)
- writer.write(data, study_path / GENERAL_DATA_PATH)
diff --git a/antarest/study/storage/study_upgrader/upgrader_720.py b/antarest/study/storage/study_upgrader/upgrader_720.py
deleted file mode 100644
index 25e740baed..0000000000
--- a/antarest/study/storage/study_upgrader/upgrader_720.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from pathlib import Path
-
-
-def upgrade_720(study_path: Path) -> None:
- # There is no input modification between the 7.1.0 and the 7.2.0 version
- pass
diff --git a/antarest/study/storage/study_upgrader/upgrader_800.py b/antarest/study/storage/study_upgrader/upgrader_800.py
deleted file mode 100644
index b53a792985..0000000000
--- a/antarest/study/storage/study_upgrader/upgrader_800.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from pathlib import Path
-
-from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.rawstudy.ini_writer import IniWriter
-from antarest.study.storage.rawstudy.model.filesystem.root.settings.generaldata import DUPLICATE_KEYS
-
-GENERAL_DATA_PATH = "settings/generaldata.ini"
-
-
-def upgrade_800(study_path: Path) -> None:
- """
- Upgrade the study configuration to version 800.
-
- NOTE:
- The file `study.antares` is not upgraded here.
-
- Args:
- study_path: path to the study directory.
- """
-
- reader = IniReader(DUPLICATE_KEYS)
- data = reader.read(study_path / GENERAL_DATA_PATH)
- data["other preferences"]["hydro-heuristic-policy"] = "accommodate rule curves"
- data["optimization"]["include-exportstructure"] = False
- data["optimization"]["include-unfeasible-problem-behavior"] = "error-verbose"
- data["general"]["custom-scenario"] = data["general"]["custom-ts-numbers"]
- del data["general"]["custom-ts-numbers"]
- writer = IniWriter(special_keys=DUPLICATE_KEYS)
- writer.write(data, study_path / GENERAL_DATA_PATH)
diff --git a/antarest/study/storage/study_upgrader/upgrader_810.py b/antarest/study/storage/study_upgrader/upgrader_810.py
deleted file mode 100644
index e28ef4d4b6..0000000000
--- a/antarest/study/storage/study_upgrader/upgrader_810.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from pathlib import Path
-
-from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.rawstudy.ini_writer import IniWriter
-from antarest.study.storage.rawstudy.model.filesystem.root.settings.generaldata import DUPLICATE_KEYS
-
-GENERAL_DATA_PATH = "settings/generaldata.ini"
-
-
-def upgrade_810(study_path: Path) -> None:
- """
- Upgrade the study configuration to version 810.
-
- NOTE:
- The file `study.antares` is not upgraded here.
-
- Args:
- study_path: path to the study directory.
- """
-
- reader = IniReader(DUPLICATE_KEYS)
- data = reader.read(study_path / GENERAL_DATA_PATH)
- data["other preferences"]["renewable-generation-modelling"] = "aggregated"
- writer = IniWriter(special_keys=DUPLICATE_KEYS)
- writer.write(data, study_path / GENERAL_DATA_PATH)
- study_path.joinpath("input", "renewables", "clusters").mkdir(parents=True, exist_ok=True)
- study_path.joinpath("input", "renewables", "series").mkdir(parents=True, exist_ok=True)
diff --git a/antarest/study/storage/study_upgrader/upgrader_820.py b/antarest/study/storage/study_upgrader/upgrader_820.py
deleted file mode 100644
index f5416d0180..0000000000
--- a/antarest/study/storage/study_upgrader/upgrader_820.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import glob
-from pathlib import Path
-from typing import cast
-
-import numpy as np
-import numpy.typing as npt
-import pandas
-
-
-def upgrade_820(study_path: Path) -> None:
- """
- Upgrade the study configuration to version 820.
-
- NOTE:
- The file `study.antares` is not upgraded here.
-
- Args:
- study_path: path to the study directory.
- """
-
- links = glob.glob(str(study_path / "input" / "links" / "*"))
- if len(links) > 0:
- for folder in links:
- folder_path = Path(folder)
- all_txt = glob.glob(str(folder_path / "*.txt"))
- if len(all_txt) > 0:
- (folder_path / "capacities").mkdir(exist_ok=True)
- for txt in all_txt:
- df = pandas.read_csv(txt, sep="\t", header=None)
- df_parameters = df.iloc[:, 2:8]
- df_direct = df.iloc[:, 0]
- df_indirect = df.iloc[:, 1]
- name = Path(txt).stem
- # noinspection PyTypeChecker
- np.savetxt(
- folder_path / f"{name}_parameters.txt",
- cast(npt.NDArray[np.float64], df_parameters.values),
- delimiter="\t",
- fmt="%.6f",
- )
- # noinspection PyTypeChecker
- np.savetxt(
- folder_path / "capacities" / f"{name}_direct.txt",
- cast(npt.NDArray[np.float64], df_direct.values),
- delimiter="\t",
- fmt="%.6f",
- )
- # noinspection PyTypeChecker
- np.savetxt(
- folder_path / "capacities" / f"{name}_indirect.txt",
- cast(npt.NDArray[np.float64], df_indirect.values),
- delimiter="\t",
- fmt="%.6f",
- )
- (folder_path / f"{name}.txt").unlink()
diff --git a/antarest/study/storage/study_upgrader/upgrader_830.py b/antarest/study/storage/study_upgrader/upgrader_830.py
deleted file mode 100644
index 414db9b8e2..0000000000
--- a/antarest/study/storage/study_upgrader/upgrader_830.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import glob
-from pathlib import Path
-
-from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.rawstudy.ini_writer import IniWriter
-from antarest.study.storage.rawstudy.model.filesystem.root.settings.generaldata import DUPLICATE_KEYS
-
-GENERAL_DATA_PATH = "settings/generaldata.ini"
-
-
-def upgrade_830(study_path: Path) -> None:
- """
- Upgrade the study configuration to version 830.
-
- NOTE:
- The file `study.antares` is not upgraded here.
-
- Args:
- study_path: path to the study directory.
- """
-
- reader = IniReader(DUPLICATE_KEYS)
- data = reader.read(study_path / GENERAL_DATA_PATH)
- data["adequacy patch"] = {
- "include-adq-patch": False,
- "set-to-null-ntc-between-physical-out-for-first-step": True,
- "set-to-null-ntc-from-physical-out-to-physical-in-for-first-step": True,
- }
- data["optimization"]["include-split-exported-mps"] = False
- writer = IniWriter(special_keys=DUPLICATE_KEYS)
- writer.write(data, study_path / GENERAL_DATA_PATH)
- areas = glob.glob(str(study_path / "input" / "areas" / "*"))
- for folder in areas:
- folder_path = Path(folder)
- if folder_path.is_dir():
- writer = IniWriter(special_keys=DUPLICATE_KEYS)
- writer.write(
- {"adequacy-patch": {"adequacy-patch-mode": "outside"}},
- folder_path / "adequacy_patch.ini",
- )
diff --git a/antarest/study/storage/study_upgrader/upgrader_840.py b/antarest/study/storage/study_upgrader/upgrader_840.py
deleted file mode 100644
index a96aa0072a..0000000000
--- a/antarest/study/storage/study_upgrader/upgrader_840.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from pathlib import Path
-
-from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.rawstudy.ini_writer import IniWriter
-from antarest.study.storage.rawstudy.model.filesystem.root.settings.generaldata import DUPLICATE_KEYS
-
-GENERAL_DATA_PATH = "settings/generaldata.ini"
-MAPPING_TRANSMISSION_CAPACITIES = {
- True: "local-values",
- False: "null-for-all-links",
- "infinite": "infinite-for-all-links",
-}
-
-
-def upgrade_840(study_path: Path) -> None:
- """
- Upgrade the study configuration to version 840.
-
- NOTE:
- The file `study.antares` is not upgraded here.
-
- Args:
- study_path: path to the study directory.
- """
-
- reader = IniReader(DUPLICATE_KEYS)
- data = reader.read(study_path / GENERAL_DATA_PATH)
- data["optimization"]["transmission-capacities"] = MAPPING_TRANSMISSION_CAPACITIES[
- data["optimization"]["transmission-capacities"]
- ]
- del data["optimization"]["include-split-exported-mps"]
- writer = IniWriter(special_keys=DUPLICATE_KEYS)
- writer.write(data, study_path / GENERAL_DATA_PATH)
diff --git a/antarest/study/storage/study_upgrader/upgrader_850.py b/antarest/study/storage/study_upgrader/upgrader_850.py
deleted file mode 100644
index 08695f1e3d..0000000000
--- a/antarest/study/storage/study_upgrader/upgrader_850.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from pathlib import Path
-
-from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.rawstudy.ini_writer import IniWriter
-from antarest.study.storage.rawstudy.model.filesystem.root.settings.generaldata import DUPLICATE_KEYS
-
-# noinspection SpellCheckingInspection
-GENERAL_DATA_PATH = "settings/generaldata.ini"
-
-
-def upgrade_850(study_path: Path) -> None:
- """
- Upgrade the study configuration to version 850.
-
- NOTE:
- The file `study.antares` is not upgraded here.
-
- Args:
- study_path: path to the study directory.
- """
-
- reader = IniReader(DUPLICATE_KEYS)
- data = reader.read(study_path / GENERAL_DATA_PATH)
-
- data["adequacy patch"]["price-taking-order"] = "DENS"
- data["adequacy patch"]["include-hurdle-cost-csr"] = False
- data["adequacy patch"]["check-csr-cost-function"] = False
- data["adequacy patch"]["threshold-initiate-curtailment-sharing-rule"] = 0.0
- data["adequacy patch"]["threshold-display-local-matching-rule-violations"] = 0.0
- data["adequacy patch"]["threshold-csr-variable-bounds-relaxation"] = 3
-
- writer = IniWriter(special_keys=DUPLICATE_KEYS)
- writer.write(data, study_path / GENERAL_DATA_PATH)
diff --git a/antarest/study/storage/study_upgrader/upgrader_860.py b/antarest/study/storage/study_upgrader/upgrader_860.py
deleted file mode 100644
index 4d6f873f0d..0000000000
--- a/antarest/study/storage/study_upgrader/upgrader_860.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from pathlib import Path
-
-from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.rawstudy.ini_writer import IniWriter
-from antarest.study.storage.rawstudy.model.filesystem.config.model import transform_name_to_id
-from antarest.study.storage.rawstudy.model.filesystem.root.settings.generaldata import DUPLICATE_KEYS
-
-# noinspection SpellCheckingInspection
-GENERAL_DATA_PATH = "settings/generaldata.ini"
-
-
-def upgrade_860(study_path: Path) -> None:
- """
- Upgrade the study configuration to version 860.
-
- NOTE:
- The file `study.antares` is not upgraded here.
-
- Args:
- study_path: path to the study directory.
- """
-
- reader = IniReader(DUPLICATE_KEYS)
- data = reader.read(study_path / GENERAL_DATA_PATH)
- data["adequacy patch"]["enable-first-step "] = True
- writer = IniWriter(special_keys=DUPLICATE_KEYS)
- writer.write(data, study_path / GENERAL_DATA_PATH)
-
- study_path.joinpath("input", "st-storage", "clusters").mkdir(parents=True, exist_ok=True)
- study_path.joinpath("input", "st-storage", "series").mkdir(parents=True, exist_ok=True)
- list_areas = (
- study_path.joinpath("input", "areas", "list.txt").read_text(encoding="utf-8").splitlines(keepends=False)
- )
- for area_name in list_areas:
- area_id = transform_name_to_id(area_name)
- st_storage_path = study_path.joinpath("input", "st-storage", "clusters", area_id)
- st_storage_path.mkdir(parents=True, exist_ok=True)
- (st_storage_path / "list.ini").touch()
-
- hydro_series_path = study_path.joinpath("input", "hydro", "series", area_id)
- hydro_series_path.mkdir(parents=True, exist_ok=True)
- (hydro_series_path / "mingen.txt").touch()
diff --git a/antarest/study/storage/study_upgrader/upgrader_870.py b/antarest/study/storage/study_upgrader/upgrader_870.py
deleted file mode 100644
index 9b67a77c52..0000000000
--- a/antarest/study/storage/study_upgrader/upgrader_870.py
+++ /dev/null
@@ -1,64 +0,0 @@
-import typing as t
-from pathlib import Path
-
-import numpy as np
-import numpy.typing as npt
-import pandas as pd
-
-from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.rawstudy.ini_writer import IniWriter
-
-
-# noinspection SpellCheckingInspection
-def upgrade_870(study_path: Path) -> None:
- """
- Upgrade the study configuration to version 870.
-
- NOTE:
- The file `study.antares` is not upgraded here.
-
- Args:
- study_path: path to the study directory.
- """
-
- # Split existing binding constraints in 3 different files
- binding_constraints_path = study_path / "input" / "bindingconstraints"
- binding_constraints_files = binding_constraints_path.glob("*.txt")
- for file in binding_constraints_files:
- name = file.stem
- if file.stat().st_size == 0:
- lt, gt, eq = pd.Series(), pd.Series(), pd.Series()
- else:
- df = pd.read_csv(file, sep="\t", header=None)
- lt, gt, eq = df.iloc[:, 0], df.iloc[:, 1], df.iloc[:, 2]
- for term, suffix in zip([lt, gt, eq], ["lt", "gt", "eq"]):
- # noinspection PyTypeChecker
- np.savetxt(
- binding_constraints_path / f"{name}_{suffix}.txt",
- t.cast(npt.NDArray[np.float64], term.values),
- delimiter="\t",
- fmt="%.6f",
- )
- file.unlink()
-
- # Add property group for every section in .ini file
- ini_file_path = binding_constraints_path / "bindingconstraints.ini"
- data = IniReader().read(ini_file_path)
- for section in data:
- data[section]["group"] = "default"
- IniWriter().write(data, ini_file_path)
-
- # Add properties for thermal clusters in .ini file
- ini_files = study_path.glob("input/thermal/clusters/*/list.ini")
- thermal_path = study_path / Path("input/thermal/series")
- for ini_file_path in ini_files:
- data = IniReader().read(ini_file_path)
- area_id = ini_file_path.parent.name
- for cluster in data:
- new_thermal_path = thermal_path / area_id / cluster.lower()
- (new_thermal_path / "CO2Cost.txt").touch()
- (new_thermal_path / "fuelCost.txt").touch()
- data[cluster]["costgeneration"] = "SetManually"
- data[cluster]["efficiency"] = 100
- data[cluster]["variableomcost"] = 0
- IniWriter().write(data, ini_file_path)
diff --git a/antarest/study/storage/study_upgrader/upgrader_880.py b/antarest/study/storage/study_upgrader/upgrader_880.py
deleted file mode 100644
index 0de50cff4b..0000000000
--- a/antarest/study/storage/study_upgrader/upgrader_880.py
+++ /dev/null
@@ -1,32 +0,0 @@
-import glob
-from pathlib import Path
-
-from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.rawstudy.ini_writer import IniWriter
-from antarest.study.storage.rawstudy.model.filesystem.root.settings.generaldata import DUPLICATE_KEYS
-
-
-# noinspection SpellCheckingInspection
-def upgrade_880(study_path: Path) -> None:
- """
- Upgrade the study configuration to version 880.
-
- NOTE:
- The file `study.antares` is not upgraded here.
-
- Args:
- study_path: path to the study directory.
- """
- st_storage_path = study_path / "input" / "st-storage" / "clusters"
- if not st_storage_path.exists():
- # The folder only exists for studies in v8.6+ that have some short term storage clusters.
- # For every other case, this upgrader has nothing to do.
- return
- writer = IniWriter(special_keys=DUPLICATE_KEYS)
- cluster_files = glob.glob(str(st_storage_path / "*" / "list.ini"))
- for file in cluster_files:
- file_path = Path(file)
- cluster_list = IniReader().read(file_path)
- for cluster in cluster_list:
- cluster_list[cluster]["enabled"] = True
- writer.write(cluster_list, file_path)
diff --git a/antarest/study/storage/utils.py b/antarest/study/storage/utils.py
index 5dc97b081b..5fbbf9ccb5 100644
--- a/antarest/study/storage/utils.py
+++ b/antarest/study/storage/utils.py
@@ -152,7 +152,8 @@ def remove_from_cache(cache: ICache, root_id: str) -> None:
def create_new_empty_study(version: str, path_study: Path, path_resources: Path) -> None:
version_template: t.Optional[str] = STUDY_REFERENCE_TEMPLATES.get(version, None)
if version_template is None:
- raise UnsupportedStudyVersion(version)
+ msg = f"{version} is not a supported version, supported versions are: {list(STUDY_REFERENCE_TEMPLATES.keys())}"
+ raise UnsupportedStudyVersion(msg)
empty_study_zip = path_resources / version_template
diff --git a/antarest/tools/cli.py b/antarest/tools/cli.py
index 902eee6d3a..9a591b0d9e 100644
--- a/antarest/tools/cli.py
+++ b/antarest/tools/cli.py
@@ -5,7 +5,7 @@
import click
from antarest.study.model import NEW_DEFAULT_STUDY_VERSION
-from antarest.study.storage.study_upgrader import upgrade_study
+from antarest.study.storage.study_upgrader import StudyUpgrader
from antarest.tools.lib import extract_commands, generate_diff, generate_study
@@ -164,7 +164,8 @@ def cli_upgrade_study(study_path: Path, target_version: str) -> None:
TARGET_VERSION is the version you want your study to be at (example 8.4.0 or 840)
"""
- upgrade_study(Path(study_path), target_version.replace(".", ""))
+ study_upgrader = StudyUpgrader(study_path, target_version.replace(".", ""))
+ study_upgrader.upgrade()
if __name__ == "__main__":
diff --git a/pyproject.toml b/pyproject.toml
index a715fb2753..7031fea678 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,3 +1,70 @@
+[build-system]
+requires = ["setuptools"]
+
+[project]
+name = "AntaREST"
+version = "2.17.5"
+authors = [{name="RTE, Antares Web Team", email="andrea.sgattoni@rte-france.com" }]
+description="Antares Server"
+readme = {file = "README.md", content-type = "text/markdown"}
+license = {file = "LICENSE"}
+requires-python = ">=3.8"
+classifiers=[
+ "Programming Language :: Python :: 3 :: Only",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "License :: Apache License :: 2.0",
+ "Operating System :: OS Independent",
+ ]
+
+[project.urls]
+Repository="https://github.com/AntaresSimulatorTeam/api-iso-antares"
+
+[tool.setuptools]
+platforms = [
+ "linux-x86_64",
+ "macosx-10.14-x86_64",
+ "macosx-10.15-x86_64",
+ "macosx-11-x86_64",
+ "macosx-12-x86_64",
+ "macosx-13-x86_64",
+ "win-amd64",
+]
+
+[tool.setuptools.packages.find]
+where = ["."]
+include = ["antarest*"] # alternatively: `exclude = ["additional*"]`
+
+[tool.mypy]
+strict = true
+files = "antarest/**/*.py"
+
+[[tool.mypy.overrides]]
+module = [
+ "antareslauncher.*",
+ "jsonschema",
+ "pytest",
+ "httpx",
+ "jsonref",
+ "jsonref",
+]
+ignore_missing_imports = true
+
+[[tool.mypy.overrides]]
+module = "alembic.*"
+no_implicit_reexport = false
+
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+markers = [
+ "unit_test",
+ "integration_test"
+]
+
+
[tool.black]
target-version = ["py38"]
line-length = 120
diff --git a/requirements.txt b/requirements.txt
index 5a543c02fc..77f77c8fb2 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,5 @@
Antares-Launcher~=1.3.2
+antares-study-version~=1.0.3
alembic~=1.7.5
asgi-ratelimit[redis]==0.7.0
diff --git a/scripts/package_antares_web.sh b/scripts/package_antares_web.sh
index 9a0efb5e15..881ee8fe75 100755
--- a/scripts/package_antares_web.sh
+++ b/scripts/package_antares_web.sh
@@ -17,6 +17,7 @@ PROJECT_DIR=$(dirname -- "${SCRIPT_DIR}")
DIST_DIR="${PROJECT_DIR}/dist/package"
RESOURCES_DIR="${PROJECT_DIR}/resources"
ANTARES_SOLVER_DIR="${DIST_DIR}/AntaresWeb/antares_solver"
+INSTALLER_DIR="${PROJECT_DIR}/installer/src/antares_web_installer/"
if [[ "$OSTYPE" == "msys"* ]]; then
ANTARES_SOLVER_FOLDER_NAME="rte-antares-$ANTARES_SOLVER_FULL_VERSION-installer-64bits"
@@ -45,6 +46,18 @@ else
popd
fi
+echo "INFO: Generating the Installer for the Desktop application..."
+echo which python
+if [[ "$OSTYPE" == "msys"* ]]; then
+ pushd ${PROJECT_DIR}
+ pyinstaller --onefile "${INSTALLER_DIR}gui/__main__.py" --distpath "${DIST_DIR}" --hidden-import antares_web_installer.shortcuts._linux_shell --hidden-import antares_web_installer.shortcuts._win32_shell --noconsole --name AntaresWebInstaller
+ popd
+else
+ pushd ${PROJECT_DIR}
+ pyinstaller --onefile "${INSTALLER_DIR}cli/__main__.py" --distpath "${DIST_DIR}" --hidden-import antares_web_installer.shortcuts._linux_shell --hidden-import antares_web_installer.shortcuts._win32_shell --noconsole --name AntaresWebInstallerCLI
+ popd
+fi
+
echo "INFO: Creating destination directory '${ANTARES_SOLVER_DIR}'..."
mkdir -p "${ANTARES_SOLVER_DIR}"
diff --git a/scripts/update_version.py b/scripts/update_version.py
index a829035ff0..6a981968ea 100755
--- a/scripts/update_version.py
+++ b/scripts/update_version.py
@@ -117,9 +117,9 @@ def upgrade_version(new_version: str, new_date: str) -> None:
# Patching version number
files_to_patch = [
(
- "setup.py",
- "version=.*",
- f'version="{new_version}",',
+ "pyproject.toml",
+ "\nversion = .*",
+ f'\nversion = "{new_version}"',
),
(
"sonar-project.properties",
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index b0f3012d17..0000000000
--- a/setup.cfg
+++ /dev/null
@@ -1,28 +0,0 @@
-[mypy]
-strict = True
-files = antarest/**/*.py
-
-[mypy-antareslauncher.*]
-ignore_missing_imports = True
-
-[mypy-jsonschema.*]
-ignore_missing_imports = True
-
-[mypy-pytest.*]
-ignore_missing_imports = True
-
-[mypy-httpx.*]
-ignore_missing_imports = True
-
-[mypy-jsonref.*]
-ignore_missing_imports = True
-
-[mypy-alembic.*]
-no_implicit_reexport = false
-
-[tool:pytest]
-testpaths =
- tests
-markers =
- unit_test
- integration_test
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 38e6d70055..0000000000
--- a/setup.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from pathlib import Path
-
-from setuptools import setup, find_packages
-
-excluded_dirs = {"alembic", "conf", "docs", "examples", "resources", "scripts", "tests", "venv", "webapp"}
-
-setup(
- name="AntaREST",
- version="2.17.5",
- description="Antares Server",
- long_description=Path("README.md").read_text(encoding="utf-8"),
- long_description_content_type="text/markdown",
- author="RTE, Antares Web Team",
- author_email="andrea.sgattoni@rte-france.com",
- url="https://github.com/AntaresSimulatorTeam/api-iso-antares",
- packages=find_packages(exclude=excluded_dirs),
- license="Apache Software License",
- platforms=[
- "linux-x86_64",
- "macosx-10.14-x86_64",
- "macosx-10.15-x86_64",
- "macosx-11-x86_64",
- "macosx-12-x86_64",
- "macosx-13-x86_64",
- "win-amd64",
- ],
- classifiers=[
- "Programming Language :: Python :: 3 :: Only",
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: 3.9",
- "Programming Language :: Python :: 3.10",
- "Programming Language :: Python :: 3.11",
- "License :: Apache License :: 2.0",
- "Operating System :: OS Independent",
- ],
- python_requires=">=3.8",
-)
diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py
index 55c1073168..08aa099dd1 100644
--- a/tests/integration/test_integration.py
+++ b/tests/integration/test_integration.py
@@ -806,6 +806,15 @@ def test_area_management(client: TestClient, admin_access_token: str) -> None:
"thresholdCsrVariableBoundsRelaxation": 3,
}
+ # asserts csr field is an int
+ res = client.put(
+ f"/v1/studies/{study_id}/config/adequacypatch/form",
+ json={"thresholdCsrVariableBoundsRelaxation": 0.8},
+ )
+ assert res.status_code == 422
+ assert res.json()["exception"] == "RequestValidationError"
+ assert res.json()["description"] == "value is not a valid integer"
+
# General form
res_general_config = client.get(f"/v1/studies/{study_id}/config/general/form")
diff --git a/tests/integration/test_studies_upgrade.py b/tests/integration/test_studies_upgrade.py
index 3eb92522af..9640fb5cf9 100644
--- a/tests/integration/test_studies_upgrade.py
+++ b/tests/integration/test_studies_upgrade.py
@@ -38,7 +38,6 @@ def test_upgrade_study__target_version(self, client: TestClient, user_access_tok
assert task.status == TaskStatus.COMPLETED
assert target_version in task.result.message, f"Version not in {task.result.message=}"
- @pytest.mark.skipif(RUN_ON_WINDOWS, reason="This test runs randomly on Windows")
def test_upgrade_study__bad_target_version(
self, client: TestClient, user_access_token: str, internal_study_id: str
):
@@ -48,12 +47,9 @@ def test_upgrade_study__bad_target_version(
headers={"Authorization": f"Bearer {user_access_token}"},
params={"target_version": target_version},
)
- assert res.status_code == 200
- task_id = res.json()
- assert task_id
- task = wait_task_completion(client, user_access_token, task_id)
- assert task.status == TaskStatus.FAILED
- assert target_version in task.result.message, f"Version not in {task.result.message=}"
+ assert res.status_code == 400
+ assert res.json()["exception"] == "UnsupportedStudyVersion"
+ assert f"Version '{target_version}' isn't among supported versions" in res.json()["description"]
def test_upgrade_study__unmet_requirements(self, client: TestClient, admin_access_token: str):
"""
diff --git a/tests/storage/business/test_study_version_upgrader.py b/tests/storage/business/test_study_version_upgrader.py
index efe1e75315..f406070308 100644
--- a/tests/storage/business/test_study_version_upgrader.py
+++ b/tests/storage/business/test_study_version_upgrader.py
@@ -5,18 +5,83 @@
import shutil
import zipfile
from pathlib import Path
-from typing import List
+from typing import List, Optional
import pandas
import pytest
+from pandas.errors import EmptyDataError
+from antarest.core.exceptions import UnsupportedStudyVersion
from antarest.study.storage.rawstudy.ini_reader import IniReader
from antarest.study.storage.rawstudy.model.filesystem.config.model import transform_name_to_id
from antarest.study.storage.rawstudy.model.filesystem.root.settings.generaldata import DUPLICATE_KEYS
-from antarest.study.storage.study_upgrader import UPGRADE_METHODS, InvalidUpgrade, upgrade_study
-from antarest.study.storage.study_upgrader.upgrader_840 import MAPPING_TRANSMISSION_CAPACITIES
+from antarest.study.storage.study_upgrader import (
+ InvalidUpgrade,
+ StudyUpgrader,
+ check_versions_coherence,
+ find_next_version,
+)
from tests.storage.business.assets import ASSETS_DIR
+MAPPING_TRANSMISSION_CAPACITIES = {
+ True: "local-values",
+ False: "null-for-all-links",
+ "infinite": "infinite-for-all-links",
+}
+
+
+class TestFindNextVersion:
+ @pytest.mark.parametrize(
+ "from_version, expected",
+ [("700", "710"), ("870", "880")],
+ )
+ def test_find_next_version_nominal(self, from_version: str, expected: str):
+ actual = find_next_version(from_version)
+ assert actual == expected
+
+ @pytest.mark.parametrize(
+ "from_version, message",
+ [
+ ("3.14", "Version '3.14' isn't among supported versions"),
+ ("880", "Your study is already in the latest supported version: '880'"),
+ ("900", "Version '900' isn't among supported versions"),
+ ],
+ )
+ def test_find_next_version_fails(self, from_version: str, message: str):
+ with pytest.raises(UnsupportedStudyVersion, match=message):
+ find_next_version(from_version)
+
+
+class TestCheckVersionCoherence:
+ @pytest.mark.parametrize(
+ "from_version, target_version",
+ [("700", "710"), ("870", "880"), ("820", "840")],
+ )
+ def test_check_version_coherence_nominal(self, from_version: str, target_version: str):
+ check_versions_coherence(from_version, target_version)
+
+ @pytest.mark.parametrize(
+ "from_version, target_version, message",
+ [
+ ("1000", "710", "Version '1000' isn't among supported versions"),
+ ("820", "32", "Version '32' isn't among supported versions"),
+ ],
+ )
+ def test_invalid_versions_fails(self, from_version: str, target_version: str, message: str):
+ with pytest.raises(UnsupportedStudyVersion, match=message):
+ check_versions_coherence(from_version, target_version)
+
+ @pytest.mark.parametrize(
+ "from_version, target_version, message",
+ [
+ ("860", "860", "Your study is already in the version you asked: 860"),
+ ("870", "840", "Cannot downgrade your study version : from 870 to 840"),
+ ],
+ )
+ def test_check_version_coherence_fails(self, from_version: str, target_version: str, message: str):
+ with pytest.raises(InvalidUpgrade, match=message):
+ check_versions_coherence(from_version, target_version)
+
def test_end_to_end_upgrades(tmp_path: Path):
# Prepare a study to upgrade
@@ -32,7 +97,8 @@ def test_end_to_end_upgrades(tmp_path: Path):
old_binding_constraint_values = get_old_binding_constraint_values(study_dir)
# Only checks if the study_upgrader can go from the first supported version to the last one
target_version = "880"
- upgrade_study(study_dir, target_version)
+ study_upgrader = StudyUpgrader(study_dir, target_version)
+ study_upgrader.upgrade()
assert_study_antares_file_is_updated(study_dir, target_version)
assert_settings_are_updated(study_dir, old_values)
assert_inputs_are_updated(study_dir, old_areas_values, old_binding_constraint_values)
@@ -46,26 +112,20 @@ def test_fails_because_of_versions_asked(tmp_path: Path):
with zipfile.ZipFile(path_study) as zip_output:
zip_output.extractall(path=study_dir)
# Try to upgrade with an unknown version
- with pytest.raises(
- InvalidUpgrade,
- match=f"Version '600' unknown: possible versions are {', '.join([u[1] for u in UPGRADE_METHODS])}",
- ):
- upgrade_study(study_dir, "600")
+ with pytest.raises(InvalidUpgrade, match="Cannot downgrade from version '7.2' to '6'"):
+ StudyUpgrader(study_dir, "600").upgrade()
# Try to upgrade with the current version
- with pytest.raises(InvalidUpgrade, match="Your study is already in version '720'"):
- upgrade_study(study_dir, "720")
+ with pytest.raises(InvalidUpgrade, match="Your study is already in version '7.2'"):
+ StudyUpgrader(study_dir, "720").upgrade()
# Try to upgrade with an old version
with pytest.raises(
InvalidUpgrade,
- match="Impossible to upgrade from version '720' to version '710'",
+ match="Cannot downgrade from version '7.2' to '7.1'",
):
- upgrade_study(study_dir, "710")
+ StudyUpgrader(study_dir, "710").upgrade()
# Try to upgrade with a version that does not exist
- with pytest.raises(
- InvalidUpgrade,
- match=f"Version '820.rc' unknown: possible versions are {', '.join([u[1] for u in UPGRADE_METHODS])}",
- ):
- upgrade_study(study_dir, "820.rc")
+ with pytest.raises(InvalidUpgrade, match="Invalid version number '820.rc'"):
+ StudyUpgrader(study_dir, "820.rc").upgrade()
def test_fallback_if_study_input_broken(tmp_path):
@@ -78,10 +138,10 @@ def test_fallback_if_study_input_broken(tmp_path):
before_upgrade_dir = tmp_path / "backup"
shutil.copytree(study_dir, before_upgrade_dir, dirs_exist_ok=True)
with pytest.raises(
- expected_exception=pandas.errors.EmptyDataError,
+ expected_exception=EmptyDataError,
match="No columns to parse from file",
):
- upgrade_study(study_dir, "850")
+ StudyUpgrader(study_dir, "850").upgrade()
assert are_same_dir(study_dir, before_upgrade_dir)
@@ -231,8 +291,8 @@ def assert_folder_is_created(path: Path) -> None:
assert (path / "series").is_dir()
-def are_same_dir(dir1, dir2) -> bool:
- dirs_cmp = filecmp.dircmp(dir1, dir2)
+def are_same_dir(dir1, dir2, ignore: Optional[List[str]] = None) -> bool:
+ dirs_cmp = filecmp.dircmp(dir1, dir2, ignore=ignore)
if len(dirs_cmp.left_only) > 0 or len(dirs_cmp.right_only) > 0 or len(dirs_cmp.funny_files) > 0:
return False
path_dir1 = Path(dir1)
diff --git a/tests/storage/study_upgrader/test_upgrade_710.py b/tests/storage/study_upgrader/test_upgrade_710.py
index a389178999..6542cd3a3e 100644
--- a/tests/storage/study_upgrader/test_upgrade_710.py
+++ b/tests/storage/study_upgrader/test_upgrade_710.py
@@ -1,5 +1,5 @@
from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.study_upgrader import upgrade_710
+from antarest.study.storage.study_upgrader import StudyUpgrader
from tests.storage.study_upgrader.conftest import StudyAssets
@@ -9,7 +9,8 @@ def test_nominal_case(study_assets: StudyAssets):
"""
# upgrade the study
- upgrade_710(study_assets.study_dir)
+ study_upgrader = StudyUpgrader(study_assets.study_dir, "710")
+ study_upgrader.upgrade()
# compare generaldata.ini
actual_path = study_assets.study_dir.joinpath("settings/generaldata.ini")
diff --git a/tests/storage/study_upgrader/test_upgrade_720.py b/tests/storage/study_upgrader/test_upgrade_720.py
index 43eb2009f1..f2757b6fd5 100644
--- a/tests/storage/study_upgrader/test_upgrade_720.py
+++ b/tests/storage/study_upgrader/test_upgrade_720.py
@@ -1,4 +1,4 @@
-from antarest.study.storage.study_upgrader import upgrade_720
+from antarest.study.storage.study_upgrader import StudyUpgrader
from tests.storage.business.test_study_version_upgrader import are_same_dir
from tests.storage.study_upgrader.conftest import StudyAssets
@@ -9,7 +9,8 @@ def test_nominal_case(study_assets: StudyAssets):
"""
# upgrade the study
- upgrade_720(study_assets.study_dir)
+ study_upgrader = StudyUpgrader(study_assets.study_dir, "720")
+ study_upgrader.upgrade()
# compare folder
- assert are_same_dir(study_assets.study_dir, study_assets.expected_dir)
+ assert are_same_dir(study_assets.study_dir, study_assets.expected_dir, ignore=["study.antares"])
diff --git a/tests/storage/study_upgrader/test_upgrade_800.py b/tests/storage/study_upgrader/test_upgrade_800.py
index b2c72fcee1..aded4de2c4 100644
--- a/tests/storage/study_upgrader/test_upgrade_800.py
+++ b/tests/storage/study_upgrader/test_upgrade_800.py
@@ -1,5 +1,5 @@
from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.study_upgrader import upgrade_800
+from antarest.study.storage.study_upgrader import StudyUpgrader
from tests.storage.study_upgrader.conftest import StudyAssets
@@ -9,7 +9,8 @@ def test_nominal_case(study_assets: StudyAssets):
"""
# upgrade the study
- upgrade_800(study_assets.study_dir)
+ study_upgrader = StudyUpgrader(study_assets.study_dir, "800")
+ study_upgrader.upgrade()
# compare generaldata.ini
actual_path = study_assets.study_dir.joinpath("settings/generaldata.ini")
diff --git a/tests/storage/study_upgrader/test_upgrade_810.py b/tests/storage/study_upgrader/test_upgrade_810.py
index 9639beb94f..2705798eb7 100644
--- a/tests/storage/study_upgrader/test_upgrade_810.py
+++ b/tests/storage/study_upgrader/test_upgrade_810.py
@@ -1,5 +1,5 @@
from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.study_upgrader import upgrade_810
+from antarest.study.storage.study_upgrader import StudyUpgrader
from tests.storage.business.test_study_version_upgrader import are_same_dir
from tests.storage.study_upgrader.conftest import StudyAssets
@@ -10,7 +10,8 @@ def test_nominal_case(study_assets: StudyAssets):
"""
# upgrade the study
- upgrade_810(study_assets.study_dir)
+ study_upgrader = StudyUpgrader(study_assets.study_dir, "810")
+ study_upgrader.upgrade()
# compare generaldata.ini
actual_path = study_assets.study_dir.joinpath("settings/generaldata.ini")
diff --git a/tests/storage/study_upgrader/test_upgrade_820.py b/tests/storage/study_upgrader/test_upgrade_820.py
index 730b7c0127..d535a9b838 100644
--- a/tests/storage/study_upgrader/test_upgrade_820.py
+++ b/tests/storage/study_upgrader/test_upgrade_820.py
@@ -1,5 +1,5 @@
from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.study_upgrader import upgrade_820
+from antarest.study.storage.study_upgrader import StudyUpgrader
from tests.storage.business.test_study_version_upgrader import are_same_dir
from tests.storage.study_upgrader.conftest import StudyAssets
@@ -10,7 +10,8 @@ def test_nominal_case(study_assets: StudyAssets):
"""
# upgrade the study
- upgrade_820(study_assets.study_dir)
+ study_upgrader = StudyUpgrader(study_assets.study_dir, "820")
+ study_upgrader.upgrade()
# compare generaldata.ini
actual_path = study_assets.study_dir.joinpath("settings/generaldata.ini")
diff --git a/tests/storage/study_upgrader/test_upgrade_830.py b/tests/storage/study_upgrader/test_upgrade_830.py
index d0612ef889..c17e110d58 100644
--- a/tests/storage/study_upgrader/test_upgrade_830.py
+++ b/tests/storage/study_upgrader/test_upgrade_830.py
@@ -1,5 +1,5 @@
from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.study_upgrader import upgrade_830
+from antarest.study.storage.study_upgrader import StudyUpgrader
from tests.storage.business.test_study_version_upgrader import are_same_dir
from tests.storage.study_upgrader.conftest import StudyAssets
@@ -10,7 +10,8 @@ def test_nominal_case(study_assets: StudyAssets):
"""
# upgrade the study
- upgrade_830(study_assets.study_dir)
+ study_upgrader = StudyUpgrader(study_assets.study_dir, "830")
+ study_upgrader.upgrade()
# compare generaldata.ini
actual_path = study_assets.study_dir.joinpath("settings/generaldata.ini")
diff --git a/tests/storage/study_upgrader/test_upgrade_840.py b/tests/storage/study_upgrader/test_upgrade_840.py
index 4f95cef265..9cd39a3a49 100644
--- a/tests/storage/study_upgrader/test_upgrade_840.py
+++ b/tests/storage/study_upgrader/test_upgrade_840.py
@@ -1,5 +1,5 @@
from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.study_upgrader import upgrade_840
+from antarest.study.storage.study_upgrader import StudyUpgrader
from tests.storage.study_upgrader.conftest import StudyAssets
@@ -9,7 +9,8 @@ def test_nominal_case(study_assets: StudyAssets):
"""
# upgrade the study
- upgrade_840(study_assets.study_dir)
+ study_upgrader = StudyUpgrader(study_assets.study_dir, "840")
+ study_upgrader.upgrade()
# compare generaldata.ini
actual_path = study_assets.study_dir.joinpath("settings/generaldata.ini")
diff --git a/tests/storage/study_upgrader/test_upgrade_850.py b/tests/storage/study_upgrader/test_upgrade_850.py
index 362cf1ab50..e40f00aa02 100644
--- a/tests/storage/study_upgrader/test_upgrade_850.py
+++ b/tests/storage/study_upgrader/test_upgrade_850.py
@@ -1,5 +1,5 @@
from antarest.study.storage.rawstudy.ini_reader import IniReader
-from antarest.study.storage.study_upgrader import upgrade_850
+from antarest.study.storage.study_upgrader import StudyUpgrader
from tests.storage.study_upgrader.conftest import StudyAssets
@@ -10,7 +10,8 @@ def test_nominal_case(study_assets: StudyAssets):
"""
# upgrade the study
- upgrade_850(study_assets.study_dir)
+ study_upgrader = StudyUpgrader(study_assets.study_dir, "850")
+ study_upgrader.upgrade()
# compare generaldata.ini
actual_path = study_assets.study_dir.joinpath("settings/generaldata.ini")
diff --git a/tests/storage/study_upgrader/test_upgrade_860.py b/tests/storage/study_upgrader/test_upgrade_860.py
index cd20d438cb..772589ee11 100644
--- a/tests/storage/study_upgrader/test_upgrade_860.py
+++ b/tests/storage/study_upgrader/test_upgrade_860.py
@@ -1,4 +1,4 @@
-from antarest.study.storage.study_upgrader import upgrade_860
+from antarest.study.storage.study_upgrader import StudyUpgrader
from tests.storage.business.test_study_version_upgrader import are_same_dir
from tests.storage.study_upgrader.conftest import StudyAssets
@@ -9,7 +9,8 @@ def test_nominal_case(study_assets: StudyAssets):
"""
# upgrade the study
- upgrade_860(study_assets.study_dir)
+ study_upgrader = StudyUpgrader(study_assets.study_dir, "860")
+ study_upgrader.upgrade()
# compare input folder
actual_input_path = study_assets.study_dir.joinpath("input")
diff --git a/tests/storage/study_upgrader/test_upgrade_870.py b/tests/storage/study_upgrader/test_upgrade_870.py
index f22a7a30ef..4024e02fee 100644
--- a/tests/storage/study_upgrader/test_upgrade_870.py
+++ b/tests/storage/study_upgrader/test_upgrade_870.py
@@ -1,4 +1,4 @@
-from antarest.study.storage.study_upgrader import upgrade_870
+from antarest.study.storage.study_upgrader import StudyUpgrader
from tests.storage.business.test_study_version_upgrader import are_same_dir
from tests.storage.study_upgrader.conftest import StudyAssets
@@ -9,7 +9,8 @@ def test_nominal_case(study_assets: StudyAssets):
"""
# upgrade the study
- upgrade_870(study_assets.study_dir)
+ study_upgrader = StudyUpgrader(study_assets.study_dir, "870")
+ study_upgrader.upgrade()
# compare input folders (bindings + thermals)
actual_input_path = study_assets.study_dir.joinpath("input")
@@ -23,7 +24,8 @@ def test_empty_binding_constraints(study_assets: StudyAssets):
"""
# upgrade the study
- upgrade_870(study_assets.study_dir)
+ study_upgrader = StudyUpgrader(study_assets.study_dir, "870")
+ study_upgrader.upgrade()
# compare input folders (bindings + thermals)
actual_input_path = study_assets.study_dir.joinpath("input")
diff --git a/tests/storage/study_upgrader/test_upgrade_880.py b/tests/storage/study_upgrader/test_upgrade_880.py
index 36dfa7dcc6..b0868daec2 100644
--- a/tests/storage/study_upgrader/test_upgrade_880.py
+++ b/tests/storage/study_upgrader/test_upgrade_880.py
@@ -1,4 +1,4 @@
-from antarest.study.storage.study_upgrader import upgrade_880
+from antarest.study.storage.study_upgrader import StudyUpgrader
from tests.storage.business.test_study_version_upgrader import are_same_dir
from tests.storage.study_upgrader.conftest import StudyAssets
@@ -9,7 +9,8 @@ def test_nominal_case(study_assets: StudyAssets):
"""
# upgrade the study
- upgrade_880(study_assets.study_dir)
+ study_upgrader = StudyUpgrader(study_assets.study_dir, "880")
+ study_upgrader.upgrade()
# compare st-storage folders (st-storage)
actual_input_path = study_assets.study_dir / "input" / "st-storage"
diff --git a/tests/storage/test_service.py b/tests/storage/test_service.py
index 891099dbcc..7ebf94a09e 100644
--- a/tests/storage/test_service.py
+++ b/tests/storage/test_service.py
@@ -1,7 +1,9 @@
import contextlib
import os
+import textwrap
import typing as t
import uuid
+from configparser import MissingSectionHeaderError
from datetime import datetime, timedelta, timezone
from pathlib import Path
from unittest.mock import ANY, Mock, call, patch, seal
@@ -1728,7 +1730,7 @@ def test_task_upgrade_study(tmp_path: Path) -> None:
@with_db_context
-@patch("antarest.study.service.upgrade_study")
+@patch("antarest.study.storage.study_upgrader.StudyUpgrader.upgrade")
@pytest.mark.parametrize("workspace", ["other_workspace", DEFAULT_WORKSPACE_NAME])
def test_upgrade_study__raw_study__nominal(
upgrade_study_mock: Mock,
@@ -1740,7 +1742,17 @@ def test_upgrade_study__raw_study__nominal(
target_version = "800"
current_version = "720"
(tmp_path / "study.antares").touch()
- (tmp_path / "study.antares").write_text(f"version = {current_version}")
+ (tmp_path / "study.antares").write_text(
+ textwrap.dedent(
+ f"""
+ [antares]
+ version = {current_version}
+ caption =
+ created = 1682506382.235618
+ lastsave = 1682506382.23562
+ author = Unknown"""
+ )
+ )
# Prepare a RAW study
# noinspection PyArgumentList
@@ -1796,8 +1808,7 @@ def test_upgrade_study__raw_study__nominal(
notifier = Mock()
actual = task(notifier)
- # The `upgrade_study()` function must be called with the right parameters
- upgrade_study_mock.assert_called_with(tmp_path, target_version)
+ upgrade_study_mock.assert_called_once_with()
# The study must be updated in the database
actual_study: RawStudy = db.session.query(Study).get(study_id)
@@ -1823,7 +1834,7 @@ def test_upgrade_study__raw_study__nominal(
@with_db_context
-@patch("antarest.study.service.upgrade_study")
+@patch("antarest.study.storage.study_upgrader.StudyUpgrader.upgrade")
def test_upgrade_study__variant_study__nominal(
upgrade_study_mock: Mock,
tmp_path: Path,
@@ -1912,14 +1923,14 @@ def test_upgrade_study__variant_study__nominal(
@with_db_context
-@patch("antarest.study.service.upgrade_study")
-def test_upgrade_study__raw_study__failed(upgrade_study_mock: Mock, tmp_path: Path) -> None:
+def test_upgrade_study__raw_study__failed(tmp_path: Path) -> None:
study_id = str(uuid.uuid4())
study_name = "my_study"
target_version = "800"
old_version = "720"
(tmp_path / "study.antares").touch()
(tmp_path / "study.antares").write_text(f"version = {old_version}")
+ # The study.antares file doesn't have an header the upgrade should fail.
# Prepare a RAW study
# noinspection PyArgumentList
@@ -1960,9 +1971,6 @@ def test_upgrade_study__raw_study__failed(upgrade_study_mock: Mock, tmp_path: Pa
# An event of type `STUDY_EDITED` must be pushed when the upgrade is done.
event_bus = Mock()
- # The `upgrade_study()` function raise an exception
- upgrade_study_mock.side_effect = Exception("INVALID_UPGRADE")
-
# Prepare the task for an upgrade
task = StudyUpgraderTask(
study_id,
@@ -1976,7 +1984,7 @@ def test_upgrade_study__raw_study__failed(upgrade_study_mock: Mock, tmp_path: Pa
# The task is called with a `TaskUpdateNotifier` a parameter.
# Some messages could be emitted using the notifier (not a requirement).
notifier = Mock()
- with pytest.raises(Exception, match="INVALID_UPGRADE"):
+ with pytest.raises(MissingSectionHeaderError, match="File contains no section headers"):
task(notifier)
# The study must not be updated in the database
diff --git a/tests/study/storage/test_study_version_upgrader.py b/tests/study/storage/test_study_version_upgrader.py
deleted file mode 100644
index 97c9a479c6..0000000000
--- a/tests/study/storage/test_study_version_upgrader.py
+++ /dev/null
@@ -1,117 +0,0 @@
-from pathlib import Path
-
-import pytest
-
-from antarest.core.exceptions import StudyValidationError
-from antarest.study.storage.study_upgrader import (
- UPGRADE_METHODS,
- InvalidUpgrade,
- can_upgrade_version,
- find_next_version,
- get_current_version,
-)
-
-
-class TestFindNextVersion:
- @pytest.mark.parametrize(
- "from_version, expected",
- [
- (UPGRADE_METHODS[0].old, UPGRADE_METHODS[0].new),
- (UPGRADE_METHODS[-1].old, UPGRADE_METHODS[-1].new),
- (UPGRADE_METHODS[-1].new, ""),
- ("3.14", ""),
- ],
- )
- def test_find_next_version(self, from_version: str, expected: str):
- actual = find_next_version(from_version)
- assert actual == expected
-
-
-class TestCanUpgradeVersion:
- @pytest.mark.parametrize(
- "from_version, to_version",
- [
- pytest.param("700", "710"),
- pytest.param(
- "123",
- "123",
- marks=pytest.mark.xfail(
- reason="same versions",
- raises=InvalidUpgrade,
- strict=True,
- ),
- ),
- pytest.param(
- "000",
- "123",
- marks=pytest.mark.xfail(
- reason="version '000' not in 'old' versions",
- raises=InvalidUpgrade,
- strict=True,
- ),
- ),
- pytest.param(
- "700",
- "999",
- marks=pytest.mark.xfail(
- reason="version '999' not in 'new' versions",
- raises=InvalidUpgrade,
- strict=True,
- ),
- ),
- pytest.param(
- "720",
- "710",
- marks=pytest.mark.xfail(
- reason="versions inverted",
- raises=InvalidUpgrade,
- strict=True,
- ),
- ),
- pytest.param(
- 800,
- "456",
- marks=pytest.mark.xfail(
- reason="integer version 800 not in 'old' versions",
- raises=InvalidUpgrade,
- strict=True,
- ),
- ),
- pytest.param(
- "700",
- 800,
- marks=pytest.mark.xfail(
- reason="integer version 800 not in 'new' versions",
- raises=InvalidUpgrade,
- strict=True,
- ),
- ),
- ],
- )
- def test_can_upgrade_version(self, from_version: str, to_version: str):
- can_upgrade_version(from_version, to_version)
-
-
-class TestGetCurrentVersion:
- @pytest.mark.parametrize(
- "version",
- [
- pytest.param("710"),
- pytest.param(" 71"),
- pytest.param(" 7.1 "),
- pytest.param(
- "",
- marks=pytest.mark.xfail(
- reason="empty version",
- raises=StudyValidationError,
- strict=True,
- ),
- ),
- ],
- )
- def test_get_current_version(self, tmp_path: Path, version: str):
- # prepare the "study.antares" file
- study_antares_path = tmp_path.joinpath("study.antares")
- study_antares_path.write_text(f"version = {version}", encoding="utf-8")
- actual = get_current_version(tmp_path)
- assert actual == version.strip(), f"{actual=}"
diff --git a/tests/variantstudy/conftest.py b/tests/variantstudy/conftest.py
index b08851b07b..5d10267bbd 100644
--- a/tests/variantstudy/conftest.py
+++ b/tests/variantstudy/conftest.py
@@ -1,4 +1,5 @@
import hashlib
+import re
import typing as t
import zipfile
from pathlib import Path
@@ -8,7 +9,7 @@
import numpy.typing as npt
import pytest
-from antarest.study.storage.study_upgrader import get_current_version
+from antarest.core.exceptions import StudyValidationError
if t.TYPE_CHECKING:
# noinspection PyPackageRequirements
@@ -163,7 +164,7 @@ def empty_study_fixture(request: "SubRequest", tmp_path: Path, matrix_service: M
zip_empty_study.extractall(empty_study_destination_path)
# Detect the version of the study from `study.antares` file.
- version = get_current_version(empty_study_destination_path)
+ version = _get_current_version(empty_study_destination_path)
config = FileStudyTreeConfig(
study_path=empty_study_destination_path,
@@ -185,3 +186,16 @@ def empty_study_fixture(request: "SubRequest", tmp_path: Path, matrix_service: M
),
)
return file_study
+
+
+def _get_current_version(study_path: Path) -> str:
+ antares_path = study_path / "study.antares"
+ pattern = r"version\s*=\s*([\w.-]+)\s*"
+ with antares_path.open(encoding="utf-8") as lines:
+ for line in lines:
+ if match := re.fullmatch(pattern, line):
+ return match[1].rstrip()
+ raise StudyValidationError(
+ f"File parsing error: the version number is not found in '{antares_path}'"
+ f" or does not match the expected '{pattern}' format."
+ )
diff --git a/tests/variantstudy/model/command/test_create_st_storage.py b/tests/variantstudy/model/command/test_create_st_storage.py
index c35ea37665..7e2a7fa7b5 100644
--- a/tests/variantstudy/model/command/test_create_st_storage.py
+++ b/tests/variantstudy/model/command/test_create_st_storage.py
@@ -7,7 +7,7 @@
from antarest.study.storage.rawstudy.model.filesystem.config.model import transform_name_to_id
from antarest.study.storage.rawstudy.model.filesystem.config.st_storage import STStorageConfig
from antarest.study.storage.rawstudy.model.filesystem.factory import FileStudy
-from antarest.study.storage.study_upgrader import upgrade_study
+from antarest.study.storage.study_upgrader import StudyUpgrader
from antarest.study.storage.variantstudy.business.utils import strip_matrix_protocol
from antarest.study.storage.variantstudy.model.command.common import CommandName
from antarest.study.storage.variantstudy.model.command.create_area import CreateArea
@@ -29,7 +29,7 @@ def recent_study_fixture(empty_study: FileStudy) -> FileStudy:
Returns:
FileStudy: The FileStudy object upgraded to the required version.
"""
- upgrade_study(empty_study.config.study_path, str(REQUIRED_VERSION))
+ StudyUpgrader(empty_study.config.study_path, str(REQUIRED_VERSION)).upgrade()
empty_study.config.version = REQUIRED_VERSION
return empty_study
diff --git a/tests/variantstudy/model/command/test_remove_st_storage.py b/tests/variantstudy/model/command/test_remove_st_storage.py
index 963a6d128a..944f3b57b4 100644
--- a/tests/variantstudy/model/command/test_remove_st_storage.py
+++ b/tests/variantstudy/model/command/test_remove_st_storage.py
@@ -5,7 +5,7 @@
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.study_upgrader import StudyUpgrader
from antarest.study.storage.variantstudy.model.command.common import CommandName
from antarest.study.storage.variantstudy.model.command.create_area import CreateArea
from antarest.study.storage.variantstudy.model.command.create_st_storage import CreateSTStorage
@@ -25,7 +25,7 @@ def recent_study_fixture(empty_study: FileStudy) -> FileStudy:
Returns:
FileStudy: The FileStudy object upgraded to the required version.
"""
- upgrade_study(empty_study.config.study_path, str(REQUIRED_VERSION))
+ StudyUpgrader(empty_study.config.study_path, str(REQUIRED_VERSION)).upgrade()
empty_study.config.version = REQUIRED_VERSION
return empty_study
diff --git a/webapp/public/locales/en/main.json b/webapp/public/locales/en/main.json
index 55a60ae4a8..f839e170eb 100644
--- a/webapp/public/locales/en/main.json
+++ b/webapp/public/locales/en/main.json
@@ -133,6 +133,7 @@
"form.field.minValue": "The minimum value is {{0}}",
"form.field.maxValue": "The maximum value is {{0}}",
"form.field.invalidNumber": "Invalid number",
+ "form.field.mustBeInteger": "The value must be an integer",
"form.field.notAllowedValue": "Not allowed value",
"form.field.specialChars": "Special characters allowed: {{0}}",
"form.field.specialCharsNotAllowed": "Special characters are not allowed",
diff --git a/webapp/public/locales/fr/main.json b/webapp/public/locales/fr/main.json
index cf34d3599e..2b8bcd3cbf 100644
--- a/webapp/public/locales/fr/main.json
+++ b/webapp/public/locales/fr/main.json
@@ -133,6 +133,7 @@
"form.field.minValue": "La valeur minimum est {{0}}",
"form.field.maxValue": "La valeur maximum est {{0}}",
"form.field.invalidNumber": "Nombre invalide",
+ "form.field.mustBeInteger": "La valeur doit être un nombre entier",
"form.field.notAllowedValue": "Valeur non autorisée",
"form.field.specialChars": "Caractères spéciaux autorisés: {{0}}",
"form.field.specialCharsNotAllowed": "Les caractères spéciaux ne sont pas autorisés",
diff --git a/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx
index a5687c6a14..d60eb0089f 100644
--- a/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx
+++ b/webapp/src/components/App/Singlestudy/explore/Configuration/AdequacyPatch/Fields.tsx
@@ -8,6 +8,7 @@ import Fieldset from "../../../../../common/Fieldset";
import { useFormContextPlus } from "../../../../../common/Form";
import { AdequacyPatchFormFields, PRICE_TAKING_ORDER_OPTIONS } from "./utils";
import { StudyMetadata } from "../../../../../../common/types";
+import { validateNumber } from "../../../../../../utils/validationUtils";
function Fields() {
const { t } = useTranslation();
@@ -81,10 +82,7 @@ function Fields() {
name="thresholdInitiateCurtailmentSharingRule"
control={control}
rules={{
- min: {
- value: 0,
- message: t("form.field.minValue", { 0: 0 }),
- },
+ validate: validateNumber({ min: 0 }),
}}
/>