def setUp(self): self.one = one self.eid = "b1c968ad-4874-468d-b2e4-5ffa9b9964e9" # Make sure the data exists locally self.session_path = self.one.path_from_eid(self.eid) self.qc = TaskQC(self.eid, one=one) self.qc.load_data(bpod_only=True) # Test session has no raw FPGA data
def _run(self): dsets, out_files = ephys_fpga.extract_all(self.session_path, save=True) self._behaviour_criterion() # Run the task QC qc = TaskQC(self.session_path, one=self.one, log=_logger) qc.extractor = TaskQCExtractor(self.session_path, lazy=True, one=qc.one) # Extract extra datasets required for QC qc.extractor.data = dsets qc.extractor.extract_data() # Aggregate and update Alyx QC fields qc.run(update=True) return out_files
def test_frame2ttl_flicker(self): from ibllib.io.extractors import ephys_fpga from ibllib.qc.task_metrics import TaskQC from ibllib.qc.task_extractors import TaskQCExtractor init_path = self.data_path.joinpath("ephys", "ephys_choice_world_task") session_path = init_path.joinpath("ibl_witten_27/2021-01-21/001") dsets, out_files = ephys_fpga.extract_all(session_path, save=True) # Run the task QC qc = TaskQC(session_path, one=None) qc.extractor = TaskQCExtractor(session_path, lazy=True, one=None) # Extr+act extra datasets required for QC qc.extractor.data = dsets qc.extractor.extract_data() # Aggregate and update Alyx QC fields _, myqc = qc.run(update=False) # from ibllib.misc import pprint # pprint(myqc) assert myqc['_task_stimOn_delays'] > 0.9 # 0.6176 assert myqc["_task_stimFreeze_delays"] > 0.9 assert myqc["_task_stimOff_delays"] > 0.9 assert myqc["_task_stimOff_itiIn_delays"] > 0.9 assert myqc["_task_stimOn_delays"] > 0.9 assert myqc["_task_stimOn_goCue_delays"] > 0.9
def _run(self): """ Extracts an iblrig training session """ trials, wheel, output_files = bpod_trials.extract_all( self.session_path, save=True) if trials is None: return None # Run the task QC # Compile task data for QC type = get_session_extractor_type(self.session_path) if type == 'habituation': qc = HabituationQC(self.session_path, one=self.one) qc.extractor = TaskQCExtractor(self.session_path, one=self.one) else: # Update wheel data qc = TaskQC(self.session_path, one=self.one) qc.extractor = TaskQCExtractor(self.session_path, one=self.one) qc.extractor.wheel_encoding = 'X1' # Aggregate and update Alyx QC fields qc.run(update=True) return output_files
def _task_extraction_assertions(self, session_path): alf_path = session_path.joinpath('alf') shutil.rmtree(alf_path, ignore_errors=True) # try once without the sync pulses trials, out_files = ephys_fpga.FpgaTrials(session_path).extract( save=False) # then extract for real sync, chmap = ephys_fpga.get_main_probe_sync(session_path, bin_exists=False) trials, out_files = ephys_fpga.FpgaTrials(session_path).extract( save=True, sync=sync, chmap=chmap) # check that the output is complete for f in BPOD_FILES: self.assertTrue(alf_path.joinpath(f).exists()) # check that the output is complete for f in FPGA_FILES: self.assertTrue(alf_path.joinpath(f).exists()) # check dimensions after alf load alf_trials = alf.io.load_object(alf_path, 'trials') self.assertTrue(alf.io.check_dimensions(alf_trials) == 0) # go deeper and check the internal fpga trials structure consistency fpga_trials = ephys_fpga.extract_behaviour_sync(sync, chmap) # check dimensions self.assertEqual(alf.io.check_dimensions(fpga_trials), 0) # check that the stimOn < stimFreeze < stimOff self.assertTrue( np.all(fpga_trials['stimOn_times'][:-1] < fpga_trials['stimOff_times'][:-1])) self.assertTrue( np.all(fpga_trials['stimFreeze_times'][:-1] < fpga_trials['stimOff_times'][:-1])) # a trial is either an error-nogo or a reward self.assertTrue( np.all( np.isnan(fpga_trials['valveOpen_times'][:-1] * fpga_trials['errorCue_times'][:-1]))) self.assertTrue( np.all( np.logical_xor(np.isnan(fpga_trials['valveOpen_times'][:-1]), np.isnan(fpga_trials['errorCue_times'][:-1])))) # do the task qc # tqc_ephys.extractor.settings['PYBPOD_PROTOCOL'] from ibllib.qc.task_extractors import TaskQCExtractor ex = TaskQCExtractor(session_path, lazy=True, one=None, bpod_only=False) ex.data = fpga_trials ex.extract_data() from ibllib.qc.task_metrics import TaskQC # '/mnt/s0/Data/IntegrationTests/ephys/ephys_choice_world_task/CSP004/2019-11-27/001' tqc_ephys = TaskQC(session_path) tqc_ephys.extractor = ex _, res_ephys = tqc_ephys.run(bpod_only=False, download_data=False) tqc_bpod = TaskQC(session_path) _, res_bpod = tqc_bpod.run(bpod_only=True, download_data=False) for k in res_ephys: if k == "_task_response_feedback_delays": continue assert (np.abs(res_bpod[k] - res_ephys[k]) < .2) shutil.rmtree(alf_path, ignore_errors=True)
def _task_extraction_assertions(self, session_path): alf_path = session_path.joinpath('alf') shutil.rmtree(alf_path, ignore_errors=True) # this gets the full output ephys_fpga.extract_all(session_path, save=True, bin_exists=False) # check that the output is complete for f in BPOD_FILES: self.assertTrue(alf_path.joinpath(f).exists()) # check that the output is complete for f in FPGA_FILES: self.assertTrue(alf_path.joinpath(f).exists()) # check dimensions after alf load alf_trials = alf.io.load_object(alf_path, 'trials') self.assertTrue(alf.io.check_dimensions(alf_trials) == 0) # go deeper and check the internal fpga trials structure consistency sync, chmap = ephys_fpga.get_main_probe_sync(session_path, bin_exists=False) fpga_trials = ephys_fpga.extract_behaviour_sync(sync, chmap) # check dimensions self.assertEqual(alf.io.check_dimensions(fpga_trials), 0) # check that the stimOn < stimFreeze < stimOff self.assertTrue( np.all(fpga_trials['stimOn_times'][:-1] < fpga_trials['stimOff_times'][:-1])) self.assertTrue( np.all(fpga_trials['stimFreeze_times'][:-1] < fpga_trials['stimOff_times'][:-1])) # a trial is either an error-nogo or a reward self.assertTrue( np.all( np.isnan(fpga_trials['valveOpen_times'][:-1] * fpga_trials['errorCue_times'][:-1]))) self.assertTrue( np.all( np.logical_xor(np.isnan(fpga_trials['valveOpen_times'][:-1]), np.isnan(fpga_trials['errorCue_times'][:-1])))) # do the task qc # tqc_ephys.extractor.settings['PYBPOD_PROTOCOL'] from ibllib.qc.task_extractors import TaskQCExtractor ex = TaskQCExtractor(session_path, lazy=True, one=None, bpod_only=False) ex.data = fpga_trials ex.extract_data() from ibllib.qc.task_metrics import TaskQC # '/mnt/s0/Data/IntegrationTests/ephys/ephys_choice_world_task/CSP004/2019-11-27/001' tqc_ephys = TaskQC(session_path) tqc_ephys.extractor = ex _, res_ephys = tqc_ephys.run(bpod_only=False, download_data=False) tqc_bpod = TaskQC(session_path) _, res_bpod = tqc_bpod.run(bpod_only=True, download_data=False) # for a swift comparison using variable explorer # import pandas as pd # df = pd.DataFrame([[res_bpod[k], res_ephys[k]] for k in res_ephys], index=res_ephys.keys()) ok = True for k in res_ephys: if k == "_task_response_feedback_delays": continue if (np.abs(res_bpod[k] - res_ephys[k]) > .2): ok = False print(f"{k} bpod: {res_bpod[k]}, ephys: {res_ephys[k]}") assert ok shutil.rmtree(alf_path, ignore_errors=True)
class TestTaskQCObject(base.IntegrationTest): def setUp(self): self.one = one self.eid = "b1c968ad-4874-468d-b2e4-5ffa9b9964e9" # Make sure the data exists locally download_taskqc_raw_data(self.eid, one=one) self.session_path = self.one.path_from_eid(self.eid) self.qc = TaskQC(self.eid, one=self.one) self.qc.load_data(bpod_only=True) # Test session has no raw FPGA data def test_compute(self): # Compute metrics self.assertTrue(self.qc.metrics is None) self.assertTrue(self.qc.passed is None) self.qc.compute() self.assertTrue(self.qc.metrics is not None) self.assertTrue(self.qc.passed is not None) def test_run(self): # Reset Alyx fields before test reset = self.qc.update('NOT_SET', override=True) assert reset == 'NOT_SET', 'failed to reset QC field for test' extended = self.one.alyx.json_field_write('sessions', field_name='extended_qc', uuid=self.eid, data={}) assert not extended, 'failed to reset extended QC field for test' # Test update as False outcome, _ = self.qc.run(update=False) self.assertEqual('FAIL', outcome) extended = self.one.alyx.rest('sessions', 'read', id=self.eid)['extended_qc'] self.assertDictEqual({}, extended, 'unexpected update to extended qc') outcome = self.one.alyx.rest('sessions', 'read', id=self.eid)['qc'] self.assertEqual('NOT_SET', outcome, 'unexpected update to qc') # Test update as True outcome, results = self.qc.run(update=True) self.assertEqual('FAIL', outcome) extended = self.one.alyx.rest('sessions', 'read', id=self.eid)['extended_qc'] expected = list(results.keys()) + ['task'] self.assertCountEqual(expected, extended.keys(), 'unexpected update to extended qc') qc_field = self.one.alyx.rest('sessions', 'read', id=self.eid)['qc'] self.assertEqual(outcome, qc_field, 'unexpected update to qc') def test_compute_session_status(self): with self.assertRaises(AttributeError): self.qc.compute_session_status() self.qc.compute() outcome, results, outcomes = self.qc.compute_session_status() self.assertEqual('FAIL', outcome) # Check each outcome matches... # NOT_SET not_set = [k for k, v in results.items() if np.isnan(v)] self.assertTrue(all(outcomes[k] == 'NOT_SET' for k in not_set)) # PASS passed = [ k for k, v in results.items() if v >= self.qc.criteria['PASS'] ] self.assertTrue(all(outcomes[k] == 'PASS' for k in passed)) # WARNING wrn = [ k for k, v in results.items() if self.qc.criteria['WARNING'] <= v <= self.qc.criteria['PASS'] ] self.assertTrue(all(outcomes[k] == 'WARNING' for k in wrn)) # FAIL fail = [k for k, v in results.items() if v <= self.qc.criteria['FAIL']] self.assertTrue(all(outcomes[k] == 'FAIL' for k in fail))
ax=ax0) ax0.set_yticklabels(ax0.get_yticklabels(), rotation=30, fontsize=8) ax0.set(xscale='symlog', title='Metrics (failed)', xlabel='metric values (units vary)') # Plot failed trial level metrics sns.barplot(data=get_trial_level_failed(qc_obj.passed), orient='h', ax=ax1) ax1.set_yticklabels(ax1.get_yticklabels(), rotation=30, fontsize=8) ax1.set(title='Counts', xlabel='proportion of trials that passed') if save_path is not None: save_path = Path(save_path) if save_path.is_dir() and not save_path.exists(): print(f"Folder {save_path} does not exist, not saving...") elif save_path.is_dir(): fig.savefig(save_path.joinpath(f"{ref}_QC.png")) else: fig.savefig(save_path) if __name__ == "__main__": one = ONE(printout=False) # Load data eid, det = oneutils.random_ephys_session() # Run QC qc = TaskQC(eid, one=one) plot_results(qc) plt.show()