Skip to content

Commit 17fb597

Browse files
committed
fixups
1 parent 3a1847d commit 17fb597

File tree

5 files changed

+52
-31
lines changed

5 files changed

+52
-31
lines changed

api/src/opentrons/protocol_engine/types/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
OnLabwareOffsetLocationSequenceComponent,
9595
OnModuleOffsetLocationSequenceComponent,
9696
OnAddressableAreaOffsetLocationSequenceComponent,
97+
LabwareOffsetLocationSequenceComponents,
9798
)
9899
from .labware_offset_vector import LabwareOffsetVector
99100
from .well_position import (
@@ -204,6 +205,7 @@
204205
# Labware offset location
205206
"LegacyLabwareOffsetLocation",
206207
"LabwareOffsetLocationSequence",
208+
"LabwareOffsetLocationSequenceComponents",
207209
"OnLabwareOffsetLocationSequenceComponent",
208210
"OnModuleOffsetLocationSequenceComponent",
209211
"OnAddressableAreaOffsetLocationSequenceComponent",

robot-server/robot_server/labware_offsets/models.py

+17-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@
1010
from robot_server.errors.error_responses import ErrorDetails
1111

1212

13+
class StoredLabwareOffsetCreate(BaseModel):
14+
"""Create an offset for storage."""
15+
16+
definitionUri: str = Field(..., description="The URI for the labware's definition.")
17+
18+
locationSequence: LabwareOffsetLocationSequence = Field(
19+
...,
20+
description="Where the labware is located on the robot. Can represent all locations, but may not be present for older runs.",
21+
)
22+
vector: LabwareOffsetVector = Field(
23+
...,
24+
description="The offset applied to matching labware.",
25+
)
26+
27+
1328
class StoredLabwareOffset(BaseModel):
1429
"""An offset that the robot adds to a pipette's position when it moves to labware."""
1530

@@ -20,8 +35,8 @@ class StoredLabwareOffset(BaseModel):
2035
createdAt: datetime = Field(..., description="When this labware offset was added.")
2136
definitionUri: str = Field(..., description="The URI for the labware's definition.")
2237

23-
locationSequence: LabwareOffsetLocationSequence | None = Field(
24-
default=None,
38+
locationSequence: LabwareOffsetLocationSequence = Field(
39+
...,
2540
description="Where the labware is located on the robot. Can represent all locations, but may not be present for older runs.",
2641
)
2742
vector: LabwareOffsetVector = Field(

robot-server/robot_server/labware_offsets/router.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from pydantic.json_schema import SkipJsonSchema
1010
from server_utils.fastapi_utils.light_router import LightRouter
1111

12-
from opentrons.protocol_engine import LabwareOffsetCreate, ModuleModel
12+
from opentrons.protocol_engine import ModuleModel
1313
from opentrons.types import DeckSlotName
1414

1515
from robot_server.labware_offsets.models import LabwareOffsetNotFound
@@ -30,7 +30,7 @@
3030
LabwareOffsetStore,
3131
)
3232
from .fastapi_dependencies import get_labware_offset_store
33-
from .models import StoredLabwareOffset
33+
from .models import StoredLabwareOffset, StoredLabwareOffsetCreate
3434

3535

3636
router = LightRouter()
@@ -54,13 +54,13 @@ async def post_labware_offset( # noqa: D103
5454
store: Annotated[LabwareOffsetStore, fastapi.Depends(get_labware_offset_store)],
5555
new_offset_id: Annotated[str, fastapi.Depends(get_unique_id)],
5656
new_offset_created_at: Annotated[datetime, fastapi.Depends(get_current_time)],
57-
request_body: Annotated[RequestModel[LabwareOffsetCreate], fastapi.Body()],
57+
request_body: Annotated[RequestModel[StoredLabwareOffsetCreate], fastapi.Body()],
5858
) -> PydanticResponse[SimpleBody[StoredLabwareOffset]]:
5959
new_offset = StoredLabwareOffset.model_construct(
6060
id=new_offset_id,
6161
createdAt=new_offset_created_at,
6262
definitionUri=request_body.data.definitionUri,
63-
location=request_body.data.location,
63+
locationSequence=request_body.data.locationSequence,
6464
vector=request_body.data.vector,
6565
)
6666
store.add(new_offset)

robot-server/robot_server/labware_offsets/store.py

+17-17
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
from opentrons.protocol_engine.types import (
77
LabwareOffsetVector,
88
ModuleModel,
9-
OnAddressableAreaOffsetSequenceComponent,
10-
OnModuleOffsetSequenceComponent,
11-
OnLabwareOffsetSequenceComponent,
9+
OnAddressableAreaOffsetLocationSequenceComponent,
10+
OnModuleOffsetLocationSequenceComponent,
11+
OnLabwareOffsetLocationSequenceComponent,
1212
LabwareOffsetLocationSequenceComponents,
1313
)
1414
from opentrons.types import DeckSlotName
@@ -42,8 +42,6 @@ class _DoNotFilter(enum.Enum):
4242
"""
4343

4444

45-
# todo(mm, 2024-12-06): Convert to be SQL-based and persistent instead of in-memory.
46-
# https://opentrons.atlassian.net/browse/EXEC-1015
4745
class LabwareOffsetStore:
4846
"""A persistent store for labware offsets, to support the `/labwareOffsets` endpoints."""
4947

@@ -69,7 +67,9 @@ def add(self, offset: StoredLabwareOffset) -> None:
6967
.row_id
7068
)
7169
transaction.execute(
72-
sqlalchemy.insert(labware_offset_table).values(
70+
sqlalchemy.insert(
71+
labware_offset_location_sequence_components_table
72+
).values(
7373
list(
7474
_pydantic_to_sql_location_sequence_iterator(
7575
offset, offset_row_id
@@ -98,7 +98,7 @@ def search(
9898
.join(
9999
labware_offset_location_sequence_components_table,
100100
labware_offset_table.c.row_id
101-
== labware_offset_sequence_table.c.offset_id,
101+
== labware_offset_location_sequence_components_table.c.offset_id,
102102
)
103103
.where(labware_offset_table.c.active == True) # noqa: E712
104104
)
@@ -112,7 +112,7 @@ def search(
112112
labware_offset_table.c.definition_uri == definition_uri_filter
113113
)
114114
if location_slot_name_filter is not DO_NOT_FILTER:
115-
filter_statement = (
115+
filter_statement = filter_statement.where(
116116
filter_statement.where(
117117
labware_offset_location_sequence_components_table.c.component_kind
118118
== "onAddressableArea"
@@ -121,15 +121,15 @@ def search(
121121
labware_offset_location_sequence_components_table.c.primary_component_value
122122
== location_slot_name_filter.value
123123
)
124-
.exists(0)
124+
.exists()
125125
)
126126
if location_module_model_filter is not DO_NOT_FILTER:
127127
location_module_model_filter_value = (
128128
location_module_model_filter.value
129129
if location_module_model_filter is not None
130130
else None
131131
)
132-
filter_statement = (
132+
filter_statement = filter_statement.where(
133133
filter_statement.where(
134134
labware_offset_location_sequence_components_table.c.component_kind
135135
== "onModule"
@@ -141,7 +141,7 @@ def search(
141141
.exists()
142142
)
143143
if location_definition_uri_filter is not DO_NOT_FILTER:
144-
filter_statement = (
144+
filter_statement = filter_statement.where(
145145
filter_statement.where(
146146
labware_offset_location_sequence_components_table.c.component_kind
147147
== "onLabware"
@@ -217,15 +217,15 @@ def _sql_sequence_component_to_pydantic_sequence_component(
217217
component_row: sqlalchemy.engine.Row,
218218
) -> LabwareOffsetLocationSequenceComponents:
219219
if component_row.component_kind == "onLabware":
220-
yield OnLabwareOffsetSequenceComponent(
220+
yield OnLabwareOffsetLocationSequenceComponent(
221221
labwareUri=component_row.primary_component_value
222222
)
223223
elif component_row.component_kind == "onModule":
224-
yield OnModuleOffsetSequenceComponent(
224+
yield OnModuleOffsetLocationSequenceComponent(
225225
moduleModel=ModuleModel(component_row.primary_component_value)
226226
)
227227
elif component_row.component_kind == "onAddressableArea":
228-
yield OnAddressableAreaOffsetSequenceComponent(
228+
yield OnAddressableAreaOffsetLocationSequenceComponent(
229229
addressableAreaName=component_row.primary_component_value
230230
)
231231
else:
@@ -297,23 +297,23 @@ def _pydantic_to_sql_location_sequence_iterator(
297297
labware_offset: StoredLabwareOffset, offset_row_id: int
298298
) -> Iterator[dict[str, object]]:
299299
for index, component in labware_offset.locationSequence:
300-
if isinstance(component, OnLabwareOffsetSequenceComponent):
300+
if isinstance(component, OnLabwareOffsetLocationSequenceComponent):
301301
yield dict(
302302
offset_id=offset_row_id,
303303
sequence_ordinal=index,
304304
component_kind=component.kind,
305305
primary_component_value=component.labwareUri,
306306
component_value_json=component.model_dump(),
307307
)
308-
elif isinstance(component, OnModuleOffsetSequenceComponent):
308+
elif isinstance(component, OnModuleOffsetLocationSequenceComponent):
309309
yield dict(
310310
offset_id=offset_row_id,
311311
sequence_ordinal=index,
312312
component_kind=component.kind,
313313
primary_component_value=component.moduleModel.value,
314314
component_value_json=component.model_dump(),
315315
)
316-
elif isinstance(component, OnAddressableAreaOffsetSequenceComponent):
316+
elif isinstance(component, OnAddressableAreaOffsetLocationSequenceComponent):
317317
yield dict(
318318
offset_id=offset_row_id,
319319
sequence_ordinal=index,

robot-server/robot_server/persistence/_migrations/v9_to_v10.py

+12-8
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ def _upmigrate_stored_offsets(connection: sqlalchemy.engine.Connection) -> None:
6262
DeckType(guess_deck_type_from_global_config()), version=5
6363
)
6464

65-
offsets = sqlalchemy.select(schema_9.labware_offset_table)
65+
offsets = connection.execute(sqlalchemy.select(schema_9.labware_offset_table))
66+
6667
for offset in offsets:
6768
new_row = (
6869
connection.execute(
@@ -100,25 +101,28 @@ def _v9_offset_to_v10_offset_locations(
100101
location_sequence = legacy_offset_location_to_offset_location_sequence(
101102
LegacyLabwareOffsetLocation(
102103
slotName=DeckSlotName(v9_offset.location_slot_name),
103-
moduleModel=ModuleModel(v9_offset.location_module_model)
104-
if v9_offset.location_module_model is not None
105-
else None,
104+
moduleModel=(
105+
ModuleModel(v9_offset.location_module_model)
106+
if v9_offset.location_module_model is not None
107+
else None
108+
),
106109
definitionUri=v9_offset.location_definition_uri,
107110
),
111+
deck_definition,
108112
)
109113
values: list[dict[str, object]] = []
110-
for index, sequence_component in location_sequence.enumerate():
114+
for index, sequence_component in enumerate(location_sequence):
111115
primary_component_value = ""
112116
component_value_json = ""
113117
if sequence_component.kind == "onLabware":
114118
primary_component_value = sequence_component.labwareUri
115-
component_value_json = sequence_component.model_dump()
119+
component_value_json = sequence_component.model_dump_json()
116120
elif sequence_component.kind == "onModule":
117121
primary_component_value = sequence_component.moduleModel.value
118-
component_value_json = sequence_component.model_dump()
122+
component_value_json = sequence_component.model_dump_json()
119123
elif sequence_component.kind == "onAddressableArea":
120124
primary_component_value = sequence_component.addressableAreaName
121-
component_value_json = sequence_component.model_dump()
125+
component_value_json = sequence_component.model_dump_json()
122126
else:
123127
# This should never happen since we're exhaustively checking kinds here
124128
continue

0 commit comments

Comments
 (0)