Skip to content

Commit

Permalink
v2.14
Browse files Browse the repository at this point in the history
v2.14 on master
  • Loading branch information
laurent-laporte-pro authored May 12, 2023
2 parents 64e4f3c + 74768e0 commit 9e918fd
Show file tree
Hide file tree
Showing 158 changed files with 6,156 additions and 1,325 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/compatibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ jobs:
python-version: [ 3.8 ]

steps:
- name: Checkout github repo (+ download lfs dependencies)
- name: Checkout github repo
uses: actions/checkout@v2
with:
submodules: recursive
- name: Set up Python
uses: actions/setup-python@v1
with:
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ jobs:
- name: Checkout github repo (+ download lfs dependencies)
uses: actions/checkout@v3
with:
submodules: recursive
fetch-depth: 0
- name: Install wget for windows
if: matrix.os == 'windows-latest'
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ jobs:
steps:
- name: Checkout github repo (+ download lfs dependencies)
uses: actions/checkout@v2
with:
submodules: recursive
- name: Set up Python
uses: actions/setup-python@v1
with:
Expand Down Expand Up @@ -41,8 +39,6 @@ jobs:
steps:
- name: Checkout github repo (+ download lfs dependencies)
uses: actions/checkout@v2
with:
submodules: recursive
- name: Set up Python
uses: actions/setup-python@v1
with:
Expand Down
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

10 changes: 0 additions & 10 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,6 @@ COPY ./scripts /scripts
COPY ./alembic /alembic
COPY ./alembic.ini /alembic.ini

# > IMPORTANT: The `antares-launcher` project (source files) is no longer needed,
# > because the `Antares-Launcher` Python library is now declared as a dependency
# > in the `requirements.txt` file.
# > In other words, we can dispense with the creation of the symbolic link.

# COPY ./antares-launcher /antares-launcher
# RUN ln -s /antares-launcher/antareslauncher /antareslauncher
# RUN mkdir /conf/antares-launcher
# RUN cp /antares-launcher/requirements.txt /conf/antares-launcher/requirements.txt

RUN ./scripts/install-debug.sh

RUN pip3 install --upgrade pip \
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ First clone the projet:
```shell script
git clone https://github.com/AntaresSimulatorTeam/AntaREST.git
cd AntaREST
git submodule init
git submodule update
```

Install back dependencies
Expand Down
1 change: 0 additions & 1 deletion antares-launcher
Submodule antares-launcher deleted from ba9202
4 changes: 2 additions & 2 deletions antarest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

# Standard project metadata

__version__ = "2.13.2"
__version__ = "2.14.0"
__author__ = "RTE, Antares Web Team"
__date__ = "2023-04-25"
__date__ = "2023-05-12"
# noinspection SpellCheckingInspection
__credits__ = "(c) Réseau de Transport de l’Électricité (RTE)"

Expand Down
10 changes: 7 additions & 3 deletions antarest/core/core_blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
from antarest.core.jwt import JWTUser
from antarest.core.requests import UserHasNotPermissionError
from antarest.core.utils.web import APITag
from antarest.core.version_info import VersionInfoDTO, get_commit_id
from antarest.core.version_info import (
VersionInfoDTO,
get_commit_id,
get_dependencies,
)
from antarest.login.auth import Auth
from fastapi import APIRouter, Depends
from pydantic import BaseModel
Expand Down Expand Up @@ -39,18 +43,18 @@ def version_info() -> Any:
"""
Returns the current version of the application, along with relevant dependency information.
- `name`: The name of the application.
- `version`: The current version of the application.
- `gitcommit`: The commit ID of the current version's Git repository.
- `dependencies`: A dictionary of dependencies, where the key is
the dependency name and the value is its version number.
"""
from antareslauncher import __version__ as antares_launcher_version
from antarest import __version__ as antarest_version

return VersionInfoDTO(
version=antarest_version,
gitcommit=get_commit_id(config.resources_path),
dependencies={"Antares_Launcher": antares_launcher_version},
dependencies=get_dependencies(),
)

@bp.get("/kill", include_in_schema=False)
Expand Down
18 changes: 15 additions & 3 deletions antarest/core/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from http import HTTPStatus
from typing import Optional

from fastapi import HTTPException
from fastapi.exceptions import HTTPException


class ShouldNotHappenException(Exception):
Expand Down Expand Up @@ -177,14 +177,26 @@ class StudyOutputNotFoundError(Exception):
pass


class AllocationDataNotFound(HTTPException):
def __init__(self, *area_ids: str) -> None:
count = len(area_ids)
ids = ", ".join(f"'{a}'" for a in area_ids)
msg = {
0: "Allocation data is found",
1: f"Allocation data for area {area_ids} is not found",
2: f"Allocation data for areas {area_ids} is not found",
}[min(count, 2)]
super().__init__(HTTPStatus.NOT_FOUND, msg)


class AreaNotFound(HTTPException):
def __init__(self, *area_ids: str) -> None:
count = len(area_ids)
ids = ", ".join(f"'{a}'" for a in area_ids)
msg = {
0: "All areas are found",
1: f"{count} area is not found: {ids}",
2: f"{count} areas are not found: {ids}",
1: f"Area is not found: {ids}",
2: f"Areas are not found: {ids}",
}[min(count, 2)]
super().__init__(HTTPStatus.NOT_FOUND, msg)

Expand Down
65 changes: 57 additions & 8 deletions antarest/core/logging/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,27 @@


class CustomDefaultFormatter(logging.Formatter):
"""
A custom logging formatter that ensures all fields specified
in the format string are available in the log record.
This formatter uses a regular expression pattern to extract
field names from the format string, and adds any missing
fields to the log record with a value of `None`.
"""

def format(self, record: logging.LogRecord) -> str:
"""
Formats the specified log record using the custom formatter,
ensuring all fields specified in the format string are available
in the record. Returns the formatted string.
Args:
record: The logging record to format.
Returns:
The formatted message.
"""
arg_pattern = re.compile(r"%\((\w+)\)")
arg_names = [x.group(1) for x in arg_pattern.finditer(self._fmt or "")]
for field in arg_names:
Expand All @@ -31,7 +51,17 @@ def format(self, record: logging.LogRecord) -> str:
return super().format(record)


def configure_logger(config: Config) -> None:
def configure_logger(
config: Config, handler_cls: str = "logging.FileHandler"
) -> None:
"""
Set up the logging configuration based on the input `config` object
and an optional `handler_cls` argument.
Args:
config: A `Config` object that contains the logging configuration parameters.
handler_cls: A string representing the class of the logging handler.
"""
logging_config: Dict[str, Any] = {
"version": 1,
"disable_existing_loggers": False,
Expand Down Expand Up @@ -91,13 +121,32 @@ def configure_logger(config: Config) -> None:
},
}
if config.logging.logfile is not None:
logging_config["handlers"]["default"] = {
"class": "logging.FileHandler",
"formatter": "console",
"level": "INFO",
"filename": config.logging.logfile,
"filters": ["context"],
}
if handler_cls == "logging.FileHandler":
logging_config["handlers"]["default"] = {
"class": handler_cls,
"formatter": "console",
"level": "INFO",
"filename": config.logging.logfile,
"filters": ["context"],
}
elif handler_cls == "logging.handlers.TimedRotatingFileHandler":
logging_config["handlers"]["default"] = {
"class": handler_cls,
"filename": config.logging.logfile,
"when": "D", # D = day
"interval": 90, # 90 days = 3 months
"backupCount": 1, # keep only 1 backup (0 means keep all)
"encoding": "utf-8",
"delay": False,
"utc": False,
"atTime": None,
"formatter": "console",
"level": "INFO",
"filters": ["context"],
}
else: # pragma: no cover
raise NotImplementedError(handler_cls)

if config.logging.level is not None and config.logging.level in [
"INFO",
"WARNING",
Expand Down
64 changes: 56 additions & 8 deletions antarest/core/version_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""
import subprocess
from pathlib import Path
from typing import Dict, Optional
from typing import Dict

from pydantic import BaseModel

Expand All @@ -14,6 +14,22 @@ class VersionInfoDTO(BaseModel):
gitcommit: str
dependencies: Dict[str, str]

class Config:
schema_extra = {
"example": {
"name": "AntaREST",
"version": "2.13.2",
"gitcommit": "879d9d641fc2e7e30e626084b431ce014de63532",
"dependencies": {
"click": "8.0.4",
"Deprecated": "1.2.13",
"fastapi": "0.73.0",
"Flask": "2.1.3",
"gunicorn": "20.1.0",
},
}
}


def get_commit_id(resources_dir: Path) -> str:
"""
Expand All @@ -36,10 +52,42 @@ def get_commit_id(resources_dir: Path) -> str:
try:
return path_commit_id.read_text(encoding="utf-8").strip()
except FileNotFoundError:
command = "git log -1 HEAD --format=%H"
try:
return subprocess.check_output(
command, encoding="utf-8", shell=True
).strip()
except (subprocess.CalledProcessError, FileNotFoundError):
return ""
return get_last_commit_from_git()


def get_last_commit_from_git() -> str:
"""Returns the commit ID of the current Git HEAD, or ""."""
command = "git log -1 HEAD --format=%H"
try:
return subprocess.check_output(
command, encoding="utf-8", shell=True
).strip()
except (subprocess.CalledProcessError, FileNotFoundError):
return ""


def get_dependencies() -> Dict[str, str]:
"""
Retrieve the list of installed dependencies and their versions.
Returns:
A dictionary containing the package names and their corresponding versions installed in the
current Python environment. The dictionary keys are the package names (as strings), and the
values are the corresponding version numbers (also as strings).
Raises:
subprocess.CalledProcessError:
If the `pip freeze` command fails for some reason.
"""
# fmt: off
output = subprocess.check_output("pip freeze", encoding="utf-8", shell=True)
lines = (
line
for line in output.splitlines(keepends=False)
if "==" in line
)
# noinspection PyTypeChecker
packages = dict(line.split("==", 1) for line in lines)
# AntaREST is not a dependency of AntaREST
return {k: v for k, v in packages.items() if k.lower() != "antarest"}
# fmt: on
18 changes: 18 additions & 0 deletions antarest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import uvicorn # type: ignore
import uvicorn.config # type: ignore
from fastapi import FastAPI, HTTPException
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi_jwt_auth import AuthJWT # type: ignore
from ratelimit import RateLimitMiddleware # type: ignore
from ratelimit.backends.redis import RedisBackend # type: ignore
Expand Down Expand Up @@ -283,6 +285,22 @@ def handle_http_exception(request: Request, exc: HTTPException) -> Any:
status_code=exc.status_code,
)

@application.exception_handler(RequestValidationError)
async def handle_validation_exception(
request: Request, exc: RequestValidationError
) -> Any:
error_message = exc.errors()[0]["msg"]
return JSONResponse(
status_code=422,
content=jsonable_encoder(
{
"description": error_message,
"exception": "RequestValidationError",
"body": exc.body,
}
),
)

@application.exception_handler(Exception)
def handle_all_exception(request: Request, exc: Exception) -> Any:
"""Return JSON instead of HTML for HTTP errors."""
Expand Down
Loading

0 comments on commit 9e918fd

Please sign in to comment.