def __rename_final_plates(self, experiment_metadata): """ Replaces the labels of the final plates by a new label derived from the plate set labels specified by the scientist. For ISO request that cannot contain more than one final plate (not regarding copies) the final plate label is equal to the ISO request label. Otherwise ISO number and aliquot number (in case of several plates per ISO), are added. Assumes the ISOs to be completed and the label of experiment metadata and ISO request to be different. """ if len(self.__final_plates) == 1 and \ experiment_metadata.experiment_metadata_type_id \ in EXPERIMENT_SCENARIOS.ONE_PLATE_TYPES: for final_plates in self.__final_plates.values(): final_plates[0].rack.label = self._iso_request.label else: for iso, final_plates in self.__final_plates.iteritems(): if len(final_plates) == 1: new_label = LABELS.create_final_plate_label(iso) final_plates[0].rack.label = new_label else: for container in final_plates: values = LABELS.parse_rack_marker(container.rack_marker) plate_num = None if values.has_key(LABELS.MARKER_RACK_NUM): plate_num = values[LABELS.MARKER_RACK_NUM] new_label = LABELS.create_final_plate_label(iso, plate_num) container.rack.label = new_label
def __rename_final_plates(self, experiment_metadata): """ Replaces the labels of the final plates by a new label derived from the plate set labels specified by the scientist. For ISO request that cannot contain more than one final plate (not regarding copies) the final plate label is equal to the ISO request label. Otherwise ISO number and aliquot number (in case of several plates per ISO), are added. Assumes the ISOs to be completed and the label of experiment metadata and ISO request to be different. """ if len(self.__final_plates) == 1 and \ experiment_metadata.experiment_metadata_type_id \ in EXPERIMENT_SCENARIOS.ONE_PLATE_TYPES: for final_plates in self.__final_plates.values(): final_plates[0].rack.label = self._iso_request.label else: for iso, final_plates in self.__final_plates.iteritems(): if len(final_plates) == 1: new_label = LABELS.create_final_plate_label(iso) final_plates[0].rack.label = new_label else: for container in final_plates: values = LABELS.parse_rack_marker( container.rack_marker) plate_num = None if values.has_key(LABELS.MARKER_RACK_NUM): plate_num = values[LABELS.MARKER_RACK_NUM] new_label = LABELS.create_final_plate_label( iso, plate_num) container.rack.label = new_label
def _create_stock_rack_layouts(self): """ Creates stock rack layout for each required rack (potential rack sector constraints are expected to be confirmed). The rack markers might not match the layout rack markers anymore but this does not matter, since further processing do not use the layout rack markers anymore. """ marker_map = dict() for marker, rack_barcode in self._stock_rack_map.iteritems(): marker_map[rack_barcode] = marker layouts = dict() used_rack_markers = set() for pool, container in self._stock_tube_containers.iteritems(): tc = container.tube_candidate rack_barcode = tc.rack_barcode if marker_map.has_key(rack_barcode): rack_marker = marker_map[rack_barcode] if layouts.has_key(rack_barcode): layout = layouts[rack_barcode] else: layout = StockRackLayout() layouts[rack_barcode] = layout else: rack_marker = container.stock_rack_marker if rack_marker in used_rack_markers: nums = [] for marker in used_rack_markers: value_parts = LABELS.parse_rack_marker(marker) nums.append(value_parts[LABELS.MARKER_RACK_NUM]) new_num = max(nums) + 1 rack_marker = LABELS.create_rack_marker(LABELS.ROLE_STOCK, new_num) marker_map[rack_barcode] = rack_marker layout = StockRackLayout() layouts[rack_barcode] = layout tts = [] for plate_label, positions in container.plate_target_positions.\ iteritems(): if plate_label == LABELS.ROLE_FINAL: trg_marker = plate_label else: trg_marker = self._rack_containers[plate_label].rack_marker for plate_pos in positions: tts.append(plate_pos.as_transfer_target(trg_marker)) sr_pos = StockRackPosition(rack_position=tc.rack_position, molecule_design_pool=pool, tube_barcode=tc.tube_barcode, transfer_targets=tts) layout.add_position(sr_pos) for rack_barcode, marker in marker_map.iteritems(): self._stock_rack_layouts[marker] = layouts[rack_barcode] if not self._stock_rack_map.has_key(marker): self._stock_rack_map[marker] = rack_barcode
def __create_worklist_label(self, target_plate_marker, source_plate_marker=None): worklist_number = self.__get_current_worklist_number(True) return LABELS.create_worklist_label(ticket_number=self.__ticket_number, worklist_number=worklist_number, target_rack_marker=target_plate_marker, source_rack_marker=source_plate_marker)
def _store_final_plate_data(self, iso): """ Helper function creating and storing a rack container of all final plates of an ISO. The rack markers for aliquot and library plates need to be distinguished. """ # FIXME: Hack around not having proper "final" plates attached to # the ISO. library_plates = set([lp.rack for lp in iso.library_plates]) for fp in iso.iso_aliquot_plates: self.__final_plate_count += 1 if not fp.rack in library_plates: self._store_rack_container(fp.rack, role=LABELS.ROLE_FINAL) else: rack_marker = LABELS.ROLE_FINAL counter = 0 one_aliquot = (self._iso_request.number_aliquots == 1) for fp in iso.library_plates: counter += 1 if one_aliquot: rack_marker = LABELS.ROLE_FINAL else: rack_marker = LABELS.create_rack_marker( LABELS.ROLE_FINAL, counter) self._store_rack_container(rack=fp.rack, role=LABELS.ROLE_FINAL, rack_marker=rack_marker)
def __get_worklist_rack_markers(self, worklist): """ Parses the worklist label (see :func:`LABELS.parse_worklist_label`). Returns *None* if the worklist is not accepted. This can be the case for worklists with unknown rack markers (in this case there are preparation plates involved that do not belong to the entity) or if the worklist might be part of both entity processings. The second is the case for some final plate worklists. """ values = LABELS.parse_worklist_label(worklist.label) trg_marker = values[LABELS.MARKER_WORKLIST_TARGET] if not self.__rack_containers.has_key(trg_marker): return None src_marker = None if values.has_key(LABELS.MARKER_WORKLIST_SOURCE): src_marker = values[LABELS.MARKER_WORKLIST_SOURCE] if not self.__rack_containers.has_key(src_marker): return None if worklist.transfer_type == TRANSFER_TYPES.SAMPLE_DILUTION: if trg_marker == LABELS.ROLE_FINAL and \ self._expected_iso_status == ISO_STATUS.IN_PROGRESS: for rack_container in self.__rack_containers[trg_marker]: if rack_container.rack.status.name == ITEM_STATUSES.MANAGED: return None return trg_marker, src_marker
def _store_final_plate_data(self, iso): """ Helper function creating and storing a rack container of all final plates of an ISO. The rack markers for aliquot and library plates need to be distinguished. """ # FIXME: Hack around not having proper "final" plates attached to # the ISO. library_plates = set([lp.rack for lp in iso.library_plates]) for fp in iso.iso_aliquot_plates: self.__final_plate_count += 1 if not fp.rack in library_plates: self._store_rack_container(fp.rack, role=LABELS.ROLE_FINAL) else: rack_marker = LABELS.ROLE_FINAL counter = 0 one_aliquot = (self._iso_request.number_aliquots == 1) for fp in iso.library_plates: counter += 1 if one_aliquot: rack_marker = LABELS.ROLE_FINAL else: rack_marker = LABELS.create_rack_marker(LABELS.ROLE_FINAL, counter) self._store_rack_container(rack=fp.rack, role=LABELS.ROLE_FINAL, rack_marker=rack_marker)
def __create_instructions_file(self): """ Creates the instruction file for the :attr:`entity` (printer :attr:`mode` only). """ rack_containers = [] for containers in self.__rack_containers.values(): rack_containers.extend(containers) for sr_label, stock_rack in self._stock_racks.iteritems(): values = LABELS.parse_rack_label(sr_label) rack_marker = values[LABELS.MARKER_RACK_MARKER] container = IsoRackContainer(rack=stock_rack.rack, rack_marker=rack_marker, label=sr_label, role=LABELS.ROLE_STOCK) rack_containers.append(container) kw = dict(entity=self.entity, iso_request=self._iso_request, rack_containers=rack_containers, parent=self) writer = create_instructions_writer(**kw) instructions_stream = writer.get_result() if instructions_stream is None: msg = 'Error when trying to generate instructions file.' self.add_error(msg) return None return instructions_stream
def __create_stock_rack_worklist_series(self): # The transfer for each worklist series are derived from the stock # rack layouts. ticket_number = self._iso_request.experiment_metadata.ticket_number robot_specs = self._get_stock_transfer_pipetting_specs() for sr_marker in sorted(self._stock_rack_layouts.keys()): sr_layout = self._stock_rack_layouts[sr_marker] worklist_series = WorklistSeries() for rack_marker in self.__get_sorted_plate_markers(): transfers = [] for sr_pos in sr_layout.working_positions(): psts = sr_pos.get_planned_sample_transfers(rack_marker) transfers.extend(psts) if len(transfers) < 1: continue wl_index = len(worklist_series) wl_label = LABELS.create_worklist_label( ticket_number, worklist_number=(wl_index + 1), target_rack_marker=rack_marker, source_rack_marker=sr_marker) worklist = PlannedWorklist( label=wl_label, transfer_type=TRANSFER_TYPES.SAMPLE_TRANSFER, planned_liquid_transfers=transfers, pipetting_specs=robot_specs) worklist_series.add_worklist(wl_index, worklist) self.__stock_transfer_series[sr_marker] = worklist_series
def __create_stock_rack_worklist_series(self): # The transfer for each worklist series are derived from the stock # rack layouts. ticket_number = self._iso_request.experiment_metadata.ticket_number robot_specs = self._get_stock_transfer_pipetting_specs() for sr_marker in sorted(self._stock_rack_layouts.keys()): sr_layout = self._stock_rack_layouts[sr_marker] worklist_series = WorklistSeries() for rack_marker in self.__get_sorted_plate_markers(): transfers = [] for sr_pos in sr_layout.working_positions(): psts = sr_pos.get_planned_sample_transfers(rack_marker) transfers.extend(psts) if len(transfers) < 1: continue wl_index = len(worklist_series) wl_label = LABELS.create_worklist_label(ticket_number, worklist_number=(wl_index + 1), target_rack_marker=rack_marker, source_rack_marker=sr_marker) worklist = PlannedWorklist(label=wl_label, transfer_type=TRANSFER_TYPES.SAMPLE_TRANSFER, planned_liquid_transfers=transfers, pipetting_specs=robot_specs) worklist_series.add_worklist(wl_index, worklist) self.__stock_transfer_series[sr_marker] = worklist_series
def __create_worklist_label(self, target_plate_marker, source_plate_marker=None): worklist_number = self.__get_current_worklist_number(True) return LABELS.create_worklist_label( ticket_number=self.__ticket_number, worklist_number=worklist_number, target_rack_marker=target_plate_marker, source_rack_marker=source_plate_marker)
def __get_stock_rack_base_kw(self, stock_rack_marker, rack_barcode): # Helper function returning the keyword dictionary for a # :class:`StockRack` entity. It contains values for all shared # keywords. tube_rack = self._barcode_map[rack_barcode] rack_layout = self._stock_rack_layouts[stock_rack_marker].\ create_rack_layout() label = LABELS.create_rack_label(stock_rack_marker, self.entity.label) return dict(rack=tube_rack, rack_layout=rack_layout, label=label, worklist_series=self.__stock_transfer_series[stock_rack_marker])
def _store_rack_container(self, rack, role, label=None, rack_marker=None): """ Helper function creating a :class:`IsoRackContainer` for a rack. The containers are store in the :attr:`_rack_containers` map. """ if label is None: label = rack.label if rack_marker is None: values = LABELS.parse_rack_label(label) rack_marker = values[LABELS.MARKER_RACK_MARKER] rack_container = IsoRackContainer(rack=rack, label=label, role=role, rack_marker=rack_marker) self._rack_containers[label] = rack_container
def __get_stock_rack_base_kw(self, stock_rack_marker, rack_barcode): # Helper function returning the keyword dictionary for a # :class:`StockRack` entity. It contains values for all shared # keywords. tube_rack = self._barcode_map[rack_barcode] rack_layout = self._stock_rack_layouts[stock_rack_marker].\ create_rack_layout() label = LABELS.create_rack_label(stock_rack_marker, self.entity.label) return dict( rack=tube_rack, rack_layout=rack_layout, label=label, worklist_series=self.__stock_transfer_series[stock_rack_marker])
def __create_stock_transfer_jobs(self, stock_rack): """ Convenience method creating a :class:`SampleTransferJob` for a transfer from stock rack to a plate. """ stock_worklists = stock_rack.worklist_series.get_sorted_worklists() for worklist in stock_worklists: values = LABELS.parse_worklist_label(worklist.label) src_marker = values[LABELS.MARKER_WORKLIST_SOURCE] if not src_marker in stock_rack.label: msg = 'Inconsistent stock rack for worklist "%s" ' \ '(stock rack: %s).' % (worklist.label, stock_rack.label) self.add_error(msg) trg_marker = values[LABELS.MARKER_WORKLIST_TARGET] for rack_container in self.__rack_containers[trg_marker]: self.__create_and_store_transfer_job(worklist=worklist, target_plate=rack_container.rack, source_rack=stock_rack.rack)
def set_iso_count(self, iso_count): """ In order to avoid duplicate transfers ISO preparation plate for job are only recorded once regardless of the number of ISOs in the job. When calculating the volumes the take out volume of the remaining positions are multiplied by the ISO count). """ self.__iso_count = iso_count for plate_label in sorted(self.__prep_positions.keys()): try: value_parts = LABELS.parse_rack_label(plate_label) except ValueError: # the label is an ISO preparation plate marker role = LABELS.ROLE_PREPARATION_ISO else: role = value_parts[LABELS.MARKER_RACK_ROLE] if role == LABELS.ROLE_PREPARATION_JOB: self.__job_plate_labels.add(plate_label)
def __compare_iso_preparation_layouts(self): # If we have starting wells for the job in ISO preparation plates and # more than one ISO in the job, we need to remove duplicates (to avoid # generation of duplicate transfers in stock worklists). # We store the layout of a reference plate (using the label of this # plate as key). Plate containers are stored for all plates. position_map = dict() reference_plates = dict() reference_layouts = dict() for iso in self.entity.isos: if self.has_errors(): break for ipp in iso.iso_preparation_plates: plate = ipp.rack plate_label = plate.label value_parts = LABELS.parse_rack_label(plate_label) rack_marker = value_parts[LABELS.MARKER_RACK_MARKER] self._store_rack_container(rack=plate, label=plate_label, rack_marker=rack_marker, role=LABELS.ROLE_PREPARATION_ISO) converter = LabIsoPrepLayoutConverter( rack_layout=ipp.rack_layout, parent=self) layout = converter.get_result() if layout is None: msg = 'Error when trying to convert ISO preparation ' \ 'plate layout for plate "%s".' % (plate_label) self.add_error(msg) break positions = [] for prep_pos in layout.get_sorted_working_positions(): if prep_pos.is_fixed and prep_pos.is_starting_well: positions.append(prep_pos) if not position_map.has_key(rack_marker): position_map[rack_marker] = positions reference_plates[rack_marker] = plate reference_layouts[rack_marker] = layout elif not position_map[rack_marker] == positions: msg = 'The ISO preparation plates for rack type "%s" are ' \ 'inconsistent!' % (rack_marker) self.add_error(msg) break for rack_marker, plate in reference_plates.iteritems(): layout = reference_layouts[rack_marker] self._plate_layouts[plate.label] = layout
def __create_stock_transfer_jobs(self, stock_rack): """ Convenience method creating a :class:`SampleTransferJob` for a transfer from stock rack to a plate. """ stock_worklists = stock_rack.worklist_series.get_sorted_worklists() for worklist in stock_worklists: values = LABELS.parse_worklist_label(worklist.label) src_marker = values[LABELS.MARKER_WORKLIST_SOURCE] if not src_marker in stock_rack.label: msg = 'Inconsistent stock rack for worklist "%s" ' \ '(stock rack: %s).' % (worklist.label, stock_rack.label) self.add_error(msg) trg_marker = values[LABELS.MARKER_WORKLIST_TARGET] for rack_container in self.__rack_containers[trg_marker]: self.__create_and_store_transfer_job( worklist=worklist, target_plate=rack_container.rack, source_rack=stock_rack.rack)
def _store_and_verify_plate(self, iso_plate, layout=None, verify=True): """ Convenience method verifying and storing the plate as rack container in the :attr:`_rack_containers` map (mapped onto the rack marker). If the expected ISO status is queued all plates must be empty, otherwise we need a :class:`LabIsoPlateVerifier`. """ plate = iso_plate.rack rack_name = '%s (%s)' % (plate.barcode, plate.label) if self._expected_iso_status == ISO_STATUS.QUEUED and verify: sample_positions = [] for well in plate.containers: if well.sample is not None: if layout is not None: ip = layout.get_working_position(well.position) if ip is not None and ip.is_library: continue sample_positions.append(well.position.label) if len(sample_positions) > 0: msg = 'Plate %s should be empty but there are samples in ' \ 'the following positions: %s.' \ % (plate.label, self._get_joined_str(sample_positions)) self.add_error(msg) return None if not isinstance(iso_plate, (IsoAliquotPlate, LibraryPlate)): converter = LabIsoPrepLayoutConverter(iso_plate.rack_layout, parent=self) layout = converter.get_result() if layout is None: msg = 'Error when trying to convert layout of plate "%s"!' \ % (plate.label) self.add_error(msg) return None else: self.__plate_layouts[plate.label] = layout elif verify: verifier = LabIsoPlateVerifier(iso_plate, self.ENTITY_CLS == IsoJob, lab_iso_layout=layout, parent=self) compatible = verifier.get_result() if compatible is None: msg = 'Error when trying to verify plate %s!' % (rack_name) self.add_error(msg) return None elif not compatible: msg = 'Rack %s does not match the expected layout!' \ % (rack_name) self.add_error(msg) return None elif layout is None: self.__plate_layouts[plate.label] = \ verifier.get_expected_layout() iso = None if self._processing_order == LAB_ISO_ORDERS.NO_ISO and \ isinstance(iso_plate, LibraryPlate): rack_marker = LABELS.ROLE_FINAL iso = iso_plate.lab_iso else: values = LABELS.parse_rack_label(plate.label) rack_marker = values[LABELS.MARKER_RACK_MARKER] rack_container = IsoRackContainer(rack=plate, rack_marker=rack_marker) if rack_container.role == LABELS.ROLE_FINAL: rack_marker = LABELS.ROLE_FINAL if iso is None: iso = iso_plate.iso add_list_map_element(self.__final_plates, iso, rack_container) add_list_map_element(self.__rack_containers, rack_marker, rack_container)
def _get_job_label(self): job_num = LABELS.get_new_job_number(iso_request=self.iso_request) ticket_number = self.iso_request.experiment_metadata.ticket_number return LABELS.create_job_label(ticket_number=ticket_number, job_number=job_num)