def get(self, process_id): try: process = PoolingProcess(int(process_id)) except LabControlUnknownIdError: raise HTTPError(404, reason='PoolingProcess %s does not exist' % process_id) text = process.generate_pool_file() plate_names_set = {x[0].container.plate.external_id for x in process.components} # Note that PoolingProcess objects (what `process` is above) definitely # *can* validly have components from multiple plates: a user could # chose to make a "plate pool" from more than one amplicon library prep # plate. However, as of 10/09/2018, the wet lab apparently currently # chooses not to do this, and instead chooses to make one pool per # library prep plate. Given this self-imposed limitation, they expect # to be able to have the (one-and-only) library plate name embedded in # the name of the resulting normpool file. This # *file naming convention* won't work--or at least, won't work as they # expect it to--if there are multiple plates represented in the pool, # so if that happens we generate an error below, at the point where the # *file name* is generated. If they decide they want to allow # themselves to make plate pools from multiple plates, all they need to # do is decide on a more flexible naming convention, and # we can change this naming code and remove this error condition. if len(plate_names_set) > 1: raise ValueError("Unable to generate normpool file name for pool " "based on more than one plate: " + ", ".join(str(x) for x in plate_names_set)) plate_name = plate_names_set.pop() name_pieces = [plate_name, "normpool"] self.deliver_text(name_pieces, process, text, extension="csv")
def post(self): pool_name = self.get_argument('pool_name') pools_info = json_decode(self.get_argument('pools_info')) concentrations = [] input_compositions = [] for p_info in pools_info: pool_comp = PoolComposition(p_info['pool_id']) concentrations.append({ 'composition': pool_comp, 'concentration': p_info['concentration'] }) input_compositions.append({ 'composition': pool_comp, 'input_volume': p_info['volume'], 'percentage_of_output': p_info['percentage'] }) # Create the quantification process (DNA conc) q_process = QuantificationProcess.create_manual( self.current_user, concentrations) # Create the pool - Magic number 5 - > the volume for this pooling # is always 5 according to the wet lab. p_process = PoolingProcess.create(self.current_user, q_process, pool_name, 5, input_compositions, { "function": "amplicon_pool", "parameters": {} }) self.write({'process': p_process.id})
def _compute_pools(self, plate_info): super()._compute_pools(plate_info) self.params['total_each'] = False self.params['vol_constant'] = 10 ** 9 pool_vals = self.function(self.raw_concs, **self.params) # if adjust blank volume, do that if self.params['blank_vol'] != '': bv = self.params['blank_vol'] pool_vals = PoolingProcess.adjust_blank_vols(pool_vals, self.comp_blanks, bv) # if only pool some blanks, do that if self.params['blank_num'] != '': bn = int(self.params['blank_num']) pool_vals = PoolingProcess.select_blanks(pool_vals, self.raw_concs, self.comp_blanks, bn) # estimate pool volume and concentration cs = self.comp_concs total_c, total_v = PoolingProcess.estimate_pool_conc_vol(pool_vals, cs) # store output values output = {} output['func_data'] = {'function': self.func_name, 'parameters': self.params} output['raw_vals'] = self.raw_concs output['comp_vals'] = self.comp_concs output['pool_vals'] = pool_vals output['pool_blanks'] = self.comp_blanks.tolist() output['plate_names'] = self.plate_names.tolist() output['plate_id'] = self.plate_id output['destination'] = self.params['destination'] output['robot'] = self.params['robot'] output['blank_vol'] = self.params['blank_vol'] output['blank_num'] = self.params['blank_num'] output['total_conc'] = total_c output['total_vol'] = total_v output['quant-process-id'] = self.quant_process_id return output
def test_properties(self): tester = Tube(7) self.assertEqual(tester.external_id, 'Test Pool from Plate 1') self.assertFalse(tester.discarded) self.assertEqual(tester.remaining_volume, 96) self.assertIsNone(tester.notes) self.assertEqual(tester.latest_process, PoolingProcess(1)) self.assertEqual(tester.container_id, 3079) self.assertEqual(tester.composition, PoolComposition(1))
def get(self): plate_ids = self.get_arguments('plate_id') process_id = self.get_argument('process_id', None) input_plate = None pool_func_data = None pool_values = [] pool_blanks = [] plate_names = [] plate_type = None if process_id is not None: try: process = PoolingProcess(process_id) except LabControlUnknownIdError: raise HTTPError(404, reason="Pooling process %s doesn't exist" % process_id) plate = process.components[0][0].container.plate input_plate = plate.id pool_func_data = process.pooling_function_data _, pool_values, pool_blanks, plate_names = \ make_2D_arrays(plate, process.quantification_process) pool_values = pool_values.tolist() pool_blanks = pool_blanks.tolist() plate_names = plate_names.tolist() elif len(plate_ids) > 0: content_types = { type(Plate(pid).get_well(1, 1).composition) for pid in plate_ids } if len(content_types) > 1: raise HTTPError(400, reason='Plates contain different types ' 'of compositions') plate_type = ('16S library prep' if content_types.pop() == LibraryPrep16SComposition else 'shotgun library prep') robots = (Equipment.list_equipment('EpMotion') + Equipment.list_equipment('echo')) self.render('library_pooling.html', plate_ids=plate_ids, robots=robots, pool_params=HTML_POOL_PARAMS, input_plate=input_plate, pool_func_data=pool_func_data, process_id=process_id, pool_values=pool_values, plate_type=plate_type, pool_blanks=pool_blanks, plate_names=plate_names)
def create_pools_pool_process(user, quant_process, pools): input_compositions = [{ 'composition': p, 'input_volume': 1, 'percentage_of_output': 1 / 9.0 } for p in pools] pool_process = PoolingProcess.create(user, quant_process, 'New pool name %s' % datetime.now(), 5, input_compositions, { "function": "amplicon_pool", "parameters": {} }) return pool_process
def _compute_pools(self, plate_info): super()._compute_pools(plate_info) # pool_vals looks like its needed in the output pool_vals = self.function(self.raw_concs, **self.params) # if adjust blank volume, do that if self.params['blank_vol'] != '': pool_vals = PoolingProcess.adjust_blank_vols( pool_vals, self.comp_blanks, self.params['blank_vol']) # if only pool some blanks, do that if self.params['blank_num'] != '': pool_vals = PoolingProcess.select_blanks( pool_vals, self.raw_concs, self.comp_blanks, int(self.params['blank_num'])) # estimate pool volume and concentration total_c, total_v = PoolingProcess.estimate_pool_conc_vol( pool_vals, self.comp_concs) # store output values output = {} output['pool_vals'] = pool_vals output['pool_blanks'] = self.comp_blanks.tolist() output['plate_names'] = self.plate_names.tolist() output['plate_id'] = self.plate_id output['destination'] = self.params['destination'] output['robot'] = self.params['robot'] output['blank_vol'] = self.params['blank_vol'] output['blank_num'] = self.params['blank_num'] output['total_conc'] = total_c output['total_vol'] = total_v output['quant-process-id'] = self.quant_process_id return output
def get(self): pool_ids = self.get_arguments('pool_id') process_id = self.get_argument('process_id', None) pool_comp_info = None pool_name = None if process_id is not None: try: process = PoolingProcess(process_id) except LabControlUnknownIdError: raise HTTPError(404, reason="Pooling process %s doesn't exist" % process_id) pool_comp_info = [[p.id, p.raw_concentration] for p, _ in process.components] pool_name = process.pool.container.external_id self.render('pool_pooling.html', pool_ids=pool_ids, process_id=process_id, pool_comp_info=pool_comp_info, pool_name=pool_name)
def post(self): plates_info = json_decode(self.get_argument('plates-info')) results = [] for pinfo in plates_info: plate_result = self._compute_pools(pinfo) plate = Plate(plate_result['plate_id']) # calculate estimated molar fraction for each element of pool amts = plate_result['comp_vals'] * plate_result['pool_vals'] pcts = amts / amts.sum() quant_process = QuantificationProcess( plate_result['quant-process-id']) pool_name = 'Pool from plate %s (%s)' % ( plate.external_id, datetime.now().strftime( quant_process.get_date_format())) input_compositions = [] for comp, _, _ in quant_process.concentrations: well = comp.container row = well.row - 1 column = well.column - 1 input_compositions.append({ 'composition': comp, 'input_volume': plate_result['pool_vals'][row][column], 'percentage_of_output': pcts[row][column] }) robot = (Equipment(plate_result['robot']) if plate_result['robot'] is not None else None) process = PoolingProcess.create( self.current_user, quant_process, pool_name, plate_result['pool_vals'].sum(), input_compositions, plate_result['func_data'], robot=robot, destination=plate_result['destination']) results.append({'plate-id': plate.id, 'process-id': process.id}) self.write(json_encode(results))
def create_plate_pool_process(user, quant_process, plate, func_data): input_compositions = [] echo = Equipment(8) for well in chain.from_iterable(plate.layout): if well is not None: input_compositions.append({ 'composition': well.composition, 'input_volume': 1, 'percentage_of_output': 1 / 9.0 }) pool_process = PoolingProcess.create(user, quant_process, 'New test pool name %s' % datetime.now(), 4, input_compositions, func_data, robot=echo) return pool_process
def get(self): pool_type = 'shotgun_plate' plate_ids = self.get_arguments('plate_id') process_id = self.get_argument('process_id', None) input_plate = None pool_func_data = None pool_values = [] pool_blanks = [] plate_names = [] if process_id is not None: try: process = PoolingProcess(process_id) except LabControlUnknownIdError: raise HTTPError(404, reason="Pooling process %s doesn't exist" % process_id) plate = process.components[0][0].container.plate input_plate = plate.id pool_func_data = process.pooling_function_data content_type = type(plate.get_well(1, 1).composition) id_plate_type = PLATE_TYPES[content_type] plate_type_mapped = PLATE_TYPE_TO_POOL_TYPE[id_plate_type] if plate_type_mapped != pool_type: raise HTTPError(400, reason='Pooling process type does not ' 'match pooling type') _, pool_values, pool_blanks, plate_names = \ make_2D_arrays(plate, process.quantification_process) pool_values = pool_values.tolist() pool_blanks = pool_blanks.tolist() plate_names = plate_names.tolist() elif len(plate_ids) > 0: content_types = {type(Plate(pid).get_well(1, 1).composition) for pid in plate_ids} if len(content_types) > 1: raise HTTPError(400, reason='Plates contain different types ' 'of compositions') # check if the observed plates are the same type as the pooling # type (i.e., no shotgun plates for 16S pooling) content_type = content_types.pop() id_plate_type = PLATE_TYPES[content_type] plate_type_mapped = PLATE_TYPE_TO_POOL_TYPE[id_plate_type] if plate_type_mapped != pool_type: raise HTTPError(400, reason='Plate type does not match ' 'pooling type') pool_type_stripped = POOL_TYPE_PARAMS[pool_type]['abbreviation'] plate_type = POOL_TYPE_TO_PLATE_TYPE[pool_type] robots = (Equipment.list_equipment('EpMotion') + Equipment.list_equipment('echo')) template = POOL_TYPE_PARAMS[pool_type]['template'] self.render(template, plate_ids=plate_ids, robots=robots, pool_params=HTML_POOL_PARAMS, input_plate=input_plate, pool_func_data=pool_func_data, process_id=process_id, pool_values=pool_values, plate_type=plate_type, pool_blanks=pool_blanks, plate_names=plate_names, pool_type=pool_type_stripped)
def _compute_pools(self, plate_info): plate_id = plate_info['plate-id'] func_name = plate_info['pool-func'] plate_type = plate_info['plate-type'] quant_process_id = plate_info['quant-process-id'] func_info = POOL_FUNCS[func_name] function = func_info['function'] plate = Plate(plate_id) quant_process = QuantificationProcess(quant_process_id) # make params dictionary for function params = {} for arg, pfx in func_info['parameters']: param_key = '%s%s' % (pfx, plate_id) if param_key not in plate_info: raise HTTPError( 400, reason='Missing parameter %s' % param_key) # empty strings are sent when we have disabled inputs. # we are testing for them explicitly where expected. if plate_info[param_key] != '': params[arg] = float(plate_info[param_key]) else: params[arg] = plate_info[param_key] # compute molar concentrations quant_process.compute_concentrations(size=params['size']) # calculate pooled values raw_concs, comp_concs, comp_blanks, \ plate_names = make_2D_arrays(plate, quant_process) # for 16S, we calculate each sample independently pool_type = PLATE_TYPE_TO_POOL_TYPE[plate_type] params['total_each'] = POOL_TYPE_PARAMS[pool_type]['total_each'] params['vol_constant'] = POOL_TYPE_PARAMS[pool_type]['vol_constant'] pool_vals = function(raw_concs, **params) # if adjust blank volume, do that if params['blank_vol'] != '': pool_vals = PoolingProcess.adjust_blank_vols(pool_vals, comp_blanks, params['blank_vol']) # if only pool some blanks, do that if params['blank_num'] != '': pool_vals = PoolingProcess.select_blanks(pool_vals, raw_concs, comp_blanks, int(params['blank_num'])) # estimate pool volume and concentration total_c, total_v = PoolingProcess.estimate_pool_conc_vol(pool_vals, comp_concs) # store output values output = {} output['func_data'] = {'function': func_name, 'parameters': params} output['raw_vals'] = raw_concs output['comp_vals'] = comp_concs output['pool_vals'] = pool_vals output['pool_blanks'] = comp_blanks.tolist() output['plate_names'] = plate_names.tolist() output['plate_id'] = plate_id output['destination'] = params['destination'] output['robot'] = params['robot'] output['blank_vol'] = params['blank_vol'] output['blank_num'] = params['blank_num'] output['total_conc'] = total_c output['total_vol'] = total_v output['quant-process-id'] = quant_process_id return output