From 5640f32b00b860d59cf708962a3447dba2a2cf73 Mon Sep 17 00:00:00 2001 From: benoit74 Date: Fri, 21 Feb 2025 09:00:22 +0000 Subject: [PATCH] Add information about requested task rank in the pipe --- dispatcher/backend/src/common/schemas/orms.py | 1 + .../routes/requested_tasks/requested_task.py | 12 +++++++ .../src/tests/integration/routes/conftest.py | 33 +++++++++++++++++-- .../requested_tasks/test_requested_task.py | 26 +++++++++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/dispatcher/backend/src/common/schemas/orms.py b/dispatcher/backend/src/common/schemas/orms.py index 2215ccf5..2dc293f6 100644 --- a/dispatcher/backend/src/common/schemas/orms.py +++ b/dispatcher/backend/src/common/schemas/orms.py @@ -121,6 +121,7 @@ def get_schedule_name(task: dbm.RequestedTask) -> str: schedule_name = mf.Function(serialize=get_schedule_name) # override base worker = mf.Function(serialize=get_worker_name) notification = mf.Dict() + rank = mf.Integer() class MostRecentTaskSchema(m.Schema): diff --git a/dispatcher/backend/src/routes/requested_tasks/requested_task.py b/dispatcher/backend/src/routes/requested_tasks/requested_task.py index 42d6f13b..5eff6a35 100644 --- a/dispatcher/backend/src/routes/requested_tasks/requested_task.py +++ b/dispatcher/backend/src/routes/requested_tasks/requested_task.py @@ -300,6 +300,18 @@ def get( ) resp = RequestedTaskFullSchema().dump(requested_task) + # also fetch all requested tasks IDs to compute estimated task rank ; this is + # only an estimation since logic to start tasks is more complex than simple + # sorting but it gives a good indicator of when the requested task might + # be expected to start (soon or in a long time) especially for zimit.kiwix.org + stmt = sa.select( + dbm.RequestedTask.id, + ).order_by( + dbm.RequestedTask.priority.desc(), dbm.RequestedTask.updated_at.asc() + ) + requested_task_ids = session.execute(stmt).scalars().all() + resp["rank"] = requested_task_ids.index(requested_task_id) + # exclude notification to not expose private information (privacy) # on anonymous requests and requests for users without schedules_update if not token or not token.get_permission("schedules", "update"): diff --git a/dispatcher/backend/src/tests/integration/routes/conftest.py b/dispatcher/backend/src/tests/integration/routes/conftest.py index 7042f2d7..f83fb829 100644 --- a/dispatcher/backend/src/tests/integration/routes/conftest.py +++ b/dispatcher/backend/src/tests/integration/routes/conftest.py @@ -269,10 +269,10 @@ def _make_requested_task( status=TaskStatus.requested, requested_by="someone", priority=0, + request_date=getnow(), ): events = [TaskStatus.requested] - now = getnow() - timestamp = {event: now for event in events} + timestamp = {event: request_date for event in events} events = [make_event(event, timestamp[event]) for event in events] config = { @@ -291,7 +291,7 @@ def _make_requested_task( requested_task = dbm.RequestedTask( status=status, timestamp=timestamp, - updated_at=now, + updated_at=request_date, events=events, requested_by=requested_by, priority=priority, @@ -336,6 +336,33 @@ def requested_tasks(make_requested_task): return tasks +@pytest.fixture(scope="module") +def requested_tasks_2(make_requested_task): + return [ + make_requested_task( + schedule_name="recipe1", + request_date=getnow() - datetime.timedelta(minutes=5), + ), + make_requested_task( + schedule_name="recipe2", + request_date=getnow() - datetime.timedelta(minutes=4), + ), + make_requested_task( + schedule_name="recipe3", + request_date=getnow() - datetime.timedelta(minutes=10), + ), + make_requested_task( + schedule_name="recipe4", + request_date=getnow() - datetime.timedelta(minutes=3), + priority=5, + ), + make_requested_task( + schedule_name="recipe5", + request_date=getnow() - datetime.timedelta(minutes=1), + ), + ] + + @pytest.fixture(scope="module") def requested_task(make_requested_task): return make_requested_task() diff --git a/dispatcher/backend/src/tests/integration/routes/requested_tasks/test_requested_task.py b/dispatcher/backend/src/tests/integration/routes/requested_tasks/test_requested_task.py index 0fdf5b71..c348104b 100644 --- a/dispatcher/backend/src/tests/integration/routes/requested_tasks/test_requested_task.py +++ b/dispatcher/backend/src/tests/integration/routes/requested_tasks/test_requested_task.py @@ -179,6 +179,32 @@ def test_get(self, client, requested_task, access_token, authenticated): else: assert data["notification"] is None + @pytest.mark.parametrize( + "recipename, expected_rank", + [ + pytest.param("recipe1", 2, id="recipe1"), + pytest.param("recipe2", 3, id="recipe2"), + pytest.param("recipe3", 1, id="recipe3"), + pytest.param("recipe4", 0, id="recipe4"), + pytest.param("recipe5", 4, id="recipe5"), + ], + ) + def test_get_requested_task_rank_ok( + self, client, requested_tasks_2, recipename, expected_rank + ): + requested_task = [ + requested_task + for requested_task in requested_tasks_2 + if requested_task["schedule_name"] == recipename + ][0] + url = f'/requested-tasks/{requested_task["_id"]}' + headers = {"Content-Type": "application/json"} + response = client.get(url, headers=headers) + assert response.status_code == 200 + + data = json.loads(response.data) + assert data["rank"] == expected_rank + class TestRequestedTaskCreate: @pytest.fixture()