From ab93799484c9d9d1c20c0e50ecf54f4895ca7cb4 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 24 Jun 2026 16:02:00 +0100 Subject: [PATCH 01/18] Create experiment type --- src/daq_queuing_service/api/api.py | 10 ++--- .../plugins/construct_task_request.py | 20 ++++++---- .../plugins/i15_1_converter.py | 20 ++++++---- src/daq_queuing_service/task.py | 38 ++++++++++++++----- 4 files changed, 57 insertions(+), 31 deletions(-) diff --git a/src/daq_queuing_service/api/api.py b/src/daq_queuing_service/api/api.py index c376c11..80c5d10 100644 --- a/src/daq_queuing_service/api/api.py +++ b/src/daq_queuing_service/api/api.py @@ -2,6 +2,7 @@ import json from collections.abc import AsyncGenerator +from blueapi.service.model import TaskRequest from fastapi import APIRouter, Request, Response from fastapi.responses import EventSourceResponse from pydantic import BaseModel @@ -9,7 +10,7 @@ from daq_queuing_service.app._config import AppConfig from daq_queuing_service.blueapi_interaction.blueapi_call import BlueapiCallResponse from daq_queuing_service.broadcaster import Broadcaster -from daq_queuing_service.task import ExperimentDefinition, Status, Task +from daq_queuing_service.task import Experiment, Status, Task from daq_queuing_service.task_queue.queue import ( QUEUE_EVENTS, QueueState, @@ -72,13 +73,10 @@ async def get_queued_tasks(status: Status | None = None) -> list[TaskWithPositio @router.post("/queue") async def add_tasks_to_queue( - experiment_definitions: list[ExperimentDefinition], + experiments: list[TaskRequest | Experiment], position: int | None = None, ) -> list[str]: - tasks = [ - Task(experiment_definition=experiment_definition) - for experiment_definition in experiment_definitions - ] + tasks = [Task(experiment=experiment) for experiment in experiments] task_ids = [task.id for task in tasks] await queue.add_tasks(tasks, position) return task_ids diff --git a/src/daq_queuing_service/plugins/construct_task_request.py b/src/daq_queuing_service/plugins/construct_task_request.py index 79e1f48..595b614 100644 --- a/src/daq_queuing_service/plugins/construct_task_request.py +++ b/src/daq_queuing_service/plugins/construct_task_request.py @@ -1,17 +1,21 @@ from blueapi.service.model import TaskRequest from daq_queuing_service.blueapi_interaction.blueapi_call import BlueapiCall -from daq_queuing_service.task import ExperimentDefinition, TaskWithPosition +from daq_queuing_service.task import Experiment, TaskWithPosition def _construct_blueapi_task_request( - experiment_definition: ExperimentDefinition, + experiment: Experiment | TaskRequest, ) -> TaskRequest: - return TaskRequest( - name=experiment_definition.plan_name, - params=experiment_definition.params, - instrument_session=experiment_definition.instrument_session, - ) + match experiment: + case TaskRequest(): + return experiment + case Experiment(): + return TaskRequest( + instrument_session=experiment.instrument_session, + name=experiment.experiment_definition.name, + params=experiment.experiment_definition.data, + ) def construct_blueapi_call_list( @@ -23,7 +27,7 @@ def construct_blueapi_call_list( call_list = [ BlueapiCall( parent_task_id=task.id, - task_request=_construct_blueapi_task_request(task.experiment_definition), + task_request=_construct_blueapi_task_request(task.experiment), ) for task in queue ] diff --git a/src/daq_queuing_service/plugins/i15_1_converter.py b/src/daq_queuing_service/plugins/i15_1_converter.py index 6fff767..da7200f 100644 --- a/src/daq_queuing_service/plugins/i15_1_converter.py +++ b/src/daq_queuing_service/plugins/i15_1_converter.py @@ -1,13 +1,13 @@ from blueapi.service.model import TaskRequest from daq_queuing_service.blueapi_interaction.blueapi_call import BlueapiCall -from daq_queuing_service.task import ExperimentDefinition, TaskWithPosition +from daq_queuing_service.task import Experiment, TaskWithPosition def construct_blueapi_tasks_from_i15_1_experiment( - experiment_definition: ExperimentDefinition, + experiment: Experiment, ) -> list[TaskRequest]: - sample_name = experiment_definition.params["sampleName"] + sample_name = experiment.sample.data["sampleName"] # Assume sample name is of form test_8_1 to load from position 8 on puck 1 _, position, puck = sample_name.split("_") @@ -15,17 +15,17 @@ def construct_blueapi_tasks_from_i15_1_experiment( TaskRequest( name="robot_load", params={"puck": puck, "position": position}, - instrument_session=experiment_definition.instrument_session, + instrument_session=experiment.instrument_session, ), TaskRequest( name="centre_sample", params={"start_z": -5, "end_z": 5, "steps": 20, "exposure_time": 0.01}, - instrument_session=experiment_definition.instrument_session, + instrument_session=experiment.instrument_session, ), TaskRequest( name="robot_unload", params={}, - instrument_session=experiment_definition.instrument_session, + instrument_session=experiment.instrument_session, ), ] @@ -39,13 +39,17 @@ def construct_i15_1_blueapi_call_list( call_list: list[BlueapiCall] = [] for task in queue: + if isinstance(task.experiment, TaskRequest): + call_list.append( + BlueapiCall(task_request=task.experiment, parent_task_id=task.id) + ) # Remove plan name from here and UI in https://github.com/DiamondLightSource/daq-queuing-service/issues/51 - if task.experiment_definition.plan_name == "run_full_collection": + elif task.experiment.experiment_definition.name == "run_full_collection": call_list.extend( [ BlueapiCall(task_request=blueapi_task, parent_task_id=task.id) for blueapi_task in construct_blueapi_tasks_from_i15_1_experiment( - task.experiment_definition + task.experiment ) ] ) diff --git a/src/daq_queuing_service/task.py b/src/daq_queuing_service/task.py index 4e2e14b..5b6d5a8 100644 --- a/src/daq_queuing_service/task.py +++ b/src/daq_queuing_service/task.py @@ -2,7 +2,7 @@ from typing import Any, Self from uuid import uuid4 -from blueapi.service.model import StrEnum +from blueapi.service.model import StrEnum, TaskRequest from pydantic import BaseModel, Field, computed_field from daq_queuing_service.blueapi_interaction.blueapi_call import ( @@ -23,19 +23,28 @@ class Status(StrEnum): CANCELLED = "Cancelled" +class Sample(BaseModel): + name: str + id: str + data: Mapping[str, Any] + + class ExperimentDefinition(BaseModel): - plan_name: str - sample_id: str - params: Mapping[str, Any] = Field( - description="Values for parameters to plan, if any", default_factory=dict - ) + name: str + id: str + data: Mapping[str, Any] + + +class Experiment(BaseModel): instrument_session: str + sample: Sample + experiment_definition: ExperimentDefinition class Task(BaseModel): - experiment_definition: ExperimentDefinition + experiment: Experiment | TaskRequest id: str = Field(default_factory=create_uuid_str) - blueapi_calls: list[BlueapiCall] = [] + blueapi_calls: list[BlueapiCall] = Field(default_factory=lambda: []) _cancelled: bool = False def cancel(self): @@ -64,9 +73,20 @@ def status(self) -> Status: return Status.IN_PROGRESS return Status.QUEUED + @computed_field + @property + def kind(self) -> str: + match self.experiment: + case Experiment(): + return "experiment" + case TaskRequest(): + return "plan" + case _: + raise TypeError(f"Unexpected experiment type: {type(self.experiment)}") + class TaskWithPosition(BaseModel): - experiment_definition: ExperimentDefinition + experiment: Experiment | TaskRequest id: str status: Status blueapi_calls: list[BlueapiCallResponse] From ab72e34e5138106a28b7f4eb70ea2b3d4fa4f191 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 24 Jun 2026 16:02:09 +0100 Subject: [PATCH 02/18] Fix tests --- tests/unit_tests/conftest.py | 23 +++- .../plugins/test_construct_task_request.py | 13 +- .../plugins/test_i15_1_converter.py | 60 +++++---- tests/unit_tests/test_api.py | 118 +++++++++--------- tests/unit_tests/test_queue.py | 100 +++++++++++---- tests/unit_tests/test_task.py | 6 +- 6 files changed, 194 insertions(+), 126 deletions(-) diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index e09b10c..bbd6cde 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -9,7 +9,13 @@ from daq_queuing_service.plugins.construct_task_request import ( construct_blueapi_call_list, ) -from daq_queuing_service.task import ExperimentDefinition, Task, TaskWithPosition +from daq_queuing_service.task import ( + Experiment, + ExperimentDefinition, + Sample, + Task, + TaskWithPosition, +) from daq_queuing_service.task_queue.queue import TaskQueue @@ -24,8 +30,12 @@ def propagate_logs(monkeypatch: MonkeyPatch): def tasks() -> list[Task]: return [ Task( - experiment_definition=ExperimentDefinition( - plan_name="test", sample_id=str(i), params={}, instrument_session="" + experiment=Experiment( + instrument_session="", + experiment_definition=ExperimentDefinition( + name="test", id=str(i), data={} + ), + sample=Sample(name=f"test_8_{i}", id=str(i), data={}), ), id=str(i), ) @@ -51,13 +61,14 @@ def construct_blueapi_call_list( call_list: list[BlueapiCall] = [] for task in queue: for _ in range(2): + assert isinstance(task.experiment, Experiment) call_list.append( BlueapiCall( parent_task_id=task.id, task_request=TaskRequest( - name=task.experiment_definition.plan_name, - params=task.experiment_definition.params, - instrument_session=task.experiment_definition.instrument_session, + name=task.experiment.experiment_definition.name, + params=task.experiment.experiment_definition.data, + instrument_session=task.experiment.instrument_session, ), ) ) diff --git a/tests/unit_tests/plugins/test_construct_task_request.py b/tests/unit_tests/plugins/test_construct_task_request.py index 71947ed..3e21d97 100644 --- a/tests/unit_tests/plugins/test_construct_task_request.py +++ b/tests/unit_tests/plugins/test_construct_task_request.py @@ -3,16 +3,19 @@ from daq_queuing_service.plugins.construct_task_request import ( _construct_blueapi_task_request, ) -from daq_queuing_service.task import ExperimentDefinition +from daq_queuing_service.task import Experiment, ExperimentDefinition, Sample def test_construct_task_request_produces_expected_task_request_from_exp_definition(): result = _construct_blueapi_task_request( - experiment_definition=ExperimentDefinition( - plan_name="sleep", - sample_id="test_sample", - params={"time": 10}, + experiment=Experiment( instrument_session="cm12345-1", + experiment_definition=ExperimentDefinition( + name="sleep", + id="", + data={"time": 10}, + ), + sample=Sample(name="test_sample", id="test_sample", data={}), ) ) assert result == TaskRequest( diff --git a/tests/unit_tests/plugins/test_i15_1_converter.py b/tests/unit_tests/plugins/test_i15_1_converter.py index cf5d1f0..7e98f82 100644 --- a/tests/unit_tests/plugins/test_i15_1_converter.py +++ b/tests/unit_tests/plugins/test_i15_1_converter.py @@ -4,30 +4,34 @@ construct_blueapi_tasks_from_i15_1_experiment, construct_i15_1_blueapi_call_list, ) -from daq_queuing_service.task import ExperimentDefinition, Status, TaskWithPosition +from daq_queuing_service.task import ( + Experiment, + ExperimentDefinition, + Sample, + Status, + TaskWithPosition, +) def test_given_sample_name_in_correct_format_then_correct_sample_loaded(): - experiment_definition = ExperimentDefinition( - plan_name="_", - sample_id="test_sample", - params={"sampleName": "test_8_1"}, + experiment = Experiment( + experiment_definition=ExperimentDefinition(name=" ", id="", data={}), + sample=Sample(name="test_sample", id="", data={"sampleName": "test_8_1"}), instrument_session="cm12345-1", ) - tasks = construct_blueapi_tasks_from_i15_1_experiment(experiment_definition) + tasks = construct_blueapi_tasks_from_i15_1_experiment(experiment) assert tasks[0].name == "robot_load" assert tasks[0].params["position"] == "8" assert tasks[0].params["puck"] == "1" def test_sample_centre_uses_expected_params(): - experiment_definition = ExperimentDefinition( - plan_name="_", - sample_id="test_sample", - params={"sampleName": "test_8_1"}, + experiment = Experiment( + experiment_definition=ExperimentDefinition(name=" ", id="", data={}), + sample=Sample(name="test_sample", id="", data={"sampleName": "test_8_1"}), instrument_session="cm12345-1", ) - tasks = construct_blueapi_tasks_from_i15_1_experiment(experiment_definition) + tasks = construct_blueapi_tasks_from_i15_1_experiment(experiment) assert tasks[1].name == "centre_sample" assert tasks[1].params == { "start_z": -5, @@ -38,27 +42,27 @@ def test_sample_centre_uses_expected_params(): def test_session_and_number_of_tasks_per_experiment_is_expected(): - experiment_definition = ExperimentDefinition( - plan_name="_", - sample_id="test_sample", - params={"sampleName": "test_8_1"}, + experiment = Experiment( + experiment_definition=ExperimentDefinition(name=" ", id="", data={}), + sample=Sample(name="test_sample", id="", data={"sampleName": "test_8_1"}), instrument_session="cm12345-1", ) - tasks = construct_blueapi_tasks_from_i15_1_experiment(experiment_definition) + tasks = construct_blueapi_tasks_from_i15_1_experiment(experiment) assert len(tasks) == 3 for task in tasks: assert task.instrument_session == "cm12345-1" def test_experiment_with_correct_plan_name_are_converted(): - experiment_definition = ExperimentDefinition( - plan_name="run_full_collection", - sample_id="test_sample", - params={"sampleName": "test_8_1"}, + experiment = Experiment( + experiment_definition=ExperimentDefinition( + name="run_full_collection", id="", data={} + ), + sample=Sample(name="test_sample", id="", data={"sampleName": "test_8_1"}), instrument_session="cm12345-1", ) task = TaskWithPosition( - experiment_definition=experiment_definition, + experiment=experiment, id="1", status=Status.QUEUED, blueapi_calls=[], @@ -69,14 +73,15 @@ def test_experiment_with_correct_plan_name_are_converted(): def test_mix_of_experiments_with_correct_plan_name_are_converted(): - good_experiment_definition = ExperimentDefinition( - plan_name="run_full_collection", - sample_id="test_sample", - params={"sampleName": "test_8_1"}, + good_experiment = Experiment( + experiment_definition=ExperimentDefinition( + name="run_full_collection", id="", data={} + ), + sample=Sample(name="test_sample", id="", data={"sampleName": "test_8_1"}), instrument_session="cm12345-1", ) good_task = TaskWithPosition( - experiment_definition=good_experiment_definition, + experiment=good_experiment, id="1", status=Status.QUEUED, blueapi_calls=[], @@ -84,7 +89,8 @@ def test_mix_of_experiments_with_correct_plan_name_are_converted(): ) bad_task = deepcopy(good_task) - bad_task.experiment_definition.plan_name = "_" + assert isinstance(bad_task.experiment, Experiment) + bad_task.experiment.experiment_definition.name = "_" tasks = [good_task, bad_task, good_task] call_list = construct_i15_1_blueapi_call_list(tasks, [], []) assert len(call_list) == 6 diff --git a/tests/unit_tests/test_api.py b/tests/unit_tests/test_api.py index 1900faa..386cb2f 100644 --- a/tests/unit_tests/test_api.py +++ b/tests/unit_tests/test_api.py @@ -29,7 +29,6 @@ ) from daq_queuing_service.broadcaster import Broadcaster, Event from daq_queuing_service.task import ( - ExperimentDefinition, Status, TaskWithPosition, ) @@ -111,11 +110,10 @@ def test_get_queued_tasks_returns_queued_task(test_client: TestClient): assert response.status_code == 200 assert response.json() == [ { - "experiment_definition": { - "plan_name": "test", - "sample_id": "2", - "params": {}, + "experiment": { "instrument_session": "", + "sample": {"name": "test_8_2", "id": "2", "data": {}}, + "experiment_definition": {"name": "test", "id": "2", "data": {}}, }, "id": "2", "status": "In progress", @@ -138,11 +136,10 @@ def test_get_queued_tasks_returns_queued_task(test_client: TestClient): "position": 0, }, { - "experiment_definition": { - "plan_name": "test", - "sample_id": "3", - "params": {}, + "experiment": { "instrument_session": "", + "sample": {"name": "test_8_3", "id": "3", "data": {}}, + "experiment_definition": {"name": "test", "id": "3", "data": {}}, }, "id": "3", "status": "Queued", @@ -165,11 +162,10 @@ def test_get_queued_tasks_returns_queued_task(test_client: TestClient): "position": 1, }, { - "experiment_definition": { - "plan_name": "test", - "sample_id": "4", - "params": {}, + "experiment": { "instrument_session": "", + "sample": {"name": "test_8_4", "id": "4", "data": {}}, + "experiment_definition": {"name": "test", "id": "4", "data": {}}, }, "id": "4", "status": "Queued", @@ -199,13 +195,13 @@ def test_get_queued_tasks_can_filter_by_task_status(test_client: TestClient): assert response.status_code == 200 assert response.json() == [ { - "experiment_definition": { - "plan_name": "test", - "sample_id": "2", - "params": {}, + "experiment": { "instrument_session": "", + "sample": {"name": "test_8_2", "id": "2", "data": {}}, + "experiment_definition": {"name": "test", "id": "2", "data": {}}, }, "id": "2", + "status": "In progress", "blueapi_calls": [ { "task_request": { @@ -223,7 +219,6 @@ def test_get_queued_tasks_can_filter_by_task_status(test_client: TestClient): } ], "position": 0, - "status": "In progress", } ] @@ -243,11 +238,10 @@ async def test_get_all_tasks_can_filter_by_task_status(test_client: TestClient): assert response.status_code == 200 assert response.json() == [ { - "experiment_definition": { - "plan_name": "test", - "sample_id": "0", - "params": {}, + "experiment": { "instrument_session": "", + "sample": {"name": "test_8_0", "id": "0", "data": {}}, + "experiment_definition": {"name": "test", "id": "0", "data": {}}, }, "id": "0", "status": "Complete", @@ -276,11 +270,10 @@ async def test_get_all_tasks_can_filter_by_task_status(test_client: TestClient): "position": None, }, { - "experiment_definition": { - "plan_name": "test", - "sample_id": "1", - "params": {}, + "experiment": { "instrument_session": "", + "sample": {"name": "test_8_1", "id": "1", "data": {}}, + "experiment_definition": {"name": "test", "id": "1", "data": {}}, }, "id": "1", "status": "Complete", @@ -326,8 +319,7 @@ async def test_add_tasks_to_queue_adds_to_queue_and_and_returns_task_ids( "/queue", json=[ { - "plan_name": "add_tasks", - "sample_id": "1", + "name": "add_tasks", "params": {"time": 10}, "instrument_session": "abc", } @@ -339,9 +331,8 @@ async def test_add_tasks_to_queue_adds_to_queue_and_and_returns_task_ids( [uuid.UUID(task_id) for task_id in task_ids] assert await task_queue_with_history.get_task_by_position(-1) == TaskWithPosition( - experiment_definition=ExperimentDefinition( - plan_name="add_tasks", - sample_id="1", + experiment=TaskRequest( + name="add_tasks", params={"time": 10}, instrument_session="abc", ), @@ -371,8 +362,7 @@ async def test_add_tasks_to_queue_adds_to_queue_and_and_returns_task_ids( [ [ { - "plan_name": "add_tasks", - "sample_id": "1", + "name": "test", "params": {"time": 10}, "instrument_session": "abc", } @@ -384,9 +374,8 @@ async def test_add_tasks_to_queue_adds_to_queue_and_and_returns_task_ids( [ [ { - "plan_name": "add_tasks", + "name": "test", "params": {"time": 10}, - "instrument_session": "abc", } ], 0, @@ -395,14 +384,28 @@ async def test_add_tasks_to_queue_adds_to_queue_and_and_returns_task_ids( "detail": [ { "type": "missing", - "loc": ["body", 0, "sample_id"], + "loc": ["body", 0, "TaskRequest", "instrument_session"], "msg": "Field required", - "input": { - "plan_name": "add_tasks", - "params": {"time": 10}, - "instrument_session": "abc", - }, - } + "input": {"name": "test", "params": {"time": 10}}, + }, + { + "type": "missing", + "loc": ["body", 0, "Experiment", "instrument_session"], + "msg": "Field required", + "input": {"name": "test", "params": {"time": 10}}, + }, + { + "type": "missing", + "loc": ["body", 0, "Experiment", "sample"], + "msg": "Field required", + "input": {"name": "test", "params": {"time": 10}}, + }, + { + "type": "missing", + "loc": ["body", 0, "Experiment", "experiment_definition"], + "msg": "Field required", + "input": {"name": "test", "params": {"time": 10}}, + }, ] }, ], @@ -523,11 +526,10 @@ async def test_cancel_tasks_removes_task_from_queue_and_returns_tasks( assert not any(task_id in task_ids_after for task_id in last_two_task_ids) assert response.json() == [ { - "experiment_definition": { - "plan_name": "test", - "sample_id": "3", - "params": {}, + "experiment": { "instrument_session": "", + "sample": {"name": "test_8_3", "id": "3", "data": {}}, + "experiment_definition": {"name": "test", "id": "3", "data": {}}, }, "id": "3", "status": "Cancelled", @@ -550,11 +552,10 @@ async def test_cancel_tasks_removes_task_from_queue_and_returns_tasks( "position": None, }, { - "experiment_definition": { - "plan_name": "test", - "sample_id": "4", - "params": {}, + "experiment": { "instrument_session": "", + "sample": {"name": "test_8_4", "id": "4", "data": {}}, + "experiment_definition": {"name": "test", "id": "4", "data": {}}, }, "id": "4", "status": "Cancelled", @@ -640,11 +641,10 @@ def test_get_task_by_position_returns_expected_task(test_client: TestClient): response = test_client.get("/queue/1") assert response.status_code == 200 assert response.json() == { - "experiment_definition": { - "plan_name": "test", - "sample_id": "3", - "params": {}, + "experiment": { "instrument_session": "", + "sample": {"name": "test_8_3", "id": "3", "data": {}}, + "experiment_definition": {"name": "test", "id": "3", "data": {}}, }, "id": "3", "status": "Queued", @@ -672,11 +672,10 @@ def test_get_task_by_id_returns_expected_task(test_client: TestClient): response = test_client.get("/tasks/3") assert response.status_code == 200 assert response.json() == { - "experiment_definition": { - "plan_name": "test", - "sample_id": "3", - "params": {}, + "experiment": { "instrument_session": "", + "sample": {"name": "test_8_3", "id": "3", "data": {}}, + "experiment_definition": {"name": "test", "id": "3", "data": {}}, }, "id": "3", "status": "Queued", @@ -746,8 +745,7 @@ class SomeError(QueueError): ... "/queue", json=[ { - "plan_name": "add_tasks", - "sample_id": "1", + "name": "add_tasks", "params": {"time": 10}, "instrument_session": "abc", } diff --git a/tests/unit_tests/test_queue.py b/tests/unit_tests/test_queue.py index 2b5221c..241a611 100644 --- a/tests/unit_tests/test_queue.py +++ b/tests/unit_tests/test_queue.py @@ -15,7 +15,13 @@ from daq_queuing_service.plugins.construct_task_request import ( construct_blueapi_call_list, ) -from daq_queuing_service.task import ExperimentDefinition, Status, Task +from daq_queuing_service.task import ( + Experiment, + ExperimentDefinition, + Sample, + Status, + Task, +) from daq_queuing_service.task_queue.queue import ( TaskQueue, TaskWithPosition, @@ -33,10 +39,16 @@ def make_new_task(id_str: str): return Task( - experiment_definition=ExperimentDefinition( - plan_name="test", sample_id=id_str, params={}, instrument_session="" - ), id=id_str, + experiment=Experiment( + instrument_session="", + experiment_definition=ExperimentDefinition( + name="test", + id=id_str, + data={}, + ), + sample=Sample(name="test_sample", id=id_str, data={}), + ), ) @@ -257,8 +269,12 @@ async def test_get_queue_only_returns_tasks_in_queue( result = await task_queue_with_history.get_queue() assert result == [ TaskWithPosition( - experiment_definition=ExperimentDefinition( - plan_name="test", sample_id="2", instrument_session="" + experiment=Experiment( + instrument_session="", + experiment_definition=ExperimentDefinition( + name="test", id="2", data={} + ), + sample=Sample(name="test_8_2", id="2", data={}), ), id="2", status=Status.IN_PROGRESS, @@ -277,8 +293,12 @@ async def test_get_queue_only_returns_tasks_in_queue( position=0, ), TaskWithPosition( - experiment_definition=ExperimentDefinition( - plan_name="test", sample_id="3", instrument_session="" + experiment=Experiment( + instrument_session="", + experiment_definition=ExperimentDefinition( + name="test", id="3", data={} + ), + sample=Sample(name="test_8_3", id="3", data={}), ), id="3", status=Status.QUEUED, @@ -297,8 +317,12 @@ async def test_get_queue_only_returns_tasks_in_queue( position=1, ), TaskWithPosition( - experiment_definition=ExperimentDefinition( - plan_name="test", sample_id="4", instrument_session="" + experiment=Experiment( + instrument_session="", + experiment_definition=ExperimentDefinition( + name="test", id="4", data={} + ), + sample=Sample(name="test_8_4", id="4", data={}), ), id="4", status=Status.QUEUED, @@ -326,8 +350,12 @@ async def test_get_history_only_returns_tasks_in_history( result = await task_queue_with_history.get_history() assert result == [ TaskWithPosition( - experiment_definition=ExperimentDefinition( - plan_name="test", sample_id="0", instrument_session="" + experiment=Experiment( + instrument_session="", + experiment_definition=ExperimentDefinition( + name="test", id="0", data={} + ), + sample=Sample(name="test_8_0", id="0", data={}), ), id="0", status=Status.COMPLETE, @@ -352,8 +380,12 @@ async def test_get_history_only_returns_tasks_in_history( position=None, ), TaskWithPosition( - experiment_definition=ExperimentDefinition( - plan_name="test", sample_id="1", instrument_session="" + experiment=Experiment( + instrument_session="", + experiment_definition=ExperimentDefinition( + name="test", id="1", data={} + ), + sample=Sample(name="test_8_1", id="1", data={}), ), id="1", status=Status.COMPLETE, @@ -382,8 +414,12 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( result = await task_queue_with_history.get_tasks() assert result == [ TaskWithPosition( - experiment_definition=ExperimentDefinition( - plan_name="test", sample_id="0", instrument_session="" + experiment=Experiment( + instrument_session="", + experiment_definition=ExperimentDefinition( + name="test", id="0", data={} + ), + sample=Sample(name="test_8_0", id="0", data={}), ), id="0", status=Status.COMPLETE, @@ -408,8 +444,12 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( position=None, ), TaskWithPosition( - experiment_definition=ExperimentDefinition( - plan_name="test", sample_id="1", instrument_session="" + experiment=Experiment( + instrument_session="", + experiment_definition=ExperimentDefinition( + name="test", id="1", data={} + ), + sample=Sample(name="test_8_1", id="1", data={}), ), id="1", status=Status.COMPLETE, @@ -428,8 +468,12 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( position=None, ), TaskWithPosition( - experiment_definition=ExperimentDefinition( - plan_name="test", sample_id="2", instrument_session="" + experiment=Experiment( + instrument_session="", + experiment_definition=ExperimentDefinition( + name="test", id="2", data={} + ), + sample=Sample(name="test_8_2", id="2", data={}), ), id="2", status=Status.IN_PROGRESS, @@ -448,8 +492,12 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( position=0, ), TaskWithPosition( - experiment_definition=ExperimentDefinition( - plan_name="test", sample_id="3", instrument_session="" + experiment=Experiment( + instrument_session="", + experiment_definition=ExperimentDefinition( + name="test", id="3", data={} + ), + sample=Sample(name="test_8_3", id="3", data={}), ), id="3", status=Status.QUEUED, @@ -468,8 +516,12 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( position=1, ), TaskWithPosition( - experiment_definition=ExperimentDefinition( - plan_name="test", sample_id="4", instrument_session="" + experiment=Experiment( + instrument_session="", + experiment_definition=ExperimentDefinition( + name="test", id="4", data={} + ), + sample=Sample(name="test_8_4", id="4", data={}), ), id="4", status=Status.QUEUED, diff --git a/tests/unit_tests/test_task.py b/tests/unit_tests/test_task.py index 2d79d57..db8da21 100644 --- a/tests/unit_tests/test_task.py +++ b/tests/unit_tests/test_task.py @@ -2,7 +2,7 @@ from blueapi.service.model import TaskRequest from daq_queuing_service.blueapi_interaction.blueapi_call import BlueapiCall, CallStatus -from daq_queuing_service.task import ExperimentDefinition, Status, Task +from daq_queuing_service.task import Status, Task @pytest.mark.parametrize( @@ -36,8 +36,6 @@ def test_task_status_derived_correctly_from_call_statuses( ) for status in blueapi_call_statuses ], - experiment_definition=ExperimentDefinition( - plan_name="", sample_id="", instrument_session="" - ), + experiment=TaskRequest(name="", instrument_session=""), ) assert task.status == expected_task_status From 23463d2c84c7205b91a294d8a938af7dc0825cf0 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 24 Jun 2026 16:07:39 +0100 Subject: [PATCH 03/18] Update generated api docs --- docs/reference/rest_api.json | 103 +++++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/docs/reference/rest_api.json b/docs/reference/rest_api.json index c8b6307..e7decf1 100644 --- a/docs/reference/rest_api.json +++ b/docs/reference/rest_api.json @@ -177,16 +177,6 @@ ], "title": "Position" } - }, - { - "name": "validate_with_blueapi", - "in": "query", - "required": false, - "schema": { - "type": "boolean", - "default": true, - "title": "Validate With Blueapi" - } } ], "requestBody": { @@ -196,9 +186,16 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/ExperimentDefinition" + "anyOf": [ + { + "$ref": "#/components/schemas/TaskRequest" + }, + { + "$ref": "#/components/schemas/Experiment" + } + ] }, - "title": "Experiment Definitions" + "title": "Experiments" } } } @@ -990,32 +987,48 @@ "title": "EnvironmentConfig", "description": "Config for the RunEngine environment" }, + "Experiment": { + "properties": { + "instrument_session": { + "type": "string", + "title": "Instrument Session" + }, + "sample": { + "$ref": "#/components/schemas/Sample" + }, + "experiment_definition": { + "$ref": "#/components/schemas/ExperimentDefinition" + } + }, + "type": "object", + "required": [ + "instrument_session", + "sample", + "experiment_definition" + ], + "title": "Experiment" + }, "ExperimentDefinition": { "properties": { - "plan_name": { + "name": { "type": "string", - "title": "Plan Name" + "title": "Name" }, - "sample_id": { + "id": { "type": "string", - "title": "Sample Id" + "title": "Id" }, - "params": { + "data": { "additionalProperties": true, "type": "object", - "title": "Params", - "description": "Values for parameters to plan, if any" - }, - "instrument_session": { - "type": "string", - "title": "Instrument Session" + "title": "Data" } }, "type": "object", "required": [ - "plan_name", - "sample_id", - "instrument_session" + "name", + "id", + "data" ], "title": "ExperimentDefinition" }, @@ -1221,6 +1234,30 @@ "type": "object", "title": "RestConfig" }, + "Sample": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "id": { + "type": "string", + "title": "Id" + }, + "data": { + "additionalProperties": true, + "type": "object", + "title": "Data" + } + }, + "type": "object", + "required": [ + "name", + "id", + "data" + ], + "title": "Sample" + }, "ScratchConfig": { "properties": { "root": { @@ -1438,8 +1475,16 @@ }, "TaskWithPosition": { "properties": { - "experiment_definition": { - "$ref": "#/components/schemas/ExperimentDefinition" + "experiment": { + "anyOf": [ + { + "$ref": "#/components/schemas/Experiment" + }, + { + "$ref": "#/components/schemas/TaskRequest" + } + ], + "title": "Experiment" }, "id": { "type": "string", @@ -1469,7 +1514,7 @@ }, "type": "object", "required": [ - "experiment_definition", + "experiment", "id", "status", "blueapi_calls", From c25671fc2c10da8972ab249862a24d2a68d3ed83 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 24 Jun 2026 16:16:59 +0100 Subject: [PATCH 04/18] Add kind enum --- src/daq_queuing_service/task.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/daq_queuing_service/task.py b/src/daq_queuing_service/task.py index 5b6d5a8..ec5f481 100644 --- a/src/daq_queuing_service/task.py +++ b/src/daq_queuing_service/task.py @@ -12,17 +12,6 @@ ) -def create_uuid_str() -> str: - return str(uuid4()) - - -class Status(StrEnum): - QUEUED = "Queued" - IN_PROGRESS = "In progress" - COMPLETE = "Complete" - CANCELLED = "Cancelled" - - class Sample(BaseModel): name: str id: str @@ -41,6 +30,22 @@ class Experiment(BaseModel): experiment_definition: ExperimentDefinition +def create_uuid_str() -> str: + return str(uuid4()) + + +class Status(StrEnum): + QUEUED = "Queued" + IN_PROGRESS = "In progress" + COMPLETE = "Complete" + CANCELLED = "Cancelled" + + +class TaskKind(StrEnum): + EXPERIMENT = "Experiment" + PLAN = "Plan" + + class Task(BaseModel): experiment: Experiment | TaskRequest id: str = Field(default_factory=create_uuid_str) @@ -75,12 +80,12 @@ def status(self) -> Status: @computed_field @property - def kind(self) -> str: + def kind(self) -> TaskKind: match self.experiment: case Experiment(): - return "experiment" + return TaskKind.EXPERIMENT case TaskRequest(): - return "plan" + return TaskKind.PLAN case _: raise TypeError(f"Unexpected experiment type: {type(self.experiment)}") From 33c2ce3519007299598d7055f428d34bba3a72cf Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 24 Jun 2026 16:19:55 +0100 Subject: [PATCH 05/18] Make kind a literal --- docs/reference/rest_api.json | 11 ++++++++++- src/daq_queuing_service/task.py | 13 ++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/docs/reference/rest_api.json b/docs/reference/rest_api.json index e7decf1..222f06c 100644 --- a/docs/reference/rest_api.json +++ b/docs/reference/rest_api.json @@ -1510,6 +1510,14 @@ } ], "title": "Position" + }, + "kind": { + "type": "string", + "enum": [ + "experiment", + "plan" + ], + "title": "Kind" } }, "type": "object", @@ -1518,7 +1526,8 @@ "id", "status", "blueapi_calls", - "position" + "position", + "kind" ], "title": "TaskWithPosition" }, diff --git a/src/daq_queuing_service/task.py b/src/daq_queuing_service/task.py index ec5f481..294cbe7 100644 --- a/src/daq_queuing_service/task.py +++ b/src/daq_queuing_service/task.py @@ -1,5 +1,5 @@ from collections.abc import Mapping -from typing import Any, Self +from typing import Any, Literal, Self from uuid import uuid4 from blueapi.service.model import StrEnum, TaskRequest @@ -41,9 +41,7 @@ class Status(StrEnum): CANCELLED = "Cancelled" -class TaskKind(StrEnum): - EXPERIMENT = "Experiment" - PLAN = "Plan" +TASK_KIND = Literal["experiment", "plan"] class Task(BaseModel): @@ -80,12 +78,12 @@ def status(self) -> Status: @computed_field @property - def kind(self) -> TaskKind: + def kind(self) -> TASK_KIND: match self.experiment: case Experiment(): - return TaskKind.EXPERIMENT + return "experiment" case TaskRequest(): - return TaskKind.PLAN + return "plan" case _: raise TypeError(f"Unexpected experiment type: {type(self.experiment)}") @@ -96,6 +94,7 @@ class TaskWithPosition(BaseModel): status: Status blueapi_calls: list[BlueapiCallResponse] position: int | None + kind: TASK_KIND @classmethod def from_task(cls, task: Task, position: int | None = None) -> Self: From ad731264a9953cd79bd2ee1c8bc4e015c47ae391 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 24 Jun 2026 16:25:53 +0100 Subject: [PATCH 06/18] Use enum --- docs/reference/rest_api.json | 15 +++++++++------ src/daq_queuing_service/task.py | 14 ++++++++------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/docs/reference/rest_api.json b/docs/reference/rest_api.json index 222f06c..6c64fd6 100644 --- a/docs/reference/rest_api.json +++ b/docs/reference/rest_api.json @@ -1421,6 +1421,14 @@ "title": "TaskError", "description": "Wrapper around an exception raised by a plan" }, + "TaskKind": { + "type": "string", + "enum": [ + "Experiment", + "Plan" + ], + "title": "TaskKind" + }, "TaskRequest": { "properties": { "name": { @@ -1512,12 +1520,7 @@ "title": "Position" }, "kind": { - "type": "string", - "enum": [ - "experiment", - "plan" - ], - "title": "Kind" + "$ref": "#/components/schemas/TaskKind" } }, "type": "object", diff --git a/src/daq_queuing_service/task.py b/src/daq_queuing_service/task.py index 294cbe7..fdda617 100644 --- a/src/daq_queuing_service/task.py +++ b/src/daq_queuing_service/task.py @@ -1,5 +1,5 @@ from collections.abc import Mapping -from typing import Any, Literal, Self +from typing import Any, Self from uuid import uuid4 from blueapi.service.model import StrEnum, TaskRequest @@ -41,7 +41,9 @@ class Status(StrEnum): CANCELLED = "Cancelled" -TASK_KIND = Literal["experiment", "plan"] +class TaskKind(StrEnum): + EXPERIMENT = "Experiment" + PLAN = "Plan" class Task(BaseModel): @@ -78,12 +80,12 @@ def status(self) -> Status: @computed_field @property - def kind(self) -> TASK_KIND: + def kind(self) -> TaskKind: match self.experiment: case Experiment(): - return "experiment" + return TaskKind.EXPERIMENT case TaskRequest(): - return "plan" + return TaskKind.PLAN case _: raise TypeError(f"Unexpected experiment type: {type(self.experiment)}") @@ -94,7 +96,7 @@ class TaskWithPosition(BaseModel): status: Status blueapi_calls: list[BlueapiCallResponse] position: int | None - kind: TASK_KIND + kind: TaskKind @classmethod def from_task(cls, task: Task, position: int | None = None) -> Self: From 800281f8763b6d772863d98e758303b28ef3e417 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Wed, 24 Jun 2026 16:34:35 +0100 Subject: [PATCH 07/18] Fix tests --- tests/unit_tests/plugins/test_i15_1_converter.py | 3 +++ tests/unit_tests/test_api.py | 12 ++++++++++++ tests/unit_tests/test_queue.py | 11 +++++++++++ 3 files changed, 26 insertions(+) diff --git a/tests/unit_tests/plugins/test_i15_1_converter.py b/tests/unit_tests/plugins/test_i15_1_converter.py index 7e98f82..1ad3bca 100644 --- a/tests/unit_tests/plugins/test_i15_1_converter.py +++ b/tests/unit_tests/plugins/test_i15_1_converter.py @@ -9,6 +9,7 @@ ExperimentDefinition, Sample, Status, + TaskKind, TaskWithPosition, ) @@ -67,6 +68,7 @@ def test_experiment_with_correct_plan_name_are_converted(): status=Status.QUEUED, blueapi_calls=[], position=None, + kind=TaskKind.EXPERIMENT, ) call_list = construct_i15_1_blueapi_call_list([task], [], []) assert len(call_list) == 3 @@ -86,6 +88,7 @@ def test_mix_of_experiments_with_correct_plan_name_are_converted(): status=Status.QUEUED, blueapi_calls=[], position=None, + kind=TaskKind.EXPERIMENT, ) bad_task = deepcopy(good_task) diff --git a/tests/unit_tests/test_api.py b/tests/unit_tests/test_api.py index 386cb2f..d536fde 100644 --- a/tests/unit_tests/test_api.py +++ b/tests/unit_tests/test_api.py @@ -30,6 +30,7 @@ from daq_queuing_service.broadcaster import Broadcaster, Event from daq_queuing_service.task import ( Status, + TaskKind, TaskWithPosition, ) from daq_queuing_service.task_queue.queue import QUEUE_EVENTS, TaskQueue @@ -134,6 +135,7 @@ def test_get_queued_tasks_returns_queued_task(test_client: TestClient): } ], "position": 0, + "kind": "Experiment", }, { "experiment": { @@ -160,6 +162,7 @@ def test_get_queued_tasks_returns_queued_task(test_client: TestClient): } ], "position": 1, + "kind": "Experiment", }, { "experiment": { @@ -186,6 +189,7 @@ def test_get_queued_tasks_returns_queued_task(test_client: TestClient): } ], "position": 2, + "kind": "Experiment", }, ] @@ -219,6 +223,7 @@ def test_get_queued_tasks_can_filter_by_task_status(test_client: TestClient): } ], "position": 0, + "kind": "Experiment", } ] @@ -268,6 +273,7 @@ async def test_get_all_tasks_can_filter_by_task_status(test_client: TestClient): } ], "position": None, + "kind": "Experiment", }, { "experiment": { @@ -298,6 +304,7 @@ async def test_get_all_tasks_can_filter_by_task_status(test_client: TestClient): } ], "position": None, + "kind": "Experiment", }, ] @@ -353,6 +360,7 @@ async def test_add_tasks_to_queue_adds_to_queue_and_and_returns_task_ids( ], position=3, status=Status.QUEUED, + kind=TaskKind.PLAN, ) @@ -550,6 +558,7 @@ async def test_cancel_tasks_removes_task_from_queue_and_returns_tasks( } ], "position": None, + "kind": "Experiment", }, { "experiment": { @@ -576,6 +585,7 @@ async def test_cancel_tasks_removes_task_from_queue_and_returns_tasks( } ], "position": None, + "kind": "Experiment", }, ] @@ -665,6 +675,7 @@ def test_get_task_by_position_returns_expected_task(test_client: TestClient): } ], "position": 1, + "kind": "Experiment", } @@ -696,6 +707,7 @@ def test_get_task_by_id_returns_expected_task(test_client: TestClient): } ], "position": 1, + "kind": "Experiment", } diff --git a/tests/unit_tests/test_queue.py b/tests/unit_tests/test_queue.py index 241a611..9569b16 100644 --- a/tests/unit_tests/test_queue.py +++ b/tests/unit_tests/test_queue.py @@ -21,6 +21,7 @@ Sample, Status, Task, + TaskKind, ) from daq_queuing_service.task_queue.queue import ( TaskQueue, @@ -291,6 +292,7 @@ async def test_get_queue_only_returns_tasks_in_queue( ) ], position=0, + kind=TaskKind.EXPERIMENT, ), TaskWithPosition( experiment=Experiment( @@ -315,6 +317,7 @@ async def test_get_queue_only_returns_tasks_in_queue( ) ], position=1, + kind=TaskKind.EXPERIMENT, ), TaskWithPosition( experiment=Experiment( @@ -339,6 +342,7 @@ async def test_get_queue_only_returns_tasks_in_queue( ) ], position=2, + kind=TaskKind.EXPERIMENT, ), ] @@ -378,6 +382,7 @@ async def test_get_history_only_returns_tasks_in_history( ) ], position=None, + kind=TaskKind.EXPERIMENT, ), TaskWithPosition( experiment=Experiment( @@ -402,6 +407,7 @@ async def test_get_history_only_returns_tasks_in_history( ) ], position=None, + kind=TaskKind.EXPERIMENT, ), ] @@ -442,6 +448,7 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( ) ], position=None, + kind=TaskKind.EXPERIMENT, ), TaskWithPosition( experiment=Experiment( @@ -466,6 +473,7 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( ) ], position=None, + kind=TaskKind.EXPERIMENT, ), TaskWithPosition( experiment=Experiment( @@ -490,6 +498,7 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( ) ], position=0, + kind=TaskKind.EXPERIMENT, ), TaskWithPosition( experiment=Experiment( @@ -514,6 +523,7 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( ) ], position=1, + kind=TaskKind.EXPERIMENT, ), TaskWithPosition( experiment=Experiment( @@ -538,6 +548,7 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( ) ], position=2, + kind=TaskKind.EXPERIMENT, ), ] From 431a021e578ff7db60949142582e56d14972a787 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Thu, 25 Jun 2026 09:16:41 +0100 Subject: [PATCH 08/18] Fix --- src/daq_queuing_service/plugins/i15_1_converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/daq_queuing_service/plugins/i15_1_converter.py b/src/daq_queuing_service/plugins/i15_1_converter.py index da7200f..e205abb 100644 --- a/src/daq_queuing_service/plugins/i15_1_converter.py +++ b/src/daq_queuing_service/plugins/i15_1_converter.py @@ -7,7 +7,7 @@ def construct_blueapi_tasks_from_i15_1_experiment( experiment: Experiment, ) -> list[TaskRequest]: - sample_name = experiment.sample.data["sampleName"] + sample_name = experiment.sample.name # Assume sample name is of form test_8_1 to load from position 8 on puck 1 _, position, puck = sample_name.split("_") From f350029ffac127a06dcb936cad36a77d68b2f670 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Thu, 25 Jun 2026 11:04:15 +0100 Subject: [PATCH 09/18] Fix tests --- tests/unit_tests/plugins/test_i15_1_converter.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unit_tests/plugins/test_i15_1_converter.py b/tests/unit_tests/plugins/test_i15_1_converter.py index 1ad3bca..cc5691f 100644 --- a/tests/unit_tests/plugins/test_i15_1_converter.py +++ b/tests/unit_tests/plugins/test_i15_1_converter.py @@ -17,7 +17,7 @@ def test_given_sample_name_in_correct_format_then_correct_sample_loaded(): experiment = Experiment( experiment_definition=ExperimentDefinition(name=" ", id="", data={}), - sample=Sample(name="test_sample", id="", data={"sampleName": "test_8_1"}), + sample=Sample(name="test_8_1", id="", data={}), instrument_session="cm12345-1", ) tasks = construct_blueapi_tasks_from_i15_1_experiment(experiment) @@ -29,7 +29,7 @@ def test_given_sample_name_in_correct_format_then_correct_sample_loaded(): def test_sample_centre_uses_expected_params(): experiment = Experiment( experiment_definition=ExperimentDefinition(name=" ", id="", data={}), - sample=Sample(name="test_sample", id="", data={"sampleName": "test_8_1"}), + sample=Sample(name="test_8_1", id="", data={}), instrument_session="cm12345-1", ) tasks = construct_blueapi_tasks_from_i15_1_experiment(experiment) @@ -45,7 +45,7 @@ def test_sample_centre_uses_expected_params(): def test_session_and_number_of_tasks_per_experiment_is_expected(): experiment = Experiment( experiment_definition=ExperimentDefinition(name=" ", id="", data={}), - sample=Sample(name="test_sample", id="", data={"sampleName": "test_8_1"}), + sample=Sample(name="test_8_1", id="", data={}), instrument_session="cm12345-1", ) tasks = construct_blueapi_tasks_from_i15_1_experiment(experiment) @@ -59,7 +59,7 @@ def test_experiment_with_correct_plan_name_are_converted(): experiment_definition=ExperimentDefinition( name="run_full_collection", id="", data={} ), - sample=Sample(name="test_sample", id="", data={"sampleName": "test_8_1"}), + sample=Sample(name="test_8_1", id="", data={}), instrument_session="cm12345-1", ) task = TaskWithPosition( @@ -79,7 +79,7 @@ def test_mix_of_experiments_with_correct_plan_name_are_converted(): experiment_definition=ExperimentDefinition( name="run_full_collection", id="", data={} ), - sample=Sample(name="test_sample", id="", data={"sampleName": "test_8_1"}), + sample=Sample(name="test_8_1", id="", data={}), instrument_session="cm12345-1", ) good_task = TaskWithPosition( From 46d97f3bc2563cac6a03a8a10a6a83350de2fbcb Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Thu, 25 Jun 2026 12:01:19 +0100 Subject: [PATCH 10/18] Use converter based on experiment type instead of plan name --- .../plugins/i15_1_converter.py | 28 +++++++++---------- src/daq_queuing_service/task.py | 3 +- .../plugins/test_i15_1_converter.py | 18 ++++++++---- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/daq_queuing_service/plugins/i15_1_converter.py b/src/daq_queuing_service/plugins/i15_1_converter.py index e205abb..1d50fb9 100644 --- a/src/daq_queuing_service/plugins/i15_1_converter.py +++ b/src/daq_queuing_service/plugins/i15_1_converter.py @@ -39,19 +39,19 @@ def construct_i15_1_blueapi_call_list( call_list: list[BlueapiCall] = [] for task in queue: - if isinstance(task.experiment, TaskRequest): - call_list.append( - BlueapiCall(task_request=task.experiment, parent_task_id=task.id) - ) - # Remove plan name from here and UI in https://github.com/DiamondLightSource/daq-queuing-service/issues/51 - elif task.experiment.experiment_definition.name == "run_full_collection": - call_list.extend( - [ - BlueapiCall(task_request=blueapi_task, parent_task_id=task.id) - for blueapi_task in construct_blueapi_tasks_from_i15_1_experiment( - task.experiment - ) - ] - ) + match task.experiment: + case TaskRequest(): + call_list.append( + BlueapiCall(task_request=task.experiment, parent_task_id=task.id) + ) + case Experiment(): + call_list.extend( + [ + BlueapiCall(task_request=blueapi_task, parent_task_id=task.id) + for blueapi_task in construct_blueapi_tasks_from_i15_1_experiment( + task.experiment + ) + ] + ) return call_list diff --git a/src/daq_queuing_service/task.py b/src/daq_queuing_service/task.py index fdda617..09a0ea2 100644 --- a/src/daq_queuing_service/task.py +++ b/src/daq_queuing_service/task.py @@ -1,8 +1,9 @@ from collections.abc import Mapping +from enum import StrEnum from typing import Any, Self from uuid import uuid4 -from blueapi.service.model import StrEnum, TaskRequest +from blueapi.service.model import TaskRequest from pydantic import BaseModel, Field, computed_field from daq_queuing_service.blueapi_interaction.blueapi_call import ( diff --git a/tests/unit_tests/plugins/test_i15_1_converter.py b/tests/unit_tests/plugins/test_i15_1_converter.py index cc5691f..e9a59d8 100644 --- a/tests/unit_tests/plugins/test_i15_1_converter.py +++ b/tests/unit_tests/plugins/test_i15_1_converter.py @@ -1,5 +1,7 @@ from copy import deepcopy +from blueapi.service.model import TaskRequest + from daq_queuing_service.plugins.i15_1_converter import ( construct_blueapi_tasks_from_i15_1_experiment, construct_i15_1_blueapi_call_list, @@ -54,7 +56,7 @@ def test_session_and_number_of_tasks_per_experiment_is_expected(): assert task.instrument_session == "cm12345-1" -def test_experiment_with_correct_plan_name_are_converted(): +def test_experiment_with_correct_experiment_type_are_converted(): experiment = Experiment( experiment_definition=ExperimentDefinition( name="run_full_collection", id="", data={} @@ -74,7 +76,7 @@ def test_experiment_with_correct_plan_name_are_converted(): assert len(call_list) == 3 -def test_mix_of_experiments_with_correct_plan_name_are_converted(): +def test_mix_of_experiments_with_correct_experiment_type_are_converted(): good_experiment = Experiment( experiment_definition=ExperimentDefinition( name="run_full_collection", id="", data={} @@ -91,9 +93,13 @@ def test_mix_of_experiments_with_correct_plan_name_are_converted(): kind=TaskKind.EXPERIMENT, ) + class BadExperiment: ... + bad_task = deepcopy(good_task) - assert isinstance(bad_task.experiment, Experiment) - bad_task.experiment.experiment_definition.name = "_" - tasks = [good_task, bad_task, good_task] + bad_task.experiment = BadExperiment() # type: ignore + plan_task = deepcopy(good_task) + plan_task.experiment = TaskRequest(name="", instrument_session="") + plan_task.kind = TaskKind.PLAN + tasks = [good_task, bad_task, plan_task, good_task] call_list = construct_i15_1_blueapi_call_list(tasks, [], []) - assert len(call_list) == 6 + assert len(call_list) == 7 From 5087b2ee34669c5019aac167b2c84574b0ac7088 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Thu, 25 Jun 2026 12:17:11 +0100 Subject: [PATCH 11/18] Fix lint --- src/daq_queuing_service/plugins/i15_1_converter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/daq_queuing_service/plugins/i15_1_converter.py b/src/daq_queuing_service/plugins/i15_1_converter.py index 1d50fb9..308cf8d 100644 --- a/src/daq_queuing_service/plugins/i15_1_converter.py +++ b/src/daq_queuing_service/plugins/i15_1_converter.py @@ -47,8 +47,8 @@ def construct_i15_1_blueapi_call_list( case Experiment(): call_list.extend( [ - BlueapiCall(task_request=blueapi_task, parent_task_id=task.id) - for blueapi_task in construct_blueapi_tasks_from_i15_1_experiment( + BlueapiCall(task_request=b_api_task, parent_task_id=task.id) + for b_api_task in construct_blueapi_tasks_from_i15_1_experiment( task.experiment ) ] From 644c44db1b911b28b6f1330fc53e12eefb2a8593 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Thu, 25 Jun 2026 17:53:56 +0100 Subject: [PATCH 12/18] Fix coverage --- src/daq_queuing_service/task.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/daq_queuing_service/task.py b/src/daq_queuing_service/task.py index 09a0ea2..3d2ab4a 100644 --- a/src/daq_queuing_service/task.py +++ b/src/daq_queuing_service/task.py @@ -87,8 +87,6 @@ def kind(self) -> TaskKind: return TaskKind.EXPERIMENT case TaskRequest(): return TaskKind.PLAN - case _: - raise TypeError(f"Unexpected experiment type: {type(self.experiment)}") class TaskWithPosition(BaseModel): From 956fff6a71cd7b9309ca2b7270427de882beb6fd Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Fri, 26 Jun 2026 14:33:04 +0100 Subject: [PATCH 13/18] Add name to experiment type --- src/daq_queuing_service/task.py | 1 + tests/unit_tests/conftest.py | 1 + .../unit_tests/plugins/test_construct_task_request.py | 1 + tests/unit_tests/plugins/test_i15_1_converter.py | 5 +++++ tests/unit_tests/test_api.py | 9 +++++++++ tests/unit_tests/test_queue.py | 11 +++++++++++ 6 files changed, 28 insertions(+) diff --git a/src/daq_queuing_service/task.py b/src/daq_queuing_service/task.py index 0e88a10..6303fa0 100644 --- a/src/daq_queuing_service/task.py +++ b/src/daq_queuing_service/task.py @@ -26,6 +26,7 @@ class ExperimentDefinition(BaseModel): class Experiment(BaseModel): + name: str instrument_session: str sample: Sample experiment_definition: ExperimentDefinition diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index bbd6cde..bf045e7 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -31,6 +31,7 @@ def tasks() -> list[Task]: return [ Task( experiment=Experiment( + name="test_experiment", instrument_session="", experiment_definition=ExperimentDefinition( name="test", id=str(i), data={} diff --git a/tests/unit_tests/plugins/test_construct_task_request.py b/tests/unit_tests/plugins/test_construct_task_request.py index 3e21d97..60b1afb 100644 --- a/tests/unit_tests/plugins/test_construct_task_request.py +++ b/tests/unit_tests/plugins/test_construct_task_request.py @@ -9,6 +9,7 @@ def test_construct_task_request_produces_expected_task_request_from_exp_definition(): result = _construct_blueapi_task_request( experiment=Experiment( + name="test_experiment", instrument_session="cm12345-1", experiment_definition=ExperimentDefinition( name="sleep", diff --git a/tests/unit_tests/plugins/test_i15_1_converter.py b/tests/unit_tests/plugins/test_i15_1_converter.py index e9a59d8..16ecabd 100644 --- a/tests/unit_tests/plugins/test_i15_1_converter.py +++ b/tests/unit_tests/plugins/test_i15_1_converter.py @@ -18,6 +18,7 @@ def test_given_sample_name_in_correct_format_then_correct_sample_loaded(): experiment = Experiment( + name="test_experiment", experiment_definition=ExperimentDefinition(name=" ", id="", data={}), sample=Sample(name="test_8_1", id="", data={}), instrument_session="cm12345-1", @@ -30,6 +31,7 @@ def test_given_sample_name_in_correct_format_then_correct_sample_loaded(): def test_sample_centre_uses_expected_params(): experiment = Experiment( + name="test_experiment", experiment_definition=ExperimentDefinition(name=" ", id="", data={}), sample=Sample(name="test_8_1", id="", data={}), instrument_session="cm12345-1", @@ -46,6 +48,7 @@ def test_sample_centre_uses_expected_params(): def test_session_and_number_of_tasks_per_experiment_is_expected(): experiment = Experiment( + name="test_experiment", experiment_definition=ExperimentDefinition(name=" ", id="", data={}), sample=Sample(name="test_8_1", id="", data={}), instrument_session="cm12345-1", @@ -58,6 +61,7 @@ def test_session_and_number_of_tasks_per_experiment_is_expected(): def test_experiment_with_correct_experiment_type_are_converted(): experiment = Experiment( + name="test_experiment", experiment_definition=ExperimentDefinition( name="run_full_collection", id="", data={} ), @@ -78,6 +82,7 @@ def test_experiment_with_correct_experiment_type_are_converted(): def test_mix_of_experiments_with_correct_experiment_type_are_converted(): good_experiment = Experiment( + name="test_experiment", experiment_definition=ExperimentDefinition( name="run_full_collection", id="", data={} ), diff --git a/tests/unit_tests/test_api.py b/tests/unit_tests/test_api.py index 553f471..997e7ea 100644 --- a/tests/unit_tests/test_api.py +++ b/tests/unit_tests/test_api.py @@ -112,6 +112,7 @@ def test_get_queued_tasks_returns_queued_task(test_client: TestClient): assert response.json() == [ { "experiment": { + "name": "test_experiment", "instrument_session": "", "sample": {"name": "test_8_2", "id": "2", "data": {}}, "experiment_definition": {"name": "test", "id": "2", "data": {}}, @@ -139,6 +140,7 @@ def test_get_queued_tasks_returns_queued_task(test_client: TestClient): }, { "experiment": { + "name": "test_experiment", "instrument_session": "", "sample": {"name": "test_8_3", "id": "3", "data": {}}, "experiment_definition": {"name": "test", "id": "3", "data": {}}, @@ -166,6 +168,7 @@ def test_get_queued_tasks_returns_queued_task(test_client: TestClient): }, { "experiment": { + "name": "test_experiment", "instrument_session": "", "sample": {"name": "test_8_4", "id": "4", "data": {}}, "experiment_definition": {"name": "test", "id": "4", "data": {}}, @@ -200,6 +203,7 @@ def test_get_queued_tasks_can_filter_by_task_status(test_client: TestClient): assert response.json() == [ { "experiment": { + "name": "test_experiment", "instrument_session": "", "sample": {"name": "test_8_2", "id": "2", "data": {}}, "experiment_definition": {"name": "test", "id": "2", "data": {}}, @@ -244,6 +248,7 @@ async def test_get_all_tasks_can_filter_by_task_status(test_client: TestClient): assert response.json() == [ { "experiment": { + "name": "test_experiment", "instrument_session": "", "sample": {"name": "test_8_1", "id": "1", "data": {}}, "experiment_definition": {"name": "test", "id": "1", "data": {}}, @@ -502,6 +507,7 @@ async def test_cancel_tasks_removes_task_from_queue_and_returns_tasks( assert response.json() == [ { "experiment": { + "name": "test_experiment", "instrument_session": "", "sample": {"name": "test_8_3", "id": "3", "data": {}}, "experiment_definition": {"name": "test", "id": "3", "data": {}}, @@ -529,6 +535,7 @@ async def test_cancel_tasks_removes_task_from_queue_and_returns_tasks( }, { "experiment": { + "name": "test_experiment", "instrument_session": "", "sample": {"name": "test_8_4", "id": "4", "data": {}}, "experiment_definition": {"name": "test", "id": "4", "data": {}}, @@ -619,6 +626,7 @@ def test_get_task_by_position_returns_expected_task(test_client: TestClient): assert response.status_code == 200 assert response.json() == { "experiment": { + "name": "test_experiment", "instrument_session": "", "sample": {"name": "test_8_3", "id": "3", "data": {}}, "experiment_definition": {"name": "test", "id": "3", "data": {}}, @@ -651,6 +659,7 @@ def test_get_task_by_id_returns_expected_task(test_client: TestClient): assert response.status_code == 200 assert response.json() == { "experiment": { + "name": "test_experiment", "instrument_session": "", "sample": {"name": "test_8_3", "id": "3", "data": {}}, "experiment_definition": {"name": "test", "id": "3", "data": {}}, diff --git a/tests/unit_tests/test_queue.py b/tests/unit_tests/test_queue.py index 90b3cff..68d5e91 100644 --- a/tests/unit_tests/test_queue.py +++ b/tests/unit_tests/test_queue.py @@ -42,6 +42,7 @@ def make_new_task(id_str: str): return Task( id=id_str, experiment=Experiment( + name="test_experiment", instrument_session="", experiment_definition=ExperimentDefinition( name="test", @@ -271,6 +272,7 @@ async def test_get_queue_only_returns_tasks_in_queue( assert result == [ TaskWithPosition( experiment=Experiment( + name="test_experiment", instrument_session="", experiment_definition=ExperimentDefinition( name="test", id="2", data={} @@ -296,6 +298,7 @@ async def test_get_queue_only_returns_tasks_in_queue( ), TaskWithPosition( experiment=Experiment( + name="test_experiment", instrument_session="", experiment_definition=ExperimentDefinition( name="test", id="3", data={} @@ -321,6 +324,7 @@ async def test_get_queue_only_returns_tasks_in_queue( ), TaskWithPosition( experiment=Experiment( + name="test_experiment", instrument_session="", experiment_definition=ExperimentDefinition( name="test", id="4", data={} @@ -355,6 +359,7 @@ async def test_get_history_only_returns_tasks_in_history( assert result == [ TaskWithPosition( experiment=Experiment( + name="test_experiment", instrument_session="", experiment_definition=ExperimentDefinition( name="test", id="0", data={} @@ -386,6 +391,7 @@ async def test_get_history_only_returns_tasks_in_history( ), TaskWithPosition( experiment=Experiment( + name="test_experiment", instrument_session="", experiment_definition=ExperimentDefinition( name="test", id="1", data={} @@ -421,6 +427,7 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( assert result == [ TaskWithPosition( experiment=Experiment( + name="test_experiment", instrument_session="", experiment_definition=ExperimentDefinition( name="test", id="0", data={} @@ -452,6 +459,7 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( ), TaskWithPosition( experiment=Experiment( + name="test_experiment", instrument_session="", experiment_definition=ExperimentDefinition( name="test", id="1", data={} @@ -477,6 +485,7 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( ), TaskWithPosition( experiment=Experiment( + name="test_experiment", instrument_session="", experiment_definition=ExperimentDefinition( name="test", id="2", data={} @@ -502,6 +511,7 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( ), TaskWithPosition( experiment=Experiment( + name="test_experiment", instrument_session="", experiment_definition=ExperimentDefinition( name="test", id="3", data={} @@ -527,6 +537,7 @@ async def test_get_tasks_returns_tasks_in_queue_and_history( ), TaskWithPosition( experiment=Experiment( + name="test_experiment", instrument_session="", experiment_definition=ExperimentDefinition( name="test", id="4", data={} From 72a6856b186825262ca8a0a61be2e1e9619a1d1b Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Fri, 26 Jun 2026 14:40:33 +0100 Subject: [PATCH 14/18] Update generated docs --- docs/reference/rest_api.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/reference/rest_api.json b/docs/reference/rest_api.json index f4ea2ad..83687dd 100644 --- a/docs/reference/rest_api.json +++ b/docs/reference/rest_api.json @@ -990,6 +990,10 @@ }, "Experiment": { "properties": { + "name": { + "type": "string", + "title": "Name" + }, "instrument_session": { "type": "string", "title": "Instrument Session" @@ -1003,6 +1007,7 @@ }, "type": "object", "required": [ + "name", "instrument_session", "sample", "experiment_definition" From 81282ff88badb6b0f15d393fb2372065e9c9d079 Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 30 Jun 2026 18:07:08 +0100 Subject: [PATCH 15/18] Improve docs --- README.md | 2 ++ docs/explanations/converters.md | 6 ++++-- docs/explanations/queue.md | 10 ---------- docs/explanations/queue_items.md | 7 +++++++ docs/explanations/tasks.md | 14 -------------- 5 files changed, 13 insertions(+), 26 deletions(-) delete mode 100644 docs/explanations/queue.md create mode 100644 docs/explanations/queue_items.md delete mode 100644 docs/explanations/tasks.md diff --git a/README.md b/README.md index 9e7c55c..ef97846 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ blueapi --config tests/test_data/test_blueapi_config.yaml serve # Local blueapi daq-queuing-service -p 8001 --dev # Local queue service ``` +See [here](./docs/explanations/queue_items.md) for more about the items inside the queue. + See https://diamondlightsource.github.io/daq-queuing-service for more detailed documentation. diff --git a/docs/explanations/converters.md b/docs/explanations/converters.md index bace142..5238ba2 100644 --- a/docs/explanations/converters.md +++ b/docs/explanations/converters.md @@ -1,6 +1,6 @@ # Converters -The queue contains ulims experiment definitions, which need to be converted to blueapi calls for anything to run. This conversion is beamline specific and configurable. The default converter simply passes the parameters of the experiment definition directly to blueapi with no conversion taking place. +The queue contains ulims experiments and/or TaskRequests. While TaskRequests are already in the right format for blueapi to receive them, experiments need to be converted to blueapi calls for anything to run. This conversion is beamline specific and configurable. The default converter simply passes the parameters of the experiment definition directly to blueapi with no conversion taking place. Converters take the list of queued tasks, the queue history, and the blueapi call history (previous calls made to blueapi) and use this information to construct a list of future blueapi calls. Not all of this information needs to be used by the converter (for example, the default converter only uses the list of queued tasks to inform its 'conversion') but the interface of a converter function needs to be as follows: @@ -13,7 +13,9 @@ def my_converter( ... ``` -Conversion is run any time the queue changes and right before a call is made to blueapi, so the blueapi call list stays up to date. This means converters can make use of current information on a beamline such as PV values to inform the conversion. +Conversion is run any time the queue changes and right before a call is made to blueapi, so the blueapi call list stays in sync. This means converters can make use of current information on a beamline such as PV values to inform the conversion. + +Note that since the queue can contain TaskRequest objects, a converter should be able to handle this. It should almost always pass them through with no conversion In config, under the converter section, add the path and name of the converter you want to use. For example: diff --git a/docs/explanations/queue.md b/docs/explanations/queue.md deleted file mode 100644 index 798dbc1..0000000 --- a/docs/explanations/queue.md +++ /dev/null @@ -1,10 +0,0 @@ -Queue -===== - -The queue is essentially a list of [tasks](./tasks.md). Tasks can be added, deleted, and moved around the queue through the REST API. A worker takes the task at the top of the queue, if one is available, and sends it to blueapi. Once this task is in progress, it can't be moved or deleted, and no other task can be sent to blueapi until this one has finished. - -The queue can be paused, meaning no task can be taken and sent to blueapi until the queue is unpaused. This will not pause a task that is already in progress. While the queue is paused, tasks can still be added, deleted and moved around. - -Once a task is complete, it is added to a history list, which is a chronological list of completed tasks. - -See the [API reference](../reference/rest-api.rst) for how to interact with the queue. diff --git a/docs/explanations/queue_items.md b/docs/explanations/queue_items.md new file mode 100644 index 0000000..c36e740 --- /dev/null +++ b/docs/explanations/queue_items.md @@ -0,0 +1,7 @@ +# Queue Items + +The items inside the queue contain either an `Experiment` object or a `TaskRequest` object. These determine which bluesky plans will run. Experiment objects match the format of an Experiment in ulims, and can involve multiple bluesky plans. It always contains a sample, experiment definition and instrument session. A TaskRequest object matches the format blueapi expects to receive to run a single plan. + +Allowing both in the queue means you can queue up whole experiments as well as individual bluesky plans. + +The queue will convert the entire list of Experiments and TaskRequests to a list of blueapi calls (the task request objects are already in the right format). See [converters](./converters.md) on how this is done. diff --git a/docs/explanations/tasks.md b/docs/explanations/tasks.md deleted file mode 100644 index cc6a038..0000000 --- a/docs/explanations/tasks.md +++ /dev/null @@ -1,14 +0,0 @@ -Tasks -===== - -The items contained inside the queue are called tasks. Tasks are produced by sending an experiment definition to the queue, which will validate it, assign it an ID it and create a task for it. Throughout the lifecycle of a task, information will be added to the task by the queue when it becomes available. This will include things like: -- Status of the task (Waiting, In Progress, Success etc) -- Time started -- Time completed -- Result of the task -- Any errors that occurred during the task -- A blueapi task ID (distinct from the ID assigned by the queue) - -When the queue is asked to retrieve a task, it will calculate its position in the queue and include that in the task object. - -Once a task reaches the top of the queue, the experiment definition it contains will be converted to a blueapi call and sent to blueapi. Converter plugins can be written and interchanged, allowing for flexibility in how experiment definitions are written by different beamlines. Once a task is complete, it will leave the queue and enter a history list. From 7f8c96b281f277bf89fd83d649591519e6737c6a Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 30 Jun 2026 18:12:04 +0100 Subject: [PATCH 16/18] Improve docs --- docs/explanations/queue_items.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/explanations/queue_items.md b/docs/explanations/queue_items.md index c36e740..d0e58e3 100644 --- a/docs/explanations/queue_items.md +++ b/docs/explanations/queue_items.md @@ -1,7 +1,7 @@ # Queue Items -The items inside the queue contain either an `Experiment` object or a `TaskRequest` object. These determine which bluesky plans will run. Experiment objects match the format of an Experiment in ulims, and can involve multiple bluesky plans. It always contains a sample, experiment definition and instrument session. A TaskRequest object matches the format blueapi expects to receive to run a single plan. +The items inside the queue contain either an `Experiment` object or a `TaskRequest` object. These determine which bluesky plans will run. Experiment objects match the format of an experiment in ulims, and can involve multiple bluesky plans. It always contains a sample, experiment definition and instrument session. A TaskRequest object matches the format blueapi expects to receive to run a single plan. Allowing both in the queue means you can queue up whole experiments as well as individual bluesky plans. -The queue will convert the entire list of Experiments and TaskRequests to a list of blueapi calls (the task request objects are already in the right format). See [converters](./converters.md) on how this is done. +The queue will convert the entire list of experiments and task requests to a list of blueapi calls (the task request objects are already in the right format). See [converters](./converters.md) on how this is done. From 3c130c67a33ada530ed4183309bae4d10353d39a Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 30 Jun 2026 18:14:40 +0100 Subject: [PATCH 17/18] Fix test --- tests/unit_tests/test_api.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/unit_tests/test_api.py b/tests/unit_tests/test_api.py index 7d526a6..e7bce36 100644 --- a/tests/unit_tests/test_api.py +++ b/tests/unit_tests/test_api.py @@ -634,11 +634,11 @@ async def test_cancel_all_tasks_removes_all_queued_tasks_from_queue_and_returns_ assert not any(task_id in task_ids_after for task_id in should_be_cancelled) assert response.json() == [ { - "experiment_definition": { - "plan_name": "test", - "sample_id": "3", - "params": {}, + "experiment": { + "name": "test_experiment", "instrument_session": "", + "sample": {"name": "test_8_3", "id": "3", "data": {}}, + "experiment_definition": {"name": "test", "id": "3", "data": {}}, }, "id": "3", "status": "Cancelled", @@ -659,13 +659,14 @@ async def test_cancel_all_tasks_removes_all_queued_tasks_from_queue_and_returns_ } ], "position": None, + "kind": "Experiment", }, { - "experiment_definition": { - "plan_name": "test", - "sample_id": "4", - "params": {}, + "experiment": { + "name": "test_experiment", "instrument_session": "", + "sample": {"name": "test_8_4", "id": "4", "data": {}}, + "experiment_definition": {"name": "test", "id": "4", "data": {}}, }, "id": "4", "status": "Cancelled", @@ -686,6 +687,7 @@ async def test_cancel_all_tasks_removes_all_queued_tasks_from_queue_and_returns_ } ], "position": None, + "kind": "Experiment", }, ] From 90acf754bbcf945f2311e0ab0043756c8627227c Mon Sep 17 00:00:00 2001 From: Jacob Williamson Date: Tue, 30 Jun 2026 18:18:06 +0100 Subject: [PATCH 18/18] Fix docs build --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 11f9d7a..8475805 100644 --- a/README.md +++ b/README.md @@ -38,8 +38,6 @@ blueapi --config tests/test_data/i15_1/test_blueapi_config.yaml serve daq-queuing-service --config tests/test_data/i15_1/test_daq_queue_config.yaml -p 8001 --dev # Local queue service pointing at i15-1 converters ``` -See [here](./docs/explanations/queue_items.md) for more about the items inside the queue. - See https://diamondlightsource.github.io/daq-queuing-service for more detailed documentation.