Skip to content

Commit 7c005da

Browse files
authored
Add starting datetimes to TaskResults (#67)
1 parent e41cd34 commit 7c005da

File tree

9 files changed

+65
-10
lines changed

9 files changed

+65
-10
lines changed

django_tasks/backends/database/admin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class DBTaskResultAdmin(admin.ModelAdmin):
1313
"get_task_name",
1414
"status",
1515
"enqueued_at",
16+
"started_at",
1617
"finished_at",
1718
"priority",
1819
"queue_name",
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 5.0.6 on 2024-07-04 09:31
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
(
9+
"django_tasks_database",
10+
"0003_dbtaskresult_enqueued_at_dbtaskresult_finished_at",
11+
),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name="dbtaskresult",
17+
name="started_at",
18+
field=models.DateTimeField(null=True),
19+
),
20+
]

django_tasks/backends/database/models.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class DBTaskResult(GenericBase[P, T], models.Model):
6464
)
6565

6666
enqueued_at = models.DateTimeField(auto_now_add=True)
67+
started_at = models.DateTimeField(null=True)
6768
finished_at = models.DateTimeField(null=True)
6869

6970
args_kwargs = models.JSONField()
@@ -112,6 +113,7 @@ def task_result(self) -> "TaskResult[T]":
112113
id=str(self.id),
113114
status=ResultStatus[self.status],
114115
enqueued_at=self.enqueued_at,
116+
started_at=self.started_at,
115117
finished_at=self.finished_at,
116118
args=self.args_kwargs["args"],
117119
kwargs=self.args_kwargs["kwargs"],
@@ -128,7 +130,8 @@ def claim(self) -> None:
128130
Mark as job as being run
129131
"""
130132
self.status = ResultStatus.RUNNING
131-
self.save(update_fields=["status"])
133+
self.started_at = timezone.now()
134+
self.save(update_fields=["status", "started_at"])
132135

133136
@retry()
134137
def set_result(self, result: Any) -> None:

django_tasks/backends/dummy.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def enqueue(
3535
id=str(uuid4()),
3636
status=ResultStatus.NEW,
3737
enqueued_at=timezone.now(),
38+
started_at=None,
3839
finished_at=None,
3940
args=json_normalize(args),
4041
kwargs=json_normalize(kwargs),

django_tasks/backends/immediate.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def enqueue(
2828
)
2929

3030
enqueued_at = timezone.now()
31+
started_at = timezone.now()
3132
try:
3233
result = json_normalize(calling_task_func(*args, **kwargs))
3334
status = ResultStatus.COMPLETE
@@ -40,6 +41,7 @@ def enqueue(
4041
id=str(uuid4()),
4142
status=status,
4243
enqueued_at=enqueued_at,
44+
started_at=started_at,
4345
finished_at=timezone.now(),
4446
args=json_normalize(args),
4547
kwargs=json_normalize(kwargs),

django_tasks/task.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,9 @@ class TaskResult(Generic[T]):
205205
enqueued_at: datetime
206206
"""The time this task was enqueued"""
207207

208+
started_at: Optional[datetime]
209+
"""The time this task was started"""
210+
208211
finished_at: Optional[datetime]
209212
"""The time this task was finished"""
210213

@@ -238,8 +241,9 @@ def refresh(self) -> None:
238241
"""
239242
refreshed_task = self.task.get_backend().get_result(self.id)
240243

241-
# status, finished_at and result are the only refreshable attributes
244+
# status, started_at, finished_at and result are the only refreshable attributes
242245
self.status = refreshed_task.status
246+
self.started_at = refreshed_task.started_at
243247
self.finished_at = refreshed_task.finished_at
244248
self._result = refreshed_task._result
245249

@@ -249,7 +253,8 @@ async def arefresh(self) -> None:
249253
"""
250254
refreshed_task = await self.task.get_backend().aget_result(self.id)
251255

252-
# status, finished_at and result are the only refreshable attributes
256+
# status, started_at, finished_at and result are the only refreshable attributes
253257
self.status = refreshed_task.status
258+
self.started_at = refreshed_task.started_at
254259
self.finished_at = refreshed_task.finished_at
255260
self._result = refreshed_task._result

tests/tests/test_database_backend.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def test_enqueue_task(self) -> None:
4040
result = default_task_backend.enqueue(task, (1,), {"two": 3})
4141

4242
self.assertEqual(result.status, ResultStatus.NEW)
43+
self.assertIsNone(result.started_at)
4344
self.assertIsNone(result.finished_at)
4445
with self.assertRaisesMessage(ValueError, "Task has not finished yet"):
4546
result.result # noqa:B018
@@ -53,6 +54,7 @@ async def test_enqueue_task_async(self) -> None:
5354
result = await default_task_backend.aenqueue(task, [], {})
5455

5556
self.assertEqual(result.status, ResultStatus.NEW)
57+
self.assertIsNone(result.started_at)
5658
self.assertIsNone(result.finished_at)
5759
with self.assertRaisesMessage(ValueError, "Task has not finished yet"):
5860
result.result # noqa:B018
@@ -82,13 +84,17 @@ def test_refresh_result(self) -> None:
8284
)
8385

8486
DBTaskResult.objects.all().update(
85-
status=ResultStatus.COMPLETE, finished_at=timezone.now()
87+
status=ResultStatus.COMPLETE,
88+
started_at=timezone.now(),
89+
finished_at=timezone.now(),
8690
)
8791

8892
self.assertEqual(result.status, ResultStatus.NEW)
93+
self.assertIsNone(result.started_at)
8994
self.assertIsNone(result.finished_at)
9095
with self.assertNumQueries(1):
9196
result.refresh()
97+
self.assertIsNotNone(result.started_at)
9298
self.assertIsNotNone(result.finished_at)
9399
self.assertEqual(result.status, ResultStatus.COMPLETE)
94100

@@ -98,12 +104,16 @@ async def test_refresh_result_async(self) -> None:
98104
)
99105

100106
await DBTaskResult.objects.all().aupdate(
101-
status=ResultStatus.COMPLETE, finished_at=timezone.now()
107+
status=ResultStatus.COMPLETE,
108+
started_at=timezone.now(),
109+
finished_at=timezone.now(),
102110
)
103111

104112
self.assertEqual(result.status, ResultStatus.NEW)
113+
self.assertIsNone(result.started_at)
105114
self.assertIsNone(result.finished_at)
106115
await result.arefresh()
116+
self.assertIsNotNone(result.started_at)
107117
self.assertIsNotNone(result.finished_at)
108118
self.assertEqual(result.status, ResultStatus.COMPLETE)
109119

@@ -226,8 +236,10 @@ def test_run_enqueued_task(self) -> None:
226236

227237
self.assertEqual(result.status, ResultStatus.NEW)
228238
result.refresh()
239+
self.assertIsNotNone(result.started_at)
229240
self.assertIsNotNone(result.finished_at)
230-
self.assertGreaterEqual(result.finished_at, result.enqueued_at)
241+
self.assertGreaterEqual(result.started_at, result.enqueued_at)
242+
self.assertGreaterEqual(result.finished_at, result.started_at)
231243
self.assertEqual(result.status, ResultStatus.COMPLETE)
232244

233245
self.assertEqual(DBTaskResult.objects.ready().count(), 0)
@@ -289,8 +301,11 @@ def test_failing_task(self) -> None:
289301

290302
self.assertEqual(result.status, ResultStatus.NEW)
291303
result.refresh()
304+
self.assertIsNotNone(result.started_at)
292305
self.assertIsNotNone(result.finished_at)
293-
self.assertGreaterEqual(result.finished_at, result.enqueued_at) # type: ignore[arg-type]
306+
307+
self.assertGreaterEqual(result.started_at, result.enqueued_at) # type: ignore
308+
self.assertGreaterEqual(result.finished_at, result.started_at) # type: ignore
294309
self.assertEqual(result.status, ResultStatus.FAILED)
295310

296311
self.assertEqual(DBTaskResult.objects.ready().count(), 0)

tests/tests/test_dummy_backend.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def test_enqueue_task(self) -> None:
2626
result = default_task_backend.enqueue(task, (1,), {"two": 3})
2727

2828
self.assertEqual(result.status, ResultStatus.NEW)
29+
self.assertIsNone(result.started_at)
2930
self.assertIsNone(result.finished_at)
3031
with self.assertRaisesMessage(ValueError, "Task has not finished yet"):
3132
result.result # noqa:B018
@@ -41,6 +42,7 @@ async def test_enqueue_task_async(self) -> None:
4142
result = await default_task_backend.aenqueue(task, (), {})
4243

4344
self.assertEqual(result.status, ResultStatus.NEW)
45+
self.assertIsNone(result.started_at)
4446
self.assertIsNone(result.finished_at)
4547
with self.assertRaisesMessage(ValueError, "Task has not finished yet"):
4648
result.result # noqa:B018

tests/tests/test_immediate_backend.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ def test_enqueue_task(self) -> None:
2424
result = default_task_backend.enqueue(task, (1,), {"two": 3})
2525

2626
self.assertEqual(result.status, ResultStatus.COMPLETE)
27+
self.assertIsNotNone(result.started_at)
2728
self.assertIsNotNone(result.finished_at)
28-
self.assertGreaterEqual(result.finished_at, result.enqueued_at)
29+
self.assertGreaterEqual(result.started_at, result.enqueued_at)
30+
self.assertGreaterEqual(result.finished_at, result.started_at)
2931
self.assertIsNone(result.result)
3032
self.assertEqual(result.task, task)
3133
self.assertEqual(result.args, [1])
@@ -37,8 +39,10 @@ async def test_enqueue_task_async(self) -> None:
3739
result = await default_task_backend.aenqueue(task, (), {})
3840

3941
self.assertEqual(result.status, ResultStatus.COMPLETE)
42+
self.assertIsNotNone(result.started_at)
4043
self.assertIsNotNone(result.finished_at)
41-
self.assertGreaterEqual(result.finished_at, result.enqueued_at)
44+
self.assertGreaterEqual(result.started_at, result.enqueued_at)
45+
self.assertGreaterEqual(result.finished_at, result.started_at)
4246
self.assertIsNone(result.result)
4347
self.assertEqual(result.task, task)
4448
self.assertEqual(result.args, [])
@@ -48,8 +52,10 @@ def test_catches_exception(self) -> None:
4852
result = default_task_backend.enqueue(test_tasks.failing_task, [], {})
4953

5054
self.assertEqual(result.status, ResultStatus.FAILED)
55+
self.assertIsNotNone(result.started_at)
5156
self.assertIsNotNone(result.finished_at)
52-
self.assertGreaterEqual(result.finished_at, result.enqueued_at)
57+
self.assertGreaterEqual(result.started_at, result.enqueued_at)
58+
self.assertGreaterEqual(result.finished_at, result.started_at)
5359
self.assertIsNone(result.result)
5460
self.assertEqual(result.task, test_tasks.failing_task)
5561
self.assertEqual(result.args, [])

0 commit comments

Comments
 (0)