Skip to content

Commit

Permalink
feat: add the show, create and upgrade applications
Browse files Browse the repository at this point in the history
  • Loading branch information
laurent-laporte-pro committed Jun 24, 2024
1 parent 54a5f6c commit 5e921f7
Show file tree
Hide file tree
Showing 90 changed files with 2,496 additions and 6 deletions.
24 changes: 21 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
requires = ["hatchling"]
build-backend = "hatchling.build"


[project]
name = "antares-study-version"
dynamic = ["version"]
Expand All @@ -25,17 +26,24 @@ classifiers = [
"Programming Language :: Python :: Implementation :: PyPy",
]

dependencies = []
dependencies = ["click", "pandas"]


[project.urls]
Documentation = "https://github.com/AntaresSimulatorTeam/antares-study-version#readme"
Issues = "https://github.com/AntaresSimulatorTeam/antares-study-version/issues"
Source = "https://github.com/AntaresSimulatorTeam/antares-study-version"


[project.scripts]
antares-study-version = "antares.study.version.cli:cli"


[tool.hatch.version]
path = "src/antares/study/version/__about__.py"
attr = "__version__"


[tool.hatch.build.targets.wheel]
packages = ["src/antares"]

Expand All @@ -48,6 +56,7 @@ dependencies = [
"pytest-freezer<0.5",
]


[tool.hatch.envs.default.scripts]
test = "pytest {args:tests}"
test-cov = "coverage run -m pytest {args:tests}"
Expand All @@ -61,6 +70,7 @@ cov = [
"cov-report",
]


[[tool.hatch.envs.all.matrix]]
python = ["3.8", "3.9", "3.10", "3.11", "3.12"]

Expand All @@ -72,6 +82,8 @@ dependencies = [
"pytest-freezer<0.5",
"mypy>=1.0.0",
]


[tool.hatch.envs.types.scripts]
check = "mypy --namespace-packages --package antares.study.version --install-types --non-interactive"
check-tests = "mypy --install-types --non-interactive {args:tests}"
Expand All @@ -81,6 +93,7 @@ check-tests = "mypy --install-types --non-interactive {args:tests}"
detached = true
dependencies = ["mkdocs"]


[tool.hatch.envs.docs.scripts]
build = "mkdocs build -f mkdocs.yml {args}"
serve = "mkdocs serve -f mkdocs.yml {args}"
Expand All @@ -90,34 +103,39 @@ gh-deploy = "mkdocs gh-deploy -f mkdocs.yml {args}"
[tool.mypy]
mypy_path = 'src, tests'


[tool.coverage.run]
source_pkgs = ["antares.study.version", "tests"]
branch = true
parallel = true
omit = [
]
omit = []


[tool.coverage.paths]
antares_study_config = ["src/antares/study/version", "*/antares-study-version/src/antares/study/version"]
tests = ["tests", "*/antares-study-version/tests"]


[tool.coverage.report]
exclude_lines = [
"no cov",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]


[tool.black]
target-version = ["py38"]
line-length = 120


[tool.isort]
profile = "black"
line_length = 120
src_paths = ["src", "tests"]
skip_gitignore = true


[tool.ruff]
# Exclude a variety of commonly ignored directories.
exclude = [
Expand Down
2 changes: 1 addition & 1 deletion src/antares/study/version/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
Antares Study (and Solver) version models.
"""

from .model import StudyVersion, SolverVersion # noqa: F401
from .converters import version_to_triplet # noqa: F401
from .model import SolverVersion, StudyVersion # noqa: F401
15 changes: 15 additions & 0 deletions src/antares/study/version/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""
Main entrypoint for the CLI application.
"""

import sys

from antares.study.version.cli import cli


def main():
cli(sys.argv[1:])


if __name__ == "__main__":
main()
152 changes: 152 additions & 0 deletions src/antares/study/version/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
"""
The CLI module for the study version.
This module defines the following CLI commands:
- antares-study-version show: display the details of a study in human-readable format (name, version, creation date, etc.)
- antares-study-version create: create a new study.
"""

import click

from antares.study.version.__about__ import __date__, __version__
from antares.study.version.create_app import CreateApp, available_versions
from antares.study.version.exceptions import ApplicationError
from antares.study.version.show_app import ShowApp
from antares.study.version.upgrade_app import UpgradeApp

INTERRUPTED_BY_THE_USER = "Operation interrupted by the user."


@click.group(context_settings={"max_content_width": 120})
@click.version_option(package_name="antares-study-version", message=f"v{__version__} ({__date__})")
def cli() -> None:
"""
Main entrypoint for the CLI application.
"""


@cli.command()
@click.argument(
"study_dir",
type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True),
)
def show(study_dir: str) -> None:
"""
Display the details of a study in human-readable format.
STUDY_DIR: The directory containing the study.
"""
try:
app = ShowApp(study_dir) # type: ignore
except (ValueError, FileNotFoundError) as e:
click.echo(f"Error: {e}", err=True)
raise click.Abort()

try:
app()
except ApplicationError as e:
click.echo(f"Error: {e}", err=True)
raise click.Abort()
except KeyboardInterrupt:
click.echo(INTERRUPTED_BY_THE_USER, err=True)
raise click.Abort()


def _display_available_versions(ctx: click.Context, _param: click.Option, value: bool) -> None:
if not value or ctx.resilient_parsing:
return
click.echo(f"Available versions: {', '.join(available_versions())}")
ctx.exit()


@cli.command()
@click.argument(
"study_dir",
type=click.Path(exists=False, file_okay=False, dir_okay=True, resolve_path=True),
)
@click.option(
"-c",
"--caption",
default="New Study",
help="Caption of the study",
show_default=True,
)
@click.option(
"-v",
"--version",
default=available_versions()[-1],
help="Version of the study to create",
show_default=True,
type=click.Choice(available_versions()),
)
@click.option(
"-a",
"--author",
default="Anonymous",
help="Author of the study",
show_default=True,
)
@click.option(
"--versions",
is_flag=True,
callback=_display_available_versions,
expose_value=False,
is_eager=True,
help="Display all available upgrade versions and quit.",
)
def create(study_dir: str, caption: str, version: str, author: str) -> None:
"""
Create a new study in the specified directory.
STUDY_DIR: The directory where the study will be created.
"""
try:
app = CreateApp(study_dir, caption=caption, version=version, author=author) # type: ignore
except (ValueError, FileExistsError) as e:
click.echo(f"Error: {e}", err=True)
raise click.Abort()

try:
app()
except ApplicationError as e:
click.echo(f"Error: {e}", err=True)
raise click.Abort()
except KeyboardInterrupt:
click.echo(INTERRUPTED_BY_THE_USER, err=True)
raise click.Abort()


@cli.command()
@click.argument(
"study_dir",
type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True),
)
@click.option(
"-v",
"--version",
default=available_versions()[-1],
help="Version of the study to create",
show_default=True,
type=click.Choice(available_versions()),
)
def upgrade(study_dir: str, version: str) -> None:
"""
Upgrade a study to a new version.
STUDY_DIR: The directory containing the study to upgrade.
"""
try:
app = UpgradeApp(study_dir, version=version) # type: ignore
except (ValueError, FileNotFoundError) as e:
click.echo(f"Error: {e}", err=True)
raise click.Abort()

try:
app()
except ApplicationError as e:
click.echo(f"Error: {e}", err=True)
raise click.Abort()
except KeyboardInterrupt:
click.echo(INTERRUPTED_BY_THE_USER, err=True)
raise click.Abort()
78 changes: 78 additions & 0 deletions src/antares/study/version/create_app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import dataclasses
import datetime
import typing as t
import zipfile
from importlib.resources import contents, open_binary
from pathlib import Path

from antares.study.version.exceptions import ApplicationError
from antares.study.version.model.study_antares import StudyAntares
from antares.study.version.model.study_version import StudyVersion

_RESOURCES_PACKAGE = "antares.study.version.create_app.resources"


def get_template_version(template_name: str) -> StudyVersion:
template_name = template_name.replace(".zip", "")
version_str = template_name.split("_")[-1] # 880.zip
return StudyVersion.parse(version_str)


TEMPLATES_BY_VERSIONS: t.Dict[StudyVersion, str] = {
get_template_version(name): name for name in contents(_RESOURCES_PACKAGE) if name.endswith(".zip")
}


def available_versions() -> t.List[str]:
"""
Return a list of available template versions.
Returns:
A list of available template versions.
"""
return [f"{ver:2d}" for ver in sorted(TEMPLATES_BY_VERSIONS)]


@dataclasses.dataclass
class CreateApp:
"""
Create a new study.
"""

study_dir: Path
caption: str
version: StudyVersion
author: str

def __post_init__(self):
self.study_dir = Path(self.study_dir)
self.caption = self.caption.strip()
self.version = StudyVersion.parse(self.version)
self.author = self.author.strip()
if self.study_dir.exists():
raise FileExistsError(f"Study directory already exists: '{self.study_dir}'")
if not self.caption:
raise ValueError("Caption cannot be empty")

def __call__(self) -> None:
try:
template_name = TEMPLATES_BY_VERSIONS[self.version]
except KeyError:
msg = f"No available template for version {self.version}: available templates are {available_versions()}"
raise ApplicationError(msg)
print(f"Extracting template {template_name} to '{self.study_dir}'...")
with open_binary(_RESOURCES_PACKAGE, template_name) as zip_file:
with zipfile.ZipFile(zip_file, mode="r") as archive:
archive.extractall(self.study_dir)

creation_date = datetime.datetime.now()
study_antares = StudyAntares(
version=self.version,
caption=self.caption,
created_date=creation_date,
last_save_date=creation_date,
author=self.author,
)
print("Writing 'study.antares' file...")
study_antares.to_ini_file(self.study_dir, update_save_date=False)
print(f"Study '{self.caption}' created successfully.")
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
4 changes: 4 additions & 0 deletions src/antares/study/version/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class ApplicationError(Exception):
"""
Base class for all exceptions raised by the application.
"""
Loading

0 comments on commit 5e921f7

Please sign in to comment.