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_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 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 LabControlUnknownIdError: 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 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(3121)) self.assertEqual(tester.get_well(7, 2), Well(3157)) self.assertEqual(tester.get_well(8, 12), Well(3643)) with self.assertRaises(LabControlError): tester.get_well(8, 13) with self.assertRaises(LabControlError): tester.get_well(9, 12)
def _compute_pools(self, plate_info): self.plate_id = plate_info['plate-id'] self.func_name = plate_info['pool-func'] self.plate_type = plate_info['plate-type'] self.quant_process_id = plate_info['quant-process-id'] func_info = POOL_FUNCS[self.func_name] self.function = func_info['function'] plate = Plate(self.plate_id) quant_process = QuantificationProcess(self.quant_process_id) # make params dictionary for function params = {} for arg, pfx in func_info['parameters']: param_key = '%s%s' % (pfx, self.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] self.params = params # compute molar concentrations quant_process.compute_concentrations(size=params['size']) # calculate pooled values self.raw_concs, self.comp_concs, self.comp_blanks, \ self.plate_names = make_2D_arrays(plate, quant_process)
def post(self): user = self.current_user plates_info = self.get_argument('plates_info') volume = self.get_argument('volume') kapa_hyperplus_kit = self.get_argument('kapa_hyperplus_kit') stub_lot = self.get_argument('stub_lot') processes = [[ pid, LibraryPrepShotgunProcess.create( user, Plate(pid), plate_name, ReagentComposition.from_external_id(kapa_hyperplus_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_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 test_plate_list_include_timestamp(self): # ...limit pathological failures by testing within an hour of creation exp = datetime.datetime.now() exp = str(datetime.datetime(exp.year, exp.month, exp.day)).split(None, 1)[0] for i in Plate.list_plates(): obs = i['creation_timestamp'].split(None, 1)[0] self.assertEqual(obs, exp)
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 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 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 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)
def post(self): plates_info = json_decode(self.get_argument('plates-info')) processes = [] for pinfo in plates_info: plate = Plate(pinfo['plate_id']) concentrations = np.asarray(pinfo['concentrations']) processes.append(QuantificationProcess.create( self.current_user, plate, concentrations).id) self.write({'processes': processes})
def post(self): plates = self.get_argument('plates') plate_ext_id = self.get_argument('plate_ext_id') robot = self.get_argument('robot') plates = [Plate(pid) for pid in json_decode(plates)] process = GDNAPlateCompressionProcess.create(self.current_user, plates, plate_ext_id, Equipment(robot)) self.write({'process': process.id})
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' # Test success - discarded plate_handler_patch_request(user, 21, 'replace', '/discarded/', True, None) self.assertEqual(tester.discarded, True) tester.discarded = False
def get(self): plate_ids = self.get_arguments('plate_id') process_id = self.get_argument('process_id', None) kapa = None stub = None volume = None norm_plate = None i5plate = None i7plate = None if process_id is not None: try: process = LibraryPrepShotgunProcess(process_id) except LabControlUnknownIdError: raise HTTPError(404, reason="Shotgun library prep process %s " "doesn't exist" % process_id) kapa = process.kapa_hyperplus_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 = [] for pp in Plate.list_plates(['primer']): plate = Plate(pp['plate_id']) if plate.process.primer_set.target_name == 'Shotgun': primer_plates.append(pp) self.render('library_prep_shotgun.html', plate_ids=plate_ids, primer_plates=primer_plates, process_id=process_id, kapa=kapa, stub=stub, volume=volume, norm_plate=norm_plate, i5plate=i5plate, i7plate=i7plate)
def test_create(self): plate_conf = PlateConfiguration.create('96-well Test desc', 8, 12) obs = Plate.create('New plate', plate_conf) self.assertEqual(obs.external_id, 'New plate') self.assertEqual(obs.plate_configuration, plate_conf) self.assertFalse(obs.discarded) self.assertIsNone(obs.notes) # This is a weird case and it should never happen in normal execution # of the code: the plate has been created without any well, hence all # the None values. In reality, all the plate creation is handled # by one of the Process classes, which ensures the creation of all # the wells. self.assertEqual(obs.layout, [[None] * 12] * 8)
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 get(self, plate_id): plate = Plate(plate_id) quant_processes = plate.quantification_processes quant_values = [] for quant in quant_processes: concentrations = np.zeros_like(plate.layout, dtype=float) names = np.empty_like(plate.layout, dtype='object') blanks = np.zeros_like(plate.layout, dtype=bool) # Get sample_composition (`smp`) from each well. # Currently this requires ugly logic because you have to go # through a named subclass that depends on plate type. # TODO: replace this with a direct SQL query in a dedicated # method of the composition class. for comp, raw_conc, _ in quant.concentrations: container = comp.container row, col = container.row - 1, container.column - 1 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[row, col] = smp.sample_composition_type == 'blank' names[row, col] = smp.sample_id concentrations[row, col] = raw_conc quant_values.append({'quant_id': quant.id, 'person': quant.personnel.name, 'date': quant.date.isoformat(), 'notes': quant.notes, 'concs': concentrations.tolist(), 'blanks': blanks.tolist(), 'names': names.tolist()}) self.render('view_quantifications.html', quantifications=quant_values, plate_type=plate.process._process_type, plate_name=plate.external_id)
def post(self): plate_comment_keywords = self.get_argument("plate_comment_keywords") well_comment_keywords = self.get_argument("well_comment_keywords") operation = self.get_argument("operation") sample_names = json_decode(self.get_argument('sample_names')) res = { "data": [[p.id, p.external_id] for p in Plate.search(samples=sample_names, plate_notes=plate_comment_keywords, well_notes=well_comment_keywords, query_type=operation)] } self.write(res)
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 rows_list = [[ p['plate_id'], p['external_id'], p['creation_timestamp'], p['studies'] if p['studies'] is not None else [] ] for p in Plate.list_plates(plate_type, only_quantified=only_quantified, include_study_titles=True)] res = {"data": rows_list} self.write(res)
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 _get_plate(plate_id): """Returns the plate object if it exists Parameters ---------- plate_id : str The plate id Raises ------ HTTPError 404, if the plate doesn't exist """ plate_id = int(plate_id) try: plate = Plate(plate_id) except LabControlUnknownIdError: raise HTTPError(404, 'Plate %s doesn\'t exist' % plate_id) return plate
def post(self): plates_info = self.get_argument('plates_info') extraction_date = self.get_argument('extraction_date') volume = self.get_argument('volume') month, day, year = map(int, extraction_date.split('/')) extraction_date = date(year, month, day) # We create one process per plate processes = [] for pid, ee, kf, ep, ept, kit, p_name, nt in json_decode(plates_info): # Check whether plate was externally extracted if ee is True: # find the id of null things eq_no = \ Equipment.list_equipment('Not applicable')[0]['equipment_id'] ep = ept = kf = Equipment(eq_no) kit = ReagentComposition.from_external_id('Not applicable') else: kf = Equipment(kf) ep = Equipment(ep) ept = Equipment(ept) kit = ReagentComposition.from_external_id(kit) processes.append( GDNAExtractionProcess.create(self.current_user, Plate(pid), kf, ep, ept, kit, volume, p_name, externally_extracted=ee, extraction_date=extraction_date, notes=nt).id) self.write({'processes': processes})
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
def test_get_plate_handler(self): response = self.get('/plate/21/') self.assertEqual(response.code, 200) obs = json_decode(response.body) exp = { 'plate_id': 21, 'plate_name': 'Test plate 1', 'discarded': False, 'plate_configuration': [1, '96-well deep-well plate', 8, 12], 'notes': None, 'studies': [1], 'duplicates': [[1, 1, '1.SKB1.640202.Test.plate.1.A1'], [1, 2, '1.SKB1.640202.Test.plate.1.A2'], [1, 3, '1.SKB1.640202.Test.plate.1.A3'], [1, 4, '1.SKB1.640202.Test.plate.1.A4'], [1, 5, '1.SKB1.640202.Test.plate.1.A5'], [1, 6, '1.SKB1.640202.Test.plate.1.A6'], [1, 7, '1.SKB1.640202.Test.plate.1.A7'], [1, 8, '1.SKB1.640202.Test.plate.1.A8'], [1, 9, '1.SKB1.640202.Test.plate.1.A9'], [1, 10, '1.SKB1.640202.Test.plate.1.A10'], [1, 11, '1.SKB1.640202.Test.plate.1.A11'], [1, 12, '1.SKB1.640202.Test.plate.1.A12'], [2, 1, '1.SKB2.640194.Test.plate.1.B1'], [2, 2, '1.SKB2.640194.Test.plate.1.B2'], [2, 3, '1.SKB2.640194.Test.plate.1.B3'], [2, 4, '1.SKB2.640194.Test.plate.1.B4'], [2, 5, '1.SKB2.640194.Test.plate.1.B5'], [2, 6, '1.SKB2.640194.Test.plate.1.B6'], [2, 7, '1.SKB2.640194.Test.plate.1.B7'], [2, 8, '1.SKB2.640194.Test.plate.1.B8'], [2, 9, '1.SKB2.640194.Test.plate.1.B9'], [2, 10, '1.SKB2.640194.Test.plate.1.B10'], [2, 11, '1.SKB2.640194.Test.plate.1.B11'], [2, 12, '1.SKB2.640194.Test.plate.1.B12'], [3, 1, '1.SKB3.640195.Test.plate.1.C1'], [3, 2, '1.SKB3.640195.Test.plate.1.C2'], [3, 3, '1.SKB3.640195.Test.plate.1.C3'], [3, 4, '1.SKB3.640195.Test.plate.1.C4'], [3, 5, '1.SKB3.640195.Test.plate.1.C5'], [3, 6, '1.SKB3.640195.Test.plate.1.C6'], [3, 7, '1.SKB3.640195.Test.plate.1.C7'], [3, 8, '1.SKB3.640195.Test.plate.1.C8'], [3, 9, '1.SKB3.640195.Test.plate.1.C9'], [3, 10, '1.SKB3.640195.Test.plate.1.C10'], [3, 11, '1.SKB3.640195.Test.plate.1.C11'], [3, 12, '1.SKB3.640195.Test.plate.1.C12'], [4, 1, '1.SKB4.640189.Test.plate.1.D1'], [4, 2, '1.SKB4.640189.Test.plate.1.D2'], [4, 3, '1.SKB4.640189.Test.plate.1.D3'], [4, 4, '1.SKB4.640189.Test.plate.1.D4'], [4, 5, '1.SKB4.640189.Test.plate.1.D5'], [4, 6, '1.SKB4.640189.Test.plate.1.D6'], [4, 7, '1.SKB4.640189.Test.plate.1.D7'], [4, 8, '1.SKB4.640189.Test.plate.1.D8'], [4, 9, '1.SKB4.640189.Test.plate.1.D9'], [4, 10, '1.SKB4.640189.Test.plate.1.D10'], [4, 11, '1.SKB4.640189.Test.plate.1.D11'], [4, 12, '1.SKB4.640189.Test.plate.1.D12'], [5, 1, '1.SKB5.640181.Test.plate.1.E1'], [5, 2, '1.SKB5.640181.Test.plate.1.E2'], [5, 3, '1.SKB5.640181.Test.plate.1.E3'], [5, 4, '1.SKB5.640181.Test.plate.1.E4'], [5, 5, '1.SKB5.640181.Test.plate.1.E5'], [5, 6, '1.SKB5.640181.Test.plate.1.E6'], [5, 7, '1.SKB5.640181.Test.plate.1.E7'], [5, 8, '1.SKB5.640181.Test.plate.1.E8'], [5, 9, '1.SKB5.640181.Test.plate.1.E9'], [5, 10, '1.SKB5.640181.Test.plate.1.E10'], [5, 11, '1.SKB5.640181.Test.plate.1.E11'], [5, 12, '1.SKB5.640181.Test.plate.1.E12'], [6, 1, '1.SKB6.640176.Test.plate.1.F1'], [6, 2, '1.SKB6.640176.Test.plate.1.F2'], [6, 3, '1.SKB6.640176.Test.plate.1.F3'], [6, 4, '1.SKB6.640176.Test.plate.1.F4'], [6, 5, '1.SKB6.640176.Test.plate.1.F5'], [6, 6, '1.SKB6.640176.Test.plate.1.F6'], [6, 7, '1.SKB6.640176.Test.plate.1.F7'], [6, 8, '1.SKB6.640176.Test.plate.1.F8'], [6, 9, '1.SKB6.640176.Test.plate.1.F9'], [6, 10, '1.SKB6.640176.Test.plate.1.F10'], [6, 11, '1.SKB6.640176.Test.plate.1.F11']], 'previous_plates': [ [[1, 1], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[2, 1], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[3, 1], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[4, 1], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[5, 1], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[6, 1], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[1, 2], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[2, 2], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[3, 2], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[4, 2], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[5, 2], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[6, 2], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[1, 3], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[2, 3], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[3, 3], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[4, 3], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[5, 3], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[6, 3], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[1, 4], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[2, 4], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[3, 4], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[4, 4], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[5, 4], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[6, 4], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[1, 5], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[2, 5], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[3, 5], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[4, 5], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[5, 5], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[6, 5], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[1, 6], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[2, 6], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[3, 6], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[4, 6], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[5, 6], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[6, 6], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[1, 7], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[2, 7], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[3, 7], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[4, 7], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[5, 7], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[6, 7], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[1, 8], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[2, 8], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[3, 8], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[4, 8], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[5, 8], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[6, 8], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[1, 9], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[2, 9], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[3, 9], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[4, 9], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[5, 9], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[6, 9], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[1, 10], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[2, 10], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[3, 10], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[4, 10], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[5, 10], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[6, 10], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[1, 11], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[2, 11], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[3, 11], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[4, 11], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[5, 11], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[6, 11], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[1, 12], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[2, 12], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[3, 12], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[4, 12], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], [[5, 12], [{ 'plate_id': 27, 'plate_name': 'Test plate 2' }, { 'plate_id': 30, 'plate_name': 'Test plate 3' }, { 'plate_id': 33, 'plate_name': 'Test plate 4' }]], ], 'process_notes': None, 'unknowns': [], 'quantitation_processes': [] } obs_duplicates = obs.pop('duplicates') exp_duplicates = exp.pop('duplicates') self.assertEqual(obs, exp) self.assertCountEqual(obs_duplicates, exp_duplicates) # Plate doesn't exist response = self.get('/plate/100/') self.assertEqual(response.code, 404) # Plate has multiple quantitation processes # Note: cannot hard-code the date in the below known-good object # because date string representation is specific to physical location # in which system running the tests is located! tester = Plate(26) first_date_str = datetime.strftime( tester.quantification_processes[0].date, Process.get_date_format()) second_date_str = datetime.strftime( tester.quantification_processes[1].date, Process.get_date_format()) response = self.get('/plate/26/') self.assertEqual(response.code, 200) obs = json_decode(response.body) exp = { 'plate_id': 26, 'plate_name': 'Test shotgun library plates 1-4', 'discarded': False, 'plate_configuration': [3, '384-well microtiter plate', 16, 24], 'notes': None, 'studies': [1], 'duplicates': [], 'previous_plates': [], 'process_notes': None, 'unknowns': [], 'quantitation_processes': [[4, 'Dude', first_date_str, None], [5, 'Dude', second_date_str, 'Requantification--oops']] } obs_duplicates = obs.pop('duplicates') exp_duplicates = exp.pop('duplicates') self.assertEqual(obs, exp) self.assertCountEqual(obs_duplicates, exp_duplicates)
def test_get_plate(self): self.assertEqual(_get_plate('21'), Plate(21)) regex = 'Plate 100 doesn\'t exist' with self.assertRaisesRegex(HTTPError, regex): _get_plate(100)