Skip to content

Commit

Permalink
Add endpoint for adding job to analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
seallard committed Jan 25, 2024
1 parent 743a20d commit a3cce39
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 4 deletions.
26 changes: 26 additions & 0 deletions tests/integration/endpoints/test_add_job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from flask.testing import FlaskClient
from http import HTTPStatus
from trailblazer.dto.create_job_request import CreateJobRequest

from trailblazer.store.models import Analysis

TYPE_JSON = "application/json"


def test_add_job_to_analysis(client: FlaskClient, analysis: Analysis):
# GIVEN an analysis

# GIVEN a valid request to add a job to the analysis
create_job_request = CreateJobRequest(name="job", slurm_id="12345")
data: str = create_job_request.model_dump_json()

# WHEN sending the request
response = client.post(
f"/api/v1/analyses/{analysis.id}/jobs", data=data, content_type=TYPE_JSON
)

# THEN it gives a success response
assert response.status_code == HTTPStatus.CREATED

# THEN it should return the job
assert response.json["name"] == create_job_request.name
9 changes: 9 additions & 0 deletions trailblazer/dto/create_job_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from pydantic import BaseModel

from trailblazer.constants import JobType


class CreateJobRequest(BaseModel):
name: str
job_type: JobType | None = JobType.ANALYSIS
slurm_id: int
7 changes: 7 additions & 0 deletions trailblazer/dto/job_response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from pydantic import BaseModel

from trailblazer.constants import JobType


class JobResponse(BaseModel):
name: str
17 changes: 17 additions & 0 deletions trailblazer/server/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
FailedJobsRequest,
FailedJobsResponse,
)
from trailblazer.dto.create_job_request import CreateJobRequest
from trailblazer.exc import MissingAnalysis
from trailblazer.server.ext import store
from trailblazer.server.utils import (
parse_analyses_request,
parse_analysis_update_request,
parse_get_failed_jobs_request,
parse_job_create_request,
stringify_timestamps,
)
from trailblazer.services import AnalysisService, JobService
Expand Down Expand Up @@ -80,6 +82,21 @@ def get_analysis(analysis_id):
return jsonify(error=str(error)), HTTPStatus.NOT_FOUND


@blueprint.route("/analyses/<int:analysis_id>/jobs", methods=["POST"])
def add_job(analysis_id):
job_service: JobService = current_app.extensions.get("job_service")
try:
data: CreateJobRequest = parse_job_create_request(request)
response: AnalysisResponse = job_service.add_job(analysis_id=analysis_id, data=data)
return jsonify(response.model_dump()), HTTPStatus.CREATED
except MissingAnalysis as error:
return jsonify(error=str(error)), HTTPStatus.NOT_FOUND
except ValidationError as error:
return jsonify(error=str(error)), HTTPStatus.BAD_REQUEST
except Exception as error:
return jsonify(error=str(error)), HTTPStatus.CONFLICT


@blueprint.route("/analyses/<int:analysis_id>", methods=["PUT"])
def update_analysis(analysis_id):
analysis_service: AnalysisService = current_app.extensions.get("analysis_service")
Expand Down
5 changes: 5 additions & 0 deletions trailblazer/server/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from flask import Request

from trailblazer.dto import AnalysesRequest, AnalysisUpdateRequest, FailedJobsRequest
from trailblazer.dto.create_job_request import CreateJobRequest


def parse_analyses_request(request: Request) -> AnalysesRequest:
Expand Down Expand Up @@ -34,3 +35,7 @@ def stringify_timestamps(data: dict) -> dict[str, str]:
if isinstance(val, datetime.datetime):
data[key] = str(val)
return data


def parse_job_create_request(request: Request) -> CreateJobRequest:
return CreateJobRequest.model_validate(request.json)
9 changes: 8 additions & 1 deletion trailblazer/services/job_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

from trailblazer.constants import TrailblazerStatus
from trailblazer.dto import FailedJobsRequest, FailedJobsResponse
from trailblazer.services.utils import create_jobs_response
from trailblazer.dto.create_job_request import CreateJobRequest
from trailblazer.dto.job_response import JobResponse
from trailblazer.services.utils import create_job_response, create_jobs_response
from trailblazer.store.models import Job
from trailblazer.store.store import Store
from trailblazer.utils.datetime import get_date_number_of_days_ago

Expand All @@ -17,3 +20,7 @@ def get_failed_jobs(self, request: FailedJobsRequest) -> FailedJobsResponse:
status=TrailblazerStatus.FAILED, since_when=time_window
)
return create_jobs_response(failed_jobs)

def add_job(self, analysis_id: int, data: CreateJobRequest) -> JobResponse:
job: Job = self.store.add_job(analysis_id=analysis_id, data=data)
return create_job_response(job)
7 changes: 6 additions & 1 deletion trailblazer/services/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from trailblazer.dto import AnalysisResponse, FailedJobsResponse
from trailblazer.store.models import Analysis
from trailblazer.dto.job_response import JobResponse
from trailblazer.store.models import Analysis, Job


def create_jobs_response(failed_job_statistics: list[dict]) -> FailedJobsResponse:
Expand All @@ -11,3 +12,7 @@ def create_analysis_response(analysis: Analysis) -> AnalysisResponse:
analysis_data["jobs"] = [job.to_dict() for job in analysis.jobs]
analysis_data["user"] = analysis.user.to_dict() if analysis.user else None
return AnalysisResponse.model_validate(analysis_data)


def create_job_response(job: Job) -> JobResponse:
return JobResponse(name=job.name)
19 changes: 17 additions & 2 deletions trailblazer/store/crud/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

from sqlalchemy.orm import Session

from trailblazer.constants import TrailblazerStatus
from trailblazer.constants import JobType, SlurmJobStatus, TrailblazerStatus
from trailblazer.dto.create_job_request import CreateJobRequest
from trailblazer.store.base import BaseHandler
from trailblazer.store.database import get_session
from trailblazer.store.models import Analysis, User
from trailblazer.store.models import Analysis, Job, User


class CreateHandler(BaseHandler):
Expand Down Expand Up @@ -49,3 +50,17 @@ def add_user(self, name: str, email: str) -> User:
session.add(new_user)
session.commit()
return new_user

def add_job(self, analysis_id: int, data: CreateJobRequest) -> Job:
"""Add a new job to the database."""
analysis: Analysis = self.get_analysis_with_id(analysis_id)
job = Job(
name=data.name,
status=SlurmJobStatus.PENDING,
slurm_id=data.slurm_id,
job_type=data.job_type,
)
analysis.jobs.append(job)
session: Session = get_session()
session.commit()
return job

0 comments on commit a3cce39

Please sign in to comment.