From 81585fff217586e8e9d2532942125baa7db002d3 Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Mon, 11 Mar 2024 17:11:38 -0400 Subject: [PATCH] feat(api,robot-server): export notes on commands (#14616) Commands in runs can have notes attached to them. Each command can have zero or more notes, and zero notes can be expressed as any of (notes=None, notes is-not-present, notes is empty-list). This is to make the public models a little more robust across versions. Closes EXEC-288 --- api/src/opentrons/protocol_engine/__init__.py | 2 ++ .../protocol_engine/commands/__init__.py | 2 ++ .../protocol_engine/commands/command.py | 8 ++++++++ .../runs/router/commands_router.py | 1 + robot-server/robot_server/runs/run_models.py | 5 +++++ .../tests/runs/router/test_commands_router.py | 20 +++++++++++++++++++ shared-data/command/types/index.ts | 1 + 7 files changed, 39 insertions(+) diff --git a/api/src/opentrons/protocol_engine/__init__.py b/api/src/opentrons/protocol_engine/__init__.py index f6737a71432..07f2ae17f9c 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 3dfe6eaf51f..97f0744a9a2 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 c2314aab579..1bf72e12352 100644 --- a/api/src/opentrons/protocol_engine/commands/command.py +++ b/api/src/opentrons/protocol_engine/commands/command.py @@ -14,6 +14,7 @@ Tuple, Union, Literal, + List, ) from pydantic import BaseModel, Field @@ -175,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 f3f81a7751c..734d1a26066 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 ee85902440a..85a1446b631 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 10819fcac9a..cc06ddd621f 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) diff --git a/shared-data/command/types/index.ts b/shared-data/command/types/index.ts index 144efa2f46a..980eb8fb124 100644 --- a/shared-data/command/types/index.ts +++ b/shared-data/command/types/index.ts @@ -47,6 +47,7 @@ export interface CommonCommandRunTimeInfo { startedAt: string | null completedAt: string | null intent?: 'protocol' | 'setup' + notes?: CommandNote[] | null } export interface CommonCommandCreateInfo { key?: string