junifer 0.0.3.dev186__py3-none-any.whl → 0.0.4__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.
Files changed (178) hide show
  1. junifer/_version.py +14 -2
  2. junifer/api/cli.py +162 -17
  3. junifer/api/functions.py +87 -419
  4. junifer/api/parser.py +24 -0
  5. junifer/api/queue_context/__init__.py +8 -0
  6. junifer/api/queue_context/gnu_parallel_local_adapter.py +258 -0
  7. junifer/api/queue_context/htcondor_adapter.py +365 -0
  8. junifer/api/queue_context/queue_context_adapter.py +60 -0
  9. junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +192 -0
  10. junifer/api/queue_context/tests/test_htcondor_adapter.py +257 -0
  11. junifer/api/res/afni/run_afni_docker.sh +6 -6
  12. junifer/api/res/ants/ResampleImage +3 -0
  13. junifer/api/res/ants/antsApplyTransforms +3 -0
  14. junifer/api/res/ants/antsApplyTransformsToPoints +3 -0
  15. junifer/api/res/ants/run_ants_docker.sh +39 -0
  16. junifer/api/res/fsl/applywarp +3 -0
  17. junifer/api/res/fsl/flirt +3 -0
  18. junifer/api/res/fsl/img2imgcoord +3 -0
  19. junifer/api/res/fsl/run_fsl_docker.sh +39 -0
  20. junifer/api/res/fsl/std2imgcoord +3 -0
  21. junifer/api/res/run_conda.sh +4 -4
  22. junifer/api/res/run_venv.sh +22 -0
  23. junifer/api/tests/data/partly_cloudy_agg_mean_tian.yml +16 -0
  24. junifer/api/tests/test_api_utils.py +21 -3
  25. junifer/api/tests/test_cli.py +232 -9
  26. junifer/api/tests/test_functions.py +211 -439
  27. junifer/api/tests/test_parser.py +1 -1
  28. junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +6 -1
  29. junifer/configs/juseless/datagrabbers/camcan_vbm.py +6 -1
  30. junifer/configs/juseless/datagrabbers/ixi_vbm.py +6 -1
  31. junifer/configs/juseless/datagrabbers/tests/test_ucla.py +8 -8
  32. junifer/configs/juseless/datagrabbers/ucla.py +44 -26
  33. junifer/configs/juseless/datagrabbers/ukb_vbm.py +6 -1
  34. junifer/data/VOIs/meta/AutobiographicalMemory_VOIs.txt +23 -0
  35. junifer/data/VOIs/meta/Power2013_MNI_VOIs.tsv +264 -0
  36. junifer/data/__init__.py +4 -0
  37. junifer/data/coordinates.py +298 -31
  38. junifer/data/masks.py +360 -28
  39. junifer/data/parcellations.py +621 -188
  40. junifer/data/template_spaces.py +190 -0
  41. junifer/data/tests/test_coordinates.py +34 -3
  42. junifer/data/tests/test_data_utils.py +1 -0
  43. junifer/data/tests/test_masks.py +202 -86
  44. junifer/data/tests/test_parcellations.py +266 -55
  45. junifer/data/tests/test_template_spaces.py +104 -0
  46. junifer/data/utils.py +4 -2
  47. junifer/datagrabber/__init__.py +1 -0
  48. junifer/datagrabber/aomic/id1000.py +111 -70
  49. junifer/datagrabber/aomic/piop1.py +116 -53
  50. junifer/datagrabber/aomic/piop2.py +116 -53
  51. junifer/datagrabber/aomic/tests/test_id1000.py +27 -27
  52. junifer/datagrabber/aomic/tests/test_piop1.py +27 -27
  53. junifer/datagrabber/aomic/tests/test_piop2.py +27 -27
  54. junifer/datagrabber/base.py +62 -10
  55. junifer/datagrabber/datalad_base.py +0 -2
  56. junifer/datagrabber/dmcc13_benchmark.py +372 -0
  57. junifer/datagrabber/hcp1200/datalad_hcp1200.py +5 -0
  58. junifer/datagrabber/hcp1200/hcp1200.py +30 -13
  59. junifer/datagrabber/pattern.py +133 -27
  60. junifer/datagrabber/pattern_datalad.py +111 -13
  61. junifer/datagrabber/tests/test_base.py +57 -6
  62. junifer/datagrabber/tests/test_datagrabber_utils.py +204 -76
  63. junifer/datagrabber/tests/test_datalad_base.py +0 -6
  64. junifer/datagrabber/tests/test_dmcc13_benchmark.py +256 -0
  65. junifer/datagrabber/tests/test_multiple.py +43 -10
  66. junifer/datagrabber/tests/test_pattern.py +125 -178
  67. junifer/datagrabber/tests/test_pattern_datalad.py +44 -25
  68. junifer/datagrabber/utils.py +151 -16
  69. junifer/datareader/default.py +36 -10
  70. junifer/external/nilearn/junifer_nifti_spheres_masker.py +6 -0
  71. junifer/markers/base.py +25 -16
  72. junifer/markers/collection.py +35 -16
  73. junifer/markers/complexity/__init__.py +27 -0
  74. junifer/markers/complexity/complexity_base.py +149 -0
  75. junifer/markers/complexity/hurst_exponent.py +136 -0
  76. junifer/markers/complexity/multiscale_entropy_auc.py +140 -0
  77. junifer/markers/complexity/perm_entropy.py +132 -0
  78. junifer/markers/complexity/range_entropy.py +136 -0
  79. junifer/markers/complexity/range_entropy_auc.py +145 -0
  80. junifer/markers/complexity/sample_entropy.py +134 -0
  81. junifer/markers/complexity/tests/test_complexity_base.py +19 -0
  82. junifer/markers/complexity/tests/test_hurst_exponent.py +69 -0
  83. junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +68 -0
  84. junifer/markers/complexity/tests/test_perm_entropy.py +68 -0
  85. junifer/markers/complexity/tests/test_range_entropy.py +69 -0
  86. junifer/markers/complexity/tests/test_range_entropy_auc.py +69 -0
  87. junifer/markers/complexity/tests/test_sample_entropy.py +68 -0
  88. junifer/markers/complexity/tests/test_weighted_perm_entropy.py +68 -0
  89. junifer/markers/complexity/weighted_perm_entropy.py +133 -0
  90. junifer/markers/falff/_afni_falff.py +153 -0
  91. junifer/markers/falff/_junifer_falff.py +142 -0
  92. junifer/markers/falff/falff_base.py +91 -84
  93. junifer/markers/falff/falff_parcels.py +61 -45
  94. junifer/markers/falff/falff_spheres.py +64 -48
  95. junifer/markers/falff/tests/test_falff_parcels.py +89 -121
  96. junifer/markers/falff/tests/test_falff_spheres.py +92 -127
  97. junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +1 -0
  98. junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +1 -0
  99. junifer/markers/functional_connectivity/functional_connectivity_base.py +1 -0
  100. junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +46 -44
  101. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +34 -39
  102. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +40 -52
  103. junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +62 -70
  104. junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +99 -85
  105. junifer/markers/parcel_aggregation.py +60 -38
  106. junifer/markers/reho/_afni_reho.py +192 -0
  107. junifer/markers/reho/_junifer_reho.py +281 -0
  108. junifer/markers/reho/reho_base.py +69 -34
  109. junifer/markers/reho/reho_parcels.py +26 -16
  110. junifer/markers/reho/reho_spheres.py +23 -9
  111. junifer/markers/reho/tests/test_reho_parcels.py +93 -92
  112. junifer/markers/reho/tests/test_reho_spheres.py +88 -86
  113. junifer/markers/sphere_aggregation.py +54 -9
  114. junifer/markers/temporal_snr/temporal_snr_base.py +1 -0
  115. junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +38 -37
  116. junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +34 -38
  117. junifer/markers/tests/test_collection.py +43 -42
  118. junifer/markers/tests/test_ets_rss.py +29 -37
  119. junifer/markers/tests/test_parcel_aggregation.py +587 -468
  120. junifer/markers/tests/test_sphere_aggregation.py +209 -157
  121. junifer/markers/utils.py +2 -40
  122. junifer/onthefly/read_transform.py +13 -6
  123. junifer/pipeline/__init__.py +1 -0
  124. junifer/pipeline/pipeline_step_mixin.py +105 -41
  125. junifer/pipeline/registry.py +17 -0
  126. junifer/pipeline/singleton.py +45 -0
  127. junifer/pipeline/tests/test_pipeline_step_mixin.py +139 -51
  128. junifer/pipeline/tests/test_update_meta_mixin.py +1 -0
  129. junifer/pipeline/tests/test_workdir_manager.py +104 -0
  130. junifer/pipeline/update_meta_mixin.py +8 -2
  131. junifer/pipeline/utils.py +154 -15
  132. junifer/pipeline/workdir_manager.py +246 -0
  133. junifer/preprocess/__init__.py +3 -0
  134. junifer/preprocess/ants/__init__.py +4 -0
  135. junifer/preprocess/ants/ants_apply_transforms_warper.py +185 -0
  136. junifer/preprocess/ants/tests/test_ants_apply_transforms_warper.py +56 -0
  137. junifer/preprocess/base.py +96 -69
  138. junifer/preprocess/bold_warper.py +265 -0
  139. junifer/preprocess/confounds/fmriprep_confound_remover.py +91 -134
  140. junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +106 -111
  141. junifer/preprocess/fsl/__init__.py +4 -0
  142. junifer/preprocess/fsl/apply_warper.py +179 -0
  143. junifer/preprocess/fsl/tests/test_apply_warper.py +45 -0
  144. junifer/preprocess/tests/test_bold_warper.py +159 -0
  145. junifer/preprocess/tests/test_preprocess_base.py +6 -6
  146. junifer/preprocess/warping/__init__.py +6 -0
  147. junifer/preprocess/warping/_ants_warper.py +167 -0
  148. junifer/preprocess/warping/_fsl_warper.py +109 -0
  149. junifer/preprocess/warping/space_warper.py +213 -0
  150. junifer/preprocess/warping/tests/test_space_warper.py +198 -0
  151. junifer/stats.py +18 -4
  152. junifer/storage/base.py +9 -1
  153. junifer/storage/hdf5.py +8 -3
  154. junifer/storage/pandas_base.py +2 -1
  155. junifer/storage/sqlite.py +1 -0
  156. junifer/storage/tests/test_hdf5.py +2 -1
  157. junifer/storage/tests/test_sqlite.py +8 -8
  158. junifer/storage/tests/test_utils.py +6 -6
  159. junifer/storage/utils.py +1 -0
  160. junifer/testing/datagrabbers.py +11 -7
  161. junifer/testing/utils.py +1 -0
  162. junifer/tests/test_stats.py +2 -0
  163. junifer/utils/__init__.py +1 -0
  164. junifer/utils/helpers.py +53 -0
  165. junifer/utils/logging.py +14 -3
  166. junifer/utils/tests/test_helpers.py +35 -0
  167. {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/METADATA +59 -28
  168. junifer-0.0.4.dist-info/RECORD +257 -0
  169. {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/WHEEL +1 -1
  170. junifer/markers/falff/falff_estimator.py +0 -334
  171. junifer/markers/falff/tests/test_falff_estimator.py +0 -238
  172. junifer/markers/reho/reho_estimator.py +0 -515
  173. junifer/markers/reho/tests/test_reho_estimator.py +0 -260
  174. junifer-0.0.3.dev186.dist-info/RECORD +0 -199
  175. {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/AUTHORS.rst +0 -0
  176. {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/LICENSE.md +0 -0
  177. {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/entry_points.txt +0 -0
  178. {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/top_level.txt +0 -0
@@ -32,36 +32,134 @@ class PatternDataGrabber(BaseDataGrabber):
32
32
  types : list of str
33
33
  The types of data to be grabbed.
34
34
  patterns : dict
35
- Patterns for each type of data as a dictionary. The keys are the types
36
- and the values are the patterns. Each occurrence of the string
37
- ``{subject}`` in the pattern will be replaced by the indexed element.
35
+ Data type patterns as a dictionary. It has the following schema:
36
+
37
+ * ``"T1w"`` :
38
+
39
+ .. code-block:: none
40
+
41
+ {
42
+ "mandatory": ["pattern", "space"],
43
+ "optional": []
44
+ }
45
+
46
+ * ``"T2w"`` :
47
+
48
+ .. code-block:: none
49
+
50
+ {
51
+ "mandatory": ["pattern", "space"],
52
+ "optional": []
53
+ }
54
+
55
+ * ``"BOLD"`` :
56
+
57
+ .. code-block:: none
58
+
59
+ {
60
+ "mandatory": ["pattern", "space"],
61
+ "optional": ["mask_item"]
62
+ }
63
+
64
+ * ``"Warp"`` :
65
+
66
+ .. code-block:: none
67
+
68
+ {
69
+ "mandatory": ["pattern", "src", "dst"],
70
+ "optional": []
71
+ }
72
+
73
+ * ``"BOLD_confounds"`` :
74
+
75
+ .. code-block:: none
76
+
77
+ {
78
+ "mandatory": ["pattern", "format"],
79
+ "optional": []
80
+ }
81
+
82
+ * ``"VBM_GM"`` :
83
+
84
+ .. code-block:: none
85
+
86
+ {
87
+ "mandatory": ["pattern", "space"],
88
+ "optional": []
89
+ }
90
+
91
+ * ``"VBM_WM"`` :
92
+
93
+ .. code-block:: none
94
+
95
+ {
96
+ "mandatory": ["pattern", "space"],
97
+ "optional": []
98
+ }
99
+
100
+ Basically, for each data type, one needs to provide ``mandatory`` keys
101
+ and can choose to also provide ``optional`` keys. The value for each
102
+ key is a string. So, one needs to provide necessary data types as a
103
+ dictionary, for example:
104
+
105
+ .. code-block:: none
106
+
107
+ {
108
+ "BOLD": {
109
+ "pattern": "...",
110
+ "space": "...",
111
+ },
112
+ "T1w": {
113
+ "pattern": "...",
114
+ "space": "...",
115
+ },
116
+ "Warp": {
117
+ "pattern": "...",
118
+ "src": "...",
119
+ "dst": "...",
120
+ }
121
+ }
122
+
123
+ taken from :class:`.HCP1200`.
38
124
  replacements : str or list of str
39
- Replacements in the patterns for each item in the "element" tuple.
125
+ Replacements in the ``pattern`` key of each data type. The value needs
126
+ to be a list of all possible replacements.
40
127
  datadir : str or pathlib.Path
41
128
  The directory where the data is / will be stored.
42
129
  confounds_format : {"fmriprep", "adhoc"} or None, optional
43
130
  The format of the confounds for the dataset (default None).
44
131
 
132
+ Raises
133
+ ------
134
+ ValueError
135
+ If ``confounds_format`` is invalid.
136
+
45
137
  """
46
138
 
47
139
  def __init__(
48
140
  self,
49
141
  types: List[str],
50
- patterns: Dict[str, str],
142
+ patterns: Dict[str, Dict[str, str]],
51
143
  replacements: Union[List[str], str],
52
144
  datadir: Union[str, Path],
53
145
  confounds_format: Optional[str] = None,
54
146
  ) -> None:
55
147
  # Validate patterns
56
148
  validate_patterns(types=types, patterns=patterns)
149
+ self.patterns = patterns
57
150
 
151
+ # Convert replacements to list if not already
58
152
  if not isinstance(replacements, list):
59
153
  replacements = [replacements]
60
154
  # Validate replacements
61
155
  validate_replacements(replacements=replacements, patterns=patterns)
156
+ self.replacements = replacements
62
157
 
63
158
  # Validate confounds format
64
- if confounds_format and confounds_format not in _CONFOUNDS_FORMATS:
159
+ if (
160
+ confounds_format is not None
161
+ and confounds_format not in _CONFOUNDS_FORMATS
162
+ ):
65
163
  raise_error(
66
164
  "Invalid value for `confounds_format`, should be one of "
67
165
  f"{_CONFOUNDS_FORMATS}."
@@ -72,8 +170,7 @@ class PatternDataGrabber(BaseDataGrabber):
72
170
  logger.debug("Initializing PatternDataGrabber")
73
171
  logger.debug(f"\tpatterns = {patterns}")
74
172
  logger.debug(f"\treplacements = {replacements}")
75
- self.patterns = patterns
76
- self.replacements = replacements
173
+ logger.debug(f"\tconfounds_format = {confounds_format}")
77
174
 
78
175
  @property
79
176
  def skip_file_check(self) -> bool:
@@ -136,6 +233,11 @@ class PatternDataGrabber(BaseDataGrabber):
136
233
  str
137
234
  The pattern with the element replaced.
138
235
 
236
+ Raises
237
+ ------
238
+ ValueError
239
+ If element keys do not match with replacements.
240
+
139
241
  """
140
242
  if list(element.keys()) != self.replacements:
141
243
  raise_error(
@@ -160,7 +262,7 @@ class PatternDataGrabber(BaseDataGrabber):
160
262
  return self.replacements
161
263
 
162
264
  def get_item(self, **element: str) -> Dict[str, Dict]:
163
- """Implement single element indexing in the database.
265
+ """Implement single element indexing for the datagrabber.
164
266
 
165
267
  This method constructs a real path to the requested item's data, by
166
268
  replacing the ``patterns`` with actual values passed via ``**element``.
@@ -177,20 +279,33 @@ class PatternDataGrabber(BaseDataGrabber):
177
279
  Dictionary of dictionaries for each type of data required for the
178
280
  specified element.
179
281
 
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
+
180
289
  """
181
290
  out = {}
182
291
  for t_type in self.types:
183
292
  t_pattern = self.patterns[t_type]
184
- t_replace = self._replace_patterns_glob(element, t_pattern)
293
+ t_replace = self._replace_patterns_glob(
294
+ element, t_pattern["pattern"]
295
+ )
185
296
  if "*" in t_replace:
186
297
  t_matches = list(self.datadir.absolute().glob(t_replace))
187
298
  if len(t_matches) > 1:
188
299
  raise_error(
189
300
  f"More than one file matches for {element} / {t_type}:"
190
- f" {t_matches}"
301
+ f" {t_matches}",
302
+ klass=RuntimeError,
191
303
  )
192
304
  elif len(t_matches) == 0:
193
- raise_error(f"No file matches for {element} / {t_type}")
305
+ raise_error(
306
+ f"No file matches for {element} / {t_type}",
307
+ klass=RuntimeError,
308
+ )
194
309
  t_out = t_matches[0]
195
310
  else:
196
311
  t_out = self.datadir / t_replace
@@ -198,22 +313,13 @@ class PatternDataGrabber(BaseDataGrabber):
198
313
  if not t_out.exists() and not t_out.is_symlink():
199
314
  raise_error(
200
315
  f"Cannot access {t_type} for {element}: "
201
- f"File {t_out} does not exist"
316
+ f"File {t_out} does not exist",
317
+ klass=RuntimeError,
202
318
  )
203
319
  # Update path for the element
204
- out[t_type] = {"path": t_out}
205
- # Update confounds format for BOLD_confounds
206
- # (if found in the datagrabber)
207
- if t_type == "BOLD_confounds":
208
- if not self.confounds_format:
209
- raise_error(
210
- "`confounds_format` needs to be one of "
211
- f"{_CONFOUNDS_FORMATS}, None provided. "
212
- "As the DataGrabber used specifies "
213
- "'BOLD_confounds', None is invalid."
214
- )
215
- # Set the format
216
- out[t_type].update({"format": self.confounds_format})
320
+ out[t_type] = t_pattern.copy() # copy data type dictionary
321
+ out[t_type].pop("pattern") # remove pattern key
322
+ out[t_type].update({"path": t_out}) # add path key
217
323
 
218
324
  return out
219
325
 
@@ -252,7 +358,7 @@ class PatternDataGrabber(BaseDataGrabber):
252
358
  re_pattern,
253
359
  glob_pattern,
254
360
  t_replacements,
255
- ) = self._replace_patterns_regex(t_pattern)
361
+ ) = self._replace_patterns_regex(t_pattern["pattern"])
256
362
  for fname in self.datadir.glob(glob_pattern):
257
363
  suffix = fname.relative_to(self.datadir).as_posix()
258
364
  m = re.match(re_pattern, suffix)
@@ -5,12 +5,11 @@
5
5
  # Synchon Mandal <s.mandal@fz-juelich.de>
6
6
  # License: AGPL
7
7
 
8
- from typing import Dict, List
9
8
 
10
9
  from ..api.decorators import register_datagrabber
10
+ from ..utils import logger
11
11
  from .datalad_base import DataladDataGrabber
12
12
  from .pattern import PatternDataGrabber
13
- from .utils import validate_patterns
14
13
 
15
14
 
16
15
  @register_datagrabber
@@ -25,11 +24,109 @@ class PatternDataladDataGrabber(DataladDataGrabber, PatternDataGrabber):
25
24
  types : list of str
26
25
  The types of data to be grabbed.
27
26
  patterns : dict
28
- Patterns for each type of data as a dictionary. The keys are the types
29
- and the values are the patterns. Each occurrence of the string
30
- ``{subject}`` in the pattern will be replaced by the indexed element.
31
- **kwargs
32
- Keyword arguments passed to superclass.
27
+ Data type patterns as a dictionary. It has the following schema:
28
+
29
+ * ``"T1w"`` :
30
+
31
+ .. code-block:: none
32
+
33
+ {
34
+ "mandatory": ["pattern", "space"],
35
+ "optional": []
36
+ }
37
+
38
+ * ``"T2w"`` :
39
+
40
+ .. code-block:: none
41
+
42
+ {
43
+ "mandatory": ["pattern", "space"],
44
+ "optional": []
45
+ }
46
+
47
+ * ``"BOLD"`` :
48
+
49
+ .. code-block:: none
50
+
51
+ {
52
+ "mandatory": ["pattern", "space"],
53
+ "optional": ["mask_item"]
54
+ }
55
+
56
+ * ``"Warp"`` :
57
+
58
+ .. code-block:: none
59
+
60
+ {
61
+ "mandatory": ["pattern", "src", "dst"],
62
+ "optional": []
63
+ }
64
+
65
+ * ``"BOLD_confounds"`` :
66
+
67
+ .. code-block:: none
68
+
69
+ {
70
+ "mandatory": ["pattern", "format"],
71
+ "optional": []
72
+ }
73
+
74
+ * ``"VBM_GM"`` :
75
+
76
+ .. code-block:: none
77
+
78
+ {
79
+ "mandatory": ["pattern", "space"],
80
+ "optional": []
81
+ }
82
+
83
+ * ``"VBM_WM"`` :
84
+
85
+ .. code-block:: none
86
+
87
+ {
88
+ "mandatory": ["pattern", "space"],
89
+ "optional": []
90
+ }
91
+
92
+ Basically, for each data type, one needs to provide ``mandatory`` keys
93
+ and can choose to also provide ``optional`` keys. The value for each
94
+ key is a string. So, one needs to provide necessary data types as a
95
+ dictionary, for example:
96
+
97
+ .. code-block:: none
98
+
99
+ {
100
+ "BOLD": {
101
+ "pattern": "...",
102
+ "space": "...",
103
+ },
104
+ "T1w": {
105
+ "pattern": "...",
106
+ "space": "...",
107
+ },
108
+ "Warp": {
109
+ "pattern": "...",
110
+ "src": "...",
111
+ "dst": "...",
112
+ }
113
+ }
114
+
115
+ taken from :class:`.HCP1200`.
116
+ replacements : str or list of str
117
+ Replacements in the ``pattern`` key of each data type. The value needs
118
+ to be a list of all possible replacements.
119
+ confounds_format : {"fmriprep", "adhoc"} or None, optional
120
+ The format of the confounds for the dataset (default None).
121
+ datadir : str or pathlib.Path or None, optional
122
+ That directory where the datalad dataset will be cloned. If None,
123
+ the datalad dataset will be cloned into a temporary directory
124
+ (default None).
125
+ rootdir : str or pathlib.Path, optional
126
+ The path within the datalad dataset to the root directory
127
+ (default ".").
128
+ uri : str or None, optional
129
+ URI of the datalad sibling (default None).
33
130
 
34
131
  See Also
35
132
  --------
@@ -42,12 +139,13 @@ class PatternDataladDataGrabber(DataladDataGrabber, PatternDataGrabber):
42
139
 
43
140
  def __init__(
44
141
  self,
45
- types: List[str],
46
- patterns: Dict[str, str],
47
142
  **kwargs,
48
143
  ) -> None:
49
- # Validate patterns
50
- validate_patterns(types=types, patterns=patterns)
144
+ # TODO(synchon): needs to be reworked, DataladDataGrabber needs to be
145
+ # a mixin to avoid multiple inheritance wherever possible.
146
+
147
+ logger.debug("Initializing PatternDataladDataGrabber")
148
+ for key, val in kwargs.items():
149
+ logger.debug(f"\t{key} = {val}")
51
150
 
52
- super().__init__(types=types, patterns=patterns, **kwargs)
53
- self.patterns = patterns
151
+ super().__init__(**kwargs)
@@ -12,12 +12,6 @@ import pytest
12
12
  from junifer.datagrabber import BaseDataGrabber
13
13
 
14
14
 
15
- def test_BaseDataGrabber_abstractness() -> None:
16
- """Test BaseDataGrabber is abstract base class."""
17
- with pytest.raises(TypeError, match=r"abstract"):
18
- BaseDataGrabber(datadir="/tmp", types=["func"]) # type: ignore
19
-
20
-
21
15
  def test_BaseDataGrabber() -> None:
22
16
  """Test BaseDataGrabber."""
23
17
 
@@ -67,3 +61,60 @@ def test_BaseDataGrabber() -> None:
67
61
 
68
62
  with pytest.raises(NotImplementedError):
69
63
  dg.get_item(subject=1) # type: ignore
64
+
65
+
66
+ def test_BaseDataGrabber_filter_single() -> None:
67
+ """Test single-keyed element filter for BaseDataGrabber."""
68
+
69
+ # Create concrete class
70
+ class FilterDataGrabber(BaseDataGrabber):
71
+ def get_item(self, subject):
72
+ return {"BOLD": {}}
73
+
74
+ def get_elements(self):
75
+ return ["sub01", "sub02", "sub03"]
76
+
77
+ def get_element_keys(self):
78
+ return ["subject"]
79
+
80
+ dg = FilterDataGrabber(datadir="/tmp", types=["BOLD"])
81
+ with dg:
82
+ assert "sub01" in list(dg.filter(["sub01"]))
83
+ assert "sub02" not in list(dg.filter(["sub01"]))
84
+
85
+
86
+ def test_BaseDataGrabber_filter_multi() -> None:
87
+ """Test multi-keyed element filter for BaseDataGrabber."""
88
+
89
+ # Create concrete class
90
+ class FilterDataGrabber(BaseDataGrabber):
91
+ def get_item(self, subject):
92
+ return {"BOLD": {}}
93
+
94
+ def get_elements(self):
95
+ return [
96
+ ("sub01", "rest"),
97
+ ("sub01", "movie"),
98
+ ("sub02", "rest"),
99
+ ("sub02", "movie"),
100
+ ("sub03", "rest"),
101
+ ("sub03", "movie"),
102
+ ]
103
+
104
+ def get_element_keys(self):
105
+ return ["subject", "task"]
106
+
107
+ dg = FilterDataGrabber(datadir="/tmp", types=["BOLD"])
108
+ with dg:
109
+ assert ("sub01", "rest") in list(
110
+ dg.filter([("sub01", "rest")]) # type: ignore
111
+ )
112
+ assert ("sub01", "movie") not in list(
113
+ dg.filter([("sub01", "rest")]) # type: ignore
114
+ )
115
+ assert ("sub02", "rest") not in list(
116
+ dg.filter([("sub01", "rest")]) # type: ignore
117
+ )
118
+ assert ("sub02", "movie") not in list(
119
+ dg.filter([("sub01", "rest")]) # type: ignore
120
+ )