Skip to content

Commit 1bb5cfd

Browse files
authored
refactor(protocol-designer): migrate all labware + tipracks to latest version (#19016)
closes AUTH-1997 AUTH-2148
1 parent e1ad32a commit 1bb5cfd

File tree

5 files changed

+177
-13
lines changed

5 files changed

+177
-13
lines changed

protocol-designer/src/assets/localization/en/shared.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
"migration_header": "Your protocol was made in an older version of Protocol Designer",
6565
"migrations": {
6666
"generic": {
67-
"body1": "Your protocol will be automatically updated to the latest version. We recommend making a separate copy of your file before importing."
67+
"body1": "Your protocol and included labware will be automatically updated to the latest version. We recommend making a separate copy of your file before importing."
6868
}
6969
},
7070
"magneticBlockAndStagingArea": "{{module}} with Staging Area",

protocol-designer/src/components/organisms/FileUploadMessagesModal/__tests__/FileUploadMessagesModal.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('FileUploadMessagesModal', () => {
4545
'Your protocol was made in an older version of Protocol Designer'
4646
)
4747
screen.getByText(
48-
'Your protocol will be automatically updated to the latest version. We recommend making a separate copy of your file before importing.'
48+
'Your protocol and included labware will be automatically updated to the latest version. We recommend making a separate copy of your file before importing.'
4949
)
5050
fireEvent.click(screen.getByRole('button', { name: 'Import' }))
5151
expect(vi.mocked(dismissFileUploadMessage)).toHaveBeenCalled()

protocol-designer/src/labware-ingred/reducers/index.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import pickBy from 'lodash/pickBy'
44
import { combineReducers } from 'redux'
55
import { handleActions } from 'redux-actions'
66

7+
import { getAllLabwareDefs } from '@opentrons/shared-data'
8+
79
import { getPDMetadata } from '../../file-types'
10+
import { getOnlyLatestDefs } from '../../labware-defs'
11+
import { getMigratedLabwareId } from '../utils'
812

913
import type { Reducer } from 'redux'
1014
import type {
@@ -180,10 +184,19 @@ export const containers: Reducer<ContainersState, any> = handleActions(
180184
): ContainersState => {
181185
const { file } = action.payload
182186
const metadata = getPDMetadata(file)
187+
const allLabwareDefs = getAllLabwareDefs()
188+
const latestDefs = getOnlyLatestDefs()
183189
const containers: ContainersState = Object.entries(
184190
metadata.labware
185191
).reduce((acc: ContainersState, [id, labwareLoadInfo], key) => {
186-
acc[id] = {
192+
const latestLabwareId = getMigratedLabwareId(
193+
id,
194+
metadata.labware,
195+
allLabwareDefs,
196+
latestDefs
197+
)
198+
199+
acc[latestLabwareId] = {
187200
nickname: labwareLoadInfo.displayName,
188201
disambiguationNumber: key,
189202
}
@@ -299,7 +312,26 @@ export const ingredLocations: Reducer<LocationsState, any> = handleActions(
299312
LOAD_FILE: (
300313
state: LocationsState,
301314
action: LoadFileAction
302-
): LocationsState => getPDMetadata(action.payload.file).ingredLocations,
315+
): LocationsState => {
316+
const ingredLocations = getPDMetadata(action.payload.file).ingredLocations
317+
const labware = getPDMetadata(action.payload.file).labware
318+
const allLabwareDefs = getAllLabwareDefs()
319+
const latestDefs = getOnlyLatestDefs()
320+
321+
return Object.entries(ingredLocations).reduce(
322+
(acc: LocationsState, [labwareId, liquidIngredient]) => {
323+
const latestLabwareId = getMigratedLabwareId(
324+
labwareId,
325+
labware,
326+
allLabwareDefs,
327+
latestDefs
328+
)
329+
acc[latestLabwareId] = liquidIngredient
330+
return acc
331+
},
332+
{}
333+
)
334+
},
303335
},
304336
{}
305337
)

protocol-designer/src/labware-ingred/utils.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import type {
2222
LabwareDefinition2,
2323
RobotType,
2424
} from '@opentrons/shared-data'
25+
import type { Labware } from '../file-types'
26+
import type { LabwareDefByDefURI } from '../labware-defs'
2527
import type { InitialDeckSetup } from '../step-forms/types'
2628
import type { DeckSlot } from '../types'
2729

@@ -132,3 +134,33 @@ export function getNextNickname(
132134
? `${proposedNickname.trim()} (${topMatchNum + 1})`
133135
: proposedNickname
134136
}
137+
138+
export const getMigratedLabwareId = (
139+
oldLabwareId: string,
140+
labware: Labware,
141+
allLabwareDefs: Record<string, LabwareDefinition2>,
142+
latestDefs: LabwareDefByDefURI
143+
): string => {
144+
const defURI = labware[oldLabwareId].labwareDefURI
145+
const loadName = allLabwareDefs[defURI]?.parameters.loadName
146+
const latestURI = Object.entries(latestDefs).find(
147+
([_, def]) => def.parameters.loadName === loadName
148+
)?.[0]
149+
const labwareIdString = oldLabwareId.split(':')[0]
150+
const latestLabwareId =
151+
latestURI != null ? `${labwareIdString}:${latestURI}` : oldLabwareId // fallback to original labwareId for custom labware
152+
153+
return latestLabwareId
154+
}
155+
156+
export const getMigratedURI = (
157+
oldURI: string,
158+
allLabwareDefs: Record<string, LabwareDefinition2>,
159+
latestDefs: LabwareDefByDefURI
160+
): string => {
161+
const loadName = allLabwareDefs[oldURI]?.parameters.loadName
162+
const latestURI = Object.entries(latestDefs).find(
163+
([_, def]) => def.parameters.loadName === loadName
164+
)?.[0]
165+
return latestURI ?? oldURI // fallback to oldURI for custom labware
166+
}

protocol-designer/src/step-forms/reducers/index.ts

Lines changed: 109 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { handleActions } from 'redux-actions'
88
import {
99
FLEX_SIMPLEST_DECK_CONFIG,
1010
getAllDefinitions,
11+
getAllLabwareDefs,
1112
getLabwareDefaultEngageHeight,
1213
getLabwareDefURI,
1314
getModuleType,
@@ -19,7 +20,14 @@ import { GRIPPER_LOCATION } from '@opentrons/step-generation'
1920

2021
import { INITIAL_DECK_SETUP_STEP_ID } from '../../constants'
2122
import { getPDMetadata } from '../../file-types'
22-
import { rootReducer as labwareDefsRootReducer } from '../../labware-defs'
23+
import {
24+
getOnlyLatestDefs,
25+
rootReducer as labwareDefsRootReducer,
26+
} from '../../labware-defs'
27+
import {
28+
getMigratedLabwareId,
29+
getMigratedURI,
30+
} from '../../labware-ingred/utils'
2331
import {
2432
getDefaultsForStepType,
2533
handleFormChange,
@@ -366,11 +374,85 @@ export const savedStepForms = (
366374

367375
case 'LOAD_FILE': {
368376
const { file } = action.payload
369-
const stepFormsFromFile = getPDMetadata(file).savedStepForms
370-
return mapValues(stepFormsFromFile, stepForm => ({
371-
...getDefaultsForStepType(stepForm.stepType),
372-
...stepForm,
373-
}))
377+
const metadata = getPDMetadata(file)
378+
const { savedStepForms: stepFormsFromFile, labware } = metadata
379+
const prevInitialDeckSetupStep =
380+
stepFormsFromFile[INITIAL_DECK_SETUP_STEP_ID]
381+
const formLabwareLocationUpdate: Record<string, string> =
382+
prevInitialDeckSetupStep.labwareLocationUpdate
383+
const allLabware = getAllDefinitions()
384+
const latestDefs = getOnlyLatestDefs()
385+
const updatedLabwareLocationUpdate = Object.entries(
386+
formLabwareLocationUpdate
387+
).reduce((acc: Record<string, string>, [id, location]) => {
388+
const updatedLabwareId = getMigratedLabwareId(
389+
id,
390+
labware,
391+
allLabware,
392+
latestDefs
393+
)
394+
acc[updatedLabwareId] = location
395+
return acc
396+
}, {})
397+
398+
return mapValues(stepFormsFromFile, (stepForm: FormData, formId) => {
399+
if (formId === INITIAL_DECK_SETUP_STEP_ID) {
400+
return {
401+
...prevInitialDeckSetupStep,
402+
labwareLocationUpdate: {
403+
...updatedLabwareLocationUpdate,
404+
},
405+
}
406+
} else if (
407+
stepForm.stepType === 'mix' ||
408+
stepForm.stepType === 'moveLabware'
409+
) {
410+
return {
411+
...getDefaultsForStepType(stepForm.stepType),
412+
...stepForm,
413+
labware: getMigratedLabwareId(
414+
stepForm.labware as string,
415+
labware,
416+
allLabware,
417+
latestDefs
418+
),
419+
tipRack:
420+
stepForm.stepType === 'mix'
421+
? getMigratedURI(
422+
stepForm.tipRack as string,
423+
allLabware,
424+
latestDefs
425+
)
426+
: undefined,
427+
}
428+
} else if (stepForm.stepType === 'moveLiquid') {
429+
return {
430+
...getDefaultsForStepType(stepForm.stepType),
431+
...stepForm,
432+
aspirate_labware: getMigratedLabwareId(
433+
stepForm.aspirate_labware as string,
434+
labware,
435+
allLabware,
436+
latestDefs
437+
),
438+
dispense_labware: getMigratedLabwareId(
439+
stepForm.dispense_labware as string,
440+
labware,
441+
allLabware,
442+
latestDefs
443+
),
444+
tipRack: getMigratedURI(
445+
stepForm.tipRack as string,
446+
allLabware,
447+
latestDefs
448+
),
449+
}
450+
}
451+
return {
452+
...getDefaultsForStepType(stepForm.stepType),
453+
...stepForm,
454+
}
455+
})
374456
}
375457
case 'CREATE_DECK_FIXTURE': {
376458
const { id, location, name } = action.payload
@@ -1065,11 +1147,13 @@ export const labwareInvariantProperties: Reducer<
10651147
const metadata = getPDMetadata(file)
10661148
const labwareDefinitionsFromFile = file.labwareDefinitions
10671149
const allLabware = getAllDefinitions()
1150+
const latestDefs = getOnlyLatestDefs()
10681151
let labware: NormalizedLabwareById = {}
10691152

10701153
labware = Object.entries(metadata.labware).reduce(
10711154
(acc: NormalizedLabwareById, [id, labwareLoadInfo]) => {
10721155
const labwareDefURI = labwareLoadInfo.labwareDefURI
1156+
10731157
const definition =
10741158
// labwareDefinitionsFromFile from file are either customLabware for py
10751159
// or all labwareDefs for JSON
@@ -1082,15 +1166,26 @@ export const labwareInvariantProperties: Reducer<
10821166
)
10831167
}
10841168

1169+
const loadName = definition.parameters.loadName
1170+
const latestDefURI = Object.entries(latestDefs).find(
1171+
([_, def]) => def.parameters.loadName === loadName
1172+
)?.[0]
1173+
10851174
const displayCategory =
10861175
definition?.metadata.displayCategory ?? 'otherLabware'
10871176

10881177
const displayCategoryCount = Object.values(acc).filter(
10891178
lw => lw.displayCategory === displayCategory
10901179
).length
10911180

1092-
acc[id] = {
1093-
labwareDefURI,
1181+
const labwareIdString = id.split(':')[0]
1182+
const latestLabwareId =
1183+
latestDefURI != null
1184+
? `${labwareIdString}:${latestDefURI}`
1185+
: labwareDefURI
1186+
1187+
acc[latestLabwareId] = {
1188+
labwareDefURI: latestDefURI ?? labwareDefURI,
10941189
pythonName: getLabwarePythonName(
10951190
displayCategory,
10961191
displayCategoryCount + 1
@@ -1227,19 +1322,24 @@ export const pipetteInvariantProperties: Reducer<
12271322
): NormalizedPipetteById => {
12281323
const { file } = action.payload
12291324
const metadata = getPDMetadata(file)
1325+
const allLabwareDefs = getAllLabwareDefs()
1326+
const latestDefs = getOnlyLatestDefs()
12301327
const pipettes = Object.entries(metadata.pipettes).reduce(
12311328
(
12321329
acc: NormalizedPipetteById,
12331330
[id, pipetteLoadInfo]: [string, PipetteLoadInfo]
12341331
) => {
12351332
const tiprackDefURI = metadata.pipetteTiprackAssignments[id]
1333+
const latestTiprackDefURIs = tiprackDefURI.map(uri =>
1334+
getMigratedURI(uri, allLabwareDefs, latestDefs)
1335+
)
12361336

12371337
return {
12381338
...acc,
12391339
[id]: {
12401340
id,
12411341
name: pipetteLoadInfo.pipetteName as PipetteName,
1242-
tiprackDefURI,
1342+
tiprackDefURI: latestTiprackDefURIs,
12431343
},
12441344
}
12451345
},

0 commit comments

Comments
 (0)