junifer 0.0.5.dev62__py3-none-any.whl → 0.0.5.dev86__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- junifer/_version.py +2 -2
- junifer/api/cli.py +26 -0
- junifer/api/functions.py +1 -1
- junifer/api/res/freesurfer/mri_binarize +3 -0
- junifer/api/res/freesurfer/mri_mc +3 -0
- junifer/api/res/freesurfer/mri_pretess +3 -0
- junifer/api/res/freesurfer/mris_convert +3 -0
- junifer/api/res/freesurfer/run_freesurfer_docker.sh +61 -0
- junifer/configs/juseless/datagrabbers/tests/test_ucla.py +1 -3
- junifer/configs/juseless/datagrabbers/ucla.py +9 -9
- junifer/data/masks.py +10 -22
- junifer/data/parcellations.py +1 -1
- junifer/data/tests/test_masks.py +8 -28
- junifer/datagrabber/aomic/id1000.py +34 -38
- junifer/datagrabber/aomic/piop1.py +33 -37
- junifer/datagrabber/aomic/piop2.py +35 -39
- junifer/datagrabber/aomic/tests/test_id1000.py +10 -11
- junifer/datagrabber/aomic/tests/test_piop1.py +10 -11
- junifer/datagrabber/aomic/tests/test_piop2.py +10 -11
- junifer/datagrabber/datalad_base.py +10 -1
- junifer/datagrabber/dmcc13_benchmark.py +36 -54
- junifer/datagrabber/pattern.py +116 -46
- junifer/datagrabber/pattern_datalad.py +22 -12
- junifer/datagrabber/tests/test_datagrabber_utils.py +15 -9
- junifer/datagrabber/tests/test_dmcc13_benchmark.py +46 -19
- junifer/datagrabber/utils.py +127 -54
- junifer/datareader/default.py +91 -42
- junifer/pipeline/utils.py +64 -1
- junifer/preprocess/base.py +2 -2
- junifer/preprocess/confounds/fmriprep_confound_remover.py +44 -60
- junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +72 -113
- junifer/testing/datagrabbers.py +5 -5
- junifer/testing/tests/test_partlycloudytesting_datagrabber.py +7 -7
- {junifer-0.0.5.dev62.dist-info → junifer-0.0.5.dev86.dist-info}/METADATA +1 -1
- {junifer-0.0.5.dev62.dist-info → junifer-0.0.5.dev86.dist-info}/RECORD +40 -35
- {junifer-0.0.5.dev62.dist-info → junifer-0.0.5.dev86.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.5.dev62.dist-info → junifer-0.0.5.dev86.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.5.dev62.dist-info → junifer-0.0.5.dev86.dist-info}/WHEEL +0 -0
- {junifer-0.0.5.dev62.dist-info → junifer-0.0.5.dev86.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.5.dev62.dist-info → junifer-0.0.5.dev86.dist-info}/top_level.txt +0 -0
@@ -40,20 +40,20 @@ def test_DataladAOMICID1000() -> None:
|
|
40
40
|
assert out["BOLD"]["path"].exists()
|
41
41
|
assert out["BOLD"]["path"].is_file()
|
42
42
|
|
43
|
-
# asserts type
|
44
|
-
assert "
|
43
|
+
# asserts type BOLD.confounds
|
44
|
+
assert "confounds" in out["BOLD"]
|
45
45
|
|
46
46
|
assert (
|
47
|
-
out["
|
47
|
+
out["BOLD"]["confounds"]["path"].name
|
48
48
|
== f"{test_element}_task-moviewatching_"
|
49
49
|
"desc-confounds_regressors.tsv"
|
50
50
|
)
|
51
51
|
|
52
|
-
assert out["
|
53
|
-
assert out["
|
52
|
+
assert out["BOLD"]["confounds"]["path"].exists()
|
53
|
+
assert out["BOLD"]["confounds"]["path"].is_file()
|
54
54
|
|
55
|
-
# assert
|
56
|
-
assert out["
|
55
|
+
# assert BOLD.mask
|
56
|
+
assert out["BOLD"]["mask"]["path"].exists()
|
57
57
|
|
58
58
|
# asserts type "T1w"
|
59
59
|
assert "T1w" in out
|
@@ -67,8 +67,8 @@ def test_DataladAOMICID1000() -> None:
|
|
67
67
|
assert out["T1w"]["path"].exists()
|
68
68
|
assert out["T1w"]["path"].is_file()
|
69
69
|
|
70
|
-
# asserts
|
71
|
-
assert out["
|
70
|
+
# asserts T1w.mask
|
71
|
+
assert out["T1w"]["mask"]["path"].exists()
|
72
72
|
|
73
73
|
# asserts type "VBM_CSF"
|
74
74
|
assert "VBM_CSF" in out
|
@@ -129,13 +129,12 @@ def test_DataladAOMICID1000() -> None:
|
|
129
129
|
"types",
|
130
130
|
[
|
131
131
|
"BOLD",
|
132
|
-
"BOLD_confounds",
|
133
132
|
"T1w",
|
134
133
|
"VBM_CSF",
|
135
134
|
"VBM_GM",
|
136
135
|
"VBM_WM",
|
137
136
|
"DWI",
|
138
|
-
["BOLD", "
|
137
|
+
["BOLD", "VBM_CSF"],
|
139
138
|
["T1w", "VBM_CSF"],
|
140
139
|
["VBM_GM", "VBM_WM"],
|
141
140
|
["DWI", "BOLD"],
|
@@ -63,19 +63,19 @@ def test_DataladAOMICPIOP1(tasks: Optional[str]) -> None:
|
|
63
63
|
assert out["BOLD"]["path"].exists()
|
64
64
|
assert out["BOLD"]["path"].is_file()
|
65
65
|
|
66
|
-
# asserts type
|
67
|
-
assert "
|
66
|
+
# asserts type BOLD.confounds
|
67
|
+
assert "confounds" in out["BOLD"]
|
68
68
|
|
69
69
|
assert (
|
70
|
-
out["
|
70
|
+
out["BOLD"]["confounds"]["path"].name == f"{sub}_task-{new_task}_"
|
71
71
|
"desc-confounds_regressors.tsv"
|
72
72
|
)
|
73
73
|
|
74
|
-
assert out["
|
75
|
-
assert out["
|
74
|
+
assert out["BOLD"]["confounds"]["path"].exists()
|
75
|
+
assert out["BOLD"]["confounds"]["path"].is_file()
|
76
76
|
|
77
|
-
# assert
|
78
|
-
assert out["
|
77
|
+
# assert BOLD.mask
|
78
|
+
assert out["BOLD"]["mask"]["path"].exists()
|
79
79
|
|
80
80
|
# asserts type "T1w"
|
81
81
|
assert "T1w" in out
|
@@ -88,8 +88,8 @@ def test_DataladAOMICPIOP1(tasks: Optional[str]) -> None:
|
|
88
88
|
assert out["T1w"]["path"].exists()
|
89
89
|
assert out["T1w"]["path"].is_file()
|
90
90
|
|
91
|
-
# asserts
|
92
|
-
assert out["
|
91
|
+
# asserts T1w.mask
|
92
|
+
assert out["T1w"]["mask"]["path"].exists()
|
93
93
|
|
94
94
|
# asserts type "VBM_CSF"
|
95
95
|
assert "VBM_CSF" in out
|
@@ -147,13 +147,12 @@ def test_DataladAOMICPIOP1(tasks: Optional[str]) -> None:
|
|
147
147
|
"types",
|
148
148
|
[
|
149
149
|
"BOLD",
|
150
|
-
"BOLD_confounds",
|
151
150
|
"T1w",
|
152
151
|
"VBM_CSF",
|
153
152
|
"VBM_GM",
|
154
153
|
"VBM_WM",
|
155
154
|
"DWI",
|
156
|
-
["BOLD", "
|
155
|
+
["BOLD", "VBM_CSF"],
|
157
156
|
["T1w", "VBM_CSF"],
|
158
157
|
["VBM_GM", "VBM_WM"],
|
159
158
|
["DWI", "BOLD"],
|
@@ -57,19 +57,19 @@ def test_DataladAOMICPIOP2(tasks: Optional[str]) -> None:
|
|
57
57
|
assert out["BOLD"]["path"].exists()
|
58
58
|
assert out["BOLD"]["path"].is_file()
|
59
59
|
|
60
|
-
# asserts type
|
61
|
-
assert "
|
60
|
+
# asserts type BOLD.confounds
|
61
|
+
assert "confounds" in out["BOLD"]
|
62
62
|
|
63
63
|
assert (
|
64
|
-
out["
|
64
|
+
out["BOLD"]["confounds"]["path"].name == f"{sub}_task-{new_task}_"
|
65
65
|
"desc-confounds_regressors.tsv"
|
66
66
|
)
|
67
67
|
|
68
|
-
assert out["
|
69
|
-
assert out["
|
68
|
+
assert out["BOLD"]["confounds"]["path"].exists()
|
69
|
+
assert out["BOLD"]["confounds"]["path"].is_file()
|
70
70
|
|
71
|
-
# assert
|
72
|
-
assert out["
|
71
|
+
# assert BOLD.mask
|
72
|
+
assert out["BOLD"]["mask"]["path"].exists()
|
73
73
|
|
74
74
|
# asserts type "T1w"
|
75
75
|
assert "T1w" in out
|
@@ -82,8 +82,8 @@ def test_DataladAOMICPIOP2(tasks: Optional[str]) -> None:
|
|
82
82
|
assert out["T1w"]["path"].exists()
|
83
83
|
assert out["T1w"]["path"].is_file()
|
84
84
|
|
85
|
-
# asserts
|
86
|
-
assert out["
|
85
|
+
# asserts T1w.mask
|
86
|
+
assert out["T1w"]["mask"]["path"].exists()
|
87
87
|
|
88
88
|
# asserts type "VBM_CSF"
|
89
89
|
assert "VBM_CSF" in out
|
@@ -141,13 +141,12 @@ def test_DataladAOMICPIOP2(tasks: Optional[str]) -> None:
|
|
141
141
|
"types",
|
142
142
|
[
|
143
143
|
"BOLD",
|
144
|
-
"BOLD_confounds",
|
145
144
|
"T1w",
|
146
145
|
"VBM_CSF",
|
147
146
|
"VBM_GM",
|
148
147
|
"VBM_WM",
|
149
148
|
"DWI",
|
150
|
-
["BOLD", "
|
149
|
+
["BOLD", "VBM_CSF"],
|
151
150
|
["T1w", "VBM_CSF"],
|
152
151
|
["VBM_GM", "VBM_WM"],
|
153
152
|
["DWI", "BOLD"],
|
@@ -176,7 +176,16 @@ class DataladDataGrabber(BaseDataGrabber):
|
|
176
176
|
The unmodified input dictionary.
|
177
177
|
|
178
178
|
"""
|
179
|
-
to_get = [
|
179
|
+
to_get = []
|
180
|
+
for type_val in out.values():
|
181
|
+
# Iterate to check for nested "types" like mask
|
182
|
+
for k, v in type_val.items():
|
183
|
+
# Add base data type path
|
184
|
+
if k == "path":
|
185
|
+
to_get.append(v)
|
186
|
+
# Add nested data type path
|
187
|
+
if isinstance(v, dict) and "path" in v:
|
188
|
+
to_get.append(v["path"])
|
180
189
|
|
181
190
|
if len(to_get) > 0:
|
182
191
|
logger.debug(f"Getting {len(to_get)} files using datalad:")
|
@@ -25,17 +25,16 @@ class DMCC13Benchmark(PatternDataladDataGrabber):
|
|
25
25
|
The directory where the datalad dataset will be cloned. If None,
|
26
26
|
the datalad dataset will be cloned into a temporary directory
|
27
27
|
(default None).
|
28
|
-
types: {"BOLD", "
|
29
|
-
|
30
|
-
"native_t1w = True")} or a list of the options, optional
|
28
|
+
types: {"BOLD", "T1w", "VBM_CSF", "VBM_GM", "VBM_WM"} or \
|
29
|
+
list of the options, optional
|
31
30
|
DMCC data types. If None, all available data types are selected.
|
32
31
|
(default None).
|
33
|
-
sessions: {"ses-wave1bas", "ses-wave1pro", "ses-wave1rea"} or
|
34
|
-
|
32
|
+
sessions: {"ses-wave1bas", "ses-wave1pro", "ses-wave1rea"} or \
|
33
|
+
list of the options, optional
|
35
34
|
DMCC sessions. If None, all available sessions are selected
|
36
35
|
(default None).
|
37
36
|
tasks: {"Rest", "Axcpt", "Cuedts", "Stern", "Stroop"} or \
|
38
|
-
|
37
|
+
list of the options, optional
|
39
38
|
DMCC task sessions. If None, all available task sessions are selected
|
40
39
|
(default None).
|
41
40
|
phase_encodings : {"AP", "PA"} or list of the options, optional
|
@@ -148,24 +147,23 @@ class DMCC13Benchmark(PatternDataladDataGrabber):
|
|
148
147
|
"space-MNI152NLin2009cAsym_desc-preproc_bold.nii.gz"
|
149
148
|
),
|
150
149
|
"space": "MNI152NLin2009cAsym",
|
151
|
-
"
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
"
|
167
|
-
|
168
|
-
"space": "MNI152NLin2009cAsym",
|
150
|
+
"mask": {
|
151
|
+
"pattern": (
|
152
|
+
"derivatives/fmriprep-1.3.2/{subject}/{session}/"
|
153
|
+
"/func/{subject}_{session}_task-{task}_acq-mb4"
|
154
|
+
"{phase_encoding}_run-{run}_"
|
155
|
+
"space-MNI152NLin2009cAsym_desc-brain_mask.nii.gz"
|
156
|
+
),
|
157
|
+
"space": "MNI152NLin2009cAsym",
|
158
|
+
},
|
159
|
+
"confounds": {
|
160
|
+
"pattern": (
|
161
|
+
"derivatives/fmriprep-1.3.2/{subject}/{session}/"
|
162
|
+
"func/{subject}_{session}_task-{task}_acq-mb4"
|
163
|
+
"{phase_encoding}_run-{run}_desc-confounds_regressors.tsv"
|
164
|
+
),
|
165
|
+
"format": "fmriprep",
|
166
|
+
},
|
169
167
|
},
|
170
168
|
"T1w": {
|
171
169
|
"pattern": (
|
@@ -173,14 +171,13 @@ class DMCC13Benchmark(PatternDataladDataGrabber):
|
|
173
171
|
"{subject}_space-MNI152NLin2009cAsym_desc-preproc_T1w.nii.gz"
|
174
172
|
),
|
175
173
|
"space": "MNI152NLin2009cAsym",
|
176
|
-
"
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
"
|
182
|
-
|
183
|
-
"space": "MNI152NLin2009cAsym",
|
174
|
+
"mask": {
|
175
|
+
"pattern": (
|
176
|
+
"derivatives/fmriprep-1.3.2/{subject}/anat/"
|
177
|
+
"{subject}_space-MNI152NLin2009cAsym_desc-brain_mask.nii.gz"
|
178
|
+
),
|
179
|
+
"space": "MNI152NLin2009cAsym",
|
180
|
+
},
|
184
181
|
},
|
185
182
|
"VBM_CSF": {
|
186
183
|
"pattern": (
|
@@ -216,14 +213,13 @@ class DMCC13Benchmark(PatternDataladDataGrabber):
|
|
216
213
|
"{subject}_desc-preproc_T1w.nii.gz"
|
217
214
|
),
|
218
215
|
"space": "native",
|
219
|
-
"
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
"
|
225
|
-
|
226
|
-
"space": "native",
|
216
|
+
"mask": {
|
217
|
+
"pattern": (
|
218
|
+
"derivatives/fmriprep-1.3.2/{subject}/anat/"
|
219
|
+
"{subject}_desc-brain_mask.nii.gz"
|
220
|
+
),
|
221
|
+
"space": "native",
|
222
|
+
},
|
227
223
|
},
|
228
224
|
"Warp": {
|
229
225
|
"pattern": (
|
@@ -298,20 +294,6 @@ class DMCC13Benchmark(PatternDataladDataGrabber):
|
|
298
294
|
phase_encoding=phase_encoding,
|
299
295
|
run=run,
|
300
296
|
)
|
301
|
-
if out.get("BOLD"):
|
302
|
-
out["BOLD"]["mask_item"] = "BOLD_mask"
|
303
|
-
# Add space information
|
304
|
-
out["BOLD"].update({"space": "MNI152NLin2009cAsym"})
|
305
|
-
if out.get("T1w"):
|
306
|
-
out["T1w"]["mask_item"] = "T1w_mask"
|
307
|
-
# Add space information
|
308
|
-
if self.native_t1w:
|
309
|
-
out["T1w"].update({"space": "native"})
|
310
|
-
else:
|
311
|
-
out["T1w"].update({"space": "MNI152NLin2009cAsym"})
|
312
|
-
if out.get("Warp"):
|
313
|
-
# Add source space information
|
314
|
-
out["Warp"].update({"src": "MNI152NLin2009cAsym"})
|
315
297
|
return out
|
316
298
|
|
317
299
|
def get_elements(self) -> List:
|
junifer/datagrabber/pattern.py
CHANGED
@@ -40,7 +40,12 @@ class PatternDataGrabber(BaseDataGrabber):
|
|
40
40
|
|
41
41
|
{
|
42
42
|
"mandatory": ["pattern", "space"],
|
43
|
-
"optional":
|
43
|
+
"optional": {
|
44
|
+
"mask": {
|
45
|
+
"mandatory": ["pattern", "space"],
|
46
|
+
"optional": []
|
47
|
+
}
|
48
|
+
}
|
44
49
|
}
|
45
50
|
|
46
51
|
* ``"T2w"`` :
|
@@ -49,7 +54,12 @@ class PatternDataGrabber(BaseDataGrabber):
|
|
49
54
|
|
50
55
|
{
|
51
56
|
"mandatory": ["pattern", "space"],
|
52
|
-
"optional":
|
57
|
+
"optional": {
|
58
|
+
"mask": {
|
59
|
+
"mandatory": ["pattern", "space"],
|
60
|
+
"optional": []
|
61
|
+
}
|
62
|
+
}
|
53
63
|
}
|
54
64
|
|
55
65
|
* ``"BOLD"`` :
|
@@ -58,7 +68,16 @@ class PatternDataGrabber(BaseDataGrabber):
|
|
58
68
|
|
59
69
|
{
|
60
70
|
"mandatory": ["pattern", "space"],
|
61
|
-
"optional":
|
71
|
+
"optional": {
|
72
|
+
"mask": {
|
73
|
+
"mandatory": ["pattern", "space"],
|
74
|
+
"optional": []
|
75
|
+
}
|
76
|
+
"confounds": {
|
77
|
+
"mandatory": ["pattern", "format"],
|
78
|
+
"optional": []
|
79
|
+
}
|
80
|
+
}
|
62
81
|
}
|
63
82
|
|
64
83
|
* ``"Warp"`` :
|
@@ -70,15 +89,6 @@ class PatternDataGrabber(BaseDataGrabber):
|
|
70
89
|
"optional": []
|
71
90
|
}
|
72
91
|
|
73
|
-
* ``"BOLD_confounds"`` :
|
74
|
-
|
75
|
-
.. code-block:: none
|
76
|
-
|
77
|
-
{
|
78
|
-
"mandatory": ["pattern", "format"],
|
79
|
-
"optional": []
|
80
|
-
}
|
81
|
-
|
82
92
|
* ``"VBM_GM"`` :
|
83
93
|
|
84
94
|
.. code-block:: none
|
@@ -246,6 +256,64 @@ class PatternDataGrabber(BaseDataGrabber):
|
|
246
256
|
)
|
247
257
|
return pattern.format(**element)
|
248
258
|
|
259
|
+
def _get_path_from_patterns(
|
260
|
+
self, element: Dict, pattern: str, data_type: str
|
261
|
+
) -> Path:
|
262
|
+
"""Get path from resolved patterns.
|
263
|
+
|
264
|
+
Parameters
|
265
|
+
----------
|
266
|
+
element : dict
|
267
|
+
The element to be used in the replacement.
|
268
|
+
pattern : str
|
269
|
+
The pattern to be replaced.
|
270
|
+
data_type : str
|
271
|
+
The data type of the pattern.
|
272
|
+
|
273
|
+
Returns
|
274
|
+
-------
|
275
|
+
pathlib.Path
|
276
|
+
The path for the resolved pattern.
|
277
|
+
|
278
|
+
Raises
|
279
|
+
------
|
280
|
+
RuntimeError
|
281
|
+
If more than one file matches for a data type's pattern or
|
282
|
+
if no file matches for a data type's pattern or
|
283
|
+
if file cannot be accessed for an element.
|
284
|
+
|
285
|
+
"""
|
286
|
+
# Replace element in the pattern for globbing
|
287
|
+
resolved_pattern = self._replace_patterns_glob(element, pattern)
|
288
|
+
# Resolve path for wildcard
|
289
|
+
if "*" in resolved_pattern:
|
290
|
+
t_matches = list(self.datadir.absolute().glob(resolved_pattern))
|
291
|
+
# Multiple matches
|
292
|
+
if len(t_matches) > 1:
|
293
|
+
raise_error(
|
294
|
+
f"More than one file matches for {element} / {data_type}:"
|
295
|
+
f" {t_matches}",
|
296
|
+
klass=RuntimeError,
|
297
|
+
)
|
298
|
+
# No matches
|
299
|
+
elif len(t_matches) == 0:
|
300
|
+
raise_error(
|
301
|
+
f"No file matches for {element} / {data_type}",
|
302
|
+
klass=RuntimeError,
|
303
|
+
)
|
304
|
+
path = t_matches[0]
|
305
|
+
else:
|
306
|
+
path = self.datadir / resolved_pattern
|
307
|
+
if not self.skip_file_check:
|
308
|
+
if not path.exists() and not path.is_symlink():
|
309
|
+
raise_error(
|
310
|
+
f"Cannot access {data_type} for {element}: "
|
311
|
+
f"File {path} does not exist",
|
312
|
+
klass=RuntimeError,
|
313
|
+
)
|
314
|
+
|
315
|
+
return path
|
316
|
+
|
249
317
|
def get_element_keys(self) -> List[str]:
|
250
318
|
"""Get element keys.
|
251
319
|
|
@@ -279,47 +347,49 @@ class PatternDataGrabber(BaseDataGrabber):
|
|
279
347
|
Dictionary of dictionaries for each type of data required for the
|
280
348
|
specified element.
|
281
349
|
|
282
|
-
Raises
|
283
|
-
------
|
284
|
-
RuntimeError
|
285
|
-
If more than one file matches for a data type's pattern or
|
286
|
-
if no file matches for a data type's pattern or
|
287
|
-
if file cannot be accessed for an element.
|
288
|
-
|
289
350
|
"""
|
290
351
|
out = {}
|
291
352
|
for t_type in self.types:
|
353
|
+
# Data type dictionary
|
292
354
|
t_pattern = self.patterns[t_type]
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
if
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
355
|
+
# Copy data type dictionary in output
|
356
|
+
out[t_type] = t_pattern.copy()
|
357
|
+
# Iterate to check for nested "types" like mask
|
358
|
+
for k, v in t_pattern.items():
|
359
|
+
# Resolve pattern for base data type
|
360
|
+
if k == "pattern":
|
361
|
+
logger.info(f"Resolving path from pattern for {t_type}")
|
362
|
+
# Resolve pattern
|
363
|
+
base_data_type_pattern_path = self._get_path_from_patterns(
|
364
|
+
element=element,
|
365
|
+
pattern=v,
|
366
|
+
data_type=t_type,
|
303
367
|
)
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
368
|
+
# Remove pattern key
|
369
|
+
out[t_type].pop("pattern")
|
370
|
+
# Add path key
|
371
|
+
out[t_type].update({"path": base_data_type_pattern_path})
|
372
|
+
# Resolve pattern for nested data type
|
373
|
+
if isinstance(v, dict) and "pattern" in v:
|
374
|
+
# Set nested type key for easier access
|
375
|
+
t_nested_type = f"{t_type}.{k}"
|
376
|
+
logger.info(
|
377
|
+
f"Resolving path from pattern for {t_nested_type}"
|
308
378
|
)
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
f"Cannot access {t_type} for {element}: "
|
316
|
-
f"File {t_out} does not exist",
|
317
|
-
klass=RuntimeError,
|
379
|
+
# Resolve pattern
|
380
|
+
nested_data_type_pattern_path = (
|
381
|
+
self._get_path_from_patterns(
|
382
|
+
element=element,
|
383
|
+
pattern=v["pattern"],
|
384
|
+
data_type=t_nested_type,
|
318
385
|
)
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
386
|
+
)
|
387
|
+
# Remove pattern key
|
388
|
+
out[t_type][k].pop("pattern")
|
389
|
+
# Add path key
|
390
|
+
out[t_type][k].update(
|
391
|
+
{"path": nested_data_type_pattern_path}
|
392
|
+
)
|
323
393
|
|
324
394
|
return out
|
325
395
|
|
@@ -32,7 +32,12 @@ class PatternDataladDataGrabber(DataladDataGrabber, PatternDataGrabber):
|
|
32
32
|
|
33
33
|
{
|
34
34
|
"mandatory": ["pattern", "space"],
|
35
|
-
"optional":
|
35
|
+
"optional": {
|
36
|
+
"mask": {
|
37
|
+
"mandatory": ["pattern", "space"],
|
38
|
+
"optional": []
|
39
|
+
}
|
40
|
+
}
|
36
41
|
}
|
37
42
|
|
38
43
|
* ``"T2w"`` :
|
@@ -41,7 +46,12 @@ class PatternDataladDataGrabber(DataladDataGrabber, PatternDataGrabber):
|
|
41
46
|
|
42
47
|
{
|
43
48
|
"mandatory": ["pattern", "space"],
|
44
|
-
"optional":
|
49
|
+
"optional": {
|
50
|
+
"mask": {
|
51
|
+
"mandatory": ["pattern", "space"],
|
52
|
+
"optional": []
|
53
|
+
}
|
54
|
+
}
|
45
55
|
}
|
46
56
|
|
47
57
|
* ``"BOLD"`` :
|
@@ -50,7 +60,16 @@ class PatternDataladDataGrabber(DataladDataGrabber, PatternDataGrabber):
|
|
50
60
|
|
51
61
|
{
|
52
62
|
"mandatory": ["pattern", "space"],
|
53
|
-
"optional":
|
63
|
+
"optional": {
|
64
|
+
"mask": {
|
65
|
+
"mandatory": ["pattern", "space"],
|
66
|
+
"optional": []
|
67
|
+
}
|
68
|
+
"confounds": {
|
69
|
+
"mandatory": ["pattern", "format"],
|
70
|
+
"optional": []
|
71
|
+
}
|
72
|
+
}
|
54
73
|
}
|
55
74
|
|
56
75
|
* ``"Warp"`` :
|
@@ -62,15 +81,6 @@ class PatternDataladDataGrabber(DataladDataGrabber, PatternDataGrabber):
|
|
62
81
|
"optional": []
|
63
82
|
}
|
64
83
|
|
65
|
-
* ``"BOLD_confounds"`` :
|
66
|
-
|
67
|
-
.. code-block:: none
|
68
|
-
|
69
|
-
{
|
70
|
-
"mandatory": ["pattern", "format"],
|
71
|
-
"optional": []
|
72
|
-
}
|
73
|
-
|
74
84
|
* ``"VBM_GM"`` :
|
75
85
|
|
76
86
|
.. code-block:: none
|
@@ -151,12 +151,18 @@ def test_validate_replacements(
|
|
151
151
|
pytest.raises(KeyError, match="Mandatory key"),
|
152
152
|
),
|
153
153
|
(
|
154
|
-
["
|
154
|
+
["BOLD"],
|
155
155
|
{
|
156
|
-
"
|
157
|
-
"pattern":
|
158
|
-
|
156
|
+
"BOLD": {
|
157
|
+
"pattern": (
|
158
|
+
"{subject}/func/{subject}_task-rest_bold.nii.gz"
|
159
|
+
),
|
159
160
|
"space": "MNINLin6Asym",
|
161
|
+
"confounds": {
|
162
|
+
"pattern": "{subject}/func/{subject}_confounds.tsv",
|
163
|
+
"format": "fmriprep",
|
164
|
+
},
|
165
|
+
"zip": "zap",
|
160
166
|
},
|
161
167
|
},
|
162
168
|
pytest.raises(RuntimeError, match="not accepted"),
|
@@ -172,7 +178,7 @@ def test_validate_replacements(
|
|
172
178
|
pytest.raises(ValueError, match="following a replacement"),
|
173
179
|
),
|
174
180
|
(
|
175
|
-
["T1w", "T2w", "BOLD"
|
181
|
+
["T1w", "T2w", "BOLD"],
|
176
182
|
{
|
177
183
|
"T1w": {
|
178
184
|
"pattern": "{subject}/anat/{subject}_T1w.nii.gz",
|
@@ -187,10 +193,10 @@ def test_validate_replacements(
|
|
187
193
|
"{subject}/func/{subject}_task-rest_bold.nii.gz"
|
188
194
|
),
|
189
195
|
"space": "MNI152NLin6Asym",
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
196
|
+
"confounds": {
|
197
|
+
"pattern": "{subject}/func/{subject}_confounds.tsv",
|
198
|
+
"format": "fmriprep",
|
199
|
+
},
|
194
200
|
},
|
195
201
|
},
|
196
202
|
nullcontext(),
|