Skip to content

Commit 49cde9d

Browse files
authored
refactor(step-generation): load_liquid more eloquantly (#18731)
closes AUTH-1454
1 parent 05d2623 commit 49cde9d

File tree

3 files changed

+100
-17
lines changed

3 files changed

+100
-17
lines changed

step-generation/src/__tests__/pythonFileUtils.test.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ liquid_2 = protocol.define_liquid(
433433
})
434434

435435
describe('getLoadLiquids', () => {
436-
it('should generate 2 liquids in 2 labware in 4 wells', () => {
436+
it('should generate 2 liquids in 2 labware in multiple wells', () => {
437437
const mockLiquidsBylabwareId: LabwareLiquidState = {
438438
[labwareId3]: {
439439
A1: { [liquid1]: { volume: 10 } },
@@ -442,6 +442,14 @@ describe('getLoadLiquids', () => {
442442
},
443443
[labwareId4]: {
444444
D1: { [liquid2]: { volume: 180 } },
445+
D2: { [liquid2]: { volume: 180 } },
446+
D3: { [liquid2]: { volume: 180 } },
447+
D4: { [liquid2]: { volume: 180 } },
448+
D5: { [liquid2]: { volume: 180 } },
449+
D6: { [liquid2]: { volume: 180 } },
450+
D7: { [liquid2]: { volume: 180 } },
451+
D8: { [liquid2]: { volume: 180 } },
452+
E3: { [liquid2]: { volume: 180 } },
445453
},
446454
}
447455
expect(
@@ -453,10 +461,24 @@ describe('getLoadLiquids', () => {
453461
).toBe(
454462
`
455463
# Load Liquids:
456-
well_plate_1["A1"].load_liquid(liquid_1, 10)
457-
well_plate_1["A2"].load_liquid(liquid_1, 10)
458-
well_plate_1["A3"].load_liquid(liquid_2, 50)
459-
well_plate_2["D1"].load_liquid(liquid_2, 180)`.trimStart()
464+
well_plate_1.load_liquid(
465+
wells=["A1", "A2"],
466+
liquid=liquid_1,
467+
volume=10,
468+
)
469+
well_plate_1.load_liquid(
470+
wells=["A3"],
471+
liquid=liquid_2,
472+
volume=50,
473+
)
474+
well_plate_2.load_liquid(
475+
wells=[
476+
"D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8",
477+
"E3"
478+
],
479+
liquid=liquid_2,
480+
volume=180,
481+
)`.trimStart()
460482
)
461483
})
462484
})

step-generation/src/utils/pythonFileUtils.ts

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
CUSTOM_LABWARE_DICT_NAME,
1515
formatPyDict,
1616
formatPyStr,
17+
getChunkForIndentingLists,
18+
INDENT,
1719
indentPyLines,
1820
OFF_DECK,
1921
PROTOCOL_CONTEXT_NAME,
@@ -293,19 +295,67 @@ export function getLoadLiquids(
293295
liquidEntities: LiquidEntities,
294296
labwareEntities: LabwareEntities
295297
): string {
296-
const pythonLoadLiquids = Object.entries(liquidsByLabwareId)
297-
.flatMap(([labwareId, liquidState]) => {
298+
const groupedLiquids = Object.entries(liquidsByLabwareId).reduce(
299+
(
300+
acc: Record<
301+
string,
302+
{
303+
labwarePythonName: string
304+
liquidPythonName: string
305+
volume: number
306+
wells: string[]
307+
}
308+
>,
309+
[labwareId, liquidState]
310+
) => {
298311
const labwarePythonName = labwareEntities[labwareId].pythonName
299312

300-
return Object.entries(liquidState).flatMap(([well, locationState]) =>
301-
Object.entries(locationState)
302-
.map(([liquidGroupId, volume]) => {
303-
const liquidPythonName = liquidEntities[liquidGroupId].pythonName
304-
return `${labwarePythonName}[${formatPyStr(
305-
well
306-
)}].load_liquid(${liquidPythonName}, ${volume.volume})`
307-
})
308-
.join('\n')
313+
Object.entries(liquidState).forEach(([well, locationState]) => {
314+
Object.entries(locationState).forEach(([liquidGroupId, volumeInfo]) => {
315+
const liquidPythonName = liquidEntities[liquidGroupId].pythonName
316+
317+
const key = `${labwarePythonName}__${liquidPythonName}__${volumeInfo.volume}`
318+
if (!acc[key]) {
319+
acc[key] = {
320+
labwarePythonName,
321+
liquidPythonName,
322+
volume: volumeInfo.volume,
323+
wells: [],
324+
}
325+
}
326+
acc[key].wells.push(well)
327+
})
328+
})
329+
330+
return acc
331+
},
332+
{}
333+
)
334+
335+
const pythonLoadLiquids = Object.values(groupedLiquids)
336+
.map(({ labwarePythonName, liquidPythonName, volume, wells }) => {
337+
const formattedWells = wells.map(w => formatPyStr(w))
338+
const wellChunks = getChunkForIndentingLists(formattedWells, 8)
339+
340+
const indentedWells = wellChunks
341+
.map(chunk => INDENT + chunk.join(', '))
342+
.join(',\n')
343+
344+
const pythonWells =
345+
formattedWells.length < 8
346+
? formattedWells.join(', ')
347+
: `\n${indentedWells}\n${INDENT}`
348+
349+
const loadLiquidArgs = [
350+
`wells=[${pythonWells}],\n` +
351+
`liquid=${liquidPythonName},\n` +
352+
`volume=${volume},\n`,
353+
].join()
354+
355+
return (
356+
`${labwarePythonName}.load_liquid(\n` +
357+
`${indentPyLines(loadLiquidArgs)}` +
358+
`)`
309359
)
310360
})
311361
.join('\n')

step-generation/src/utils/pythonFormat.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const OFF_DECK = 'protocol_api.OFF_DECK'
1414
/** The variable name for the Python dict containing the custom labware defintions. */
1515
export const CUSTOM_LABWARE_DICT_NAME = 'CUSTOM_LABWARE'
1616

17-
const INDENT = ' '
17+
export const INDENT = ' '
1818

1919
/** Indent each of the lines in `text`. */
2020
export function indentPyLines(text: string): string {
@@ -151,3 +151,14 @@ export function formatPyWellLocation(wellLocation?: WellLocation): string {
151151
}
152152
return python
153153
}
154+
155+
export function getChunkForIndentingLists(
156+
lists: string[],
157+
size: number
158+
): string[][] {
159+
const chunks: string[][] = []
160+
for (let index = 0; index < lists.length; index += size) {
161+
chunks.push(lists.slice(index, index + size))
162+
}
163+
return chunks
164+
}

0 commit comments

Comments
 (0)