8
8
import importlib
9
9
10
10
from .readers import spikeglx , kilosort , openephys
11
- from . import probe
11
+ from . import probe , find_valid_full_path
12
12
13
13
schema = dj .schema ()
14
14
@@ -61,40 +61,23 @@ def activate(ephys_schema_name, probe_schema_name=None, *, create_schema=True,
61
61
62
62
# -------------- Functions required by the elements-ephys ---------------
63
63
64
- def get_ephys_root_data_dir () -> str :
64
+ def get_ephys_root_data_dir () -> list :
65
65
"""
66
66
All data paths, directories in DataJoint Elements are recommended to be stored as
67
67
relative paths, with respect to some user-configured "root" directory,
68
68
which varies from machine to machine (e.g. different mounted drive locations)
69
69
70
- get_ephys_root_data_dir() -> str
71
- This user-provided function retrieves the root data directory
72
- containing ephys data for all subjects/sessions
73
- (e.g. acquired SpikeGLX or Open Ephys raw files)
74
- :return: a string for full path to the ephys root data directory
70
+ get_ephys_root_data_dir() -> list
71
+ This user-provided function retrieves the possible root data directories
72
+ containing the ephys data for all subjects/sessions
73
+ (e.g. acquired SpikeGLX or Open Ephys raw files,
74
+ output files from spike sorting routines, etc.)
75
+ :return: a string for full path to the ephys root data directory,
76
+ or list of strings for possible root data directories
75
77
"""
76
78
return _linking_module .get_ephys_root_data_dir ()
77
79
78
80
79
- def get_clustering_root_data_dir () -> str :
80
- """
81
- All data paths, directories in DataJoint Elements are recommended to be stored as
82
- relative paths, with respect to some user-configured "root" directory,
83
- which varies from machine to machine (e.g. different mounted drive locations)
84
-
85
- get_clustering_root_data_dir() -> str
86
- This user-provided function retrieves the root data directory
87
- containing clustering results for all subjects/sessions
88
- (e.g. output files from spike sorting routines)
89
- Note: if not provided, use "get_ephys_root_data_dir()"
90
- :return: a string for full path to the clustering root data directory
91
- """
92
- if not hasattr (_linking_module , 'get_clustering_root_data_dir' ):
93
- return get_ephys_root_data_dir ()
94
-
95
- return _linking_module .get_clustering_root_data_dir ()
96
-
97
-
98
81
def get_session_directory (session_key : dict ) -> str :
99
82
"""
100
83
get_session_directory(session_key: dict) -> str
@@ -159,7 +142,6 @@ class EphysFile(dj.Part):
159
142
"""
160
143
161
144
def make (self , key ):
162
- root_dir = pathlib .Path (get_ephys_root_data_dir ())
163
145
sess_dir = pathlib .Path (get_session_directory (key ))
164
146
165
147
inserted_probe_serial_number = (ProbeInsertion * probe .Probe & key ).fetch1 ('probe' )
@@ -207,6 +189,8 @@ def make(self, key):
207
189
** generate_electrode_config (probe_type , electrode_group_members ),
208
190
'acq_software' : acq_software ,
209
191
'sampling_rate' : spikeglx_meta .meta ['imSampRate' ]})
192
+
193
+ _ , root_dir = find_valid_full_path (get_ephys_root_data_dir (), meta_filepath )
210
194
self .EphysFile .insert1 ({
211
195
** key ,
212
196
'file_path' : meta_filepath .relative_to (root_dir ).as_posix ()})
@@ -240,6 +224,10 @@ def make(self, key):
240
224
** generate_electrode_config (probe_type , electrode_group_members ),
241
225
'acq_software' : acq_software ,
242
226
'sampling_rate' : probe_data .ap_meta ['sample_rate' ]})
227
+
228
+ _ , root_dir = find_valid_full_path (
229
+ get_ephys_root_data_dir (),
230
+ probe_data .recording_info ['recording_files' ][0 ])
243
231
self .EphysFile .insert ([{** key ,
244
232
'file_path' : fp .relative_to (root_dir ).as_posix ()}
245
233
for fp in probe_data .recording_info ['recording_files' ]])
@@ -431,10 +419,9 @@ class Clustering(dj.Imported):
431
419
"""
432
420
433
421
def make (self , key ):
434
- root_dir = pathlib .Path (get_clustering_root_data_dir ())
435
422
task_mode , output_dir = (ClusteringTask & key ).fetch1 (
436
423
'task_mode' , 'clustering_output_dir' )
437
- kilosort_dir = root_dir / output_dir
424
+ kilosort_dir , _ = find_valid_full_path ( get_ephys_root_data_dir (), output_dir )
438
425
439
426
if task_mode == 'load' :
440
427
kilosort_dataset = kilosort .Kilosort (kilosort_dir ) # check if the directory is a valid Kilosort output
@@ -470,10 +457,10 @@ def create1_from_clustering_task(self, key, curation_note=''):
470
457
raise ValueError (f'No corresponding entry in Clustering available'
471
458
f' for: { key } ; do `Clustering.populate(key)`' )
472
459
473
- root_dir = pathlib .Path (get_clustering_root_data_dir ())
474
460
task_mode , output_dir = (ClusteringTask & key ).fetch1 (
475
461
'task_mode' , 'clustering_output_dir' )
476
- kilosort_dir = root_dir / output_dir
462
+ kilosort_dir , _ = find_valid_full_path (get_ephys_root_data_dir (), output_dir )
463
+
477
464
creation_time , is_curated , is_qc = kilosort .extract_clustering_info (kilosort_dir )
478
465
# Synthesize curation_id
479
466
curation_id = dj .U ().aggr (self & key , n = 'ifnull(max(curation_id)+1,1)' ).fetch1 ('n' )
@@ -503,8 +490,9 @@ class Unit(dj.Part):
503
490
"""
504
491
505
492
def make (self , key ):
506
- root_dir = pathlib .Path (get_clustering_root_data_dir ())
507
- kilosort_dir = root_dir / (Curation & key ).fetch1 ('curation_output_dir' )
493
+ output_dir = (Curation & key ).fetch1 ('curation_output_dir' )
494
+ kilosort_dir , _ = find_valid_full_path (get_ephys_root_data_dir (), output_dir )
495
+
508
496
kilosort_dataset = kilosort .Kilosort (kilosort_dir )
509
497
acq_software = (EphysRecording & key ).fetch1 ('acq_software' )
510
498
@@ -521,7 +509,7 @@ def make(self, key):
521
509
# spike_times_sec_adj > spike_times_sec > spike_times
522
510
spike_time_key = ('spike_times_sec_adj' if 'spike_times_sec_adj' in kilosort_dataset .data
523
511
else 'spike_times_sec' if 'spike_times_sec'
524
- in kilosort_dataset .data else 'spike_times' )
512
+ in kilosort_dataset .data else 'spike_times' )
525
513
spike_times = kilosort_dataset .data [spike_time_key ]
526
514
kilosort_dataset .extract_spike_depths ()
527
515
@@ -576,8 +564,9 @@ class UnitElectrode(dj.Part):
576
564
"""
577
565
578
566
def make (self , key ):
579
- root_dir = pathlib .Path (get_clustering_root_data_dir ())
580
- kilosort_dir = root_dir / (Curation & key ).fetch1 ('curation_output_dir' )
567
+ output_dir = (Curation & key ).fetch1 ('curation_output_dir' )
568
+ kilosort_dir , _ = find_valid_full_path (get_ephys_root_data_dir (), output_dir )
569
+
581
570
kilosort_dataset = kilosort .Kilosort (kilosort_dir )
582
571
583
572
acq_software , probe_serial_number = (EphysRecording * ProbeInsertion & key ).fetch1 (
@@ -656,25 +645,28 @@ def yield_unit_waveforms():
656
645
657
646
def get_spikeglx_meta_filepath (ephys_recording_key ):
658
647
# attempt to retrieve from EphysRecording.EphysFile
659
- ephys_root_dir = get_ephys_root_data_dir ()
660
- spikeglx_meta_filepath = ephys_root_dir / (
661
- EphysRecording .EphysFile & ephys_recording_key
662
- & 'file_path LIKE "%.ap.meta"' ).fetch1 ('file_path' )
663
- # if not found, search in session_dir again
664
- if not spikeglx_meta_filepath .exists ():
665
- sess_dir = pathlib .Path (get_session_directory (ephys_recording_key ))
666
- inserted_probe_serial_number = (ProbeInsertion * probe .Probe
667
- & ephys_recording_key ).fetch1 ('probe' )
668
-
669
- spikeglx_meta_filepaths = [fp for fp in sess_dir .rglob ('*.ap.meta' )]
670
- for meta_filepath in spikeglx_meta_filepaths :
671
- spikeglx_meta = spikeglx .SpikeGLXMeta (meta_filepath )
672
- if str (spikeglx_meta .probe_SN ) == inserted_probe_serial_number :
673
- spikeglx_meta_filepath = meta_filepath
674
- break
675
- else :
676
- raise FileNotFoundError (
677
- 'No SpikeGLX data found for probe insertion: {}' .format (ephys_recording_key ))
648
+ spikeglx_meta_filepath = (EphysRecording .EphysFile & ephys_recording_key
649
+ & 'file_path LIKE "%.ap.meta"' ).fetch1 ('file_path' )
650
+
651
+ try :
652
+ spikeglx_meta_filepath , _ = find_valid_full_path (get_ephys_root_data_dir (),
653
+ spikeglx_meta_filepath )
654
+ except FileNotFoundError :
655
+ # if not found, search in session_dir again
656
+ if not spikeglx_meta_filepath .exists ():
657
+ sess_dir = pathlib .Path (get_session_directory (ephys_recording_key ))
658
+ inserted_probe_serial_number = (ProbeInsertion * probe .Probe
659
+ & ephys_recording_key ).fetch1 ('probe' )
660
+
661
+ spikeglx_meta_filepaths = [fp for fp in sess_dir .rglob ('*.ap.meta' )]
662
+ for meta_filepath in spikeglx_meta_filepaths :
663
+ spikeglx_meta = spikeglx .SpikeGLXMeta (meta_filepath )
664
+ if str (spikeglx_meta .probe_SN ) == inserted_probe_serial_number :
665
+ spikeglx_meta_filepath = meta_filepath
666
+ break
667
+ else :
668
+ raise FileNotFoundError (
669
+ 'No SpikeGLX data found for probe insertion: {}' .format (ephys_recording_key ))
678
670
679
671
return spikeglx_meta_filepath
680
672
0 commit comments