def humanize_address(self, address): """ Returns a human-readable string for a particular address. If ((0, 0), (1, 0)) is passed and no labels are attached to A1, this will return 'A1:B1'. For ('label', (1, 0)), it will return the valid label with the first provided capitalization, for example "LaBeL:B1". """ start, end = address try: start = normalize_position(start) # Try to convert 'A1'. # Find a label for that tuple position. label = self.get_container_label(start) if label is not None: start = label except ValueError: # If it's not a tuple position, it's a string label. if start.lower() not in self._container_labels: raise x.ContainerMissing( "Invalid container: {}".format(start) ) start = self._label_case.get(start.lower(), start) end = humanize_position(end) return "{}:{}".format(start, end)
def get_coordinates(self, position, axis=None, tool=None): """ Returns the calibrated coordinates for a position. """ if len(position) == 1: position = [position[0], (0, 0)] if tool is not None: axis = tool.axis cal = self.get_axis_calibration(axis) slot, well = position if slot not in cal: raise ex.CalibrationMissing( "No calibration for {} (axis {}).".format( humanize_position(slot), axis)) defaults = ({'top': 0, 'bottom': 0, 'x': 0, 'y': 0}) output = {} # Calibration for A1 in this container (x, y, top, bottom). slot_cal = {} slot_cal.update(defaults) slot_cal.update(cal.get((slot), {})) # Default offset on x, y calculated from container definition. ox, oy = self._deck.slot(slot).get_child(well).coordinates() # x, y, top bottom well_cal = cal.get((slot, well), {}) output.update(well_cal) # Use calculated offsets if no custom well calibration provided. if 'x' not in output: output['x'] = slot_cal['x'] + ox if 'y' not in output: output['y'] = slot_cal['y'] + oy # Merge slot and well calibration if 'top' not in output: output['top'] = slot_cal['top'] if 'bottom' not in output: output['bottom'] = slot_cal['bottom'] return output
def find_well(self, value): """ Returns the well position on this plate matching a particular value. """ for pos in sorted(self.map.keys()): if self.map[pos].strip() == value: return humanize_position(pos)
def get_coordinates(self, position, axis=None, tool=None): """ Returns the calibrated coordinates for a position. """ if len(position) == 1: position = [position[0], (0, 0)] if tool is not None: axis = tool.axis cal = self.get_axis_calibration(axis) slot, well = position if slot not in cal: raise ex.CalibrationMissing( "No calibration for {} (axis {}).". format(humanize_position(slot), axis) ) defaults = ({'top': 0, 'bottom': 0, 'x': 0, 'y': 0}) output = {} # Calibration for A1 in this container (x, y, top, bottom). slot_cal = {} slot_cal.update(defaults) slot_cal.update(cal.get((slot), {})) # Default offset on x, y calculated from container definition. ox, oy = self._deck.slot(slot).get_child(well).coordinates() # x, y, top bottom well_cal = cal.get((slot, well), {}) output.update(well_cal) # Use calculated offsets if no custom well calibration provided. if 'x' not in output: output['x'] = slot_cal['x'] + ox if 'y' not in output: output['y'] = slot_cal['y'] + oy # Merge slot and well calibration if 'top' not in output: output['top'] = slot_cal['top'] if 'bottom' not in output: output['bottom'] = slot_cal['bottom'] return output
def slot(self, position): pos = self._normalize_position(position) if pos not in self._children: raise x.ContainerMissing( "No deck module at slot {}." .format(humanize_position(pos)) ) return self._children[pos]
def slot(self, position): pos = self._normalize_position(position) if pos not in self._children: raise KeyError( "No deck module at slot {}/{}." .format(humanize_position(pos), pos) ) return self._children[pos]
def add_module(self, position, mod): pos = self._normalize_position(position) if isinstance(mod, str): mod = load_container(mod)() if pos not in self._children: self._children[pos] = mod mod.position = position else: raise x.ContainerConflict( "Module already allocated to slot: {}.".format( humanize_position(pos)))
def add_module(self, position, mod): pos = self._normalize_position(position) if isinstance(mod, str): mod = load_container(mod)() if pos not in self._children: self._children[pos] = mod mod.position = position else: raise x.ContainerConflict( "Module already allocated to slot: {}." .format(humanize_position(pos)) )
def map(self): """ Uses the coordinates of label cell to list plate contents by well. """ plate_map = {} for col in range(0, self._cols): for row in range(0, self._rows): pos = (col, row) loc = humanize_position(pos) data = self.get_well(pos) if data: plate_map[loc] = data return plate_map
def apply_protocol(self, b): """ Applies all the operational data from another Protocol to this one. """ # Second info supercedes first. self.set_info(**b.info) # Add the containers from second. for slot, name in b._containers.items(): if slot in self._containers and self._containers[slot] != name: raise x.ContainerConflict( "Slot {} already allocated to {}".format(slot, name) ) if slot not in self._containers: # Add container if it's not there already. self.add_container(slot, name) # Add the labels from second. labels = {v: k for k, v in self._container_labels.items()} for label, slot in b._container_labels.items(): if slot in labels and labels[slot] != label: raise x.ContainerConflict( "Conflicting labels at {}: {} vs {}" .format( humanize_position(slot), labels[slot], label ) ) self._container_labels[label] = slot # Supercede labelcase from second. for label, case in b._label_case.items(): self._label_case[label] = case # Add the instruments from second. for axis, name in b._head.items(): if axis in self._head \ and self._head[axis] != name: raise x.InstrumentConflict( "Axis {} already allocated to {}".format(axis, name) ) self.add_instrument(axis, name) # Rerun command definitions from second. for command in b.actions: c = copy.deepcopy(command) # Make sure this command runs properly. self.add_command(c.pop('command'), **c) self.commands.append(command)
def export(self): info = OrderedDict() i = self._protocol.info order = ['name', 'author', 'description', 'version', 'version_hash', 'created', 'updated'] for key in order: v = i.pop(key, None) if v is not None: info[key] = v instruments = OrderedDict() for axis, name in sorted(self._protocol.instruments.items()): label = "{}_{}".format(name, axis.lower()) instruments[label] = OrderedDict([ ('axis', axis), ('name', name) ]) modules = [] for slot, name in sorted(self._protocol._containers.items()): c = OrderedDict([('name', name)]) label = self._protocol.get_container_label(slot) if label: c['label'] = label if slot: c['slot'] = humanize_position(slot) modules.append(c) instructions = [] for command in self._protocol.commands: command = self._export_command(command) instructions.append(command) out = OrderedDict() out['info'] = info out['instruments'] = instruments out['containers'] = modules out['instructions'] = instructions return json.dumps(out, indent=4)
def test_well_offsets(self): """ Well offsets. """ # We have a plate map listing offset order. cdir = os.path.dirname(os.path.realpath(__file__)) self.csv_file = os.path.join( cdir, '../../fixtures/offset_map.csv' ) self.plate_map = PlateMap(self.csv_file) plate = self.plate_map.get_plate('A1', rotated=True) # Go through every row, col in a standard 96 well plate # and check against the documented offset order. n = 0 for col in range(0, 8): for row in range(0, 12): c = humanize_position((col, row)) self.assertEqual(int(plate.get_well(c)), n) n += 1
def apply_protocol(self, b): """ Applies all the operational data from another Protocol to this one. """ # Second info supercedes first. self.set_info(**b.info) # Add the containers from second. for slot, name in b._containers.items(): if slot in self._containers and self._containers[slot] != name: raise x.ContainerConflict( "Slot {} already allocated to {}".format(slot, name)) if slot not in self._containers: # Add container if it's not there already. self.add_container(slot, name) # Add the labels from second. labels = {v: k for k, v in self._container_labels.items()} for label, slot in b._container_labels.items(): if slot in labels and labels[slot] != label: raise x.ContainerConflict( "Conflicting labels at {}: {} vs {}".format( humanize_position(slot), labels[slot], label)) self._container_labels[label] = slot # Supercede labelcase from second. for label, case in b._label_case.items(): self._label_case[label] = case # Add the instruments from second. for axis, name in b._head.items(): if axis in self._head \ and self._head[axis] != name: raise x.InstrumentConflict( "Axis {} already allocated to {}".format(axis, name)) self.add_instrument(axis, name) # Rerun command definitions from second. for command in b.actions: c = copy.deepcopy(command) # Make sure this command runs properly. self.add_command(c.pop('command'), **c) self.commands.append(command)
def export(self): info = OrderedDict() i = self._protocol.info order = [ 'name', 'author', 'description', 'version', 'version_hash', 'created', 'updated' ] for key in order: v = i.pop(key, None) if v is not None: info[key] = v instruments = OrderedDict() for axis, name in sorted(self._protocol.instruments.items()): label = "{}_{}".format(name, axis.lower()) instruments[label] = OrderedDict([('axis', axis), ('name', name)]) modules = [] for slot, name in sorted(self._protocol._containers.items()): c = OrderedDict([('name', name)]) label = self._protocol.get_container_label(slot) if label: c['label'] = label if slot: c['slot'] = humanize_position(slot) modules.append(c) instructions = [] for command in self._protocol.commands: command = self._export_command(command) instructions.append(command) out = OrderedDict() out['info'] = info out['instruments'] = instruments out['containers'] = modules out['instructions'] = instructions return json.dumps(out, indent=4)
def humanize_address(self, address): """ Returns a human-readable string for a particular address. If ((0, 0), (1, 0)) is passed and no labels are attached to A1, this will return 'A1:B1'. For ('label', (1, 0)), it will return the valid label with the first provided capitalization, for example "LaBeL:B1". """ start, end = address try: start = normalize_position(start) # Try to convert 'A1'. # Find a label for that tuple position. label = self.get_container_label(start) if label is not None: start = label except ValueError: # If it's not a tuple position, it's a string label. if start.lower() not in self._container_labels: raise x.ContainerMissing("Invalid container: {}".format(start)) start = self._label_case.get(start.lower(), start) end = humanize_position(end) return "{}:{}".format(start, end)
def slot(self, position): pos = self._normalize_position(position) if pos not in self._children: raise x.ContainerMissing("No deck module at slot {}.".format( humanize_position(pos))) return self._children[pos]