def __get_rack_labels(self, rack_specifier): """ Gets the rack labels for a verified rack specifier. """ rack_labels = [] tokens = rack_specifier[rack_specifier.find(" ") + 1 :].split(",") for token in tokens: token = token.strip() if len(token) < 1: continue if "-" in token: start, stop = token.split("-") try: # only number-specified racks might be specified in ranges rack_nums = range(int(start), int(stop) + 1) if len(rack_nums) < 1: msg = 'Unable to parse rack range "%s".' % (token) self._create_error(msg) else: for rack_num in rack_nums: rack_labels.append(get_trimmed_string(rack_num)) except ValueError: msg = 'Invalid range "%s" in racks ' 'specifier "%s".' % (token, rack_specifier) self._create_error(msg) return None else: rack_labels.append(get_trimmed_string(token)) # without out a range it can be a rack_numer (int) or a barcode if len(rack_labels) < 1: return None return rack_labels
def _create_worklist_series(self): """ Generates all the buffer dilution worklists (as series). The worklists for the transfer from 1-molecule-design stock rack to pool stock rack are not included but stored at the ISO sample stock racks to enable quadrant tracking. """ self.add_debug('Create worklist series ...') volume_calculator = \ VolumeCalculator(self.target_volume, self.target_concentration, self.__number_designs, self.__stock_concentration) generator = StockSampleCreationWorklistGenerator( volume_calculator, self.iso_request_label, parent=self) self.__worklist_series = generator.get_result() if self.__worklist_series is None: msg = 'Error when trying to generate worklist series.' self.add_error(msg) else: self.__adj_target_volume = \ volume_calculator.get_adjusted_target_volume() if not self.__adj_target_volume is None: robot_specs = get_pipetting_specs_cybio() msg = 'The target volume you have requested needs to be ' \ 'increased slightly because of the constraints of the ' \ 'pipetting robot (%s, min. transfer volume: %s ul, ' \ 'step size: 0.1 ul). The target volume will be ' \ 'increased from %s ul to %s ul.' \ % (robot_specs.name, get_trimmed_string(robot_specs.min_transfer_volume \ * VOLUME_CONVERSION_FACTOR), get_trimmed_string(self.target_volume), get_trimmed_string(self.__adj_target_volume)) self.add_warning(msg)
def __write_general_section(self): # Volume, concentration, dilution factors, etc. header = self.GENERAL_TITLE % (self.generator.return_value.label) self._write_headline(header, preceding_blank_lines=0, trailing_blank_lines=1) general_lines = [] if self.__support_mastermix: robot_support = 'yes' else: robot_support = 'no' general_lines.append(self.SUPPORTS_MASTERMIX_LINE % robot_support) general_lines.append(self.LIBRARY_LINE % (self.__library.label)) iso_vol = self.__library.final_volume * VOLUME_CONVERSION_FACTOR general_lines.append(self.ISO_VOLUME_LINE % (get_trimmed_string( iso_vol))) self.__iso_conc = self.__library.final_concentration \ * CONCENTRATION_CONVERSION_FACTOR general_lines.append(self.ISO_CONCENTRATION_LINE \ % get_trimmed_string(self.__iso_conc)) parameters = TransfectionParameters self.__final_conc = self.__parameter_values[ parameters.FINAL_CONCENTRATION] general_lines.append(self.FINAL_CONCENTRATION_LINE % ( get_trimmed_string(self.__final_conc))) odf = self.__parameter_values[parameters.OPTIMEM_DIL_FACTOR] general_lines.append(self.ODF_LINE % (get_trimmed_string(odf))) reagent_name = self.__parameter_values[parameters.REAGENT_NAME] general_lines.append(self.REAGENT_NAME_LINE % (reagent_name)) reagent_df = self.__parameter_values[parameters.REAGENT_DIL_FACTOR] general_lines.append(self.REAGENT_DF_LINE % ( get_trimmed_string(reagent_df))) self._write_body_lines(general_lines)
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_rack_labels(self, rack_specifier): """ Gets the rack labels for a verified rack specifier. """ rack_labels = [] tokens = rack_specifier[rack_specifier.find(' ') + 1:].split(',') for token in tokens: token = token.strip() if len(token) < 1: continue if '-' in token: start, stop = token.split('-') try: # only number-specified racks might be specified in ranges rack_nums = range(int(start), int(stop) + 1) if len(rack_nums) < 1: msg = 'Unable to parse rack range "%s".' % (token) self._create_error(msg) else: for rack_num in rack_nums: rack_labels.append(get_trimmed_string(rack_num)) except ValueError: msg = 'Invalid range "%s" in racks ' \ 'specifier "%s".' % (token, rack_specifier) self._create_error(msg) return None else: rack_labels.append(get_trimmed_string(token)) # without out a range it can be a rack_numer (int) or a barcode if len(rack_labels) < 1: return None return rack_labels
def __store_line_value(self, dil_info, total_vol, pos_label): """ Stores the values for one line. """ dil_info = dil_info.strip() info_tokens = dil_info.split(' (') final_dil_factor = float(info_tokens[1][:-1]) initial_dil_factor = TransfectionParameters.\ calculate_initial_reagent_dilution(final_dil_factor) reagent_vol = round_up((total_vol / initial_dil_factor)) total_vol = reagent_vol * initial_dil_factor total_vol = round(total_vol, 1) diluent_vol = total_vol - reagent_vol initial_dil_factor = round(initial_dil_factor, 1) self.__position_values.append(pos_label) self.__name_values.append(info_tokens[0]) self.__final_dil_factor_values.append( get_trimmed_string(final_dil_factor)) self.__ini_dil_factor_values.append( get_trimmed_string(initial_dil_factor)) self.__total_volume_values.append(get_trimmed_string(total_vol)) self.__reagent_volume_values.append(get_trimmed_string(reagent_vol)) self.__diluent_volume_values.append(get_trimmed_string(diluent_vol))
def __write_general_section(self): # Volume, concentration, dilution factors, etc. header = self.GENERAL_TITLE % (self.generator.return_value.label) self._write_headline(header, preceding_blank_lines=0, trailing_blank_lines=1) general_lines = [] if self.__support_mastermix: robot_support = 'yes' else: robot_support = 'no' general_lines.append(self.SUPPORTS_MASTERMIX_LINE % robot_support) general_lines.append(self.LIBRARY_LINE % (self.__library.label)) iso_vol = self.__library.final_volume * VOLUME_CONVERSION_FACTOR general_lines.append(self.ISO_VOLUME_LINE % (get_trimmed_string(iso_vol))) self.__iso_conc = self.__library.final_concentration \ * CONCENTRATION_CONVERSION_FACTOR general_lines.append(self.ISO_CONCENTRATION_LINE \ % get_trimmed_string(self.__iso_conc)) parameters = TransfectionParameters self.__final_conc = self.__parameter_values[ parameters.FINAL_CONCENTRATION] general_lines.append(self.FINAL_CONCENTRATION_LINE % (get_trimmed_string(self.__final_conc))) odf = self.__parameter_values[parameters.OPTIMEM_DIL_FACTOR] general_lines.append(self.ODF_LINE % (get_trimmed_string(odf))) reagent_name = self.__parameter_values[parameters.REAGENT_NAME] general_lines.append(self.REAGENT_NAME_LINE % (reagent_name)) reagent_df = self.__parameter_values[parameters.REAGENT_DIL_FACTOR] general_lines.append(self.REAGENT_DF_LINE % (get_trimmed_string(reagent_df))) self._write_body_lines(general_lines)
def __write_formula_section(self): # Writes the formula for the determination of the OptiMem dilution # factor. self._write_headline(self.ODF_TITLE) formula = self.ODF_FORMULA % (get_trimmed_string(self.__iso_conc), get_trimmed_string(self.__final_conc), TransfectionParameters.REAGENT_MM_DILUTION_FACTOR, TransfectionParameters.CELL_DILUTION_FACTOR) self._stream.write(formula)
def __write_formula_section(self): # Writes the formula for the determination of the OptiMem dilution # factor. self._write_headline(self.ODF_TITLE) formula = self.ODF_FORMULA % ( get_trimmed_string( self.__iso_conc), get_trimmed_string(self.__final_conc), TransfectionParameters.REAGENT_MM_DILUTION_FACTOR, TransfectionParameters.CELL_DILUTION_FACTOR) self._stream.write(formula)
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 hash_full(self): """ A string that can be used as hash value for comparison. This hash covers all four parameters (molecule design pool, reagent name, reagent concentration and final concentration) needed to make well associations. """ if self.is_empty: return '%s' % (self.rack_position) fc = self.final_concentration if self.is_mock: fc = None return '%s%s%s%s' % (self.molecule_design_pool_id, self.reagent_name, get_trimmed_string(self.reagent_dil_factor), get_trimmed_string(fc))
def __store_column_values(self): # Store the values for the columns. self.add_debug('Store values ...') target_rack_map = dict() for ew in self.executed_worklists.values(): for et in ew.executed_transfers: target_rack_barcode = et.target_container.location.rack.barcode if not target_rack_map.has_key(target_rack_barcode): target_rack_map[target_rack_barcode] = [] target_rack_map[target_rack_barcode].append(et) barcodes = sorted(target_rack_map.keys()) well_containers = set() for target_rack_barcode in barcodes: non_single_md_src_pool = [] executed_transfers = target_rack_map[target_rack_barcode] pool_map = self.__get_sorted_executed_transfers(executed_transfers, target_rack_barcode) if self.has_errors(): break pools = sorted(pool_map.keys(), cmp=lambda p1, p2: cmp(p1.id, p2.id)) for pool in pools: ets = pool_map[pool] for et in ets: self.__lib_pool_values.append(get_trimmed_string(pool.id)) volume = et.planned_transfer.volume \ * VOLUME_CONVERSION_FACTOR self.__volume_values.append(get_trimmed_string(volume)) self.__trg_rack_barcode_values.append(target_rack_barcode) trg_label = et.planned_transfer.target_position.label self.__trg_position_values.append(trg_label) src_tube = et.source_container self.__tube_barcode_values.append(src_tube.barcode) md_id = self.__get_molecule_design_id(src_tube) if md_id is None: info = '%s (rack %s)' % (src_tube.barcode, target_rack_barcode) non_single_md_src_pool.append(info) else: self.__md_values.append(get_trimmed_string(md_id)) if len(non_single_md_src_pool) > 0: msg = 'Some source container contain more than one ' \ 'molecule design: %s.' % (sorted(non_single_md_src_pool)) self.add_error(msg) if len(well_containers) > 0: well_container_list = list(well_containers) well_container_list.sort() msg = 'Some source containers in the worklists are wells: %s!' \ % (well_container_list) self.add_error(msg)
def __calculate_single_stock_transfer_volume(self): # Determines the volume that has to be transferred from a single design # stock tube to a future pool stock tube (for the given volume, # concentration, and number of designs). The target volume might # be increased if the resulting single design transfer volume has # more than 1 decimal place. # :raises ValueErrors: if something the values are not compatible target_single_conc = float(self.__target_concentration) \ / self.__number_designs if target_single_conc > self.__stock_concentration: msg = 'The requested target concentration (%i nM) cannot be ' \ 'achieved since it would require a concentration of %s nM ' \ 'for each single design in the pool. However, the stock ' \ 'concentration for this design type is only %s nM.' \ % (self.__target_concentration, get_trimmed_string(target_single_conc), get_trimmed_string(self.__stock_concentration)) raise ValueError(msg) dil_factor = self.__stock_concentration / target_single_conc min_target_volume = round_up(dil_factor * self.__min_cybio_transfer_vol) if (min_target_volume > self.__target_volume): msg = 'The target volume you have requested (%i ul) is too low ' \ 'for the required dilution (1:%s) since the CyBio cannot ' \ 'pipet less than %.1f ul per transfer. The volume that has ' \ 'to be taken from the stock for each single molecule ' \ 'design would be lower that that. Increase the target ' \ 'volume to %.1f ul or increase the target concentration.' \ % (self.__target_volume, get_trimmed_string(dil_factor), self.__min_cybio_transfer_vol, round_up(min_target_volume, 0)) raise ValueError(msg) self.__stock_transfer_vol = round_up(self.__target_volume / dil_factor) self.__adjusted_target_vol = round( self.__stock_transfer_vol * dil_factor, 1) # must be at least 1 ul according to the last check total_transfer_vol = self.__stock_transfer_vol * self.__number_designs if total_transfer_vol > self.__target_volume: msg = 'The target volume you have requested (%i ul) is too low ' \ 'for the concentration you have ordered (%i uM) since it ' \ 'would require already %s ul per molecule design (%s ul in ' \ 'total) to achieve the requested concentration. Increase ' \ 'the volume or lower the concentration, please.' \ % (self.__target_volume, self.__target_concentration, get_trimmed_string(self.__stock_transfer_vol), get_trimmed_string(total_transfer_vol)) raise ValueError(msg)
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 _get_joined_str(self, item_list, is_strs=True, sort_items=True, separator=', '): """ Helper method converting the passed list into a joined string, separated by comma (default). By default, the elements are sorted before conversion. This is handy i.e. when printing error messages. :param item_list: The recorded events in a list. :type item_list: :class:`list` or iterable that can be converted into a list. :param bool is_strs: If not the items must be converted first. Without conversion the join method will raise an error. :default is_strs: *True* :param bool sort_items: Shall the items be sorted? :default sort_items: *True* :param str separator: The string to use for the joining. :default separator: Comma and 1 space. """ if not isinstance(item_list, list): item_list = list(item_list) if sort_items: item_list.sort() if is_strs: item_strs = item_list else: item_strs = [get_trimmed_string(item) for item in item_list] return separator.join(item_strs)
def __stock_concentration_match(self, candidate, container, error_list): """ Makes sure tube candidate and stock tube container expect the same stock concentration. """ container_conc = container.get_stock_concentration() candidate_conc = candidate.concentration if not are_equal_values(container_conc, candidate_conc): info = '%s (pool: %i, expected: %s nM, found at tube: %s nM)' \ % (candidate.tube_barcode, candidate.pool_id, get_trimmed_string(container_conc), get_trimmed_string(candidate_conc)) error_list.append(info) return False else: return True
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 __check_and_assign_tube_candidates(self): """ Checks whether there is pool for each requested pool and whether volume and concentration match. """ missing_pools = [] invalid_conc = [] invalid_vol = [] for pool, container in self._stock_tube_containers.iteritems(): if not self.__tube_candidates.has_key(pool): missing_pools.append(pool.id) continue candidate = self.__tube_candidates[pool] exp_stock_conc = pool.default_stock_concentration \ * CONCENTRATION_CONVERSION_FACTOR if not are_equal_values(candidate.concentration, exp_stock_conc): info = '%s (pool: %s, expected: %s nM, found: %s nM)' % ( candidate.tube_barcode, pool, get_trimmed_string(exp_stock_conc), get_trimmed_string(candidate.concentration)) invalid_conc.append(info) continue required_vol = STOCK_DEAD_VOLUME \ + container.get_total_required_volume() if is_smaller_than(candidate.volume, required_vol): info = '%s (pool: %s, required: %s ul, found: %s ul)' % ( candidate.tube_barcode, pool, get_trimmed_string(required_vol), get_trimmed_string(candidate.volume)) invalid_vol.append(info) continue container.tube_candidate = candidate if len(missing_pools) > 0: msg = 'Could not find tubes for the following pools: %s.' % ( self._get_joined_str(missing_pools, is_strs=False)) self.add_error(msg) if len(invalid_conc) > 0: msg = 'The concentrations in some tubes do not match the ' \ 'expected ones: %s.' % (self._get_joined_str(invalid_conc)) self.add_error(msg) if len(invalid_vol) > 0: msg = 'The volumes in some tubes (dead volume included) are not ' \ 'sufficient: %s.' % (self._get_joined_str(invalid_vol)) self.add_error(msg)
def _generate_column_values(self): """ This method generates the value lists for the CSV columns. """ target_rack_barcode = self.target_rack.barcode sorted_transfers = self.__get_sorted_transfers() for pt in sorted_transfers: if pt.target_position in self.ignored_positions: continue if not self._check_transfer_volume(pt.volume, pt.target_position): continue split_volumes = self.__split_into_transferable_amounts(pt) for i in range(len(split_volumes)): volume = round(split_volumes[i], 1) source_pos = self.__get_and_adjust_source_position( pt.diluent_info, volume, i) if self.has_errors(): break self._source_rack_values.append(self.source_rack_barcode) self._source_pos_values.append(source_pos.label) self._volume_values.append(get_trimmed_string(volume)) self._target_rack_values.append(target_rack_barcode) self._target_pos_values.append(pt.target_position.label) self._diluent_info_values.append(pt.diluent_info) if self.__has_split_volumes: msg = 'Some dilution volumes exceed the allowed maximum transfer ' \ 'volume of %s ul. The dilution volumes have been distributed ' \ 'over different source wells. Have a look on the generated ' \ 'worklist file for details, please.' \ % (get_trimmed_string(self._max_transfer_volume)) self.add_warning(msg) if len(self.__split_sources) > 0: msg = 'The source for the following diluents has been split ' \ 'and distributed over several containers because one ' \ 'single container could not have taken up the required ' \ 'volume (max volume of a source container: %s ul, dead ' \ 'volume: %s ul): %s. Have a look onto the generated ' \ 'worklist files for details, please.' \ % (get_trimmed_string(self.reservoir_specs.max_volume \ * VOLUME_CONVERSION_FACTOR), get_trimmed_string(self.reservoir_specs.max_dead_volume \ * VOLUME_CONVERSION_FACTOR), list(self.__split_sources)) self.add_warning(msg)
def __write_formulas(self): # Write the formula explanations in the stream. self._write_headline(self.FORMULA_TITLE, preceding_blank_lines=0, trailing_blank_lines=0) self._stream.write(self.FORMULA_BASE) min_dead_volume = self.reservoir_specs.min_dead_volume \ * VOLUME_CONVERSION_FACTOR max_dead_volume = self.reservoir_specs.max_dead_volume \ * VOLUME_CONVERSION_FACTOR self._stream.write( self.ISO_VOLUME_FORMULA % (self.number_replicates, TransfectionParameters.TRANSFER_VOLUME, TransfectionParameters.MINIMUM_ISO_VOLUME, get_trimmed_string(min_dead_volume), LIMIT_TARGET_WELLS, DEAD_VOLUME_COEFFICIENT, get_trimmed_string(max_dead_volume))) self._stream.write(self.ISO_CONCENTRATION_FORMULA % (TransfectionParameters.REAGENT_MM_DILUTION_FACTOR, TransfectionParameters.CELL_DILUTION_FACTOR))
def hash_partial(self): """ A string that can be used as hash value. This hash covers only three three of the four parameters (molecule design pool, reagent name and reagent concentration). It is meant to enable comparison of manual ISO definitions, in which the final concentraiton is not known. """ if self.is_empty: return '%s' % (self.rack_position) return '%s%s%s' % (self.molecule_design_pool_id, self.reagent_name, get_trimmed_string(self.reagent_dil_factor))
def __write_formulas(self): # Write the formula explanations in the stream. self._write_headline(self.FORMULA_TITLE, preceding_blank_lines=0, trailing_blank_lines=0) self._stream.write(self.FORMULA_BASE) min_dead_volume = self.reservoir_specs.min_dead_volume \ * VOLUME_CONVERSION_FACTOR max_dead_volume = self.reservoir_specs.max_dead_volume \ * VOLUME_CONVERSION_FACTOR self._stream.write(self.ISO_VOLUME_FORMULA % ( self.number_replicates, TransfectionParameters.TRANSFER_VOLUME, TransfectionParameters.MINIMUM_ISO_VOLUME, get_trimmed_string(min_dead_volume), LIMIT_TARGET_WELLS, DEAD_VOLUME_COEFFICIENT, get_trimmed_string(max_dead_volume) )) self._stream.write(self.ISO_CONCENTRATION_FORMULA % (TransfectionParameters.REAGENT_MM_DILUTION_FACTOR, TransfectionParameters.CELL_DILUTION_FACTOR))
def __write_general_section(self): # Writes the GENERAL section. self._write_headline(self.GENERAL_HEADER, preceding_blank_lines=1) if self.racks_to_empty is None: rack_line = self.RACK_TO_EMTPTY_LINE % (self.NOT_SPECIFIED_MARKER) else: rack_line = self.RACK_TO_EMTPTY_LINE \ % (get_trimmed_string(self.racks_to_empty)) total_transfer_count = 0 for scr in self.donor_racks.values(): for transfer_count in scr.associated_racks.values(): total_transfer_count += transfer_count moved_tubes_line = self.TUBES_MOVED_LINE % (total_transfer_count) general_lines = [rack_line, moved_tubes_line] self._write_body_lines(general_lines)
def get_value_for_code(self, code): """ Returns the tag value encrypted by the code. :param code: Tag value code (string of pattern-color-definition). :returns: associated tag value (if any; string) """ try: result = self.__code_map[code] except KeyError: msg = 'Tag value (level) code "%s" not found for factor "%s".' \ % (get_trimmed_string(code), self.predicate) self._create_error(msg) result = None return result
def __fetch_entities(self, interface, agg_filter, ids, entity_name, lookup): # Convenience method fetch the entities for the given IDs from the DB. # Also records errors if an ID is unknown agg = get_root_aggregate(interface) agg.filter = agg_filter for ent in agg: lookup[ent.id] = ent missing_ids = [] for exp_id in ids: if not lookup.has_key(exp_id): missing_ids.append(exp_id) if len(missing_ids) > 0: msg = "Unable to find %ss for the following IDs in the DB: %s." % ( entity_name, ", ".join([get_trimmed_string(eid) for eid in sorted(missing_ids)]), ) self.add_error(msg)
def _get_joined_map_str(self, item_map, str_pattern='%s (%s)', all_strs=True, sort_lists=True, separator=' - '): """ Helper method converting the passed map into a string joined by the given separator. By default, the elements of the lists are sorted before conversion (map keys are always sorted). This is handy i.e. when printing error messages. If the map values are iterables, :func:`_get_joined_str` is used to generate a string for the list. :param item_map: The recorded events in a map. :type item_map: :class:`map` having iterables as values :param str str_pattern: Is used to convert key-value pairs into a string. The first placeholder is used by the key, the second by the joined string value. :default str_pattern: *%s (%s)* :param bool all_strs: Flag indicating if the value list items are strings (only applicable if the value is an iterable)? If not the items must be converted first. Without conversion the join method will raise an error. :default all_strs: *True* :param bool sort_lists: Flag indicating if the values items should be sorted (only applicable if the value is an iterable). :default sort_lists: *True* :param str separator: The string to use for the joining the key-value strings. :default separator: whitespace, dash, whitespace """ details = [] for k in sorted(item_map.keys()): v = item_map[k] if isinstance(v, (list, set, dict, tuple)): v_str = self._get_joined_str(v, is_strs=all_strs, sort_items=sort_lists) else: v_str = str(v) details_str = str_pattern % (get_trimmed_string(k), v_str) details.append(details_str) return separator.join(details)
def __fetch_entities(self, interface, agg_filter, ids, entity_name, lookup): # Convenience method fetch the entities for the given IDs from the DB. # Also records errors if an ID is unknown agg = get_root_aggregate(interface) agg.filter = agg_filter for ent in agg: lookup[ent.id] = ent missing_ids = [] for exp_id in ids: if not lookup.has_key(exp_id): missing_ids.append(exp_id) if len(missing_ids) > 0: msg = 'Unable to find %ss for the following IDs in the DB: %s.' \ % (entity_name, ', '.join([get_trimmed_string(eid) for eid \ in sorted(missing_ids)])) self.add_error(msg)
def _generate_column_values(self): """ This method generates the value lists for the CSV columns. """ source_rack_barcode = self.source_rack.barcode target_rack_barcode = self.target_rack.barcode sorted_transfers = self.__get_sorted_transfers() for pt in sorted_transfers: if pt.source_position in self.ignored_positions: continue if not self._check_transfer_volume(pt.volume, pt.target_position, pt.source_position): continue self._source_rack_values.append(source_rack_barcode) self._source_pos_values.append(pt.source_position.label) self._target_rack_values.append(target_rack_barcode) self._target_pos_values.append(pt.target_position.label) volume = get_trimmed_string(pt.volume * VOLUME_CONVERSION_FACTOR) self._volume_values.append(volume)
def _record_errors(self): """ Records errors that have been found during value list creation. """ if len(self._transfer_volume_too_small) > 0: msg = 'Some transfer volume are smaller than the allowed minimum ' \ 'transfer volume of %s ul: %s.' % (self._min_transfer_volume, ', '.join(sorted(self._transfer_volume_too_small))) self.add_error(msg) if len(self._transfer_volume_too_large) > 0: msg = 'Some transfer volume are larger than the allowed maximum ' \ 'transfer volume of %s ul: %s.' % (self._max_transfer_volume, ', '.join(sorted(self._transfer_volume_too_large))) self.add_error(msg) if len(self._source_volume_too_small) > 0: error_list = sorted(list(self._source_volume_too_small)) msg = 'Some source containers do not contain enough volume to ' \ 'provide liquid for all target containers: %s.' \ % (', '.join(error_list)) self.add_error(msg) if len(self._source_container_missing) > 0: error_list = sorted(list(self._source_container_missing)) msg = 'Could not find containers for the following source ' \ 'positions: %s.' % (', '.join(error_list)) self.add_error(msg) if len(self._target_volume_too_large) > 0: msg = 'Some target containers cannot take up the transfer volume: ' \ '%s. ' % (', '.join(sorted(self._target_volume_too_large))) if self._target_max_volume is not None: msg += 'Assumed maximum volume per target well: %s ul.' \ % (get_trimmed_string(self._target_max_volume)) self.add_error(msg) if len(self._target_container_missing) > 0: msg = 'Could not find containers for the following target ' \ 'positions: %s.' \ % (', '.join(sorted(self._target_container_missing))) self.add_error(msg)