Пример #1
0
    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
Пример #2
0
 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
Пример #3
0
    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)
Пример #4
0
    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))