Skip to content

Commit 5b9f5ff

Browse files
authored
Implement automatic date tracking for job start and finish times (#24)
1 parent a819d60 commit 5b9f5ff

4 files changed

Lines changed: 158 additions & 9 deletions

File tree

.github/workflows/code-checks.yaml

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@ jobs:
1414
timeout-minutes: 10
1515
steps:
1616
- uses: actions/checkout@v4
17-
- name: Install poetry
18-
run: pipx install poetry
1917
- uses: actions/setup-python@v5
2018
with:
2119
python-version: '3.11.10'
22-
cache: 'poetry'
20+
- name: Install poetry
21+
run: pip install poetry
2322
- name: Install dependencies
24-
run: poetry install
23+
run: python -m poetry install
2524
- name: Ruff check
2625
run: poetry run ruff check .
2726
- name: Ruff format check
@@ -33,14 +32,13 @@ jobs:
3332
timeout-minutes: 60
3433
steps:
3534
- uses: actions/checkout@v4
36-
- name: Install poetry
37-
run: pipx install poetry
3835
- uses: actions/setup-python@v5
3936
with:
4037
python-version: '3.11.10'
41-
cache: 'poetry'
38+
- name: Install poetry
39+
run: pip install poetry
4240
- name: Install dependencies
43-
run: poetry install
41+
run: python -m poetry install
4442
- name: configure AWS
4543
uses: aws-actions/configure-aws-credentials@v4
4644
with:

api/endpoints/job_update.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import datetime
2+
13
from fastapi import APIRouter, Depends, HTTPException, status
24
from sqlalchemy.orm import Session
35

@@ -24,7 +26,21 @@ def update_job_status(
2426
db_job = job_crud.get_job(db, update.job_id)
2527
if db_job is None:
2628
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
29+
30+
# Update the status
2731
db_job.status = update.status.value
32+
33+
# Update date_started when job is first pulled/started
34+
if update.status == JobStates.pulled and db_job.date_started is None:
35+
db_job.date_started = datetime.datetime.now(datetime.timezone.utc)
36+
37+
# Update date_finished when job reaches a terminal state
38+
if (
39+
update.status in [JobStates.finished, JobStates.error]
40+
and db_job.date_finished is None
41+
):
42+
db_job.date_finished = datetime.datetime.now(datetime.timezone.utc)
43+
2844
if update.runtime_details:
2945
db_job.runtime_details = (
3046
(db_job.runtime_details or "") + "\n" + update.runtime_details

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "api"
3-
version = "0.1.0"
3+
version = "0.1.1"
44
description = "User-facing API of DECODE OpenCloud."
55
authors = ["Arthur Jaques <arthur.jaques@hispeed.ch>"]
66
readme = "README.md"

tests/integration/endpoints/test_job_update.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,138 @@ def test_finished_notification(
6262
headers={"x-api-key": internal_api_key_secret},
6363
)
6464
mock_email_sender.send_email.assert_called_once()
65+
66+
67+
def test_date_started_set_on_pulled_status(
68+
client: TestClient,
69+
db_session: Session,
70+
internal_api_key_secret: str,
71+
jobs: list[Job],
72+
) -> None:
73+
"""Test that date_started is set when job status changes to 'pulled'."""
74+
job = jobs[0]
75+
assert job.date_started is None # Initially no start date
76+
77+
response = client.put(
78+
ENDPOINT,
79+
json={"job_id": job.id, "status": "pulled"},
80+
headers={"x-api-key": internal_api_key_secret},
81+
)
82+
assert response.status_code == 200
83+
84+
# Refresh job from database
85+
db_session.refresh(job)
86+
assert job.status == "pulled"
87+
assert job.date_started is not None # Should now have a start date
88+
assert job.date_finished is None # Should not have finished date yet
89+
90+
91+
def test_date_started_not_overwritten(
92+
client: TestClient,
93+
db_session: Session,
94+
internal_api_key_secret: str,
95+
jobs: list[Job],
96+
) -> None:
97+
"""Test that date_started is not overwritten if already set."""
98+
job = jobs[0]
99+
100+
# First update to 'pulled' to set date_started
101+
client.put(
102+
ENDPOINT,
103+
json={"job_id": job.id, "status": "pulled"},
104+
headers={"x-api-key": internal_api_key_secret},
105+
)
106+
db_session.refresh(job)
107+
original_start_date = job.date_started
108+
assert original_start_date is not None
109+
110+
# Update to another status and then back to pulled
111+
client.put(
112+
ENDPOINT,
113+
json={"job_id": job.id, "status": "running"},
114+
headers={"x-api-key": internal_api_key_secret},
115+
)
116+
client.put(
117+
ENDPOINT,
118+
json={"job_id": job.id, "status": "pulled"},
119+
headers={"x-api-key": internal_api_key_secret},
120+
)
121+
122+
db_session.refresh(job)
123+
assert job.date_started == original_start_date # Should not change
124+
125+
126+
def test_date_finished_set_on_finished_status(
127+
client: TestClient,
128+
db_session: Session,
129+
internal_api_key_secret: str,
130+
jobs: list[Job],
131+
) -> None:
132+
"""Test that date_finished is set when job status changes to 'finished'."""
133+
job = jobs[0]
134+
assert job.date_finished is None # Initially no finish date
135+
136+
response = client.put(
137+
ENDPOINT,
138+
json={"job_id": job.id, "status": "finished"},
139+
headers={"x-api-key": internal_api_key_secret},
140+
)
141+
assert response.status_code == 200
142+
143+
# Refresh job from database
144+
db_session.refresh(job)
145+
assert job.status == "finished"
146+
assert job.date_finished is not None # Should now have a finish date
147+
148+
149+
def test_date_finished_set_on_error_status(
150+
client: TestClient,
151+
db_session: Session,
152+
internal_api_key_secret: str,
153+
jobs: list[Job],
154+
) -> None:
155+
"""Test that date_finished is set when job status changes to 'error'."""
156+
job = jobs[0]
157+
assert job.date_finished is None # Initially no finish date
158+
159+
response = client.put(
160+
ENDPOINT,
161+
json={"job_id": job.id, "status": "error"},
162+
headers={"x-api-key": internal_api_key_secret},
163+
)
164+
assert response.status_code == 200
165+
166+
# Refresh job from database
167+
db_session.refresh(job)
168+
assert job.status == "error"
169+
assert job.date_finished is not None # Should now have a finish date
170+
171+
172+
def test_date_finished_not_overwritten(
173+
client: TestClient,
174+
db_session: Session,
175+
internal_api_key_secret: str,
176+
jobs: list[Job],
177+
) -> None:
178+
"""Test that date_finished is not overwritten if already set."""
179+
job = jobs[0]
180+
181+
# First update to 'finished' to set date_finished
182+
client.put(
183+
ENDPOINT,
184+
json={"job_id": job.id, "status": "finished"},
185+
headers={"x-api-key": internal_api_key_secret},
186+
)
187+
db_session.refresh(job)
188+
original_finish_date = job.date_finished
189+
assert original_finish_date is not None
190+
191+
# Update to error status (should not overwrite date_finished)
192+
client.put(
193+
ENDPOINT,
194+
json={"job_id": job.id, "status": "error"},
195+
headers={"x-api-key": internal_api_key_secret},
196+
)
197+
198+
db_session.refresh(job)
199+
assert job.date_finished == original_finish_date # Should not change

0 commit comments

Comments
 (0)