diff --git a/api/src/opentrons/protocol_engine/__init__.py b/api/src/opentrons/protocol_engine/__init__.py index f6737a714320..07f2ae17f9c3 100644 --- a/api/src/opentrons/protocol_engine/__init__.py +++ b/api/src/opentrons/protocol_engine/__init__.py @@ -20,6 +20,7 @@ CommandStatus, CommandType, CommandIntent, + CommandNote, ) from .state import State, StateView, StateSummary, CommandSlice, CurrentCommand, Config from .plugins import AbstractPlugin @@ -79,6 +80,7 @@ "CommandStatus", "CommandType", "CommandIntent", + "CommandNote", # state interfaces and models "State", "StateView", diff --git a/api/src/opentrons/protocol_engine/commands/__init__.py b/api/src/opentrons/protocol_engine/commands/__init__.py index 3dfe6eaf51f7..97f0744a9a2a 100644 --- a/api/src/opentrons/protocol_engine/commands/__init__.py +++ b/api/src/opentrons/protocol_engine/commands/__init__.py @@ -28,6 +28,7 @@ BaseCommandCreate, CommandStatus, CommandIntent, + CommandNote, ) from .command_unions import ( @@ -332,6 +333,7 @@ "BaseCommandCreate", "CommandStatus", "CommandIntent", + "CommandNote", # command parameter hashing "hash_command_params", # command schema generation diff --git a/api/src/opentrons/protocol_engine/commands/command.py b/api/src/opentrons/protocol_engine/commands/command.py index 72164cea7229..1bf72e12352d 100644 --- a/api/src/opentrons/protocol_engine/commands/command.py +++ b/api/src/opentrons/protocol_engine/commands/command.py @@ -176,6 +176,13 @@ class BaseCommand(GenericModel, Generic[CommandParamsT, CommandResultT]): " a command that is part of a calibration procedure." ), ) + notes: Optional[List[CommandNote]] = Field( + None, + description=( + "Information not critical to the execution of the command derived from either" + " the command's execution or the command's generation." + ), + ) class AbstractCommandImpl( diff --git a/robot-server/robot_server/runs/router/commands_router.py b/robot-server/robot_server/runs/router/commands_router.py index f3f81a7751c0..734d1a260669 100644 --- a/robot-server/robot_server/runs/router/commands_router.py +++ b/robot-server/robot_server/runs/router/commands_router.py @@ -296,6 +296,7 @@ async def get_run_commands( completedAt=c.completedAt, params=c.params, error=c.error, + notes=c.notes, ) for c in command_slice.commands ] diff --git a/robot-server/robot_server/runs/run_models.py b/robot-server/robot_server/runs/run_models.py index ee85902440a0..85a1446b6311 100644 --- a/robot-server/robot_server/runs/run_models.py +++ b/robot-server/robot_server/runs/run_models.py @@ -16,6 +16,7 @@ LabwareOffset, LabwareOffsetCreate, Liquid, + CommandNote, ) from opentrons_shared_data.errors import GeneralError from robot_server.service.json_api import ResourceModel @@ -56,6 +57,10 @@ class RunCommandSummary(ResourceModel): None, description="Why this command was added to the run.", ) + notes: Optional[List[CommandNote]] = Field( + None, + description="Notes pertaining to this command.", + ) class Run(ResourceModel): diff --git a/robot-server/tests/runs/router/test_commands_router.py b/robot-server/tests/runs/router/test_commands_router.py index 10819fcac9a5..cc06ddd621fe 100644 --- a/robot-server/tests/runs/router/test_commands_router.py +++ b/robot-server/tests/runs/router/test_commands_router.py @@ -249,6 +249,24 @@ async def test_get_run_commands( decoy: Decoy, mock_run_data_manager: RunDataManager ) -> None: """It should return a list of all commands in a run.""" + long_note = pe_commands.CommandNote( + noteKind="warning", + shortMessage="this is a warning.", + longMessage=""" + hello, friends. I bring a warning.... + + + + FROM THE FUTURE! + """, + source="test", + ) + unenumed_note = pe_commands.CommandNote( + noteKind="lahsdlasd", + shortMessage="Oh no", + longMessage="its a notekind not in the enum", + source="test2", + ) command = pe_commands.WaitForResume( id="command-id", key="command-key", @@ -264,6 +282,7 @@ async def test_get_run_commands( createdAt=datetime(year=2024, month=4, day=4), detail="Things are not looking good.", ), + notes=[long_note, unenumed_note], ) decoy.when(mock_run_data_manager.get_current_command("run-id")).then_return( @@ -306,6 +325,7 @@ async def test_get_run_commands( createdAt=datetime(year=2024, month=4, day=4), detail="Things are not looking good.", ), + notes=[long_note, unenumed_note], ) ] assert result.content.meta == MultiBodyMeta(cursor=1, totalLength=3)