def test_plate_handler_patch_request(self): tester = Plate(21) user = User('*****@*****.**') # Incorrect path parameter regex = 'Incorrect path parameter' with self.assertRaisesRegex(HTTPError, regex): plate_handler_patch_request(user, 21, 'replace', '/name/newname', 'NewName', None) # Unknown attribute regex = 'Attribute unknown not recognized' with self.assertRaisesRegex(HTTPError, regex): plate_handler_patch_request(user, 21, 'replace', '/unknown/', 'NewName', None) # Unknown operation regex = ('Operation add not supported. Current supported ' 'operations: replace') with self.assertRaisesRegex(HTTPError, regex): plate_handler_patch_request(user, 21, 'add', '/name/', 'NewName', None) # Plate doesn't exist regex = 'Plate 100 doesn\'t exist' with self.assertRaisesRegex(HTTPError, regex): plate_handler_patch_request(user, 100, 'replace', '/name/', 'NewName', None) # Test success - Name plate_handler_patch_request(user, 21, 'replace', '/name/', 'NewName', None) self.assertEqual(tester.external_id, 'NewName') tester.external_id = 'Test plate 1'
def test_patch_plate_handler(self): tester = Plate(21) data = {'op': 'replace', 'path': '/name/', 'value': 'NewName'} response = self.patch('/plate/21/', data) self.assertEqual(response.code, 200) self.assertEqual(tester.external_id, 'NewName') tester.external_id = 'Test plate 1'
def post(self): plates_info = self.get_argument('plates_info') volume = self.get_argument('volume') preparation_date = self.get_argument('preparation_date') month, day, year = map(int, preparation_date.split('/')) preparation_date = date(year, month, day) processes = [ LibraryPrep16SProcess.create( self.current_user, Plate(pid), Plate(pp), pn, Equipment(ep), Equipment(ep300), Equipment(ep50), ReagentComposition.from_external_id(mm), ReagentComposition.from_external_id(w), volume, preparation_date=preparation_date).id for pid, pn, pp, ep, ep300, ep50, mm, w in json_decode(plates_info) ] self.write({'processes': processes})
def get_primer_plate(is_96): plates = [ Plate(item['plate_id']) for item in Plate.list_plates(['primer']) ] if is_96: # use different plates for 16S shuffle(plates) hit = None for plate in plates: pc = plate.plate_configuration if pc.description == '96-well microtiter plate': hit = plate break if hit is None: raise ValueError("Unable to identify a primer plate") return (hit, None) else: # not shuffling as the order of the plates implicitly matters hits = [] for plate in plates: pc = plate.plate_configuration if pc.description == '384-well microtiter plate': if 'primer' in plate.external_id.lower(): hits.append(plate) hits = hits[:2] if len(hits) != 2: raise ValueError("Unable to identify two primer plates") return hits
def test_patch_plate_discarded_handler(self): tester = Plate(21) data = {'op': 'replace', 'path': '/discarded/', 'value': True} response = self.patch('/plate/21/', data) self.assertEqual(response.code, 200) self.assertEqual(tester.discarded, True) tester.discarded = False
def test_get_wells_by_sample(self): tester = Plate(21) exp = [Well(3073), Well(3121), Well(3169), Well(3217), Well(3265), Well(3313), Well(3361), Well(3409), Well(3457), Well(3505), Well(3553), Well(3601)] self.assertEqual(tester.get_wells_by_sample('1.SKB1.640202'), exp) self.assertEqual(tester.get_wells_by_sample('1.SKM1.640183'), [])
def create_shotgun_process(user, norm_plate): kappa = ReagentComposition(4) stub = ReagentComposition(5) shotgun_process = LibraryPrepShotgunProcess.create( user, norm_plate, 'Test Shotgun Library %s' % datetime.now(), kappa, stub, 4000, Plate(19), Plate(20)) shotgun_plate = shotgun_process.plates[0] return shotgun_process, shotgun_plate
def get(self): plate_type = self.get_argument('plate_type', None) res = { "data": [[ p['plate_id'], p['external_id'], [s.title for s in Plate(p['plate_id']).studies] ] for p in Plate.list_plates(plate_type)] } self.write(res)
def get(self): plate_ids = self.get_arguments('plate_id') process_id = self.get_argument('process_id', None) gdna_plate = None epmotion = None epmotion_tm300 = None epmotion_tm50 = None primer_plate = None master_mix = None water_lot = None volume = None prep_date = None if process_id is not None: try: process = LibraryPrep16SProcess(process_id) except LabmanUnknownIdError: raise HTTPError(404, reason="Amplicon process %s doesn't exist" % process_id) gdna_plate = process.gdna_plate.id epmotion = process.epmotion.id epmotion_tm300 = process.epmotion_tm300_tool.id epmotion_tm50 = process.epmotion_tm50_tool.id master_mix = process.mastermix.external_lot_id water_lot = process.water_lot.external_lot_id primer_plate = process.primer_plate.id volume = process.volume prep_date = process.date.strftime(process.get_date_format()) robots = Equipment.list_equipment('EpMotion') tools_tm300_8 = Equipment.list_equipment( 'tm 300 8 channel pipette head') tools_tm50_8 = Equipment.list_equipment('tm 50 8 channel pipette head') primer_plates = [] for pp in Plate.list_plates(['primer']): plate = Plate(pp['plate_id']) if plate.process.primer_set.target_name == 'Amplicon': primer_plates.append(pp) self.render('library_prep_16S.html', plate_ids=plate_ids, robots=robots, tools_tm300_8=tools_tm300_8, tools_tm50_8=tools_tm50_8, primer_plates=primer_plates, process_id=process_id, gdna_plate=gdna_plate, epmotion=epmotion, epmotion_tm300=epmotion_tm300, epmotion_tm50=epmotion_tm50, master_mix=master_mix, water_lot=water_lot, primer_plate=primer_plate, preparationDate=prep_date, volume=volume)
def get(self): plate_type = self.get_argument('plate_type', None) only_quantified = self.get_argument('only_quantified', False) plate_type = (json_decode(plate_type) if plate_type is not None else None) only_quantified = True if only_quantified == 'true' else False res = {"data": [[p['plate_id'], p['external_id'], [s.title for s in Plate(p['plate_id']).studies]] for p in Plate.list_plates( plate_type, only_quantified=only_quantified)]} self.write(res)
def test_get_wells_by_sample(self): tester = Plate(21) exp = [ Well(3073), Well(3253), Well(3433), Well(3613), Well(3793), Well(3973) ] self.assertEqual(tester.get_wells_by_sample('1.SKB1.640202'), exp) self.assertEqual(tester.get_wells_by_sample('1.SKM1.640183'), [])
def test_primer_set_attributes(self): obs = PrimerSet(1) self.assertEqual(obs.external_id, 'EMP 16S V4 primer set') self.assertEqual(obs.target_name, 'Amplicon') self.assertIsNone(obs.notes) self.assertEqual(obs.plates, [Plate(1), Plate(2), Plate(3), Plate(4), Plate(5), Plate(6), Plate(7), Plate(8)])
def test_get_well(self): # Plate 21 - Defined in the test DB tester = Plate(21) self.assertEqual(tester.get_well(1, 1), Well(3073)) self.assertEqual(tester.get_well(1, 2), Well(3088)) self.assertEqual(tester.get_well(7, 2), Well(4168)) self.assertEqual(tester.get_well(8, 12), Well(4498)) with self.assertRaises(LabmanError): tester.get_well(8, 13) with self.assertRaises(LabmanError): tester.get_well(9, 12)
def post(self): plate_id = self.get_argument('plate-id') concentrations = json_decode(self.get_argument('concentrations')) concentrations = np.asarray(concentrations) plate = Plate(plate_id) q_process = QuantificationProcess.create(self.current_user, plate, concentrations) pool_name = 'Pool - %s' % plate.external_id input_compositions = [] concentrations = q_process.concentrations total_vol = 0 for conc in concentrations: in_vol = conc[1] total_vol += in_vol input_compositions.append({ 'composition': conc[0], 'input_volume': in_vol, 'percentage_of_output': in_vol }) for ic in input_compositions: ic['percentage_of_output'] = ic['percentage_of_output'] / total_vol process = PoolingProcess.create(self.current_user, q_process, pool_name, total_vol, input_compositions) self.write({'process': process.id})
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']) pool_name = 'Pool from plate %s (%s)' % ( plate.external_id, datetime.now().strftime('%Y-%m-%d %H:%M:%S')) # create input molar percentages pcts = calc_pool_pcts(plate_result['comp_vals'], plate_result['pool_vals']) quant_process = plate.quantification_process 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 get(self): plate_ids = self.get_arguments('plate_id') plates = [[p['plate_id'], p['external_id']] for p in Plate.list_plates('16S library prep')] self.render('parse_quantification.html', plate_ids=plate_ids, plates=plates)
def post(self): user = self.current_user plates_info = self.get_argument('plates_info') water = self.get_argument('water') total_vol = self.get_argument('total_vol') ng = self.get_argument('ng') min_vol = self.get_argument('min_vol') max_vol = self.get_argument('max_vol') resolution = self.get_argument('resolution') reformat = self.get_argument('reformat') processes = [[ plate_id, NormalizationProcess.create( user, Plate(plate_id).quantification_process, ReagentComposition.from_external_id(water), plate_name, total_vol=float(total_vol), ng=float(ng), min_vol=float(min_vol), max_vol=float(max_vol), resolution=float(resolution), reformat=reformat).id ] for plate_id, plate_name in json_decode(plates_info)] self.write({'processes': processes})
def post(self): user = self.current_user plate_id = self.get_argument('plate_id') water = self.get_argument('water') plate_name = self.get_argument('plate_name') total_vol = self.get_argument('total_vol') ng = self.get_argument('ng') min_vol = self.get_argument('min_vol') max_vol = self.get_argument('max_vol') resolution = self.get_argument('resolution') reformat = self.get_argument('reformat') qprocess = Plate(plate_id).quantification_process process = NormalizationProcess.create( user, qprocess, ReagentComposition.from_external_id(water), plate_name, total_vol=float(total_vol), ng=float(ng), min_vol=float(min_vol), max_vol=float(max_vol), resolution=float(resolution), reformat=reformat) self.write({'process': process.id})
def get(self): plate_ids = self.get_arguments('plate_id') process_id = self.get_argument('process_id', None) kappa = None stub = None volume = None norm_plate = None i5plate = None i7plate = None if process_id is not None: try: process = LibraryPrepShotgunProcess(process_id) except LabmanUnknownIdError: raise HTTPError(404, reason="Shotgun library prep process %s " "doesn't exist" % process_id) kappa = process.kappa_hyper_plus_kit.external_lot_id stub = process.stub_lot.external_lot_id norm_plate = process.normalized_plate.id i5plate = process.i5_primer_plate.id i7plate = process.i7_primer_plate.id volume = process.volume primer_plates = Plate.list_plates(['primer']) self.render('library_prep_shotgun.html', plate_ids=plate_ids, primer_plates=primer_plates, process_id=process_id, kappa=kappa, stub=stub, volume=volume, norm_plate=norm_plate, i5plate=i5plate, i7plate=i7plate)
def post(self): plate_name = self.get_argument('plate_name') volume = self.get_argument('volume') plate = self.get_argument('plate') i5_plate = self.get_argument('i5_plate') i7_plate = self.get_argument('i7_plate') kappa_hyper_plus_kit = self.get_argument('kappa_hyper_plus_kit') stub_lot = self.get_argument('stub_lot') process = LibraryPrepShotgunProcess.create( self.current_user, Plate(plate), plate_name, ReagentComposition.from_external_id(kappa_hyper_plus_kit), ReagentComposition.from_external_id(stub_lot), volume, Plate(i5_plate), Plate(i7_plate)) self.write({'process': process.id})
def post(self): user = self.current_user plates_info = self.get_argument('plates_info') volume = self.get_argument('volume') kappa_hyper_plus_kit = self.get_argument('kappa_hyper_plus_kit') stub_lot = self.get_argument('stub_lot') processes = [[ pid, LibraryPrepShotgunProcess.create( user, Plate(pid), plate_name, ReagentComposition.from_external_id(kappa_hyper_plus_kit), ReagentComposition.from_external_id(stub_lot), volume, Plate(i5p), Plate(i7p)).id ] for pid, plate_name, i5p, i7p in json_decode(plates_info)] self.write({'processes': processes})
def test_properties(self): # Plate 21 - Defined in the test DB tester = Plate(21) self.assertEqual(tester.external_id, 'Test plate 1') self.assertEqual(tester.plate_configuration, PlateConfiguration(1)) self.assertFalse(tester.discarded) self.assertIsNone(tester.notes) obs_layout = tester.layout self.assertEqual(len(obs_layout), 8) for row in obs_layout: self.assertEqual(len(row), 12) # Test changing the name of the plate tester.external_id = 'Some new name' self.assertEqual(tester.external_id, 'Some new name') tester.external_id = 'Test plate 1' self.assertEqual(tester.external_id, 'Test plate 1')
def _compute_pools(self, plate_info): plate_id = plate_info['plate-id'] func_name = plate_info['pool-func'] func_info = POOL_FUNCS[func_name] function = func_info['function'] plate = Plate(plate_id) quant_process = plate.quantification_process output = {} if func_name == 'amplicon': 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) if arg in ('robot', 'destination'): params[arg] = plate_info[param_key] else: params[arg] = float(plate_info[param_key]) # Amplicon output['robot'] = params.pop('robot') output['destination'] = params.pop('destination') output['func_data'] = {'function': 'amplicon', 'parameters': params} # Compute the normalized concentrations quant_process.compute_concentrations(**params) # Compute the pooling values raw_concs, comp_concs = make_2D_arrays(plate, quant_process) output['raw_vals'] = raw_concs output['comp_vals'] = comp_concs output['pool_vals'] = comp_concs else: # Shotgun 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) params[arg] = float(plate_info[param_key]) # Compute the normalized concentrations output['func_data'] = {'function': func_name, 'parameters': deepcopy(params)} size = params.pop('size') quant_process.compute_concentrations(size=size) # Compute the pooling values raw_concs, comp_concs = make_2D_arrays(plate, quant_process) output['raw_vals'] = raw_concs output['comp_vals'] = comp_concs output['pool_vals'] = function(comp_concs, **params) output['robot'] = None output['destination'] = None # Make sure the results are JSON serializable output['plate_id'] = plate_id output['pool_vals'] = output['pool_vals'] return output
def post(self): # We will receive as many files as plates the user has selected # The key of the self.request.files dictionary is of the form # plate-file-<PLATE_ID> so use the keys to know the plates # that we need to quantify plates = [] for key in self.request.files: plate_id = key.rsplit('-', 1)[1] # The 0 is because for each key we have a single file file_content = self.request.files[key][0]['body'].decode('utf-8') plate = Plate(plate_id) pc = plate.plate_configuration concentrations = QuantificationProcess.parse(file_content, rows=pc.num_rows, cols=pc.num_columns) names = np.empty_like(plate.layout, dtype='object') blanks = np.zeros_like(plate.layout, dtype=bool) # fetch the sample names and whether or not the samples are blanks # by default these are set to be None and False. for i, full_row in enumerate(plate.layout): for j, well in enumerate(full_row): # some wells have no compositions at all so skip those if well is None: continue comp = well.composition # cache the sample compositions to avoid extra intermediate # queries if isinstance(comp, GDNAComposition): smp = comp.sample_composition elif isinstance(comp, (CompressedGDNAComposition, LibraryPrep16SComposition)): smp = comp.gdna_composition.sample_composition elif isinstance(comp, LibraryPrepShotgunComposition): smp = comp.normalized_gdna_composition\ .compressed_gdna_composition.gdna_composition\ .sample_composition else: raise ValueError('This composition type is not ' 'supported') blanks[i][j] = smp.sample_composition_type == 'blank' names[i][j] = smp.sample_id plates.append({ 'plate_name': plate.external_id, 'plate_id': plate_id, 'concentrations': concentrations.tolist(), 'names': names.tolist(), 'blanks': blanks.tolist(), 'type': plate.process._process_type }) self.render('quantification.html', plates=plates)
def test_get_previously_plated_wells(self): tester = Plate(21) self.assertEqual(tester.get_previously_plated_wells(), {}) # Create another plate and plate some samples in it spp = SamplePlatingProcess.create( User('*****@*****.**'), PlateConfiguration(1), 'New Plate For Prev') spp.update_well(1, 1, '1.SKD1.640179') exp = {} plate = spp.plate exp[Well(3208)] = [plate] exp[Well(3388)] = [plate] exp[Well(3568)] = [plate] exp[Well(3748)] = [plate] exp[Well(3928)] = [plate] exp[Well(4108)] = [plate] obs = tester.get_previously_plated_wells() self.assertEqual(obs, exp)
def post(self): master_mix = self.get_argument('master_mix') water = self.get_argument('water') robot = self.get_argument('robot') tm300_8_tool = self.get_argument('tm300_8_tool') tm50_8_tool = self.get_argument('tm50_8_tool') volume = self.get_argument('volume') plates = self.get_argument('plates') plates = [(Plate(pid), Plate(ppid)) for pid, ppid in json_decode(plates)] process = LibraryPrep16SProcess.create( self.current_user, ReagentComposition.from_external_id(master_mix), ReagentComposition.from_external_id(water), Equipment(robot), Equipment(tm300_8_tool), Equipment(tm50_8_tool), volume, plates) self.write({'process': process.id})
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 LabmanUnknownIdError: 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, plate.quantification_process) pool_values = pool_values.tolist() pool_blanks = pool_blanks.tolist() plate_names = plate_names.tolist() if pool_func_data['function'] == 'amplicon': pool_func_data['parameters']['epmotion-'] = process.robot.id pool_func_data['parameters'][ 'dest-tube-'] = process.destination 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') epmotions = Equipment.list_equipment('EpMotion') self.render('library_pooling.html', plate_ids=plate_ids, epmotions=epmotions, 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 test_properties(self): tester = Well(3073) self.assertEqual(tester.plate, Plate(21)) self.assertEqual(tester.row, 1) self.assertEqual(tester.column, 1) self.assertEqual(tester.remaining_volume, 10) self.assertIsNone(tester.notes) self.assertEqual(tester.latest_process, SamplePlatingProcess(11)) self.assertEqual(tester.container_id, 3082) self.assertEqual(tester.composition, SampleComposition(1))
def post(self): plates = self.get_argument('plates') plate_ext_id = self.get_argument('plate_ext_id') plates = [Plate(pid) for pid in json_decode(plates)] process = GDNAPlateCompressionProcess.create(self.current_user, plates, plate_ext_id) self.write({'process': process.id})
def get(self, plate_id): urls = { SamplePlatingProcess: '/plate', GDNAExtractionProcess: '/process/gdna_extraction', LibraryPrep16SProcess: '/process/library_prep_16S', LibraryPrepShotgunProcess: '/process/library_prep_shotgun', NormalizationProcess: '/process/normalize', GDNAPlateCompressionProcess: '/process/gdna_compression'} process = Plate(plate_id).process self.redirect(urls[process.__class__] + '?process_id=%s' % process.id)