def test_get_ophys_timestamps(monkeypatch, plane_group, ophys_timestamps,
                              dff_traces, expected, context):
    """Test the acquisition frame truncation only happens for
    non-mesoscope data (and raises error for scientifica data with
    longer trace frames than acquisition frames (ophys_timestamps))."""

    def dummy_init(self, ophys_experiment_id, **kwargs):
        self.ophys_experiment_id = ophys_experiment_id

    with monkeypatch.context() as ctx:
        ctx.setattr(BehaviorOphysLimsExtractor, "__init__", dummy_init)
        ctx.setattr(BehaviorOphysLimsExtractor,
                    "get_behavior_session_id", lambda x: 123)
        ctx.setattr(BehaviorOphysLimsExtractor, "_get_ids", lambda x: {})
        patched_extractor = BehaviorOphysLimsExtractor(123)

        api = BehaviorOphysLimsApi(extractor=patched_extractor)

    # Mocking any db calls
    monkeypatch.setattr(api, "get_sync_data",
                        lambda: {"ophys_frames": ophys_timestamps})
    monkeypatch.setattr(api, "get_raw_dff_data", lambda: dff_traces)
    monkeypatch.setattr(api.extractor, "get_imaging_plane_group",
                        lambda: plane_group)
    monkeypatch.setattr(api.extractor, "get_plane_group_count", lambda: 2)
    with context:
        actual = api.get_ophys_timestamps()
        if expected is not None:
            np.testing.assert_array_equal(expected, actual)
示例#2
0
def validate_ophys_timestamps(ophys_experiment_id, api=None):
    api = BehaviorOphysLimsApi() if api is None else api

    ophys_experiment_dir = api.get_ophys_experiment_dir(ophys_experiment_id)
    raw_filepath = os.path.join(ophys_experiment_dir, str(ophys_experiment_id)+'.h5')
    raw_data_shape = get_raw_ophys_file_shape(raw_filepath)

    ophys_timestamps_shape = api.get_ophys_timestamps(ophys_experiment_id=ophys_experiment_id).shape

    if raw_data_shape[0] != ophys_timestamps_shape[0]:
        raise ValidationError('ophys_timestamp length does not match raw data length')
def test_get_behavior_stimulus_file(ophys_experiment_id, compare_val):

    if compare_val is None:
        expected_fail = False
        try:
            api = BehaviorOphysLimsApi(ophys_experiment_id)
            api.extractor.get_behavior_stimulus_file()
        except OneResultExpectedError:
            expected_fail = True
        assert expected_fail is True
    else:
        api = BehaviorOphysLimsApi(ophys_experiment_id)
        assert api.extractor.get_behavior_stimulus_file() == compare_val
def test_corrected_fluorescence_trace_order(monkeypatch, tmpdir):
    """
    Test that BehaviorOphysLimsApi.get_corrected_fluorescence_traces
    can reorder ROIs to align with what is in the cell_specimen_table
    """

    out_fname = os.path.join(tmpdir, "dummy_ftrace_data.h5")
    rng = np.random.RandomState(1234)
    n_t = 100
    data = rng.random_sample((5, n_t))
    roi_names = np.array([5, 3, 4, 2, 1])
    with h5py.File(out_fname, "w") as out_file:
        out_file.create_dataset("data", data=data)
        out_file.create_dataset("roi_names", data=roi_names.astype(bytes))

    cell_data = {"junk": [6, 7, 8, 9, 10],
                 "cell_roi_id": [b"1", b"2", b"3", b"4", b"5"]}

    cell_table = pd.DataFrame(data=cell_data,
                              index=pd.Index([10, 20, 30, 40, 50],
                                             name="cell_specimen_id"))

    def dummy_init(self, ophys_experiment_id, **kwargs):
        self.ophys_experiment_id = ophys_experiment_id
        self.get_behavior_session_id = 2

    with monkeypatch.context() as ctx:
        ctx.setattr(BehaviorOphysLimsExtractor, "__init__", dummy_init)
        ctx.setattr(BehaviorOphysLimsExtractor,
                    "get_demix_file", lambda *args: out_fname)
        patched_extractor = BehaviorOphysLimsExtractor(123)

        ctx.setattr(BehaviorOphysLimsApi, "get_ophys_timestamps",
                    lambda *args: np.zeros(n_t))
        ctx.setattr(BehaviorOphysLimsApi, "get_cell_specimen_table",
                    lambda *args: cell_table)
        api = BehaviorOphysLimsApi(extractor=patched_extractor)

        f_traces = api.get_corrected_fluorescence_traces()

    # check that the f_traces data frame was correctly joined
    # on roi_id
    colname = "corrected_fluorescence"
    roi_to_dex = {1: 4, 2: 3, 3: 1, 4: 2, 5: 0}
    for ii, roi_id in enumerate([1, 2, 3, 4, 5]):
        cell = (f_traces.loc[f_traces.cell_roi_id
                == bytes(f"{roi_id}", "utf-8")])
        assert cell.index.values[0] == 10*roi_id
        np.testing.assert_array_almost_equal(cell[colname].values[0],
                                             data[roi_to_dex[roi_id]],
                                             decimal=10)
def test_get_nwb_filepath(ophys_experiment_id):

    api = BehaviorOphysLimsApi(ophys_experiment_id)
    assert api.extractor.get_nwb_filepath() == (
        "/allen/programs/braintv/production/visualbehavior/prod0/"
        "specimen_823826986/ophys_session_859701393/"
        "ophys_experiment_860030092/behavior_ophys_session_860030092.nwb")
def test_eye_tracking_rig_geometry_returns_single_rig(monkeypatch):
    """
    This test tests that when there are multiple rig geometries for an experiment,
    that only the most recent is returned
    """
    def dummy_init(self, ophys_experiment_id):
        self.ophys_experiment_id = ophys_experiment_id

    with monkeypatch.context() as ctx:
        ctx.setattr(BehaviorOphysLimsExtractor, '__init__', dummy_init)
        patched_extractor = BehaviorOphysLimsExtractor(123)

        api = BehaviorOphysLimsApi(extractor=patched_extractor)

        resources_dir = Path(os.path.dirname(__file__)) / 'resources'
        rig_geometry = (
            pd.read_pickle(resources_dir
                           / 'rig_geometry_multiple_rig_configs.pkl'))
        rig_geometry = api.extractor._process_eye_tracking_rig_geometry(
            rig_geometry=rig_geometry)

    expected = {
        'camera_position_mm': [102.8, 74.7, 31.6],
        'led_position': [246.0, 92.3, 52.6],
        'monitor_position_mm': [118.6, 86.2, 31.6],
        'camera_rotation_deg': [0.0, 0.0, 2.8],
        'monitor_rotation_deg': [0.0, 0.0, 0.0],
        'equipment': 'CAM2P.5'
    }

    assert rig_geometry == expected
示例#7
0
 def get_session_data(self, ophys_session_id: int) -> BehaviorOphysSession:
     """Returns a BehaviorOphysSession object that contains methods
     to analyze a single behavior+ophys session.
     :param ophys_session_id: id that corresponds to a behavior session
     :type ophys_session_id: int
     :rtype: BehaviorOphysSession
     """
     return BehaviorOphysSession(BehaviorOphysLimsApi(ophys_session_id))
 def from_lims(
         cls,
         ophys_experiment_id: int,
         eye_tracking_z_threshold: float = 3.0,
         eye_tracking_dilation_frames: int = 2) -> "BehaviorOphysSession":
     return cls(api=BehaviorOphysLimsApi(ophys_experiment_id),
                eye_tracking_z_threshold=eye_tracking_z_threshold,
                eye_tracking_dilation_frames=eye_tracking_dilation_frames)
def test_corrected_fluorescence_trace_exceptions2(monkeypatch, tmpdir):
    """
    Test that BehaviorOphysLimsApi.get_corrected_fluorescence_traces
    raises exceptions when the trace file and cell_specimen_table have
    different ROI IDs

    Check case where fluorescence traces have an ROI that
    the cell_specimen_table does not
    """

    out_fname = os.path.join(tmpdir, "dummy_ftrace_data_exc2.h5")
    rng = np.random.RandomState(1234)
    n_t = 100
    data = rng.random_sample((5, n_t))
    roi_names = np.array([1, 5, 3, 4, 2])
    with h5py.File(out_fname, "w") as out_file:
        out_file.create_dataset("data", data=data)
        out_file.create_dataset("roi_names", data=roi_names.astype(bytes))

    cell_data = {"junk": [6, 7, 8, 9, 10, 11],
                 "cell_roi_id": [b"1", b"2", b"3", b"4", b"5", b"6"]}

    cell_table = pd.DataFrame(data=cell_data,
                              index=pd.Index([10, 20, 30, 40, 50, 60],
                                             name="cell_specimen_id"))

    def dummy_init(self, ophys_experiment_id, **kwargs):
        self.ophys_experiment_id = ophys_experiment_id
        self.get_behavior_session_id = 2

    with monkeypatch.context() as ctx:
        ctx.setattr(BehaviorOphysLimsExtractor, "__init__", dummy_init)
        ctx.setattr(BehaviorOphysLimsExtractor,
                    "get_demix_file", lambda *args: out_fname)
        patched_extractor = BehaviorOphysLimsExtractor(123)

        ctx.setattr(BehaviorOphysLimsApi, "get_ophys_timestamps",
                    lambda *args: np.zeros(n_t))
        ctx.setattr(BehaviorOphysLimsApi, "get_cell_specimen_table",
                    lambda *args: cell_table)
        api = BehaviorOphysLimsApi(extractor=patched_extractor)

        with pytest.raises(RuntimeError):
            _ = api.get_corrected_fluorescence_traces()
def test_dff_trace_order(monkeypatch, tmpdir):
    """
    Test that BehaviorOphysLimsApi.get_raw_dff_data can reorder
    ROIs to align with what is in the cell_specimen_table
    """

    out_fname = os.path.join(tmpdir, "dummy_dff_data.h5")
    rng = np.random.RandomState(1234)
    n_t = 100
    data = rng.random_sample((5, n_t))
    roi_names = np.array([5, 3, 4, 2, 1])
    with h5py.File(out_fname, "w") as out_file:
        out_file.create_dataset("data", data=data)
        out_file.create_dataset("roi_names", data=roi_names.astype(bytes))

    def dummy_init(self, ophys_experiment_id, **kwargs):
        self.ophys_experiment_id = ophys_experiment_id
        self.get_behavior_session_id = 2

    with monkeypatch.context() as ctx:
        ctx.setattr(BehaviorOphysLimsExtractor, "__init__", dummy_init)
        ctx.setattr(BehaviorOphysLimsExtractor, "get_dff_file",
                    lambda *args: out_fname)
        patched_extractor = BehaviorOphysLimsExtractor(123)

        ctx.setattr(BehaviorOphysLimsApi, "get_cell_roi_ids",
                    lambda *args: np.array([1, 2, 3, 4, 5]).astype(bytes))
        api = BehaviorOphysLimsApi(extractor=patched_extractor)

        dff_traces = api.get_raw_dff_data()

    # compare the returned traces with the input data
    # mapping to the order of the monkeypatched cell_roi_id list
    np.testing.assert_array_almost_equal(
        dff_traces[0, :], data[4, :], decimal=10)
    np.testing.assert_array_almost_equal(
        dff_traces[1, :], data[3, :], decimal=10)
    np.testing.assert_array_almost_equal(
        dff_traces[2, :], data[1, :], decimal=10)
    np.testing.assert_array_almost_equal(
        dff_traces[3, :], data[2, :], decimal=10)
    np.testing.assert_array_almost_equal(
        dff_traces[4, :], data[0, :], decimal=10)
 def get_behavior_ophys_experiment(
         self, ophys_experiment_id: int) -> BehaviorOphysExperiment:
     """Returns a BehaviorOphysExperiment object that contains methods
     to analyze a single behavior+ophys session.
     :param ophys_experiment_id: id that corresponds to an ophys experiment
     :type ophys_experiment_id: int
     :rtype: BehaviorOphysExperiment
     """
     return BehaviorOphysExperiment(
         BehaviorOphysLimsApi(ophys_experiment_id))
def test_get_extended_trials(ophys_experiment_id):

    api = BehaviorOphysLimsApi(ophys_experiment_id)
    df = api.get_extended_trials()
    ets = ExtendedTrialSchema(partial=False, many=True)
    data_list_cs = df.to_dict("records")
    data_list_cs_sc = ets.dump(data_list_cs)
    ets.load(data_list_cs_sc)

    df_fail = df.drop(["behavior_session_uuid"], axis=1)
    ets = ExtendedTrialSchema(partial=False, many=True)
    data_list_cs = df_fail.to_dict("records")
    data_list_cs_sc = ets.dump(data_list_cs)
    try:
        ets.load(data_list_cs_sc)
        raise RuntimeError("This should have failed with "
                           "marshmallow.schema.ValidationError")
    except ValidationError:
        pass
def test_rig_geometry_newer_than_experiment():
    """
    This test ensures that if the experiment date_of_acquisition
    is before a rig activate_date that it is not returned as the rig
    used for the experiment
    """
    # This experiment has rig config more recent than the
    # experiment date_of_acquisition
    ophys_experiment_id = 521405260
    api = BehaviorOphysLimsApi(ophys_experiment_id)
    rig_geometry = api.get_eye_tracking_rig_geometry()

    expected = {
        'camera_position_mm': [130.0, 0.0, 0.0],
        'led_position': [265.1, -39.3, 1.0],
        'monitor_position_mm': [170.0, 0.0, 0.0],
        'camera_rotation_deg': [0.0, 0.0, 13.1],
        'monitor_rotation_deg': [0.0, 0.0, 0.0],
        'equipment': 'CAM2P.1'
    }
    assert rig_geometry == expected
def test_get_ophys_timestamps(monkeypatch, plane_group, ophys_timestamps,
                              dff_traces, expected, context):
    """Test the acquisition frame truncation only happens for
    non-mesoscope data (and raises error for scientifica data with
    longer trace frames than acquisition frames (ophys_timestamps))."""

    monkeypatch.setattr(BehaviorOphysLimsApi, "get_behavior_session_id",
                        lambda x: 123)
    monkeypatch.setattr(BehaviorOphysLimsApi, "_get_ids", lambda x: {})

    api = BehaviorOphysLimsApi(123)
    # Mocking any db calls
    monkeypatch.setattr(api, "get_sync_data",
                        lambda: {"ophys_frames": ophys_timestamps})
    monkeypatch.setattr(api, "get_raw_dff_data", lambda: dff_traces)
    monkeypatch.setattr(api, "get_imaging_plane_group", lambda: plane_group)
    monkeypatch.setattr(api, "get_plane_group_count", lambda: 2)
    with context:
        actual = api.get_ophys_timestamps()
        if expected is not None:
            np.testing.assert_array_equal(expected, actual)
def test_legacy_dff_api():
    ophys_experiment_id = 792813858
    api = BehaviorOphysLimsApi(ophys_experiment_id)
    session = BehaviorOphysSession(api)

    _, dff_array = session.get_dff_traces()
    for csid in session.dff_traces.index.values:
        dff_trace = session.dff_traces.loc[csid]['dff']
        ind = session.get_cell_specimen_indices([csid])[0]
        np.testing.assert_array_almost_equal(dff_trace, dff_array[ind, :])

    assert dff_array.shape[0] == session.dff_traces.shape[0]
def test_trial_response_window_bounds_reward(ophys_experiment_id):
    api = BehaviorOphysLimsApi(ophys_experiment_id)
    session = BehaviorOphysSession(api)
    response_window = session.task_parameters['response_window_sec']
    for _, row in session.trials.iterrows():

        lick_times = [(t - row.change_time) for t in row.lick_times]
        if not np.isnan(row.reward_time):

            # monitor delay is incorporated into the trials table change time
            # TODO: where is this set in the session object?
            camstim_change_time = row.change_time - 0.0351

            reward_time = (row.reward_time - camstim_change_time)
            assert response_window[0] < reward_time + 1 / 60
            assert reward_time < response_window[1] + 1 / 60
            if len(session.licks) > 0:
                assert lick_times[0] < reward_time
示例#17
0
def write_behavior_ophys_nwb(session_data: dict, nwb_filepath: str,
                             skip_eye_tracking: bool):

    nwb_filepath_inprogress = nwb_filepath + '.inprogress'
    nwb_filepath_error = nwb_filepath + '.error'

    # Clean out files from previous runs:
    for filename in [
            nwb_filepath_inprogress, nwb_filepath_error, nwb_filepath
    ]:
        if os.path.exists(filename):
            os.remove(filename)

    try:
        json_api = BehaviorOphysJsonApi(data=session_data,
                                        skip_eye_tracking=skip_eye_tracking)
        json_session = BehaviorOphysExperiment(api=json_api)
        lims_api = BehaviorOphysLimsApi(
            ophys_experiment_id=session_data['ophys_experiment_id'],
            skip_eye_tracking=skip_eye_tracking)
        lims_session = BehaviorOphysExperiment(api=lims_api)

        logging.info("Comparing a BehaviorOphysExperiment created from JSON "
                     "with a BehaviorOphysExperiment created from LIMS")
        assert sessions_are_equal(json_session, lims_session, reraise=True)

        BehaviorOphysNwbApi(nwb_filepath_inprogress).save(json_session)

        logging.info("Comparing a BehaviorOphysExperiment created from JSON "
                     "with a BehaviorOphysExperiment created from NWB")
        nwb_api = BehaviorOphysNwbApi(nwb_filepath_inprogress)
        nwb_session = BehaviorOphysExperiment(api=nwb_api)
        assert sessions_are_equal(json_session, nwb_session, reraise=True)

        os.rename(nwb_filepath_inprogress, nwb_filepath)
        return {'output_path': nwb_filepath}
    except Exception as e:
        if os.path.isfile(nwb_filepath_inprogress):
            os.rename(nwb_filepath_inprogress, nwb_filepath_error)
        raise e
示例#18
0
def validate_last_trial_ends_adjacent_to_flash(ophys_experiment_id, api=None, verbose=False):
        # ensure that the last trial ends sometime on the last flash/blank cycle
        # i.e, if this is the last flash/blank iteration (high = stimulus present):
        #           -------           -------           -------           -------
        #          |       |         |       |         |       |         |       |
        # ---------         ---------         ---------         ---------         -------------------------------------------
        #                                                                ^                                                  ^
        # The last trial has to have ended somewhere between the two carrots where:
        #   the first carrot represents the time of the last recorded stimulus flash
        #   the second carrot represents the time at which another flash should have started, after accounting for the possibility of the session ending on an omitted flash
        
        api = BehaviorOphysLimsApi() if api is None else api
        session = BehaviorOphysExperiment(api)

        # get the flash/blank parameters
        max_flash_duration = session.stimulus_presentations['duration'].max()
        max_blank_duration = session.task_parameters['blank_duration_sec'][1]
        
        # count number of omitted flashes at the very end of the session
        N_final_omitted_flashes = session.stimulus_presentations.index.max() - session.stimulus_presentations.query('omitted == False').index.max()
        
        # get the start/end time of the last valid (non-omitted) flash
        last_flash_start = session.stimulus_presentations.query('omitted == False')['start_time'].iloc[-1]
        last_flash_end = session.stimulus_presentations.query('omitted == False')['stop_time'].iloc[-1]
        
        # calculate when the next stimulus should have flashed, after accounting for any omitted flashes at the end of the session
        next_flash_would_have_started = last_flash_end + max_blank_duration + N_final_omitted_flashes*(max_flash_duration + max_blank_duration)
        
        # get the end time of the last trial
        last_trial_end = session.trials.iloc[-1]['stop_time']
        
        if verbose:
            print('last flash ended at {}'.format(last_flash_end))
            print('another flash should have started by {}'.format(next_flash_would_have_started))
            print('last trial ended at {}'.format(last_trial_end))
        
        if not last_flash_start <= last_trial_end <= next_flash_would_have_started:
            raise ValidationError('The last trial does not end between the start of the last flash and the expected start time of the next flash')
def test_dff_trace_exceptions(monkeypatch, tmpdir):
    """
    Test that BehaviorOphysLimsApi.get_raw_dff_data() raises exceptions when
    dff trace file and cell_specimen_table contain different ROI IDs
    """

    # check that an exception is raised if dff_traces has an ROI ID
    # that cell_specimen_table does not
    out_fname = os.path.join(tmpdir, "dummy_dff_data_for_exceptions.h5")
    rng = np.random.RandomState(1234)
    n_t = 100
    data = rng.random_sample((5, n_t))
    roi_names = np.array([5, 3, 4, 2, 1])
    with h5py.File(out_fname, "w") as out_file:
        out_file.create_dataset("data", data=data)
        out_file.create_dataset("roi_names", data=roi_names.astype(bytes))

    def dummy_init(self, ophys_experiment_id, **kwargs):
        self.ophys_experiment_id = ophys_experiment_id
        self.get_behavior_session_id = 2

    with monkeypatch.context() as ctx:
        ctx.setattr(BehaviorOphysLimsExtractor, "__init__", dummy_init)
        ctx.setattr(BehaviorOphysLimsExtractor, "get_dff_file",
                    lambda *args: out_fname)
        patched_extractor = BehaviorOphysLimsExtractor(123)

        ctx.setattr(BehaviorOphysLimsApi, "get_cell_roi_ids",
                    lambda *args: np.array([1, 3, 4, 5]).astype(bytes))
        api = BehaviorOphysLimsApi(extractor=patched_extractor)

        with pytest.raises(RuntimeError):
            _ = api.get_raw_dff_data()

    # check that an exception is raised if the cell_specimen_table
    # has an ROI ID that dff_traces does not
    out_fname = os.path.join(tmpdir, "dummy_dff_data_for_exceptions2.h5")
    rng = np.random.RandomState(1234)
    n_t = 100
    data = rng.random_sample((5, n_t))
    roi_names = np.array([5, 3, 4, 2, 1])
    with h5py.File(out_fname, "w") as out_file:
        out_file.create_dataset("data", data=data)
        out_file.create_dataset("roi_names", data=roi_names.astype(bytes))

    def dummy_init(self, ophys_experiment_id, **kwargs):
        self.ophys_experiment_id = ophys_experiment_id
        self.get_behavior_session_id = 2

    with monkeypatch.context() as ctx:
        ctx.setattr(BehaviorOphysLimsExtractor, "__init__", dummy_init)
        ctx.setattr(BehaviorOphysLimsExtractor, "get_dff_file",
                    lambda *args: out_fname)
        patched_extractor = BehaviorOphysLimsExtractor(123)

        ctx.setattr(BehaviorOphysLimsApi, "get_cell_roi_ids",
                    lambda *args: np.array([1, 2, 3, 4, 5, 6]).astype(bytes))
        api = BehaviorOphysLimsApi(extractor=patched_extractor)

        with pytest.raises(RuntimeError):
            _ = api.get_raw_dff_data()
示例#20
0
        next_flash_would_have_started = last_flash_end + max_blank_duration + N_final_omitted_flashes*(max_flash_duration + max_blank_duration)
        
        # get the end time of the last trial
        last_trial_end = session.trials.iloc[-1]['stop_time']
        
        if verbose:
            print('last flash ended at {}'.format(last_flash_end))
            print('another flash should have started by {}'.format(next_flash_would_have_started))
            print('last trial ended at {}'.format(last_trial_end))
        
        if not last_flash_start <= last_trial_end <= next_flash_would_have_started:
            raise ValidationError('The last trial does not end between the start of the last flash and the expected start time of the next flash')

if __name__ == "__main__":

    api = BehaviorOphysLimsApi()
    ophys_experiment_id_list = [775614751, 778644591, 787461073, 782675436, 783928214, 783927872,
                                787501821, 787498309, 788490510, 788488596, 788489531, 789359614,
                                790149413, 790709081, 791119849, 791453282, 791980891, 792813858,
                                792812544, 792816531, 792815735, 794381992, 794378505, 795076128,
                                795073741, 795952471, 795952488, 795953296, 795948257, 796106850,
                                796106321, 796108483, 796105823, 796308505, 797255551, 795075034,
                                798403387, 798404219, 799366517, 799368904, 799368262, 803736273,
                                805100431, 805784331, 805784313, 806456687, 806455766, 806989729,
                                807753318, 807752719, 807753334, 807753920, 796105304, 784482326,
                                779335436, 782675457, 791974731, 791979236,
                                800034837, 802649986, 806990245, 808621958,
                                808619526, 808619543, 808621034, 808621015]

    for ophys_experiment_id in ophys_experiment_id_list:
        validation_functions_to_run = [
示例#21
0
 def setup_class(cls):
     cls.bd = BehaviorLimsApi(976012750)
     cls.od = BehaviorOphysLimsApi(976255949)
def test_process_ophys_plane_timestamps(
        timestamps, plane_group, group_count, expected):
    actual = BehaviorOphysLimsApi._process_ophys_plane_timestamps(
        timestamps, plane_group, group_count)
    np.testing.assert_array_equal(expected, actual)
def test_get_cell_roi_table(ophys_experiment_id):
    api = BehaviorOphysLimsApi(ophys_experiment_id)
    assert len(api.get_cell_specimen_table()) == 128