Skip to content

Commit

Permalink
feat(api): fixed enum, tests and added patch
Browse files Browse the repository at this point in the history
  • Loading branch information
salemsd committed Dec 13, 2024
1 parent 6df58ba commit 1d3adec
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 37 deletions.
10 changes: 5 additions & 5 deletions src/antares/exceptions/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,17 +296,17 @@ def __init__(self, study_id: str, message: str) -> None:

class SimulationTimeOutError(Exception):
def __init__(self, job_id: str, time_out: int) -> None:
self.message = (
f"Job {job_id} exceeded timeout of {time_out} seconds"
)
self.message = f"Job {job_id} exceeded timeout of {time_out} seconds"
super().__init__(self.message)


class AntaresSimulationUnzipError(Exception):
def __init__(self, study_id: str, message: str) -> None:
self.message = f"Could not unzip simulation for study {study_id}: " + message
super().__init__(self.message)

class SimulationFailureError(Exception):

class SimulationFailedError(Exception):
def __init__(self, study_id: str) -> None:
self.message = f"Simulation failed for {study_id}"
super().__init__(self.message)
super().__init__(self.message)
10 changes: 9 additions & 1 deletion src/antares/model/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@
# This file is part of the Antares project.

from enum import Enum
from typing import Optional, Any
from typing import Any, Optional

from pydantic import BaseModel, Field


class Solver(Enum):
COIN = "coin"
XPRESS = "xpress"
SIRIUS = "sirius"


class AntaresSimulationParameters(BaseModel):
solver: Solver = Solver.SIRIUS
nb_cpu: Optional[int] = None
Expand All @@ -44,12 +46,18 @@ def to_api(self) -> dict[str, Any]:
data.pop("presolve", None)
return data


class JobStatus(Enum):
PENDING = "pending"
RUNNING = "running"
SUCCESS = "success"
FAILED = "failed"

@staticmethod
def from_str(input: str) -> "JobStatus":
return JobStatus.__getitem__(input.upper())


class Job(BaseModel):
job_id: str
status: JobStatus
Expand Down
2 changes: 1 addition & 1 deletion src/antares/model/study.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@
from antares.exceptions.exceptions import APIError, LinkCreationError, StudyCreationError
from antares.model.area import Area, AreaProperties, AreaUi
from antares.model.binding_constraint import BindingConstraint, BindingConstraintProperties, ConstraintTerm
from antares.model.simulation import Job, AntaresSimulationParameters
from antares.model.link import Link, LinkProperties, LinkUi
from antares.model.settings.study_settings import DefaultStudySettings, StudySettings, StudySettingsLocal
from antares.model.settings.time_series import correlation_defaults
from antares.model.simulation import AntaresSimulationParameters, Job
from antares.service.api_services.study_api import _returns_study_settings
from antares.service.base_services import BaseStudyService
from antares.service.service_factory import ServiceFactory
Expand Down
19 changes: 9 additions & 10 deletions src/antares/service/api_services/run_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@
import json
import time

from typing import Optional, Any
from typing import Any, Optional

from antares.api_conf.api_conf import APIconf
from antares.api_conf.request_wrapper import RequestWrapper
from antares.exceptions.exceptions import (
AntaresSimulationRunningError,
AntaresSimulationUnzipError,
APIError,
SimulationTimeOutError, SimulationFailureError,
SimulationFailedError,
SimulationTimeOutError,
)
from antares.model.simulation import Job, JobStatus, AntaresSimulationParameters
from antares.model.simulation import AntaresSimulationParameters, Job, JobStatus
from antares.service.base_services import BaseRunService


Expand Down Expand Up @@ -51,7 +52,7 @@ def _get_job_from_id(self, job_id: str) -> Job:
url = f"{self._base_url}/launcher/jobs/{job_id}"
response = self._wrapper.get(url)
job_info = response.json()
status = job_info["status"]
status = JobStatus.from_str(job_info["status"])
output_id = job_info["output_id"] if status == JobStatus.SUCCESS else None
launcher_params = json.loads(job_info["launcher_params"])
return Job(job_id=job_id, status=status, unzip_output=launcher_params["auto_unzip"], output_id=output_id)
Expand All @@ -65,9 +66,10 @@ def wait_job_completion(self, job: Job, time_out: int) -> None:
time.sleep(repeat_interval)
updated_job = self._get_job_from_id(job.job_id)
job.status = updated_job.status
job.output_id = updated_job.output_id

if job.status == JobStatus.FAILED:
raise SimulationFailureError(self.study_id)
raise SimulationFailedError(self.study_id)

if job.unzip_output:
self._wait_unzip_output(self.study_id, ["UNARCHIVE"], job.output_id)
Expand All @@ -77,10 +79,7 @@ def wait_job_completion(self, job: Job, time_out: int) -> None:
def _wait_unzip_output(self, ref_id: str, type: list[str], job_output_id: str) -> None:
url = f"{self._base_url}/tasks"
repeat_interval = 2
payload = {
"type": type,
"ref_id": ref_id
}
payload = {"type": type, "ref_id": ref_id}
try:
response = self._wrapper.post(url, json=payload)
tasks = response.json()
Expand All @@ -93,7 +92,7 @@ def _wait_unzip_output(self, ref_id: str, type: list[str], job_output_id: str) -
def _get_task_id(self, job_output_id: str, tasks: list[dict[str, Any]]) -> str:
for task in tasks:
task_name = task["name"]
output_id = task_name.split('/')[-1].split(' ')[0]
output_id = task_name.split("/")[-1].split(" ")[0]
if output_id == job_output_id:
return task["id"]

Expand Down
2 changes: 1 addition & 1 deletion src/antares/service/base_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
ConstraintTerm,
)
from antares.model.hydro import Hydro, HydroMatrixName, HydroProperties
from antares.model.simulation import Job, AntaresSimulationParameters
from antares.model.link import Link, LinkProperties, LinkUi
from antares.model.renewable import RenewableCluster, RenewableClusterProperties
from antares.model.settings.study_settings import StudySettings
from antares.model.simulation import AntaresSimulationParameters, Job
from antares.model.st_storage import STStorage, STStorageProperties
from antares.model.thermal import ThermalCluster, ThermalClusterMatrixName, ThermalClusterProperties

Expand Down
2 changes: 1 addition & 1 deletion src/antares/service/local_services/run_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from typing import Any, Optional

from antares.config.local_configuration import LocalConfiguration
from antares.model.simulation import Job, AntaresSimulationParameters
from antares.model.simulation import AntaresSimulationParameters, Job
from antares.service.base_services import BaseRunService


Expand Down
40 changes: 22 additions & 18 deletions tests/antares/services/api_services/test_study_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,26 @@
import re

from json import dumps
from unittest.mock import Mock
from unittest.mock import Mock, patch

from antares.api_conf.api_conf import APIconf
from antares.exceptions.exceptions import (
AreaCreationError,
BindingConstraintCreationError,
LinkCreationError,
SimulationFailedError,
SimulationTimeOutError,
StudyCreationError,
StudySettingsUpdateError,
StudyVariantCreationError, SimulationFailureError,
StudyVariantCreationError,
)
from antares.model.area import Area, AreaProperties, AreaUi
from antares.model.binding_constraint import BindingConstraint, BindingConstraintProperties
from antares.model.hydro import HydroProperties
from antares.model.simulation import Job, JobStatus, AntaresSimulationParameters, Solver
from antares.model.link import Link, LinkProperties, LinkUi
from antares.model.settings.general import GeneralParameters
from antares.model.settings.study_settings import StudySettings
from antares.model.simulation import AntaresSimulationParameters, Job, JobStatus, Solver
from antares.model.study import Study, create_study_api, create_variant_api, read_study_api
from antares.service.service_factory import ServiceFactory

Expand Down Expand Up @@ -337,15 +338,17 @@ def test_create_link_same_area(self):

def test_run_and_wait_antares_simulation(self):
parameters = AntaresSimulationParameters(solver=Solver.COIN, nb_cpu=2, unzip_output=True, presolve=False)
with requests_mock.Mocker() as mocker:

# patch simulates the repeating intervals so that we don't have to wait X seconds during the tests
with requests_mock.Mocker() as mocker, patch("time.sleep", return_value=None):
run_url = f"https://antares.com/api/v1/launcher/run/{self.study_id}"
job_id = "1234-g6z17"
mocker.post(run_url, json={"id": job_id}, status_code=200)

job_url = f"https://antares.com/api/v1/launcher/jobs/{job_id}"
output_id = "2024abcdefg-output"

# mocker.get() simulates multiple responses sequentially when given a list
# since multiple requests are done, each one is mapped one by one to each response in the list
# mock can take a list of responses that it maps one by one to each request, since we're doing multiple ones
response_list = [
{
"json": {
Expand All @@ -368,6 +371,7 @@ def test_run_and_wait_antares_simulation(self):
"id": job_id,
"status": "success",
"launcher_params": dumps(parameters.to_api()),
"output_id": output_id,
},
"status_code": 200,
},
Expand All @@ -376,7 +380,7 @@ def test_run_and_wait_antares_simulation(self):

tasks_url = "https://antares.com/api/v1/tasks"
task_id = "task-5678"
mocker.post(tasks_url, json=[{"id": task_id, "name": f"UNARCHIVE/{job_id}"}], status_code=200)
mocker.post(tasks_url, json=[{"id": task_id, "name": f"UNARCHIVE/{output_id}"}], status_code=200)

task_url = f"{tasks_url}/{task_id}"
mocker.get(task_url, json={"result": {"success": True}}, status_code=200)
Expand All @@ -390,7 +394,6 @@ def test_run_and_wait_antares_simulation(self):

assert job.status == JobStatus.SUCCESS

# timeout
response_list.pop()
mocker.get(job_url, response_list)
mocker.post(tasks_url, status_code=200)
Expand All @@ -399,18 +402,19 @@ def test_run_and_wait_antares_simulation(self):
with pytest.raises(SimulationTimeOutError):
self.study.wait_job_completion(job, time_out=2)

# failing
response_list.append({
"json": {
"id": job_id,
"status": "failure",
"launcher_params": dumps(parameters.to_api()),
},
"status_code": 200,
})
response_list.append(
{
"json": {
"id": job_id,
"status": "failed",
"launcher_params": dumps(parameters.to_api()),
},
"status_code": 200,
}
)
mocker.get(job_url, response_list)
mocker.post(tasks_url, status_code=200)

job = self.study.run_antares_simulation(parameters)
with pytest.raises(SimulationFailureError):
with pytest.raises(SimulationFailedError):
self.study.wait_job_completion(job, time_out=10)

0 comments on commit 1d3adec

Please sign in to comment.