Skip to content

Commit e26f64d

Browse files
committed
Merge branch 'trajectory_fix' into protected_view
2 parents 886a660 + 67c865b commit e26f64d

File tree

10 files changed

+53
-15
lines changed

10 files changed

+53
-15
lines changed

ibllib/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import logging
33
import warnings
44

5-
__version__ = '2.32.4'
5+
__version__ = '2.32.5'
66
warnings.filterwarnings('always', category=DeprecationWarning, module='ibllib')
77

88
# if this becomes a full-blown library we should let the logging configuration to the discretion of the dev

ibllib/io/session_params.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,9 @@ def merge_params(a, b, copy=False):
169169
if k == 'sync':
170170
assert k not in a or a[k] == b[k], 'multiple sync fields defined'
171171
if isinstance(b[k], list):
172-
prev = a.get(k, [])
172+
prev = list(a.get(k, []))
173173
# For procedures and projects, remove duplicates
174-
to_add = b[k] if k == 'tasks' else set(prev) ^ set(b[k])
174+
to_add = b[k] if k == 'tasks' else set(b[k]) - set(prev)
175175
a[k] = prev + list(to_add)
176176
elif isinstance(b[k], dict):
177177
a[k] = {**a.get(k, {}), **b[k]}

ibllib/oneibl/patcher.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def _patch_dataset(self, path, dset_id=None, revision=None, dry=False, ftp=False
115115
assert is_uuid_string(dset_id)
116116
# If the revision is not None then we need to add the revision into the path. Note the moving of the file
117117
# is handled by one registration client
118-
if revision is not None:
118+
if revision is not None and f'#{revision}' not in str(path):
119119
path = path.parent.joinpath(f'#{revision}#', path.name)
120120
assert path.exists()
121121
dset = self.one.alyx.rest('datasets', 'read', id=dset_id)

ibllib/oneibl/registration.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ def register_session(self, ses_path, file_list=True, projects=None, procedures=N
241241
# assert len({md['IBLRIG_VERSION_TAG'] for md in settings}) == 1
242242

243243
users = []
244-
for user in filter(None, map(lambda x: x.get('PYBPOD_CREATOR'), settings)):
244+
for user in filter(lambda x: x and x[1], map(lambda x: x.get('PYBPOD_CREATOR'), settings)):
245245
user = self.assert_exists(user[0], 'users') # user is list of [username, uuid]
246246
users.append(user['username'])
247247

@@ -266,7 +266,7 @@ def register_session(self, ses_path, file_list=True, projects=None, procedures=N
266266
if poo_counts:
267267
json_field['POOP_COUNT'] = int(sum(poo_counts))
268268

269-
if not session: # Create session and weighings
269+
if not len(session): # Create session and weighings
270270
ses_ = {'subject': subject['nickname'],
271271
'users': users or [subject['responsible_user']],
272272
'location': settings[0]['PYBPOD_BOARD'],
@@ -293,9 +293,13 @@ def register_session(self, ses_path, file_list=True, projects=None, procedures=N
293293
self.register_weight(subject['nickname'], md['SUBJECT_WEIGHT'],
294294
date_time=md['SESSION_DATETIME'], user=user)
295295
else: # if session exists update a few key fields
296-
data = {'procedures': procedures, 'projects': projects}
296+
data = {'procedures': procedures, 'projects': projects,
297+
'n_correct_trials': n_correct_trials, 'n_trials': n_trials}
297298
if task_protocols:
298299
data['task_protocol'] = '/'.join(task_protocols)
300+
if end_time:
301+
data['end_time'] = self.ensure_ISO8601(end_time)
302+
299303
session = self.one.alyx.rest('sessions', 'partial_update', id=session_id[0], data=data)
300304
if json_field:
301305
session['json'] = self.one.alyx.json_field_update('sessions', session['id'], data=json_field)
@@ -421,14 +425,16 @@ def _get_session_times(fn, md, ses_data):
421425
"""
422426
if isinstance(md, dict):
423427
start_time = _start_time = isostr2date(md['SESSION_DATETIME'])
428+
end_time = isostr2date(md['SESSION_END_TIME']) if md.get('SESSION_END_TIME') else None
424429
else:
425430
start_time = isostr2date(md[0]['SESSION_DATETIME'])
426431
_start_time = isostr2date(md[-1]['SESSION_DATETIME'])
432+
end_time = isostr2date(md[-1]['SESSION_END_TIME']) if md[-1].get('SESSION_END_TIME') else None
427433
assert isinstance(ses_data, (list, tuple)) and len(ses_data) == len(md)
428434
assert len(md) == 1 or start_time < _start_time
429435
ses_data = ses_data[-1]
430-
if not ses_data:
431-
return start_time, None
436+
if not ses_data or end_time is not None:
437+
return start_time, end_time
432438
c = ses_duration_secs = 0
433439
for sd in reversed(ses_data):
434440
ses_duration_secs = (sd['behavior_data']['Trial end timestamp'] -

ibllib/pipes/histology.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,8 +396,10 @@ def create_trajectory_dict(probe_id, insertion, provenance, endpoint='insertions
396396
}
397397
if endpoint == 'chronic-insertions':
398398
tdict['chronic_insertion'] = probe_id
399+
tdict['probe_insertion'] = None
399400
else:
400401
tdict['probe_insertion'] = probe_id
402+
tdict['chronic_insertion'] = None
401403

402404
return tdict
403405

ibllib/qc/alignment_qc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ def upload_channels(self, alignment_key, upload_alyx, upload_flatiron):
316316
ephys_traj = self.one.alyx.get(f'/trajectories?&probe_insertion={self.eid}'
317317
'&provenance=Ephys aligned histology track',
318318
clobber=True)
319-
patch_dict = {'json': self.alignments}
319+
patch_dict = {'probe_insertion': self.eid, 'json': self.alignments}
320320
self.one.alyx.rest('trajectories', 'partial_update', id=ephys_traj[0]['id'],
321321
data=patch_dict)
322322

ibllib/tests/qc/test_alignment_qc.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ def _02_one_alignment(self):
186186
self.alignments['2020-06-26T16:40:14_Karolina_Socha']}
187187
trajectory = copy.deepcopy(self.trajectory)
188188
trajectory.update({'json': alignments})
189+
trajectory.update({'chronic_insertion': None})
189190
_ = one.alyx.rest('trajectories', 'create', data=trajectory)
190191
align_qc = AlignmentQC(self.probe_id, one=one, brain_atlas=brain_atlas, channels=False)
191192
align_qc.run(update=True, upload_alyx=True, upload_flatiron=False)
@@ -277,6 +278,7 @@ def setUpClass(cls) -> None:
277278
cls.probe_id = probe_insertion['id']
278279
cls.trajectory = data['trajectory'].tolist()
279280
cls.trajectory.update({'probe_insertion': cls.probe_id})
281+
cls.trajectory.update({'chronic_insertion': None})
280282
cls.trajectory.update({'json': cls.alignments})
281283
cls.traj = one.alyx.rest('trajectories', 'create', data=cls.trajectory)
282284

@@ -415,6 +417,7 @@ def setUpClass(cls) -> None:
415417
cls.probe_name = probe_insertion['name']
416418
cls.trajectory = data['trajectory'].tolist()
417419
cls.trajectory.update({'probe_insertion': cls.probe_id})
420+
cls.trajectory.update({'chronic_insertion': None})
418421
cls.trajectory.update({'json': cls.alignments})
419422
cls.traj = one.alyx.rest('trajectories', 'create', data=cls.trajectory)
420423

ibllib/tests/test_io.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,23 @@ def test_get_collections_repeat_protocols(self):
555555
collections = session_params.get_collections(tasks, flat=True)
556556
self.assertEqual(collections, {'raw_passive_data_bis', 'raw_passive_data', 'raw_behavior_data'})
557557

558+
def test_merge_params(self):
559+
"""Test for ibllib.io.session_params.merge_params functions."""
560+
a = self.fixture
561+
b = {'procedures': ['Imaging', 'Injection'], 'tasks': [{'fooChoiceWorld': {'collection': 'bar'}}]}
562+
c = session_params.merge_params(a, b, copy=True)
563+
self.assertCountEqual(['Imaging', 'Behavior training/tasks', 'Injection'], c['procedures'])
564+
self.assertCountEqual(['passiveChoiceWorld', 'ephysChoiceWorld', 'fooChoiceWorld'], (list(x)[0] for x in c['tasks']))
565+
# Ensure a and b not modified
566+
self.assertNotEqual(set(c['procedures']), set(a['procedures']))
567+
self.assertNotEqual(set(a['procedures']), set(b['procedures']))
568+
# Test without copy
569+
session_params.merge_params(a, b, copy=False)
570+
self.assertCountEqual(['Imaging', 'Behavior training/tasks', 'Injection'], a['procedures'])
571+
# Test assertion on duplicate sync
572+
b['sync'] = {'foodaq': {'collection': 'raw_sync_data'}}
573+
self.assertRaises(AssertionError, session_params.merge_params, a, b)
574+
558575

559576
class TestRawDaqLoaders(unittest.TestCase):
560577
"""Tests for raw_daq_loaders module"""

ibllib/tests/test_oneibl.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def test_patch_datasets(self):
126126

127127
# Mock the post method of AlyxClient and assert that it was called during registration
128128
with mock.patch.object(self.one.alyx, 'post') as rest_mock:
129-
rest_mock.side_effect = responses
129+
rest_mock.side_effect = [[r] for r in responses]
130130
self.globus_patcher.patch_datasets(file_list)
131131
self.assertEqual(rest_mock.call_count, 2)
132132
for call, file in zip(rest_mock.call_args_list, file_list):
@@ -434,16 +434,20 @@ def test_registration_session(self):
434434
query_type='remote')[0]
435435
ses_info = self.one.alyx.rest('sessions', 'read', id=eid)
436436
self.assertTrue(ses_info['procedures'] == ['Behavior training/tasks'])
437-
self.one.alyx.rest('sessions', 'delete', id=eid)
438-
# re-register the session as unknown protocol this time
437+
# re-register the session as unknown protocol, this time without removing session first
439438
self.settings['PYBPOD_PROTOCOL'] = 'gnagnagna'
439+
# also add an end time
440+
start = datetime.datetime.fromisoformat(self.settings['SESSION_DATETIME'])
441+
self.settings['SESSION_START_TIME'] = rc.ensure_ISO8601(start)
442+
self.settings['SESSION_END_TIME'] = rc.ensure_ISO8601(start + datetime.timedelta(hours=1))
440443
with open(settings_file, 'w') as fid:
441444
json.dump(self.settings, fid)
442445
rc.register_session(self.session_path)
443446
eid = self.one.search(subject=self.subject, date_range=['2018-04-01', '2018-04-01'],
444447
query_type='remote')[0]
445448
ses_info = self.one.alyx.rest('sessions', 'read', id=eid)
446449
self.assertTrue(ses_info['procedures'] == [])
450+
self.assertEqual(self.settings['SESSION_END_TIME'], ses_info['end_time'])
447451
self.one.alyx.rest('sessions', 'delete', id=eid)
448452

449453
def test_register_chained_session(self):
@@ -462,12 +466,13 @@ def test_register_chained_session(self):
462466

463467
# Save experiment description
464468
session_params.write_params(self.session_path, experiment_description)
465-
469+
self.settings['POOP_COUNT'] = 10
466470
with open(behaviour_paths[1].joinpath('_iblrig_taskSettings.raw.json'), 'w') as fid:
467471
json.dump(self.settings, fid)
468472

469473
settings = self.settings.copy()
470474
settings['PYBPOD_PROTOCOL'] = '_iblrig_tasks_passiveChoiceWorld'
475+
settings['POOP_COUNT'] = 53
471476
start_time = (datetime.datetime.fromisoformat(settings['SESSION_DATETIME']) -
472477
datetime.timedelta(hours=1, minutes=2, seconds=12))
473478
settings['SESSION_DATETIME'] = start_time.isoformat()
@@ -480,7 +485,9 @@ def test_register_chained_session(self):
480485
ses_info = self.one.alyx.rest('sessions', 'read', id=session['id'])
481486
self.assertCountEqual(experiment_description['procedures'], ses_info['procedures'])
482487
self.assertCountEqual(experiment_description['projects'], ses_info['projects'])
483-
self.assertCountEqual({'IS_MOCK': False, 'IBLRIG_VERSION': None}, ses_info['json'])
488+
# Poo count should be sum of values in both settings files
489+
expected = {'IS_MOCK': False, 'IBLRIG_VERSION': '5.4.1', 'POOP_COUNT': 63}
490+
self.assertDictEqual(expected, ses_info['json'])
484491
self.assertEqual('2018-04-01T11:46:14.795526', ses_info['start_time'])
485492
# Test task protocol
486493
expected = '_iblrig_tasks_passiveChoiceWorld5.4.1/_iblrig_tasks_ephysChoiceWorld5.4.1'

release_notes.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
#### 2.32.4
1414
- Add support for variations of the biaseCW task in the json task description
1515

16+
#### 2.32.5
17+
- Minor fixes to IBL registration client, including use of SESSION_END_TIME key
18+
1619
## Release Notes 2.31
1720

1821
### features

0 commit comments

Comments
 (0)