diff --git a/+bids/+internal/get_meta_list.m b/+bids/+internal/get_meta_list.m index bb5163ec..975e732f 100644 --- a/+bids/+internal/get_meta_list.m +++ b/+bids/+internal/get_meta_list.m @@ -43,6 +43,15 @@ return end + % in deepest level look for file with only a change in extension + basename = bids.internal.file_utils(filename, 'basename'); + if strcmp(bids.internal.file_utils(basename, 'ext'), 'nii') + basename = bids.internal.file_utils(basename, 'basename'); + end + ideal_metafile = bids.internal.file_utils('FPList', ... + pth, ... + sprintf(pattern, basename)); + % Default assumes we are dealing with a file in the root directory % like "participants.tsv". % If the file has underscore separated entities ("sub-01_T1w.nii") @@ -59,7 +68,7 @@ for n = 1:N - % List the potential metadata files associated with this file suffix type + % List the metadata files associated with this file metafile = bids.internal.file_utils('FPList', pth, sprintf(pattern, p.suffix)); if isempty(metafile) @@ -68,6 +77,14 @@ metafile = cellstr(metafile); end + % in deepest level look for file with only a change in extension + if n == 1 && ~isempty(ideal_metafile) + metalist{end + 1, 1} = ideal_metafile; %#ok<*AGROW> + % Go up to the parent folder + pth = fullfile(pth, '..'); + continue + end + % For all those files we find which one is potentially associated % with the file of interest % TODO: not more than one file per level is allowed @@ -102,7 +119,7 @@ % append path to list if ismeta - metalist{end + 1, 1} = metafile{i}; %#ok + metalist{end + 1, 1} = metafile{i}; end end diff --git a/tests/tests_layout/layout_timing.m b/tests/tests_layout/layout_timing.m index 19c4dd27..201a4384 100644 --- a/tests/tests_layout/layout_timing.m +++ b/tests/tests_layout/layout_timing.m @@ -8,155 +8,168 @@ %% WITH SCHEMA % data time (sec) - % qmri_tb1tfl 0.090 - % qmri_qsm 0.090 - % qmri_sa2rage 0.093 - % pet004 0.105 - % pet003 0.106 - % asl003 0.106 - % qmri_irt1 0.107 - % qmri_vfa 0.110 - % micr_SEMzarr 0.112 - % qmri_mtsat 0.115 - % hcp_example_bids 0.116 - % pet001 0.117 - % fnirs_tapping 0.117 - % asl005 0.118 - % qmri_mp2rage 0.121 - % asl002 0.123 - % asl004 0.124 - % micr_SEM 0.128 - % qmri_megre 0.138 - % eeg_ds003654s_hed_inheritance 0.151 - % ds000246 0.152 - % pet002 0.159 - % asl001 0.163 - % pet005 0.166 - % micr_SPIM 0.176 - % ieeg_epilepsy_ecog 0.183 - % ds000248 0.188 - % ieeg_visual 0.191 - % qmri_mp2rageme 0.192 - % eeg_ds003654s_hed 0.201 - % ieeg_epilepsy 0.204 - % eeg_ds003654s_hed_library 0.212 - % eeg_rest_fmri 0.224 - % eeg_ds003654s_hed_longform 0.226 - % fnirs_automaticity 0.229 - % eeg_matchingpennies 0.308 - % ds000247 0.321 - % ieeg_filtered_speech 0.325 - % eeg_face13 0.379 - % ds003 0.410 - % qmri_mese 0.473 - % eeg_cbm 0.599 - % ieeg_motorMiller2007 0.722 - % synthetic 0.806 - % ds101 0.807 - % ds001 0.864 - % ds005 0.865 - % genetics_ukbb 0.882 - % ieeg_visual_multimodal 0.907 - % ds052 0.945 - % ds105 0.961 - % ds102 1.018 - % qmri_mpm 1.174 - % eeg_rishikesh 1.289 - % ds011 1.310 - % ds008 1.400 - % ds109 1.462 - % ds114 1.464 - % ds051 1.526 - % ds002 1.555 - % ds116 1.749 - % eeg_ds000117 1.777 - % ds007 1.920 - % ds107 2.171 - % ds113b 2.232 - % ds210 2.336 - % ds009 2.476 - % ds110 2.583 - % ds006 2.866 - % ds108 3.218 - % ds000117 5.198 - % 7t_trt 5.443 + % asl001 0.084 + % asl005 0.094 + % asl003 0.095 + % asl002 0.098 + % asl004 0.109 + % pet004 0.114 + % pet003 0.127 + % qmri_tb1tfl 0.137 + % qmri_qsm 0.138 + % qmri_irt1 0.139 + % qmri_sa2rage 0.153 + % micr_SEMzarr 0.159 + % pet001 0.165 + % qmri_mp2rage 0.174 + % pet005 0.182 + % qmri_vfa 0.185 + % qmri_mtsat 0.187 + % micr_SEM 0.198 + % ds000246 0.215 + % qmri_megre 0.217 + % hcp_example_bids 0.244 + % eeg_ds003645s_hed_inheritance 0.257 + % pet002 0.259 + % ds000248 0.292 + % micr_SPIM 0.312 + % qmri_mp2rageme 0.334 + % eeg_ds003645s_hed_longform 0.383 + % ieeg_visual 0.396 + % eeg_ds003645s_hed_library 0.405 + % ieeg_epilepsy_ecog 0.475 + % fnirs_tapping 0.557 + % eeg_rest_fmri 0.602 + % ds000247 0.693 + % ieeg_epilepsyNWB 0.752 + % motion_systemvalidation 0.817 + % eeg_matchingpennies 0.819 + % qmri_mese 0.862 + % eeg_face13 0.871 + % ieeg_epilepsy 1.097 + % ds004332 1.127 + % eeg_ds003645s_hed 1.209 + % ieeg_filtered_speech 1.783 + % ds005 1.904 + % ds003 2.030 + % ds101 2.090 + % qmri_mpm 2.333 + % ds105 2.390 + % motion_spotrotation 2.525 + % ieeg_visual_multimodal 2.544 + % ds052 2.627 + % ds001 2.692 + % genetics_ukbb 2.812 + % synthetic 3.061 + % ds011 3.139 + % ds008 3.390 + % eeg_cbm 3.691 + % ds051 3.726 + % ds114 3.818 + % ds002 3.877 + % ds102 4.276 + % ds007 4.544 + % ds109 4.726 + % ds116 5.002 + % eeg_rishikesh 5.175 + % ieeg_motorMiller2007 5.342 + % ds009 5.821 + % ds006 6.574 + % ds107 6.991 + % ds110 7.362 + % ds113b 7.766 + % eeg_ds000117 8.033 + % ds210 8.614 + % motion_dualtask 8.633 + % ds108 10.668 + % ds000117 15.543 + % 7t_trt 15.960 + % fnirs_automaticity 19.642 + % docs NaN % ds000001-fmriprep NaN + % tools NaN %% WITHOUT SCHEMA - % data time (sec) - % asl001 0.089 - % asl003 0.092 - % asl005 0.094 - % asl002 0.097 - % asl004 0.099 - % qmri_qsm 0.103 - % qmri_tb1tfl 0.106 - % pet004 0.113 - % qmri_sa2rage 0.113 - % hcp_example_bids 0.116 - % ds000248 0.117 - % qmri_vfa 0.119 - % pet003 0.120 - % qmri_mp2rage 0.122 - % ds000246 0.125 - % micr_SEMzarr 0.128 + % qmri_qsm 0.071 + % qmri_tb1tfl 0.073 + % qmri_sa2rage 0.081 + % qmri_vfa 0.105 + % qmri_irt1 0.108 + % hcp_example_bids 0.113 + % asl001 0.120 + % pet004 0.122 + % pet001 0.126 + % qmri_mp2rage 0.128 + % asl003 0.128 % qmri_mtsat 0.132 - % pet001 0.133 - % eeg_ds003654s_hed_inheritance 0.135 - % qmri_irt1 0.141 - % pet005 0.148 - % micr_SEM 0.149 - % eeg_ds003654s_hed_longform 0.174 - % eeg_ds003654s_hed 0.176 - % eeg_ds003654s_hed_library 0.176 - % pet002 0.182 - % qmri_megre 0.183 - % micr_SPIM 0.185 - % ieeg_epilepsy 0.195 - % ieeg_visual 0.196 - % ieeg_epilepsy_ecog 0.199 - % qmri_mp2rageme 0.212 - % eeg_rest_fmri 0.216 - % fnirs_tapping 0.219 - % ds000247 0.264 - % eeg_matchingpennies 0.283 - % eeg_face13 0.335 - % ds003 0.375 - % ieeg_filtered_speech 0.385 - % eeg_cbm 0.476 - % qmri_mese 0.592 - % ds001 0.720 - % ds005 0.789 - % ieeg_motorMiller2007 0.808 - % ds101 0.813 - % ds105 0.829 - % genetics_ukbb 0.902 - % synthetic 0.963 - % ds102 0.970 - % ds052 0.993 - % ds008 1.127 - % eeg_rishikesh 1.144 - % ds011 1.190 - % ieeg_visual_multimodal 1.194 - % ds114 1.213 - % ds109 1.245 - % ds002 1.338 - % ds051 1.353 - % qmri_mpm 1.358 - % ds116 1.461 - % eeg_ds000117 1.518 - % ds007 1.586 - % ds113b 1.774 - % ds009 1.986 - % ds107 2.048 - % ds210 2.191 - % ds006 2.359 - % ds110 2.416 - % ds108 2.917 - % 7t_trt 3.905 - % fnirs_automaticity 5.766 - % ds000117 6.191 + % asl002 0.133 + % asl005 0.135 + % pet003 0.145 + % micr_SEMzarr 0.152 + % qmri_megre 0.176 + % asl004 0.177 + % micr_SEM 0.183 + % eeg_ds003645s_hed_inheritance 0.217 + % pet005 0.220 + % qmri_mp2rageme 0.243 + % ds000248 0.264 + % pet002 0.268 + % micr_SPIM 0.292 + % ds000246 0.307 + % ieeg_epilepsyNWB 0.333 + % eeg_ds003645s_hed_longform 0.335 + % ieeg_visual 0.351 + % eeg_ds003645s_hed_library 0.359 + % eeg_ds003645s_hed 0.362 + % ieeg_epilepsy 0.380 + % fnirs_tapping 0.463 + % ieeg_epilepsy_ecog 0.468 + % motion_systemvalidation 0.508 + % eeg_rest_fmri 0.523 + % qmri_mese 0.716 + % eeg_face13 0.767 + % ieeg_filtered_speech 0.775 + % eeg_matchingpennies 0.813 + % ds003 1.049 + % ds000247 1.092 + % ds004332 1.406 + % qmri_mpm 1.735 + % ieeg_motorMiller2007 1.781 + % motion_spotrotation 1.857 + % genetics_ukbb 1.915 + % ieeg_visual_multimodal 2.170 + % ds005 2.202 + % ds101 2.350 + % synthetic 2.446 + % ds052 2.544 + % eeg_cbm 2.554 + % ds001 2.638 + % eeg_rishikesh 2.776 + % ds102 2.939 + % ds105 3.204 + % ds109 3.461 + % ds114 3.507 + % ds011 4.585 + % ds051 4.758 + % ds002 4.927 + % ds008 5.239 + % ds116 5.307 + % eeg_ds000117 5.373 + % ds107 6.259 + % ds113b 6.300 + % ds210 7.619 + % ds110 7.992 + % motion_dualtask 8.380 + % ds108 8.887 + % ds007 10.000 + % ds009 10.363 + % ds006 10.371 + % fnirs_automaticity 14.192 + % 7t_trt 15.023 + % ds000117 23.928 + % docs NaN % ds000001-fmriprep NaN + % tools NaN pth_bids_example = get_test_data_dir(); diff --git a/tests/tests_layout/test_layout_bugs.m b/tests/tests_layout/test_layout_bugs.m new file mode 100644 index 00000000..ad21f82a --- /dev/null +++ b/tests/tests_layout/test_layout_bugs.m @@ -0,0 +1,90 @@ +function test_suite = test_layout_bugs %#ok<*STOUT> + try % assignment of 'localfunctions' is necessary in Matlab >= 2016 + test_functions = localfunctions(); %#ok<*NASGU> + catch % no problem; early Matlab versions can use initTestSuite fine + end + initTestSuite; +end + +function test_inheritance_several_files_per_level() + + bids_dir = fullfile(get_test_data_dir(), 'asl001'); + + verbose = false; + + BIDS = bids.layout(bids_dir, 'verbose', verbose); + + data = bids.query(BIDS, 'data', 'modality', 'anat'); + + bf = bids.File(data{1}); + + bf.entities.run = '1'; + bf.update(); + + new_file = bids.internal.file_utils(data{1}, 'filename', bf.filename); + copyfile(data{1}, new_file); + + new_json = fullfile(fileparts(bf.path), bf.json_filename); + bids.util.jsonencode(new_json, bf.metadata); + + BIDS = bids.layout(bids_dir, 'verbose', verbose); + assertEqual(numel(BIDS.subjects.anat(1).metafile), 1); + assertEqual(numel(BIDS.subjects.anat(2).metafile), 1); + + BIDS = bids.layout(bids_dir, 'verbose', verbose, 'use_schema', false); + assertEqual(numel(BIDS.subjects.anat(1).metafile), 1); + assertEqual(numel(BIDS.subjects.anat(2).metafile), 1); + + delete(new_file); + delete(new_json); + +end + +function test_inheritance_several_files_per_level_derivatives() + + bids_dir = tempname; + mkdir(bids_dir); + + folders.subjects = {'01'}; + folders.sessions = {'01'}; + folders.modalities = {'anat'}; + + bids.init(bids_dir, ... + 'folders', folders, ... + 'is_derivative', true, ... + 'is_datalad_ds', false); + + input = struct('ext', '.nii', ... + 'suffix', 'T1w', ... + 'modality', 'anat', ... + 'entities', struct('sub', '01', ... + 'ses', '01', ... + 'desc', 'preproc')); + bf = bids.File(input); + bf = touch(bf, bids_dir); + bf = bf.metadata_add('RepetitionTime', 1); + bf.metadata_write(); + + input.entities.space = 'MNI'; + bf = bids.File(input); + bf = touch(bf, bids_dir); + bf = bf.metadata_add('RepetitionTime', 1); + bf.metadata_write(); + + verbose = false; + + BIDS = bids.layout(bids_dir, 'verbose', verbose, 'use_schema', false); + + assertEqual(numel(BIDS.subjects.anat(1).metafile), 1); + assertEqual(numel(BIDS.subjects.anat(2).metafile), 1); + +end + +function bf = touch(bf, bids_dir) + bf.path = fullfile(bids_dir, bf.bids_path, bf.filename); + bids.util.mkdir(fileparts(bf.path)); + + fid = fopen(bf.path, 'w'); + fprintf(fid, 'foo'); + fclose(fid); +end