Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): Add accessor functions in new labware class #2418

Merged
merged 3 commits into from
Oct 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 87 additions & 7 deletions api/opentrons/protocol_api/labware.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""This module will replace Placeable"""
import re
from typing import List, Dict
from enum import Enum, auto
from opentrons.types import Point
from collections import defaultdict


class WellShape(Enum):
Expand Down Expand Up @@ -116,25 +118,103 @@ class Labware:
provides methods for accessing wells within the labware.
"""
def __init__(self, definition: dict, parent: Point) -> None:
pass
self._ordering = [well
for col in definition['ordering']
for well in col]
self._wells = definition['wells']
offset = definition['cornerOffsetFromSlot']
self._offset = Point(x=offset['x'], y=offset['y'], z=offset['z'])
self._pattern = re.compile(r'^([A-Z]+)([1-9][0-9]*)$', re.X)

def wells(self) -> List[Well]:
pass
"""
Accessor function used to generate a list of wells in top -> down,
left -> right order. This is representative of moving down `rows` and
across `columns` (e.g. 'A1', 'B1', 'C1'...'A2', 'B2', 'C2')

With indexing one can treat it as a typical python
list. To access well A1, for example, simply write: labware.wells()[0]

:return: Ordered list of all wells in a labware
"""
return [Well(self._wells[well], self._offset)
for well in self._ordering]

def wells_by_index(self) -> Dict[str, Well]:
pass
"""
Accessor function used to create a look-up table of Wells by name.

With indexing one can treat it as a typical python
dictionary whose keys are well names. To access well A1, for example,
simply write: labware.wells_by_index()['A1']

:return: Dictionary of well objects keyed by well name
"""
return {well: Well(self._wells[well], self._offset)
for well in self._ordering}

def rows(self) -> List[List[Well]]:
pass
"""
Accessor function used to navigate through a labware by row.

With indexing one can treat it as a typical python nested list.
To access row A for example, simply write: labware.rows()[0]. This
will output ['A1', 'A2', 'A3', 'A4'...]

:return: A list of row lists
"""
rowDict = self._create_indexed_dictionary(group=1)
keys = sorted(rowDict)
return [rowDict[key] for key in keys]

def rows_by_index(self) -> Dict[str, List[Well]]:
pass
"""
Accessor function used to navigate through a labware by row name.

With indexing one can treat it as a typical python dictionary.
To access row A for example, simply write: labware.rows_by_index()['A']
This will output ['A1', 'A2', 'A3', 'A4'...].

:return: Dictionary of Well lists keyed by row name
"""
rowDict = self._create_indexed_dictionary(group=1)
return rowDict

def columns(self) -> List[List[Well]]:
pass
"""
Accessor function used to navigate through a labware by column.

With indexing one can treat it as a typical python nested list.
To access row A for example,
simply write: labware.columns()[0]
This will output ['A1', 'B1', 'C1', 'D1'...].

:return: A list of column lists
"""
colDict = self._create_indexed_dictionary(group=2)
keys = sorted(colDict)
return [colDict[key] for key in keys]

def columns_by_index(self) -> Dict[str, List[Well]]:
pass
"""
Accessor function used to navigate through a labware by column name.

With indexing one can treat it as a typical python dictionary.
To access row A for example,
simply write: labware.columns_by_index()['1']
This will output ['A1', 'B1', 'C1', 'D1'...].

:return: Dictionary of Well lists keyed by column name
"""
colDict = self._create_indexed_dictionary(group=2)
return colDict

def _create_indexed_dictionary(self, group=0):
dictList = defaultdict(list)
for well in self._ordering:
wellObj = Well(self._wells[well], self._offset)
dictList[self._pattern.match(well).group(group)].append(wellObj)
return dictList


def _load_definition_by_name(name: str) -> dict:
Expand Down
201 changes: 201 additions & 0 deletions api/tests/opentrons/protocol_api/test_accessor_fn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
from opentrons.protocol_api import labware
from opentrons.types import Point

minimalLabwareDef = {
"cornerOffsetFromSlot": {
"x": 10,
"y": 10,
"z": 5
},
"ordering": [["A1"], ["A2"]],
"wells": {
"A1": {
"depth": 40,
"totalLiquidVolume": 100,
"diameter": 30,
"x": 0,
"y": 0,
"z": 0,
"shape": "circular"
},
"A2": {
"depth": 40,
"totalLiquidVolume": 100,
"diameter": 30,
"x": 10,
"y": 0,
"z": 0,
"shape": "circular"
}
}
}

minimalLabwareDef2 = {
"cornerOffsetFromSlot": {
"x": 10,
"y": 10,
"z": 5
},
"ordering": [["A1", "B1", "C1"], ["A2", "B2", "C2"]],
"wells": {
"A1": {
"depth": 40,
"totalLiquidVolume": 100,
"diameter": 30,
"x": 0,
"y": 18,
"z": 0,
"shape": "circular"
},
"B1": {
"depth": 40,
"totalLiquidVolume": 100,
"diameter": 30,
"x": 0,
"y": 9,
"z": 0,
"shape": "circular"
},
"C1": {
"depth": 40,
"totalLiquidVolume": 100,
"diameter": 30,
"x": 0,
"y": 0,
"z": 0,
"shape": "circular"
},
"A2": {
"depth": 40,
"totalLiquidVolume": 100,
"diameter": 30,
"x": 9,
"y": 18,
"z": 0,
"shape": "circular"
},
"B2": {
"depth": 40,
"totalLiquidVolume": 100,
"diameter": 30,
"x": 9,
"y": 9,
"z": 0,
"shape": "circular"
},
"C2": {
"depth": 40,
"totalLiquidVolume": 100,
"diameter": 30,
"x": 9,
"y": 0,
"z": 0,
"shape": "circular"
}
}
}


def test_labware_init():
deck = Point(0, 0, 0)
fakeLabware = labware.Labware(minimalLabwareDef, deck)
ordering = [well for col in minimalLabwareDef['ordering'] for well in col]
assert fakeLabware._ordering == ordering
assert fakeLabware._wells == minimalLabwareDef['wells']
assert fakeLabware._offset == Point(x=10, y=10, z=5)


def test_well_pattern():
deck = Point(0, 0, 0)
fakeLabware = labware.Labware(minimalLabwareDef, deck)
assert fakeLabware._pattern.match('A1')
assert fakeLabware._pattern.match('A10')
assert not fakeLabware._pattern.match('A0')


def test_wells_accessor():
deck = Point(0, 0, 0)
fakeLabware = labware.Labware(minimalLabwareDef, deck)
depth1 = minimalLabwareDef['wells']['A1']['depth']
depth2 = minimalLabwareDef['wells']['A2']['depth']
x = minimalLabwareDef['wells']['A2']['x']
y = minimalLabwareDef['wells']['A2']['y']
offset = fakeLabware._offset
a1 = Point(x=offset[0], y=offset[1], z=offset[2] + depth1)
a2 = Point(x=offset[0] + x, y=offset[1] + y, z=offset[2] + depth2)
assert fakeLabware.wells()[0]._position == a1
assert fakeLabware.wells()[1]._position == a2


def test_wells_index_accessor():
deck = Point(0, 0, 0)
fakeLabware = labware.Labware(minimalLabwareDef, deck)
depth1 = minimalLabwareDef['wells']['A1']['depth']
depth2 = minimalLabwareDef['wells']['A2']['depth']
x = minimalLabwareDef['wells']['A2']['x']
y = minimalLabwareDef['wells']['A2']['y']
offset = fakeLabware._offset
a1 = Point(x=offset[0], y=offset[1], z=offset[2] + depth1)
a2 = Point(x=offset[0] + x, y=offset[1] + y, z=offset[2] + depth2)
assert fakeLabware.wells_by_index()['A1']._position == a1
assert fakeLabware.wells_by_index()['A2']._position == a2


def test_rows_accessor():
deck = Point(0, 0, 0)
fakeLabware = labware.Labware(minimalLabwareDef2, deck)
depth1 = minimalLabwareDef2['wells']['A1']['depth']
x1 = minimalLabwareDef2['wells']['A1']['x']
y1 = minimalLabwareDef2['wells']['A1']['y']
depth2 = minimalLabwareDef2['wells']['B2']['depth']
x2 = minimalLabwareDef2['wells']['B2']['x']
y2 = minimalLabwareDef2['wells']['B2']['y']
offset = fakeLabware._offset
a1 = Point(x=offset[0] + x1, y=offset[1] + y1, z=offset[2] + depth1)
b2 = Point(x=offset[0] + x2, y=offset[1] + y2, z=offset[2] + depth2)
assert fakeLabware.rows()[0][0]._position == a1
assert fakeLabware.rows()[1][1]._position == b2


def test_row_index_accessor():
deck = Point(0, 0, 0)
fakeLabware = labware.Labware(minimalLabwareDef2, deck)
depth1 = minimalLabwareDef2['wells']['A1']['depth']
x1 = minimalLabwareDef2['wells']['A1']['x']
y1 = minimalLabwareDef2['wells']['A1']['y']
depth2 = minimalLabwareDef2['wells']['B2']['depth']
x2 = minimalLabwareDef2['wells']['B2']['x']
y2 = minimalLabwareDef2['wells']['B2']['y']
offset = fakeLabware._offset
a1 = Point(x=offset[0] + x1, y=offset[1] + y1, z=offset[2] + depth1)
b2 = Point(x=offset[0] + x2, y=offset[1] + y2, z=offset[2] + depth2)
assert fakeLabware.rows_by_index()['A'][0]._position == a1
assert fakeLabware.rows_by_index()['B'][1]._position == b2


def test_cols_accessor():
deck = Point(0, 0, 0)
fakeLabware = labware.Labware(minimalLabwareDef, deck)
depth1 = minimalLabwareDef['wells']['A1']['depth']
depth2 = minimalLabwareDef['wells']['A2']['depth']
x = minimalLabwareDef['wells']['A2']['x']
y = minimalLabwareDef['wells']['A2']['y']
offset = fakeLabware._offset
a1 = Point(x=offset[0], y=offset[1], z=offset[2] + depth1)
a2 = Point(x=offset[0] + x, y=offset[1] + y, z=offset[2] + depth2)
assert fakeLabware.columns()[0][0]._position == a1
assert fakeLabware.columns()[1][0]._position == a2


def test_col_index_accessor():
deck = Point(0, 0, 0)
fakeLabware = labware.Labware(minimalLabwareDef, deck)
depth1 = minimalLabwareDef['wells']['A1']['depth']
depth2 = minimalLabwareDef['wells']['A2']['depth']
x = minimalLabwareDef['wells']['A2']['x']
y = minimalLabwareDef['wells']['A2']['y']
offset = fakeLabware._offset
a1 = Point(x=offset[0], y=offset[1], z=offset[2] + depth1)
a2 = Point(x=offset[0] + x, y=offset[1] + y, z=offset[2] + depth2)
assert fakeLabware.columns_by_index()['1'][0]._position == a1
assert fakeLabware.columns_by_index()['2'][0]._position == a2