def _create_planned_liquid_transfers(self): """ Generates the planned container dilutions for the worklist. """ self.add_debug('Generate planned container dilutions ...') invalid_dil_factor = dict() for rack_pos, tf_pos in self.transfection_layout.iterpositions(): if tf_pos.is_empty: continue dil_volume = tf_pos.calculate_reagent_dilution_volume() \ / VOLUME_CONVERSION_FACTOR ini_dil_factor = TransfectionParameters.\ calculate_initial_reagent_dilution( float(tf_pos.reagent_dil_factor)) if ini_dil_factor <= 1: add_list_map_element(invalid_dil_factor, tf_pos.reagent_dil_factor, rack_pos.label) continue rdf_str = get_trimmed_string(tf_pos.reagent_dil_factor) diluent_info = '%s (%s)' % (tf_pos.reagent_name, rdf_str) psd = PlannedSampleDilution.get_entity(volume=dil_volume, target_position=rack_pos, diluent_info=diluent_info) self._add_planned_transfer(psd) if len(invalid_dil_factor) > 0: msg = 'Invalid dilution reagent factor for rack positions: %s. ' \ 'The factor would result in an initial dilution factor of ' \ 'less then 1!' % (self._get_joined_map_str(invalid_dil_factor)) self.add_error(msg)
def add_preparation_position(self, plate_label, plate_pos): """ Registers a target preparation position for this stock tube (used later to generated planned worklists). """ add_list_map_element(self.__prep_positions, plate_label, plate_pos) self.__set_stock_rack_marker(plate_pos)
def __find_new_tubes(self): """ Finds tubes for pool that do not have a tube candidate yet. """ self.add_debug('Find tubes for missing pools ...') for pool_id in self.__replaced_tube_containers: pool = self.__pool_map[pool_id] container = self.stock_tube_containers[pool] required_volume = self.__volume_map[pool_id] query = SinglePoolQuery( pool_id=pool_id, concentration=container.get_stock_concentration(), minimum_volume=required_volume) self._run_query(query, None) candidates = query.get_query_results() while len(candidates) > 0: candidate = candidates.pop(0) tube_barcode = candidate.tube_barcode if candidate.rack_barcode in self.excluded_racks: add_list_map_element(self.__excluded_tubes, pool_id, tube_barcode, as_set=True) else: container.tube_candidate = candidate candidate.set_pool(container.pool) break if container.tube_candidate is None: self.__missing_pools.append(container.pool)
def __accept_tube_candidate(self, tube_candidate, conc_mismatch_list, insufficent_vol_map): """ A valid tube container must have a matching concentration, contain sufficent volume and must be excluded via the rack barcode. Accepted candidates are assigned to the referring containers. """ pool_id = tube_candidate.pool_id pool = self.__pool_map[pool_id] container = self.stock_tube_containers[pool] if not self.__stock_concentration_match(tube_candidate, container, conc_mismatch_list): return False required_vol = self.__volume_map[pool_id] + STOCK_DEAD_VOLUME if is_smaller_than(tube_candidate.volume, required_vol): params = [tube_candidate.tube_barcode, get_trimmed_string(required_vol), get_trimmed_string(tube_candidate.volume)] insufficent_vol_map[pool_id] = params return False if tube_candidate.rack_barcode in self.excluded_racks: add_list_map_element(self.__excluded_tubes, pool_id, tube_candidate.tube_barcode, as_set=True) return False else: tube_candidate.set_pool(container.pool) container.tube_candidate = tube_candidate return True
def __init_step_transfer_codes(self): """ Initialises the transfer codes for a particular step. """ self._current_row = self.__current_step.starting_row + 2 codes = [] while not self._end_reached: code = self._get_cell_value(self._current_row, self._CODE_COLUMN_INDEX) if code is None: break codes.append(code) self._step_to_next_row() if len(codes) < 1: msg = 'There are no codes for step %i!' \ % (self.__current_step.number) cell_name = self._get_cell_name( self.__current_step.starting_row + 2, 1) self._create_error(msg, cell_name) # there must be at least one codes, otherwise we would have got an # error when looking for the tag definition for code in codes: self.__current_step.add_transfer_code(code) transfer_tag_def = self.__current_step.transfer_tag_definition self._tags.add(transfer_tag_def) add_list_map_element(self._tags_by_row, self.__current_step.get_transfer_tag_row_index(), transfer_tag_def)
def __accept_tube_candidate(self, tube_candidate, conc_mismatch_list, insufficent_vol_map): """ A valid tube container must have a matching concentration, contain sufficent volume and must be excluded via the rack barcode. Accepted candidates are assigned to the referring containers. """ pool_id = tube_candidate.pool_id pool = self.__pool_map[pool_id] container = self.stock_tube_containers[pool] if not self.__stock_concentration_match(tube_candidate, container, conc_mismatch_list): return False required_vol = self.__volume_map[pool_id] + STOCK_DEAD_VOLUME if is_smaller_than(tube_candidate.volume, required_vol): params = [ tube_candidate.tube_barcode, get_trimmed_string(required_vol), get_trimmed_string(tube_candidate.volume) ] insufficent_vol_map[pool_id] = params return False if tube_candidate.rack_barcode in self.excluded_racks: add_list_map_element(self.__excluded_tubes, pool_id, tube_candidate.tube_barcode, as_set=True) return False else: tube_candidate.set_pool(container.pool) container.tube_candidate = tube_candidate return True
def __get_sorted_executed_transfers(self, executed_transfers, target_rack_barcode): # Sorts the executed transfer of a worklist by pool and source tube # barcode. pool_map = dict() no_pools = set() for elt in executed_transfers: rack_pos = elt.target_container.location.position ssc_pos = self.stock_sample_creation_layout.get_working_position( rack_pos) if ssc_pos is None: info = '%s (rack %s)' % (rack_pos.label, target_rack_barcode) no_pools.add(info) continue pool = ssc_pos.pool add_list_map_element(pool_map, pool, elt) if len(no_pools) > 0: msg = 'Could not find molecule design pools for the following ' \ 'target positions: %s.' % (self._get_joined_str(no_pools)) self.add_error(msg) for pool, elts in pool_map.iteritems(): elts.sort(cmp=lambda elt1, elt2: cmp( elt1.source_container.barcode, elt2.source_container.barcode)) return pool_map
def _create_planned_liquid_transfers(self): """ Generates the planned container dilutions for the worklist. """ self.add_debug('Generate planned container dilutions ...') invalid_dil_factor = dict() for rack_pos, tf_pos in self.transfection_layout.iterpositions(): if tf_pos.is_empty: continue dil_volume = tf_pos.calculate_reagent_dilution_volume() \ / VOLUME_CONVERSION_FACTOR ini_dil_factor = TransfectionParameters.\ calculate_initial_reagent_dilution( float(tf_pos.reagent_dil_factor)) if ini_dil_factor <= 1: add_list_map_element(invalid_dil_factor, tf_pos.reagent_dil_factor, rack_pos.label) continue rdf_str = get_trimmed_string(tf_pos.reagent_dil_factor) diluent_info = '%s (%s)' % (tf_pos.reagent_name, rdf_str) psd = PlannedSampleDilution.get_entity(volume=dil_volume, target_position=rack_pos, diluent_info=diluent_info) self._add_planned_transfer(psd) if len(invalid_dil_factor) > 0: msg = 'Invalid dilution reagent factor for rack positions: %s. ' \ 'The factor would result in an initial dilution factor of ' \ 'less then 1!' % (self._get_joined_map_str(invalid_dil_factor)) self.add_error(msg)
def _store_result(self, result_record): """ Results are converted into :class:`CANDIDATE_CLS` objects before storage. The """ candidate = self._create_candidate_from_query_result(result_record) add_list_map_element(self._results, candidate.pool_id, candidate)
def __get_sorted_executed_transfers(self, executed_liquid_transfers, stock_rack_layout, rack_barcode): # Sorts executed transfer of a worklist by molecule design pool ID. pool_map = dict() no_pools = set() for elt in executed_liquid_transfers: rack_pos = elt.planned_liquid_transfer.source_position sr_pos = stock_rack_layout.get_working_position(rack_pos) if sr_pos is None: no_pools.add(rack_pos.label) continue pool_id = sr_pos.molecule_design_pool.id add_list_map_element(pool_map, pool_id, elt) if len(no_pools) > 0: msg = "Could not find molecule design pools for the following " "source positions in rack %s: %s." % ( rack_barcode, self._get_joined_str(no_pools, is_strs=False), ) self.add_error(msg) else: for elts in pool_map.values(): elts.sort( cmp=lambda elt1, elt2: cmp( elt1.planned_liquid_transfer.target_position, elt2.planned_liquid_transfer.target_position ) ) elts.sort( cmp=lambda elt1, elt2: cmp(elt1.target_container.rack.barcode, elt2.target_container.rack.barcode) ) return pool_map
def __get_sorted_executed_transfers(self, executed_liquid_transfers, stock_rack_layout, rack_barcode): # Sorts executed transfer of a worklist by molecule design pool ID. pool_map = dict() no_pools = set() for elt in executed_liquid_transfers: rack_pos = elt.planned_liquid_transfer.source_position sr_pos = stock_rack_layout.get_working_position(rack_pos) if sr_pos is None: no_pools.add(rack_pos.label) continue pool_id = sr_pos.molecule_design_pool.id add_list_map_element(pool_map, pool_id, elt) if len(no_pools) > 0: msg = 'Could not find molecule design pools for the following ' \ 'source positions in rack %s: %s.' % (rack_barcode, self._get_joined_str(no_pools, is_strs=False)) self.add_error(msg) else: for elts in pool_map.values(): elts.sort(cmp=lambda elt1, elt2: cmp( elt1.planned_liquid_transfer.target_position, elt2. planned_liquid_transfer.target_position)) elts.sort(cmp=lambda elt1, elt2: cmp( elt1.target_container.rack.barcode, elt2.target_container. rack.barcode)) return pool_map
def _store_candidate_data(self, candidate): """ Stores the candidate in the :attr:`_picked_candidates` map. Subclasses may filter candidates here, too. """ pool = self._pool_map[candidate.pool_id] add_list_map_element(self._picked_candidates, pool, candidate)
def __find_new_tubes(self): """ Finds tubes for pool that do not have a tube candidate yet. """ self.add_debug('Find tubes for missing pools ...') for pool_id in self.__replaced_tube_containers: pool = self.__pool_map[pool_id] container = self.stock_tube_containers[pool] required_volume = self.__volume_map[pool_id] query = SinglePoolQuery(pool_id=pool_id, concentration=container.get_stock_concentration(), minimum_volume=required_volume) self._run_query(query, None) candidates = query.get_query_results() while len(candidates) > 0: candidate = candidates.pop(0) tube_barcode = candidate.tube_barcode if candidate.rack_barcode in self.excluded_racks: add_list_map_element(self.__excluded_tubes, pool_id, tube_barcode, as_set=True) else: container.tube_candidate = candidate candidate.set_pool(container.pool) break if container.tube_candidate is None: self.__missing_pools.append(container.pool)
def __find_relationships(self, layout, tool, record_errors): # Sets up the association data (if there is more than one # concentration). # :raises ValueError: If the association fails. associator = self._init_associator(layout, tool) if not record_errors: associator.disable_error_and_warning_recording() self._associated_sectors = associator.get_result() if self._associated_sectors is None: if record_errors: msg = '-- '.join(associator.get_messages()) else: msg = 'Error when trying to find rack sector association.' raise ValueError(msg) self._sector_concentrations = associator.get_sector_concentrations() del_sectors = [] for sector_index, conc in self._sector_concentrations.iteritems(): if conc is None: del_sectors.append(sector_index) for sector_index in del_sectors: del self._sector_concentrations[sector_index] for sectors in self._associated_sectors: # concentrations for associated sectors concentrations_map = dict() for sector_index in sectors: conc = self._sector_concentrations[sector_index] add_list_map_element(concentrations_map, conc, sector_index) concentrations = sorted(concentrations_map.keys()) last_sector = None for conc in concentrations: sectors = concentrations_map[conc] for sector_index in sorted(sectors, reverse=True): if not last_sector is None: self._parent_sectors[last_sector] = sector_index last_sector = sector_index self._parent_sectors[sector_index] = None
def _store_candidate_data(self, candidate): """ Stores the candidate in the :attr:`_picked_candidates` map. Subclasses may filter candidates here, too. """ pool = self._pool_map[candidate.pool_id] add_list_map_element(self._picked_candidates, pool, candidate)
def _store_result(self, result_record): """ Results are converted into :class:`CANDIDATE_CLS` objects before storage. The """ candidate = self._create_candidate_from_query_result(result_record) add_list_map_element(self._results, candidate.pool_id, candidate)
def add_preparation_position(self, plate_label, plate_pos): """ Registers a target preparation position for this stock tube (used later to generated planned worklists). """ add_list_map_element(self.__prep_positions, plate_label, plate_pos) self.__set_stock_rack_marker(plate_pos)
def __get_sorted_executed_transfers(self, executed_transfers, target_rack_barcode): # Sorts the executed transfer of a worklist by molecule design pool # ID. pool_map = dict() no_pools = set() for et in executed_transfers: translator = self.__get_translator(target_rack_barcode) if translator is None: return None rack_pos_96 = et.target_container.location.position rack_pos_384 = translator.translate(rack_pos_96) lib_pos = self.library_layout.get_working_position(rack_pos_384) if lib_pos is None: info = '%s (rack %s)' % (rack_pos_96.label, target_rack_barcode) no_pools.add(info) continue pool = lib_pos.pool add_list_map_element(pool_map, pool, et) if len(no_pools) > 0: no_pools_list = list(no_pools) no_pools_list.sort() msg = 'Could not find molecule design pools for the following ' \ 'target positions: %s.' % (no_pools_list) self.add_error(msg) return pool_map
def __get_tube_candidates_for_tubes(self, tube_barcodes, tube_type): """ As far as possible we use the tube aggregate to fetch tubes from the DB. The tubes for the barcodes are converted into :class:`TubeCandidate` objects and mapped onto pools. Valid tubes must contain stock samples and be managed. """ self.__tube_agg.filter = cntd(barcode=tube_barcodes) iterator = self.__tube_agg.iterator() candidate_map = dict() no_stock_sample = [] not_managed = [] while True: try: tube = iterator.next() except StopIteration: break else: if not tube.item_status == ITEM_STATUS_NAMES.MANAGED.upper(): not_managed.append(tube.barcode) continue sample = tube.sample if not isinstance(sample, StockSample): no_stock_sample.append(tube.barcode) continue pool = sample.molecule_design_pool tc = TubeCandidate(pool_id=pool.id, rack_barcode=tube.location.rack.barcode, rack_position=tube.location.position, tube_barcode=tube.barcode, concentration=sample.concentration, volume=sample.volume) # we could store the pool itself to (instead of its ID), but # for some reason the pool entities are not recognised as equal add_list_map_element(candidate_map, pool.id, tc) self.__tube_map[tube.barcode] = tube if len(no_stock_sample) > 0: msg = 'The following %s tubes do not contain stock ' \ 'samples: %s! The referring tubes are replaced, if ' \ 'possible.' % (tube_type, self._get_joined_str(no_stock_sample)) self.add_warning(msg) if len(not_managed) > 0: msg = 'The following %s tubes are not managed: %s. They ' \ 'are replaced, if possible.' \ % (tube_type, self._get_joined_str(not_managed)) self.add_warning(msg) not_found = [] for tube_barcode in tube_barcodes: if not self.__tube_map.has_key(tube_barcode): not_found.append(tube_barcode) if len(not_found) > 0: msg = 'The following %s tubes have not been found in the DB: %s.' \ % (tube_type, self._get_joined_str(not_found)) self.add_warning(msg) return candidate_map
def __get_tube_candidates_for_tubes(self, tube_barcodes, tube_type): """ As far as possible we use the tube aggregate to fetch tubes from the DB. The tubes for the barcodes are converted into :class:`TubeCandidate` objects and mapped onto pools. Valid tubes must contain stock samples and be managed. """ self.__tube_agg.filter = cntd(barcode=tube_barcodes) iterator = self.__tube_agg.iterator() candidate_map = dict() no_stock_sample = [] not_managed = [] while True: try: tube = iterator.next() except StopIteration: break else: if not tube.item_status == ITEM_STATUS_NAMES.MANAGED.upper(): not_managed.append(tube.barcode) continue sample = tube.sample if not isinstance(sample, StockSample): no_stock_sample.append(tube.barcode) continue pool = sample.molecule_design_pool tc = TubeCandidate(pool_id=pool.id, rack_barcode=tube.location.rack.barcode, rack_position=tube.location.position, tube_barcode=tube.barcode, concentration=sample.concentration, volume=sample.volume) # we could store the pool itself to (instead of its ID), but # for some reason the pool entities are not recognised as equal add_list_map_element(candidate_map, pool.id, tc) self.__tube_map[tube.barcode] = tube if len(no_stock_sample) > 0: msg = 'The following %s tubes do not contain stock ' \ 'samples: %s! The referring tubes are replaced, if ' \ 'possible.' % (tube_type, self._get_joined_str(no_stock_sample)) self.add_warning(msg) if len(not_managed) > 0: msg = 'The following %s tubes are not managed: %s. They ' \ 'are replaced, if possible.' \ % (tube_type, self._get_joined_str(not_managed)) self.add_warning(msg) not_found = [] for tube_barcode in tube_barcodes: if not self.__tube_map.has_key(tube_barcode): not_found.append(tube_barcode) if len(not_found) > 0: msg = 'The following %s tubes have not been found in the DB: %s.' \ % (tube_type, self._get_joined_str(not_found)) self.add_warning(msg) return candidate_map
def add_tagged_position(self, tag_predicate, tag_value, cell_indices): """ Stores the tag data in the :attr:`tag_data` map. """ tag_container = TagParsingContainer(parser=self._parser, predicate=tag_predicate, value=tag_value) rack_pos_container = self.get_rack_position_container(cell_indices) add_list_map_element(self.tag_data, tag_container, rack_pos_container)
def __store_worklist_roles(self, worklist, role, ror): # Sets the passed RackOrReservoirItem as source or target for the # given worklist. if self.__transfer_roles.has_key(worklist.label): role_map = self.__transfer_roles[worklist.label] else: role_map = dict() self.__transfer_roles[worklist.label] = role_map add_list_map_element(role_map, role, ror)
def __create_pool_candidates(self): # Initialises empty pool candidates (without stock sample data and #candidates) for every pool to be generate. self.add_debug('Initialise library candidates ...') for pool in self.molecule_design_pools: pool_cand = PoolCandidate(pool) self.__pool_candidates[pool.id] = pool_cand for md_id in pool_cand.get_molecule_design_ids(): add_list_map_element(self.__md_map, md_id, pool.id)
def __store_worklist_roles(self, worklist, role, ror): # Sets the passed RackOrReservoirItem as source or target for the # given worklist. if self.__transfer_roles.has_key(worklist.label): role_map = self.__transfer_roles[worklist.label] else: role_map = dict() self.__transfer_roles[worklist.label] = role_map add_list_map_element(role_map, role, ror)
def add_tagged_position(self, tag_predicate, tag_value, cell_indices): """ Stores the tag data in the :attr:`tag_data` map. """ tag_container = TagParsingContainer(parser=self._parser, predicate=tag_predicate, value=tag_value) rack_pos_container = self.get_rack_position_container(cell_indices) add_list_map_element(self.tag_data, tag_container, rack_pos_container)
def plate_target_positions(self): """ Returns the target positions mapped onto plate labels. The plate label for the final layout is :attr:`LAEBLS.ROLE_FINAL`. """ target_positions = dict() if len(self.__final_positions) > 0: for final_pos in self.__final_positions: add_list_map_element(target_positions, LABELS.ROLE_FINAL, final_pos) target_positions.update(self.__prep_positions) return target_positions
def add_worklist(self, role, worklist): """ Registers a worklist in which this object takes over the specified role. :param role: source or target (see :class:`thelma.tools.worklists.TRANSFER_ROLES`) :type role: :class:`str` :param worklist: a worklist in which this rack or reservoir occurs :type worklist: :class:`thelma.entities.liquidtransfer.PlannedWorklist` """ add_list_map_element(self.__worklists, role, worklist)
def add_position(self, role, pos_container): """ Adds the position to the position list of the given role. :param role: source or target (see :class:`TransferStepParsingContainer`) :type role: :class:`str` :param pos_container: The position to store :type pos_container: :class:`RackPositionParsingContainer` """ add_list_map_element(self.positions, role, pos_container)
def plate_target_positions(self): """ Returns the target positions mapped onto plate labels. The plate label for the final layout is :attr:`LAEBLS.ROLE_FINAL`. """ target_positions = dict() if len(self.__final_positions) > 0: for final_pos in self.__final_positions: add_list_map_element(target_positions, LABELS.ROLE_FINAL, final_pos) target_positions.update(self.__prep_positions) return target_positions
def __create_library_candidates(self): """ Initialises empty library candidates (without stock sample data and candidates) for every library pool. """ self.add_debug('Initialise library candidates ...') for pool in self.molecule_design_pools: libcand = LibraryCandidate(pool) self.__library_candidates[pool.id] = libcand for md_id in libcand.get_molecule_design_ids(): add_list_map_element(self.__md_map, md_id, pool.id)
def _find_hash_values(self): """ Initialises :attr:`_hash_values` and :attr:`_column_maps`. """ self.add_debug('Sort hash values ...') for label, tf_layout in self.design_rack_layouts.iteritems(): column_map = dict() for tf_pos in tf_layout.get_sorted_working_positions(): self._hash_values.add(tf_pos.hash_full) add_list_map_element(column_map, tf_pos.rack_position.column_index, tf_pos) self._column_maps[label] = column_map
def _find_hash_values(self): """ Initialises :attr:`_hash_values` and :attr:`_column_maps`. """ self.add_debug('Sort hash values ...') for label, tf_layout in self.design_rack_layouts.iteritems(): column_map = dict() for tf_pos in tf_layout.get_sorted_working_positions(): self._hash_values.add(tf_pos.hash_full) add_list_map_element(column_map, tf_pos.rack_position.column_index, tf_pos) self._column_maps[label] = column_map
def __sort_subcolumns(self): """ Sorts the subcolumns by length. """ self.add_debug('Sort subcolumns ...') found_before = set() for subcolumn in self.__subcolumn_tids.values(): if subcolumn in found_before: continue found_before.add(subcolumn) add_list_map_element(self.__subcolumn_lengths, len(subcolumn), subcolumn)
def __get_expected_sector_positions(self): """ Returns the expected position for each pool sorted by sector index. """ inconsistent = [] contains_non_sectors = False has_sectors = False expected_positions = dict() for pool, container in self._stock_tube_containers.iteritems(): stock_rack_marker = container.stock_rack_marker for plate_label, positions in container.plate_target_positions.\ iteritems(): plate_layout = self._plate_layouts[plate_label] sector_positions = dict() for plate_pos in positions: sector_index = plate_pos.sector_index if sector_index is None: contains_non_sectors = True continue elif not has_sectors: has_sectors = True add_list_map_element(sector_positions, sector_index, plate_pos) if len(sector_positions) < 1: continue exp_data = self.__get_expected_stock_rack_position(plate_layout, sector_positions) if exp_data is None: inconsistent.append(pool.id) else: sector_index = exp_data[1] self._stock_rack_sectors[stock_rack_marker] = sector_index sector_pools = get_nested_dict(expected_positions, sector_index) exp_pos = exp_data[0] sector_pools[pool] = exp_pos if contains_non_sectors and has_sectors: msg = 'The sector data for the layouts are inconsistent - some ' \ 'sector indices for samples are None!' self.add_error(msg) return None if inconsistent: msg = 'The sector for the following pools are inconsistent ' \ 'in the layouts: %s.' % (self._get_joined_str(inconsistent, is_strs=False)) self.add_error(msg) return None return expected_positions
def __store_column_values(self): # Store the values for the columns. self.add_debug('Store values ...') source_rack_map = dict() for ew in self.executed_worklists: for elt in ew: source_rack_barcode = elt.source_container.rack.barcode add_list_map_element(source_rack_map, source_rack_barcode, elt) well_containers = dict() missing_layouts = [] sorted_elts = dict() for source_rack_barcode in sorted(source_rack_map.keys()): executed_transfers = source_rack_map[source_rack_barcode] if not self.stock_rack_data.has_key(source_rack_barcode): missing_layouts.append(source_rack_barcode) continue layout = self.stock_rack_data[source_rack_barcode] pool_map = self.__get_sorted_executed_transfers( executed_transfers, layout, source_rack_barcode) if self.has_errors(): break for pool_id, elts in pool_map.iteritems(): sorted_elts[pool_id] = elts for pool_id in sorted(sorted_elts.keys()): elts = sorted_elts[pool_id] for elt in elts: self.__pool_values.append(get_trimmed_string(pool_id)) source_container = elt.source_container plt = elt.planned_liquid_transfer if not isinstance(source_container, Tube): pos_label = plt.source_position.label add_list_map_element(well_containers, source_container.rack.barcode, pos_label) continue self.__tube_barcode_values.append(source_container.barcode) volume = plt.volume * VOLUME_CONVERSION_FACTOR self.__volume_values.append(get_trimmed_string(volume)) self.__trg_rack_barcode_values.append( elt.target_container.rack.barcode) self.__trg_position_values.append(plt.target_position.label) if len(well_containers) > 0: msg = 'Some source containers in the worklists are wells: %s!' \ % (self._get_joined_map_str(well_containers, 'plate %s (positions %s)')) self.add_error(msg) if len(missing_layouts) > 0: msg = 'Unable to find the layouts for the following stock ' \ 'racks: %s.' % (self._get_joined_str(missing_layouts)) self.add_error(msg)
def __devide_subcolumn(self, subcolumn): """ Devides a subcolumn into 2 parts, of which one stored and the second is put pack into the queue. """ ssc = self.__get_column_with_less_positions(len(subcolumn)) if not ssc is None: free_count = len(ssc) storage_subcolumn = subcolumn.split(free_count) self.__assign_subcolumn_positions(ssc, storage_subcolumn) add_list_map_element(self.__subcolumn_lengths, len(subcolumn), subcolumn)
def _create_one_to_one_map(self): """ Creates one position map for one to one sorting (with source rack positions as keys and template working positions as values). Return *None* if one-to-one sorting is not possible. This is some sort of short cut for the very simple layouts. In one-to-one sorting mode we simply assign the rack position of the earliest occurrence of a design rack transfection position. If the position is already occupied, the process is aborted and we switch back to the "normal" optimisation algorithm. One-to-one assumes equal rack shapes and (almost) equal design racks. """ hash_map = dict() # rack positions onto hashes tf_map = dict() # tf positions onto rack positions abort_sorting = False is_first_layout = None for tf_layout in self.design_rack_layouts.values(): if is_first_layout is None: is_first_layout = True elif is_first_layout: is_first_layout = False if abort_sorting: break for tf_pos in tf_layout.get_sorted_working_positions(): hash_value = tf_pos.hash_full rack_pos = tf_pos.rack_position if not hash_map.has_key(hash_value): # new hash if not tf_map.has_key(rack_pos): # position available tf_map[rack_pos] = tf_pos add_list_map_element(hash_map, hash_value, rack_pos) else: # position occupied abort_sorting = True break elif is_first_layout: add_list_map_element(hash_map, hash_value, rack_pos) elif not rack_pos in hash_map[hash_value]: abort_sorting = True break if abort_sorting: return None else: return tf_map
def __find_floating_only_associations(self): """ Since all floating are treated in the same way we exclude the possibility of equal concentrations in a rack sectors if controls are not regarded as in this case we have 2 ways to interpreted findings (remember that the floating placeholder have not been assigned yet). Example: 4 floatings with the same contration can either be regarded as 4 independent pools or as 1 pool in 4-fold replicate. As the first case is more likely in our company we choose the this case for interpretation. If scientists want a different interpretation we have to adjust this manually. """ concentrations = dict() present_sectors = [] for sector_index, conc in self._sector_concentrations.iteritems(): if conc is not None: add_list_map_element(concentrations, conc, sector_index) present_sectors.append(sector_index) if len(self._associated_sectors) > 1: msg = 'Unable to adjust floating position association ' \ 'because basic assumptions are not met. This is ' \ 'a programming error. Talk to IT, please.' raise AssertionError(msg) if len(present_sectors) > 1: current_sets = [] if len(concentrations) == 1: for sector_index in present_sectors: current_sets.append([sector_index]) elif len(concentrations) > 1: while len(concentrations) > 0: current_set = [] del_conc = [] for conc, sectors in concentrations.iteritems(): sectors.sort() si = sectors.pop(0) current_set.append(si) if len(sectors) < 1: del_conc.append(conc) current_sets.append(current_set) for conc in del_conc: del concentrations[conc] else: current_sets = [[present_sectors[0]]] if self._are_valid_sets(current_sets): self._associated_sectors = current_sets
def _create_one_to_one_map(self): """ Creates one position map for one to one sorting (with source rack positions as keys and template working positions as values). Return *None* if one-to-one sorting is not possible. This is some sort of short cut for the very simple layouts. In one-to-one sorting mode we simply assign the rack position of the earliest occurrence of a design rack transfection position. If the position is already occupied, the process is aborted and we switch back to the "normal" optimisation algorithm. One-to-one assumes equal rack shapes and (almost) equal design racks. """ hash_map = dict() # rack positions onto hashes tf_map = dict() # tf positions onto rack positions abort_sorting = False is_first_layout = None for tf_layout in self.design_rack_layouts.values(): if is_first_layout is None: is_first_layout = True elif is_first_layout: is_first_layout = False if abort_sorting: break for tf_pos in tf_layout.get_sorted_working_positions(): hash_value = tf_pos.hash_full rack_pos = tf_pos.rack_position if not hash_map.has_key(hash_value): # new hash if not tf_map.has_key(rack_pos): # position available tf_map[rack_pos] = tf_pos add_list_map_element(hash_map, hash_value, rack_pos) else: # position occupied abort_sorting = True break elif is_first_layout: add_list_map_element(hash_map, hash_value, rack_pos) elif not rack_pos in hash_map[hash_value]: abort_sorting = True break if abort_sorting: return None else: return tf_map
def __store_column_values(self): # Store the values for the columns. self.add_debug("Store values ...") source_rack_map = dict() for ew in self.executed_worklists: for elt in ew: source_rack_barcode = elt.source_container.rack.barcode add_list_map_element(source_rack_map, source_rack_barcode, elt) well_containers = dict() missing_layouts = [] sorted_elts = dict() for source_rack_barcode in sorted(source_rack_map.keys()): executed_transfers = source_rack_map[source_rack_barcode] if not self.stock_rack_data.has_key(source_rack_barcode): missing_layouts.append(source_rack_barcode) continue layout = self.stock_rack_data[source_rack_barcode] pool_map = self.__get_sorted_executed_transfers(executed_transfers, layout, source_rack_barcode) if self.has_errors(): break for pool_id, elts in pool_map.iteritems(): sorted_elts[pool_id] = elts for pool_id in sorted(sorted_elts.keys()): elts = sorted_elts[pool_id] for elt in elts: self.__pool_values.append(get_trimmed_string(pool_id)) source_container = elt.source_container plt = elt.planned_liquid_transfer if not isinstance(source_container, Tube): pos_label = plt.source_position.label add_list_map_element(well_containers, source_container.rack.barcode, pos_label) continue self.__tube_barcode_values.append(source_container.barcode) volume = plt.volume * VOLUME_CONVERSION_FACTOR self.__volume_values.append(get_trimmed_string(volume)) self.__trg_rack_barcode_values.append(elt.target_container.rack.barcode) self.__trg_position_values.append(plt.target_position.label) if len(well_containers) > 0: msg = "Some source containers in the worklists are wells: %s!" % ( self._get_joined_map_str(well_containers, "plate %s (positions %s)") ) self.add_error(msg) if len(missing_layouts) > 0: msg = "Unable to find the layouts for the following stock " "racks: %s." % ( self._get_joined_str(missing_layouts) ) self.add_error(msg)
def __find_floating_only_associations(self): """ Since all floating are treated in the same way we exclude the possibility of equal concentrations in a rack sectors if controls are not regarded as in this case we have 2 ways to interpreted findings (remember that the floating placeholder have not been assigned yet). Example: 4 floatings with the same contration can either be regarded as 4 independent pools or as 1 pool in 4-fold replicate. As the first case is more likely in our company we choose the this case for interpretation. If scientists want a different interpretation we have to adjust this manually. """ concentrations = dict() present_sectors = [] for sector_index, conc in self._sector_concentrations.iteritems(): if conc is not None: add_list_map_element(concentrations, conc, sector_index) present_sectors.append(sector_index) if len(self._associated_sectors) > 1: msg = 'Unable to adjust floating position association ' \ 'because basic assumptions are not met. This is ' \ 'a programming error. Talk to IT, please.' raise AssertionError(msg) if len(present_sectors) > 1: current_sets = [] if len(concentrations) == 1: for sector_index in present_sectors: current_sets.append([sector_index]) elif len(concentrations) > 1: while len(concentrations) > 0: current_set = [] del_conc = [] for conc, sectors in concentrations.iteritems(): sectors.sort() si = sectors.pop(0) current_set.append(si) if len(sectors) < 1: del_conc.append(conc) current_sets.append(current_set) for conc in del_conc: del concentrations[conc] else: current_sets = [[present_sectors[0]]] if self._are_valid_sets(current_sets): self._associated_sectors = current_sets
def _check_associated_sectors(self): """ Sectors sets are sorted by size first. Larger sets must incorporate all known smaller sets sharing these sector indices. All final sets must have the same length to insure all potential floating positions are treated in the same manner. """ length_map = dict() for sectors in self.__sector_set_hashes.values(): add_list_map_element(length_map, len(sectors), sectors) current_sets = [] used_sectors = dict() invalid = False for length in sorted(length_map.keys()): if invalid: break sector_sets = length_map[length] for sector_set in sector_sets: all_new = True all_present = True for si in sector_set: if not used_sectors.has_key(si): all_present = False used_sectors[si] = sector_set else: all_new = False if all_new: current_sets.append(sector_set) continue elif not all_present: invalid = True break for si in sector_set: smaller_set = used_sectors[si] for smaller_set_index in smaller_set: if not smaller_set_index in sector_set: invalid = True break if invalid: msg = 'The molecule design pools in the different quadrants are ' \ 'not consistent. Found associated pools: %s.' \ % (self._get_joined_map_str(self.__sector_set_positions, all_strs=False)) self.add_error(msg) elif self._are_valid_sets(current_sets): self._associated_sectors = current_sets
def __find_rack_association(self, donor_rack): # Initialises a donor rack and finds the receiver racks for it. receiver_racks = dict() associations = dict() # receiver barcode, num tubes # check remaining_tubes = donor_rack.tube_count while remaining_tubes > 0: receiver = None while receiver is None: receiver_candidate = self.__find_receiver_rack(remaining_tubes) if receiver_candidate is None: break if receiver_candidate.rack_barcode in self.excluded_racks: continue receiver = receiver_candidate if receiver is None: return False if receiver.role is None: occupied_positions = receiver.tube_count else: occupied_positions = receiver.resulting_tube_count free_positions = self.__stock_rack_size - occupied_positions transferred_tubes = min(free_positions, remaining_tubes) associations[receiver.rack_barcode] = transferred_tubes remaining_tubes -= transferred_tubes receiver_racks[receiver.rack_barcode] = receiver # record donor_rack.set_role(STOCK_CONDENSE_ROLES.DONOR) for receiver_barcode, num_tubes in associations.iteritems(): receiver = receiver_racks[receiver_barcode] if receiver.role is None: receiver.set_role(STOCK_CONDENSE_ROLES.RECEIVER) donor_rack.add_rack_association(rack_barcode=receiver_barcode, number_tubes=num_tubes) receiver.add_rack_association(number_tubes=num_tubes, rack_barcode=donor_rack.rack_barcode) self.__receiver_racks[receiver_barcode] = receiver resulting_receiver_tubes = receiver.resulting_tube_count if resulting_receiver_tubes < self.__stock_rack_size: add_list_map_element(self.__tube_count_map, resulting_receiver_tubes, receiver) self.__donor_racks[donor_rack.rack_barcode] = donor_rack return True
def __find_rack_association(self, donor_rack): # Initialises a donor rack and finds the receiver racks for it. receiver_racks = dict() associations = dict() # receiver barcode, num tubes # check remaining_tubes = donor_rack.tube_count while remaining_tubes > 0: receiver = None while receiver is None: receiver_candidate = self.__find_receiver_rack(remaining_tubes) if receiver_candidate is None: break if receiver_candidate.rack_barcode in self.excluded_racks: continue receiver = receiver_candidate if receiver is None: return False if receiver.role is None: occupied_positions = receiver.tube_count else: occupied_positions = receiver.resulting_tube_count free_positions = self.__stock_rack_size - occupied_positions transferred_tubes = min(free_positions, remaining_tubes) associations[receiver.rack_barcode] = transferred_tubes remaining_tubes -= transferred_tubes receiver_racks[receiver.rack_barcode] = receiver # record donor_rack.set_role(STOCK_CONDENSE_ROLES.DONOR) for receiver_barcode, num_tubes in associations.iteritems(): receiver = receiver_racks[receiver_barcode] if receiver.role is None: receiver.set_role(STOCK_CONDENSE_ROLES.RECEIVER) donor_rack.add_rack_association(rack_barcode=receiver_barcode, number_tubes=num_tubes) receiver.add_rack_association(number_tubes=num_tubes, rack_barcode=donor_rack.rack_barcode) self.__receiver_racks[receiver_barcode] = receiver resulting_receiver_tubes = receiver.resulting_tube_count if resulting_receiver_tubes < self.__stock_rack_size: add_list_map_element(self.__tube_count_map, resulting_receiver_tubes, receiver) self.__donor_racks[donor_rack.rack_barcode] = donor_rack return True
def _store_column_values(self): """ Stores the column values. """ self.add_debug('Store column values ...') src_rack_map = dict() for tube_transfer in self._tube_transfer_data: add_list_map_element(src_rack_map, tube_transfer.src_rack_barcode, tube_transfer) src_racks = sorted(src_rack_map.keys()) for src_rack in src_racks: tube_barcodes = sorted(src_rack_map[src_rack], cmp=lambda tt1, tt2: cmp(tt1.tube_barcode, tt2.tube_barcode)) for tube_transfer in tube_barcodes: self._source_rack_values.append(src_rack) self._source_position_values.append(tube_transfer.src_pos.label) self._tube_barcode_values.append(tube_transfer.tube_barcode) self._dest_rack_values.append(tube_transfer.trg_rack_barcode) self._dest_position_values.append(tube_transfer.trg_pos.label)
def _store_column_values(self): """ Stores the column values. """ self.add_debug('Store column values ...') src_rack_map = dict() for tube_transfer in self._tube_transfer_data: add_list_map_element(src_rack_map, tube_transfer.src_rack_barcode, tube_transfer) src_racks = sorted(src_rack_map.keys()) for src_rack in src_racks: tube_barcodes = sorted( src_rack_map[src_rack], cmp=lambda tt1, tt2: cmp(tt1.tube_barcode, tt2.tube_barcode)) for tube_transfer in tube_barcodes: self._source_rack_values.append(src_rack) self._source_position_values.append( tube_transfer.src_pos.label) self._tube_barcode_values.append(tube_transfer.tube_barcode) self._dest_rack_values.append(tube_transfer.trg_rack_barcode) self._dest_position_values.append(tube_transfer.trg_pos.label)
def __are_valid_transfer_targets(self, transfer_targets, rack_position, parameter_name): """ Stores the transfer targets and checks their consistency. """ if transfer_targets is None or len(transfer_targets) < 1: if self.PARAMETER_SET.must_have_transfer_targets(parameter_name): add_list_map_element(self.__missing_transfer_target, parameter_name, rack_position.label) return False else: return True allow_duplicates = self.LAYOUT_CLS.ALLOW_DUPLICATE_TARGET_WELLS[ parameter_name] for transfer_target in transfer_targets: if not allow_duplicates and transfer_target.hash_value \ in self._transfer_targets[parameter_name]: error_msg = '%s' % (transfer_target) add_list_map_element(self.__duplicate_targets, parameter_name, error_msg) return False else: add_list_map_element(self._transfer_targets, parameter_name, transfer_target.hash_value) return True
def __fetch_rack_locations(self): """ Searches and adds the location for the selected candidates. """ self.add_debug('Fetch stock rack locations ...') rack_barcodes = dict() for container in self.stock_tube_containers.values(): if container.tube_candidate is None: continue rack_barcode = container.tube_candidate.rack_barcode add_list_map_element(rack_barcodes, rack_barcode, container) query = None if len(rack_barcodes) > 0: query = RackLocationQuery(rack_barcodes=rack_barcodes.keys()) self._run_query(query, base_error_msg='Error when trying to ' \ 'fetch rack locations: ') if not self.has_errors() and not query is None: results = query.get_query_results() for rack_barcode, location in results.iteritems(): for container in rack_barcodes[rack_barcode]: container.location = location
def __fetch_rack_locations(self): """ Searches and adds the location for the selected candidates. """ self.add_debug('Fetch stock rack locations ...') rack_barcodes = dict() for container in self.stock_tube_containers.values(): if container.tube_candidate is None: continue rack_barcode = container.tube_candidate.rack_barcode add_list_map_element(rack_barcodes, rack_barcode, container) query = None if len(rack_barcodes) > 0: query = RackLocationQuery(rack_barcodes=rack_barcodes.keys()) self._run_query(query, base_error_msg='Error when trying to ' \ 'fetch rack locations: ') if not self.has_errors() and not query is None: results = query.get_query_results() for rack_barcode, location in results.iteritems(): for container in rack_barcodes[rack_barcode]: container.location = location
def _parse_target_tag_value(self, target_tag_value, rack_position, parameter_name): """ Converts the value of a target tag into a TargetTransfer List. """ if target_tag_value is None: transfer_targets = [] else: try: transfer_targets = TransferPosition.parse_target_tag_value( target_tag_value) except ValueError: error_msg = '"%s" (%s)' % (target_tag_value, rack_position.label) add_list_map_element(self.__invalid_target_string, parameter_name, error_msg) return None if not self.__are_valid_transfer_targets(transfer_targets, rack_position, parameter_name): return None return transfer_targets
def __get_sorted_executed_transfers(self, executed_transfers, target_rack_barcode): # Sorts the executed transfer of a worklist by molecule design pool # ID. pool_map = dict() no_pools = set() for et in executed_transfers: ssc_layout = self.__ssc_layout_map[et.source_rack.barcode] rack_pos = et.target_container.location.position ssc_pos = ssc_layout.get_working_position(rack_pos) if ssc_pos is None: info = '%s (rack %s)' % (rack_pos.label, target_rack_barcode) no_pools.add(info) continue pool = ssc_pos.pool add_list_map_element(pool_map, pool, et) if len(no_pools) > 0: msg = 'Could not find molecule design pools for the following ' \ 'target positions: %s.' % (self._get_joined_str(no_pools)) self.add_error(msg) for ets in pool_map.itervalues(): ets.sort(key=lambda et: et.source_container.barcode) return pool_map
def __check_numerical_values(self, value_maps, label): """ Checks the values of the numerical parameters. """ invalid_numericals = dict() invalid_mock = dict() invalid_untreated = dict() pool_map = value_maps[TransfectionParameters.MOLECULE_DESIGN_POOL] for parameter, value_map in value_maps.iteritems(): if not parameter in _SCENARIO_PARAMETERS.NUMERICAL_PARAMETERS: continue for rack_pos, value in value_map.iteritems(): if value is None: continue pool = pool_map[rack_pos] if (pool == MOCK_POSITION_TYPE): if not TransfectionParameters.is_valid_mock_value( value, parameter): add_list_map_element(invalid_mock, parameter, rack_pos.label) elif TransfectionParameters.is_untreated_type(pool): if parameter in (TransfectionParameters.FINAL_CONCENTRATION, TransfectionParameters.REAGENT_DIL_FACTOR) \ and not TransfectionPosition.\ is_valid_untreated_value(value): add_list_map_element(invalid_untreated, parameter, rack_pos.label) elif not is_valid_number(value): info = '%s (%s)' % (rack_pos.label, value) add_list_map_element(invalid_numericals, parameter, info) if len(invalid_numericals) > 0: records_str = self.__get_error_record_string(invalid_numericals) msg = 'The levels of some factors must be positive numbers. The ' \ 'following positions in design rack %s have invalid ' \ 'values: %s.' % (label, records_str) self.add_error(msg) if len(invalid_mock) > 0: records_str = self.__get_error_record_string(invalid_mock) msg = 'The levels of some factors for mock positions allow only ' \ 'for the values "None" or "mock" (or no level). Some ' \ 'positions in design rack "%s" have invalid levels. ' \ 'Affected positions: %s.' % (label, records_str) self.add_error(msg) if len(invalid_untreated) > 0: records_str = self.__get_error_record_string(invalid_untreated) msg = 'The levels of some factors for untreated positions allow ' \ 'only for the values "None" and "untreated" (or no level). ' \ 'Some position in design rack "%s" have invalid levels. ' \ 'Affected positions: %s.' % (label, records_str) self.add_error(msg)
def __run_association_round(self): # Runs one association round. while len(self.__tube_count_map) > 0: if self.__stop_associations: break if not self.racks_to_empty is None and \ len(self.__donor_racks) >= self.racks_to_empty: self.__stop_associations = True break tube_counts = self.__tube_count_map.keys() tube_counts.sort() tube_count = tube_counts[0] if tube_count > (self.__stock_rack_size / 2): break condense_racks = self.__tube_count_map[tube_count] potential_donor = condense_racks.pop() if len(condense_racks) == 0: del self.__tube_count_map[tube_count] if potential_donor.rack_barcode in self.excluded_racks: continue found_associations = self.__find_rack_association(potential_donor) if not found_associations: add_list_map_element(self.__tube_count_map, tube_count, potential_donor) break