diff --git a/CHANGELOG.md b/CHANGELOG.md index 451f27a27..962050828 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Changelog ========= +8.24.2 +------ +* fix: create the `raw_ephys_data` folder even if there are no probes (when running behavior sessions on ephys rig) + 8.24.2 ------ * make Frame2TTL validation more robust diff --git a/iblrig/__init__.py b/iblrig/__init__.py index 60f029c22..a7fb4293c 100644 --- a/iblrig/__init__.py +++ b/iblrig/__init__.py @@ -6,7 +6,7 @@ # 5) git tag the release in accordance to the version number below (after merge!) # >>> git tag 8.15.6 # >>> git push origin --tags -__version__ = '8.24.2' +__version__ = '8.24.3' from iblrig.version_management import get_detailed_version_string diff --git a/iblrig/base_tasks.py b/iblrig/base_tasks.py index 0833b3955..d2a11c311 100644 --- a/iblrig/base_tasks.py +++ b/iblrig/base_tasks.py @@ -1215,7 +1215,7 @@ def __init__(self, *_, remote_rigs=None, **kwargs): if isinstance(remote_rigs, list): # For now we flatten to list of remote rig names but could permit list of (name, URI) tuples remote_rigs = list(filter(None, flatten(remote_rigs))) - all_remote_rigs = net.get_remote_devices(iblrig_settings=kwargs.get('iblrig_settings', None)) + all_remote_rigs = net.get_remote_devices(iblrig_settings=kwargs.get('iblrig_settings')) if not set(remote_rigs).issubset(all_remote_rigs.keys()): raise ValueError('Selected remote rigs not in remote rigs list') remote_rigs = {k: v for k, v in all_remote_rigs.items() if k in remote_rigs} diff --git a/iblrig/test/test_hardware_mixins.py b/iblrig/test/test_hardware_mixins.py index f89d7bb5a..1fcfae39f 100644 --- a/iblrig/test/test_hardware_mixins.py +++ b/iblrig/test/test_hardware_mixins.py @@ -109,12 +109,12 @@ def test_rotary_encoder_mixin(self): 'RotaryEncoder1_3', 'RotaryEncoder1_4', ] - assert { + assert session.device_rotary_encoder.THRESHOLD_EVENTS == { -35: 'RotaryEncoder1_1', 35: 'RotaryEncoder1_2', -2: 'RotaryEncoder1_3', 2: 'RotaryEncoder1_4', - } == session.device_rotary_encoder.THRESHOLD_EVENTS + } with self.assertRaises(ValueError): RotaryEncoderMixin.start_mixin_rotary_encoder(session) diff --git a/iblrig/test/test_transfers.py b/iblrig/test/test_transfers.py index 4a82be0f6..edfdae5e8 100644 --- a/iblrig/test/test_transfers.py +++ b/iblrig/test/test_transfers.py @@ -6,6 +6,9 @@ from pathlib import Path from unittest import mock +from packaging import version + +import ibllib import iblrig.commands import iblrig.path_helper import iblrig.raw_data_loaders @@ -228,6 +231,52 @@ def test_behavior_ephys_video_copy(self): self.assertEqual(set(final_experiment_description['devices']['cameras'].keys()), {'left'}) self.assertEqual(set(final_experiment_description['sync'].keys()), {'nidq'}) + # Requires recent change to ibllib test fixture code supporting no probe ephys recording files + @unittest.skipIf(version.parse(ibllib.__version__) < version.parse('2.39'), 'ibllib < 2.39') + def test_ephys_no_probe(self): + """Test copying a session at ephys rig when no probes were used (DAQ only).""" + # First create a behavior session + task_kwargs = copy.deepcopy(self.session_kwargs) + task_kwargs['hardware_settings'].update( + { + 'device_cameras': None, + 'MAIN_SYNC': False, # this is quite important for ephys sessions + } + ) + session = _create_behavior_session(kwargs=task_kwargs, ntrials=50) + folder_session_ephys = Path(self.td.name).joinpath('ephys', 'Subjects', *session.paths.SESSION_FOLDER.parts[-3:]) + + # Create an ephys acquisition + n_probes = 0 + # SpikeGLX then saves these files into the session folder + populate_raw_spikeglx(folder_session_ephys, model='3B', n_probes=n_probes) + + # Test the copiers + sc = BehaviorCopier(session_path=session.paths.SESSION_FOLDER, remote_subjects_folder=session.paths.REMOTE_SUBJECT_FOLDER) + self.assertEqual('.status_pending', sc.glob_file_remote_copy_status().suffix) + self.assertEqual(1, sc.state) + sc.copy_collections() + self.assertEqual(2, sc.state) + self.assertEqual('.status_complete', sc.glob_file_remote_copy_status().suffix) + sc.copy_collections() + self.assertEqual(2, sc.state) + sc.finalize_copy(number_of_expected_devices=None) + self.assertEqual(2, sc.state) # here we still don't have all devices so we stay in state 2 + + ec = EphysCopier(session_path=folder_session_ephys, remote_subjects_folder=session.paths.REMOTE_SUBJECT_FOLDER) + self.assertEqual(0, ec.state) + ec.initialize_experiment() + self.assertEqual(1, ec.state) + self.assertIn('sync', ec.experiment_description) + ec.copy_collections() + self.assertEqual(2, ec.state) + # this time it's all there and we move on + ec.finalize_copy(number_of_expected_devices=None) + self.assertEqual(3, ec.state) + final_experiment_description = session_params.read_params(ec.remote_session_path) + self.assertEqual(1, len(final_experiment_description['tasks'])) + self.assertEqual(set(final_experiment_description['sync'].keys()), {'nidq'}) + def test_copy_snapshots(self): """Test copy of snapshots folder(s).""" # Create without task data diff --git a/iblrig/transfer_experiments.py b/iblrig/transfer_experiments.py index 97f8fdcef..c4d3ef266 100644 --- a/iblrig/transfer_experiments.py +++ b/iblrig/transfer_experiments.py @@ -553,8 +553,9 @@ def initialize_experiment(self, acquisition_description=None, nprobes=None, main self._experiment_description = acquisition_description super().initialize_experiment(acquisition_description=acquisition_description, **kwargs) # once the session folders have been initialized, create the probe folders + (ephys_path := self.session_path.joinpath('raw_ephys_data')).mkdir(exist_ok=True) for n in range(nprobes): - self.session_path.joinpath('raw_ephys_data', f'probe{n:02}').mkdir(exist_ok=True, parents=True) + ephys_path.joinpath(f'probe{n:02}').mkdir(exist_ok=True) def _copy_collections(self): """Here we overload the copy to be able to rename the probes properly and also create the insertions."""