junifer 0.0.7.dev332__py3-none-any.whl → 0.0.8__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 (293) hide show
  1. junifer/_version.py +9 -19
  2. junifer/api/__init__.pyi +17 -2
  3. junifer/api/functions.py +222 -39
  4. junifer/api/queue_context/__init__.pyi +11 -3
  5. junifer/api/queue_context/gnu_parallel_local_adapter.py +81 -120
  6. junifer/api/queue_context/htcondor_adapter.py +118 -181
  7. junifer/api/queue_context/queue_context_adapter.py +51 -7
  8. junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +10 -35
  9. junifer/api/queue_context/tests/test_htcondor_adapter.py +13 -50
  10. junifer/api/res/fsl/run_fsl_docker.sh +4 -0
  11. junifer/api/tests/test_functions.py +863 -36
  12. junifer/cli/__init__.py +1 -7
  13. junifer/cli/cli.py +41 -22
  14. junifer/cli/parser.py +23 -147
  15. junifer/cli/tests/data/gmd_mean.yaml +6 -6
  16. junifer/cli/tests/data/gmd_mean_htcondor.yaml +6 -5
  17. junifer/cli/tests/data/partly_cloudy_agg_mean_tian.yml +1 -0
  18. junifer/cli/tests/test_cli.py +120 -4
  19. junifer/cli/tests/test_cli_utils.py +23 -6
  20. junifer/cli/utils.py +14 -9
  21. junifer/configs/__init__.py +5 -0
  22. junifer/configs/__init__.pyi +0 -0
  23. junifer/configs/juseless/datagrabbers/__init__.pyi +4 -2
  24. junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +18 -24
  25. junifer/configs/juseless/datagrabbers/camcan_vbm.py +20 -27
  26. junifer/configs/juseless/datagrabbers/ixi_vbm.py +44 -52
  27. junifer/configs/juseless/datagrabbers/tests/test_ixi_vbm.py +0 -7
  28. junifer/configs/juseless/datagrabbers/tests/test_ucla.py +2 -19
  29. junifer/configs/juseless/datagrabbers/ucla.py +111 -104
  30. junifer/configs/juseless/datagrabbers/ukb_vbm.py +19 -25
  31. junifer/configs/py.typed +0 -0
  32. junifer/conftest.py +63 -24
  33. junifer/data/_dispatch.py +9 -19
  34. junifer/data/coordinates/_ants_coordinates_warper.py +4 -1
  35. junifer/data/coordinates/_coordinates.py +51 -6
  36. junifer/data/coordinates/_fsl_coordinates_warper.py +4 -1
  37. junifer/data/coordinates/tests/test_coordinates.py +15 -3
  38. junifer/data/coordinates/tests/test_coordinates_names.py +37 -0
  39. junifer/data/maps/_ants_maps_warper.py +18 -12
  40. junifer/data/maps/_fsl_maps_warper.py +7 -3
  41. junifer/data/maps/_maps.py +12 -8
  42. junifer/data/maps/tests/test_maps.py +13 -21
  43. junifer/data/masks/_ants_mask_warper.py +18 -12
  44. junifer/data/masks/_fsl_mask_warper.py +7 -3
  45. junifer/data/masks/_masks.py +15 -12
  46. junifer/data/masks/tests/test_masks.py +20 -17
  47. junifer/data/parcellations/_ants_parcellation_warper.py +18 -12
  48. junifer/data/parcellations/_fsl_parcellation_warper.py +7 -5
  49. junifer/data/parcellations/_parcellations.py +125 -91
  50. junifer/data/parcellations/tests/test_parcellations.py +122 -1
  51. junifer/data/template_spaces.py +117 -50
  52. junifer/data/tests/test_template_spaces.py +10 -7
  53. junifer/data/utils.py +10 -8
  54. junifer/datagrabber/__init__.pyi +38 -5
  55. junifer/datagrabber/aomic/__init__.pyi +8 -1
  56. junifer/datagrabber/aomic/_types.py +22 -0
  57. junifer/datagrabber/aomic/id1000.py +193 -183
  58. junifer/datagrabber/aomic/piop1.py +215 -259
  59. junifer/datagrabber/aomic/piop2.py +211 -244
  60. junifer/datagrabber/aomic/tests/test_id1000.py +70 -53
  61. junifer/datagrabber/aomic/tests/test_piop1.py +98 -102
  62. junifer/datagrabber/aomic/tests/test_piop2.py +88 -85
  63. junifer/datagrabber/base.py +253 -79
  64. junifer/datagrabber/datalad_base.py +127 -76
  65. junifer/datagrabber/dmcc13_benchmark.py +204 -289
  66. junifer/datagrabber/hcp1200/__init__.pyi +7 -2
  67. junifer/datagrabber/hcp1200/datalad_hcp1200.py +61 -40
  68. junifer/datagrabber/hcp1200/hcp1200.py +151 -164
  69. junifer/datagrabber/hcp1200/tests/test_hcp1200.py +116 -128
  70. junifer/datagrabber/multiple.py +183 -63
  71. junifer/datagrabber/pattern.py +358 -227
  72. junifer/datagrabber/pattern_datalad.py +84 -124
  73. junifer/datagrabber/pattern_validation_mixin.py +22 -50
  74. junifer/datagrabber/tests/test_base.py +17 -4
  75. junifer/datagrabber/tests/test_datalad_base.py +20 -27
  76. junifer/datagrabber/tests/test_dmcc13_benchmark.py +110 -122
  77. junifer/datagrabber/tests/test_multiple.py +293 -31
  78. junifer/datagrabber/tests/test_pattern.py +577 -18
  79. junifer/datagrabber/tests/test_pattern_datalad.py +177 -107
  80. junifer/datagrabber/tests/test_pattern_validation_mixin.py +9 -51
  81. junifer/datareader/default.py +8 -5
  82. junifer/datareader/tests/test_default_reader.py +1 -1
  83. junifer/external/BrainPrint/.codecov.yml +2 -0
  84. junifer/external/BrainPrint/.codespellignore +2 -0
  85. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/bug-report.md +45 -0
  86. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/documentation.md +12 -0
  87. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/feature-request.md +24 -0
  88. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/questions-help-support.md +28 -0
  89. junifer/external/BrainPrint/.github/dependabot.yml +6 -0
  90. junifer/external/BrainPrint/.github/workflows/build.yml +53 -0
  91. junifer/external/BrainPrint/.github/workflows/code-style.yml +44 -0
  92. junifer/external/BrainPrint/.github/workflows/doc.yml +65 -0
  93. junifer/external/BrainPrint/.github/workflows/publish.yml +31 -0
  94. junifer/external/BrainPrint/.github/workflows/pytest.yml +54 -0
  95. junifer/external/BrainPrint/.gitignore +123 -0
  96. junifer/external/BrainPrint/LICENSE +21 -0
  97. junifer/external/BrainPrint/README.md +108 -0
  98. junifer/external/BrainPrint/brainprint/cli/__init__.py +11 -0
  99. junifer/external/BrainPrint/brainprint/cli/help_text.py +169 -0
  100. junifer/external/BrainPrint/brainprint/cli/parser.py +150 -0
  101. junifer/external/BrainPrint/brainprint/cli/utils.py +14 -0
  102. junifer/external/BrainPrint/brainprint/commands/__init__.py +0 -0
  103. junifer/external/BrainPrint/brainprint/commands/sys_info.py +18 -0
  104. junifer/external/BrainPrint/brainprint/utils/tests/__init__.py +0 -0
  105. junifer/external/BrainPrint/brainprint/utils/tests/test_config.py +33 -0
  106. junifer/external/BrainPrint/doc/Makefile +20 -0
  107. junifer/external/BrainPrint/doc/_static/.gitkeep +0 -0
  108. junifer/external/BrainPrint/doc/_templates/autosummary/class.rst +10 -0
  109. junifer/external/BrainPrint/doc/_templates/autosummary/function.rst +8 -0
  110. junifer/external/BrainPrint/doc/_templates/autosummary/module.rst +6 -0
  111. junifer/external/BrainPrint/doc/api/index.rst +11 -0
  112. junifer/external/BrainPrint/doc/conf.py +272 -0
  113. junifer/external/BrainPrint/doc/index.rst +140 -0
  114. junifer/external/BrainPrint/doc/links.inc +36 -0
  115. junifer/external/BrainPrint/doc/make.bat +35 -0
  116. junifer/external/BrainPrint/doc/references.bib +23 -0
  117. junifer/external/BrainPrint/pyproject.toml +170 -0
  118. junifer/external/BrainPrint/setup.py +3 -0
  119. junifer/external/__init__.py +5 -0
  120. junifer/external/__init__.pyi +0 -0
  121. junifer/external/nilearn/junifer_connectivity_measure.py +95 -39
  122. junifer/external/nilearn/junifer_nifti_spheres_masker.py +226 -67
  123. junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +39 -27
  124. junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +231 -21
  125. junifer/external/py.typed +0 -0
  126. junifer/markers/__init__.pyi +6 -3
  127. junifer/markers/base.py +126 -76
  128. junifer/markers/brainprint.py +30 -39
  129. junifer/markers/complexity/__init__.py +20 -3
  130. junifer/markers/complexity/complexity_base.py +40 -41
  131. junifer/markers/complexity/hurst_exponent.py +22 -35
  132. junifer/markers/complexity/multiscale_entropy_auc.py +53 -41
  133. junifer/markers/complexity/perm_entropy.py +22 -36
  134. junifer/markers/complexity/range_entropy.py +22 -35
  135. junifer/markers/complexity/range_entropy_auc.py +27 -36
  136. junifer/markers/complexity/sample_entropy.py +22 -36
  137. junifer/markers/complexity/tests/test_complexity_registry.py +48 -0
  138. junifer/markers/complexity/tests/test_hurst_exponent.py +47 -36
  139. junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +50 -36
  140. junifer/markers/complexity/tests/test_perm_entropy.py +48 -36
  141. junifer/markers/complexity/tests/test_range_entropy.py +47 -36
  142. junifer/markers/complexity/tests/test_range_entropy_auc.py +47 -36
  143. junifer/markers/complexity/tests/test_sample_entropy.py +48 -36
  144. junifer/markers/complexity/tests/test_weighted_perm_entropy.py +50 -36
  145. junifer/markers/complexity/weighted_perm_entropy.py +22 -36
  146. junifer/markers/ets_rss.py +35 -31
  147. junifer/markers/falff/__init__.pyi +7 -1
  148. junifer/markers/falff/_afni_falff.py +22 -9
  149. junifer/markers/falff/_junifer_falff.py +47 -31
  150. junifer/markers/falff/falff_base.py +80 -60
  151. junifer/markers/falff/falff_maps.py +17 -41
  152. junifer/markers/falff/falff_parcels.py +26 -51
  153. junifer/markers/falff/falff_spheres.py +34 -61
  154. junifer/markers/falff/tests/test_falff_maps.py +20 -23
  155. junifer/markers/falff/tests/test_falff_mask.py +99 -0
  156. junifer/markers/falff/tests/test_falff_parcels.py +14 -10
  157. junifer/markers/falff/tests/test_falff_spheres.py +14 -10
  158. junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +32 -34
  159. junifer/markers/functional_connectivity/edge_functional_connectivity_maps.py +12 -25
  160. junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +19 -34
  161. junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +24 -44
  162. junifer/markers/functional_connectivity/functional_connectivity_base.py +50 -40
  163. junifer/markers/functional_connectivity/functional_connectivity_maps.py +12 -25
  164. junifer/markers/functional_connectivity/functional_connectivity_parcels.py +19 -34
  165. junifer/markers/functional_connectivity/functional_connectivity_spheres.py +24 -45
  166. junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +21 -22
  167. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_maps.py +8 -7
  168. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +8 -4
  169. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +8 -4
  170. junifer/markers/functional_connectivity/tests/test_functional_connectivity_maps.py +8 -7
  171. junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +9 -5
  172. junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +8 -12
  173. junifer/markers/maps_aggregation.py +77 -55
  174. junifer/markers/parcel_aggregation.py +91 -70
  175. junifer/markers/reho/__init__.pyi +7 -1
  176. junifer/markers/reho/_afni_reho.py +23 -14
  177. junifer/markers/reho/_junifer_reho.py +112 -123
  178. junifer/markers/reho/reho_base.py +115 -37
  179. junifer/markers/reho/reho_maps.py +18 -32
  180. junifer/markers/reho/reho_parcels.py +26 -41
  181. junifer/markers/reho/reho_spheres.py +33 -54
  182. junifer/markers/reho/tests/test_junifer_reho.py +178 -0
  183. junifer/markers/reho/tests/test_reho_maps.py +27 -25
  184. junifer/markers/reho/tests/test_reho_parcels.py +28 -22
  185. junifer/markers/reho/tests/test_reho_spheres.py +16 -12
  186. junifer/markers/sphere_aggregation.py +88 -72
  187. junifer/markers/temporal_snr/temporal_snr_base.py +32 -30
  188. junifer/markers/temporal_snr/temporal_snr_maps.py +9 -18
  189. junifer/markers/temporal_snr/temporal_snr_parcels.py +19 -27
  190. junifer/markers/temporal_snr/temporal_snr_spheres.py +27 -41
  191. junifer/markers/temporal_snr/tests/test_temporal_snr_base.py +2 -2
  192. junifer/markers/temporal_snr/tests/test_temporal_snr_maps.py +17 -22
  193. junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +11 -3
  194. junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +9 -9
  195. junifer/markers/tests/test_brainprint.py +10 -9
  196. junifer/markers/tests/test_ets_rss.py +7 -3
  197. junifer/markers/tests/test_lost_regions.py +279 -0
  198. junifer/markers/tests/test_maps_aggregation.py +67 -114
  199. junifer/markers/tests/test_marker_utils.py +40 -1
  200. junifer/markers/tests/test_markers_base.py +275 -6
  201. junifer/markers/tests/test_parcel_aggregation.py +91 -53
  202. junifer/markers/tests/test_sphere_aggregation.py +53 -47
  203. junifer/markers/utils.py +267 -5
  204. junifer/onthefly/__init__.py +2 -3
  205. junifer/onthefly/__init__.pyi +4 -0
  206. junifer/onthefly/_brainprint.py +3 -5
  207. junifer/onthefly/py.typed +0 -0
  208. junifer/onthefly/read_transform.py +11 -9
  209. junifer/onthefly/tests/test_read_transform.py +7 -7
  210. junifer/pipeline/__init__.pyi +4 -2
  211. junifer/pipeline/marker_collection.py +14 -10
  212. junifer/pipeline/pipeline_component_registry.py +25 -6
  213. junifer/pipeline/pipeline_step_mixin.py +5 -12
  214. junifer/pipeline/tests/test_data_object_dumper.py +10 -10
  215. junifer/pipeline/tests/test_marker_collection.py +145 -168
  216. junifer/pipeline/tests/test_pipeline_component_registry.py +52 -8
  217. junifer/pipeline/tests/test_pipeline_step_mixin.py +12 -26
  218. junifer/pipeline/tests/test_update_meta_mixin.py +4 -5
  219. junifer/pipeline/tests/test_workdir_manager.py +39 -0
  220. junifer/pipeline/update_meta_mixin.py +13 -10
  221. junifer/pipeline/utils.py +22 -17
  222. junifer/pipeline/workdir_manager.py +41 -32
  223. junifer/preprocess/__init__.pyi +9 -4
  224. junifer/preprocess/_temporal_filter.py +34 -33
  225. junifer/preprocess/_temporal_slicer.py +17 -30
  226. junifer/preprocess/base.py +99 -55
  227. junifer/preprocess/confounds/__init__.pyi +10 -2
  228. junifer/preprocess/confounds/fmriprep_confound_remover.py +87 -109
  229. junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +64 -45
  230. junifer/preprocess/smoothing/__init__.pyi +2 -2
  231. junifer/preprocess/smoothing/_afni_smoothing.py +8 -7
  232. junifer/preprocess/smoothing/_fsl_smoothing.py +6 -4
  233. junifer/preprocess/smoothing/_nilearn_smoothing.py +7 -5
  234. junifer/preprocess/smoothing/smoothing.py +59 -40
  235. junifer/preprocess/smoothing/tests/test_smoothing.py +28 -8
  236. junifer/preprocess/tests/test_preprocess_base.py +17 -9
  237. junifer/preprocess/tests/test_temporal_filter.py +37 -16
  238. junifer/preprocess/tests/test_temporal_slicer.py +12 -12
  239. junifer/preprocess/warping/__init__.pyi +2 -2
  240. junifer/preprocess/warping/_ants_warper.py +56 -40
  241. junifer/preprocess/warping/_fsl_warper.py +49 -36
  242. junifer/preprocess/warping/_utils.py +106 -0
  243. junifer/preprocess/warping/space_warper.py +99 -98
  244. junifer/preprocess/warping/tests/test_space_warper.py +46 -41
  245. junifer/preprocess/warping/tests/test_warping_utils.py +163 -0
  246. junifer/stats.py +46 -34
  247. junifer/storage/__init__.pyi +8 -4
  248. junifer/storage/base.py +129 -64
  249. junifer/storage/hdf5.py +307 -199
  250. junifer/storage/pandas_base.py +46 -24
  251. junifer/storage/sqlite.py +99 -62
  252. junifer/storage/tests/data/collected_h5io_fork.hdf5 +0 -0
  253. junifer/storage/tests/test_hdf5.py +147 -12
  254. junifer/storage/tests/test_repeated_storage.py +293 -0
  255. junifer/storage/tests/test_sqlite.py +9 -20
  256. junifer/storage/tests/test_storage_base.py +18 -21
  257. junifer/storage/tests/test_utils.py +83 -22
  258. junifer/storage/utils.py +58 -38
  259. junifer/testing/__init__.pyi +2 -2
  260. junifer/testing/datagrabbers.py +480 -44
  261. junifer/testing/registry.py +12 -0
  262. junifer/testing/tests/test_adhdtesting_datagrabber.py +24 -0
  263. junifer/testing/tests/test_oasisvmbtesting_datagrabber.py +5 -0
  264. junifer/testing/tests/test_partlycloudytesting_datagrabber.py +52 -0
  265. junifer/testing/tests/test_spmauditory_datagrabber.py +16 -0
  266. junifer/testing/tests/test_testing_registry.py +31 -0
  267. junifer/testing/tests/test_testing_utils.py +35 -0
  268. junifer/testing/tests/test_uclacnpvbmtesting_datagrabber.py +81 -0
  269. junifer/testing/utils.py +33 -1
  270. junifer/tests/test_stats.py +1 -5
  271. junifer/typing/__init__.pyi +2 -0
  272. junifer/typing/_typing.py +34 -19
  273. junifer/typing/py.typed +0 -0
  274. junifer/utils/__init__.pyi +10 -1
  275. junifer/utils/_config.py +1 -2
  276. junifer/utils/helpers.py +68 -2
  277. junifer/utils/logging.py +31 -7
  278. junifer/utils/tests/test_helpers.py +36 -1
  279. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/METADATA +43 -35
  280. junifer-0.0.8.dist-info/RECORD +402 -0
  281. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/WHEEL +1 -1
  282. junifer/cli/tests/test_parser.py +0 -280
  283. junifer/external/h5io/h5io/__init__.py +0 -15
  284. junifer/external/h5io/h5io/_h5io.py +0 -767
  285. junifer/external/h5io/h5io/_version.py +0 -1
  286. junifer/external/h5io/h5io/chunked_array.py +0 -94
  287. junifer/external/h5io/h5io/chunked_list.py +0 -65
  288. junifer/markers/complexity/__init__.pyi +0 -17
  289. junifer-0.0.7.dev332.dist-info/RECORD +0 -353
  290. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/entry_points.txt +0 -0
  291. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/licenses/AUTHORS.rst +0 -0
  292. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/licenses/LICENSE.md +0 -0
  293. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/top_level.txt +0 -0
@@ -3,18 +3,31 @@
3
3
  # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
4
  # License: AGPL
5
5
 
6
+ from copy import deepcopy
7
+ from pathlib import Path
8
+
6
9
  import pytest
10
+ from pydantic import AnyUrl
7
11
 
8
- from junifer.datagrabber import MultipleDataGrabber, PatternDataladDataGrabber
12
+ from junifer.datagrabber import (
13
+ BaseDataGrabber,
14
+ MultipleDataGrabber,
15
+ PatternDataladDataGrabber,
16
+ )
17
+ from junifer.pipeline import PipelineComponentRegistry
9
18
 
10
19
 
11
20
  _testing_dataset = {
12
21
  "example_bids": {
13
- "uri": "https://gin.g-node.org/juaml/datalad-example-bids",
22
+ "uri": (
23
+ "https://cerebra.fz-juelich.de/junifer/datalad-example-bids.git"
24
+ ),
14
25
  "id": "522dfb203afcd2cd55799bf347f9b211919a7338",
15
26
  },
16
27
  "example_bids_ses": {
17
- "uri": "https://gin.g-node.org/juaml/datalad-example-bids-ses",
28
+ "uri": (
29
+ "https://cerebra.fz-juelich.de/junifer/datalad-example-bids-ses.git"
30
+ ),
18
31
  "id": "3d08d55d1faad4f12ab64ac9497544a0d924d47a",
19
32
  },
20
33
  }
@@ -22,8 +35,8 @@ _testing_dataset = {
22
35
 
23
36
  def test_MultipleDataGrabber() -> None:
24
37
  """Test MultipleDataGrabber."""
25
- repo_uri = _testing_dataset["example_bids_ses"]["uri"]
26
- rootdir = "example_bids_ses"
38
+ repo_uri = AnyUrl(_testing_dataset["example_bids_ses"]["uri"])
39
+ rootdir = Path("example_bids_ses")
27
40
  replacements = ["subject", "session"]
28
41
 
29
42
  dg1 = PatternDataladDataGrabber(
@@ -73,7 +86,7 @@ def test_MultipleDataGrabber() -> None:
73
86
  dg2 = PatternDataladDataGrabber(
74
87
  rootdir=rootdir,
75
88
  uri=repo_uri,
76
- types=["BOLD"],
89
+ types="BOLD",
77
90
  patterns={
78
91
  "BOLD": {
79
92
  "pattern": (
@@ -93,7 +106,7 @@ def test_MultipleDataGrabber() -> None:
93
106
  replacements=replacements,
94
107
  )
95
108
 
96
- dg = MultipleDataGrabber([dg1, dg2])
109
+ dg = MultipleDataGrabber(datagrabbers=[dg1, dg2])
97
110
 
98
111
  types = dg.get_types()
99
112
  assert "T1w" in types
@@ -129,12 +142,12 @@ def test_MultipleDataGrabber() -> None:
129
142
 
130
143
  def test_MultipleDataGrabber_no_intersection() -> None:
131
144
  """Test MultipleDataGrabber without intersection (0 elements)."""
132
- rootdir = "example_bids_ses"
145
+ rootdir = Path("example_bids_ses")
133
146
  replacements = ["subject", "session"]
134
147
 
135
148
  dg1 = PatternDataladDataGrabber(
136
149
  rootdir=rootdir,
137
- uri=_testing_dataset["example_bids"]["uri"],
150
+ uri=AnyUrl(_testing_dataset["example_bids"]["uri"]),
138
151
  types=["T1w", "Warp"],
139
152
  patterns={
140
153
  "T1w": {
@@ -171,8 +184,8 @@ def test_MultipleDataGrabber_no_intersection() -> None:
171
184
 
172
185
  dg2 = PatternDataladDataGrabber(
173
186
  rootdir=rootdir,
174
- uri=_testing_dataset["example_bids_ses"]["uri"],
175
- types=["BOLD"],
187
+ uri=AnyUrl(_testing_dataset["example_bids_ses"]["uri"]),
188
+ types="BOLD",
176
189
  patterns={
177
190
  "BOLD": {
178
191
  "pattern": (
@@ -185,7 +198,7 @@ def test_MultipleDataGrabber_no_intersection() -> None:
185
198
  replacements=replacements,
186
199
  )
187
200
 
188
- dg = MultipleDataGrabber([dg1, dg2])
201
+ dg = MultipleDataGrabber(datagrabbers=[dg1, dg2])
189
202
  expected_subs = set()
190
203
  with dg:
191
204
  subs = list(dg)
@@ -195,9 +208,9 @@ def test_MultipleDataGrabber_no_intersection() -> None:
195
208
  def test_MultipleDataGrabber_get_item() -> None:
196
209
  """Test MultipleDataGrabber get_item() error."""
197
210
  dg1 = PatternDataladDataGrabber(
198
- rootdir="example_bids_ses",
199
- uri=_testing_dataset["example_bids"]["uri"],
200
- types=["T1w"],
211
+ rootdir=Path("example_bids_ses"),
212
+ uri=AnyUrl(_testing_dataset["example_bids"]["uri"]),
213
+ types="T1w",
201
214
  patterns={
202
215
  "T1w": {
203
216
  "pattern": (
@@ -209,19 +222,19 @@ def test_MultipleDataGrabber_get_item() -> None:
209
222
  replacements=["subject", "session"],
210
223
  )
211
224
 
212
- dg = MultipleDataGrabber([dg1])
225
+ dg = MultipleDataGrabber(datagrabbers=[dg1])
213
226
  with pytest.raises(NotImplementedError):
214
- dg.get_item(subject="sub-01") # type: ignore
227
+ dg.get_item(subject="sub-01")
215
228
 
216
229
 
217
230
  def test_MultipleDataGrabber_validation() -> None:
218
231
  """Test MultipleDataGrabber init validation."""
219
- rootdir = "example_bids_ses"
232
+ rootdir = Path("example_bids_ses")
220
233
 
221
234
  dg1 = PatternDataladDataGrabber(
222
235
  rootdir=rootdir,
223
- uri=_testing_dataset["example_bids"]["uri"],
224
- types=["T1w"],
236
+ uri=AnyUrl(_testing_dataset["example_bids"]["uri"]),
237
+ types="T1w",
225
238
  patterns={
226
239
  "T1w": {
227
240
  "pattern": (
@@ -235,8 +248,8 @@ def test_MultipleDataGrabber_validation() -> None:
235
248
 
236
249
  dg2 = PatternDataladDataGrabber(
237
250
  rootdir=rootdir,
238
- uri=_testing_dataset["example_bids_ses"]["uri"],
239
- types=["BOLD"],
251
+ uri=AnyUrl(_testing_dataset["example_bids_ses"]["uri"]),
252
+ types="BOLD",
240
253
  patterns={
241
254
  "BOLD": {
242
255
  "pattern": "{subject}/func/{subject}_task-rest_bold.nii.gz",
@@ -246,23 +259,24 @@ def test_MultipleDataGrabber_validation() -> None:
246
259
  replacements=["subject"],
247
260
  )
248
261
 
249
- with pytest.raises(RuntimeError, match="have different element keys"):
250
- MultipleDataGrabber([dg1, dg2])
262
+ # Different element keys are joined
263
+ dg = MultipleDataGrabber(datagrabbers=[dg1, dg2])
264
+ assert dg.get_element_keys() == ["subject", "session"]
251
265
 
252
- with pytest.raises(RuntimeError, match="have overlapping mandatory"):
253
- MultipleDataGrabber([dg1, dg1])
266
+ with pytest.raises(RuntimeError, match="`T1w` is grabbed by more than"):
267
+ MultipleDataGrabber(datagrabbers=[dg1, dg1])
254
268
 
255
269
 
256
270
  def test_MultipleDataGrabber_partial_pattern() -> None:
257
271
  """Test MultipleDataGrabber partial pattern."""
258
- repo_uri = _testing_dataset["example_bids_ses"]["uri"]
259
- rootdir = "example_bids_ses"
272
+ repo_uri = AnyUrl(_testing_dataset["example_bids_ses"]["uri"])
273
+ rootdir = Path("example_bids_ses")
260
274
  replacements = ["subject", "session"]
261
275
 
262
276
  dg1 = PatternDataladDataGrabber(
263
277
  rootdir=rootdir,
264
278
  uri=repo_uri,
265
- types=["BOLD"],
279
+ types="BOLD",
266
280
  patterns={
267
281
  "BOLD": {
268
282
  "pattern": (
@@ -278,7 +292,7 @@ def test_MultipleDataGrabber_partial_pattern() -> None:
278
292
  dg2 = PatternDataladDataGrabber(
279
293
  rootdir=rootdir,
280
294
  uri=repo_uri,
281
- types=["BOLD"],
295
+ types="BOLD",
282
296
  patterns={
283
297
  "BOLD": {
284
298
  "confounds": {
@@ -295,7 +309,7 @@ def test_MultipleDataGrabber_partial_pattern() -> None:
295
309
  partial_pattern_ok=True,
296
310
  )
297
311
 
298
- dg = MultipleDataGrabber([dg1, dg2])
312
+ dg = MultipleDataGrabber(datagrabbers=[dg1, dg2])
299
313
 
300
314
  types = dg.get_types()
301
315
  assert "BOLD" in types
@@ -324,3 +338,251 @@ def test_MultipleDataGrabber_partial_pattern() -> None:
324
338
  assert len(meta["datagrabbers"]) == 2
325
339
  assert meta["datagrabbers"][0]["class"] == "PatternDataladDataGrabber"
326
340
  assert meta["datagrabbers"][1]["class"] == "PatternDataladDataGrabber"
341
+
342
+
343
+ def _datasets(tmp_path: Path) -> dict[str, Path]:
344
+ """Create local datasets with the data of different datagrabbers.
345
+
346
+ * ``fmriprep``: BOLD (with confounds) for ``sub-01`` and ``sub-02``, and
347
+ the ``rest`` and ``movie`` tasks.
348
+ * ``cat``: VBM_GM for ``sub-01``, ``sub-02`` and ``sub-03``.
349
+ * ``confounds``: BOLD confounds for ``sub-01`` and ``sub-02``, and only
350
+ the ``rest`` task.
351
+
352
+ Parameters
353
+ ----------
354
+ tmp_path : pathlib.Path
355
+ The path to the test directory.
356
+
357
+ Returns
358
+ -------
359
+ dict of str and pathlib.Path
360
+ The paths to the datasets, by name.
361
+
362
+ """
363
+ files = [
364
+ f"fmriprep/{s}/func/{s}_task-{t}_{suffix}"
365
+ for s in ("sub-01", "sub-02")
366
+ for t in ("rest", "movie")
367
+ for suffix in ("bold.nii", "desc-confounds.tsv")
368
+ ]
369
+ files += [
370
+ f"cat/{s}/mri/mwp1{s}.nii" for s in ("sub-01", "sub-02", "sub-03")
371
+ ]
372
+ files += [f"confounds/{s}/{s}_task-rest.tsv" for s in ("sub-01", "sub-02")]
373
+ for fname in files:
374
+ (tmp_path / fname).parent.mkdir(parents=True, exist_ok=True)
375
+ (tmp_path / fname).touch()
376
+ return {name: tmp_path / name for name in ("fmriprep", "cat", "confounds")}
377
+
378
+
379
+ def _configs(datasets: dict[str, Path]) -> dict[str, dict]:
380
+ """Get the configurations of the datagrabbers of the local datasets.
381
+
382
+ Parameters
383
+ ----------
384
+ datasets : dict of str and pathlib.Path
385
+ The paths to the datasets, by name (see ``_datasets``).
386
+
387
+ Returns
388
+ -------
389
+ dict of str and dict
390
+ The configurations of the datagrabbers, by dataset name.
391
+
392
+ """
393
+ return {
394
+ "fmriprep": {
395
+ "kind": "PatternDataGrabber",
396
+ "datadir": str(datasets["fmriprep"]),
397
+ "types": ["BOLD"],
398
+ "patterns": {
399
+ "BOLD": {
400
+ "pattern": "{subject}/func/{subject}_task-{task}_bold.nii",
401
+ "space": "MNI152NLin6Asym",
402
+ "confounds": {
403
+ "pattern": (
404
+ "{subject}/func/{subject}_task-{task}_"
405
+ "desc-confounds.tsv"
406
+ ),
407
+ "format": "fmriprep",
408
+ },
409
+ },
410
+ },
411
+ "replacements": ["subject", "task"],
412
+ },
413
+ "cat": {
414
+ "kind": "PatternDataGrabber",
415
+ "datadir": str(datasets["cat"]),
416
+ "types": ["VBM_GM"],
417
+ "patterns": {
418
+ "VBM_GM": {
419
+ "pattern": "{subject}/mri/mwp1{subject}.nii",
420
+ "space": "MNI152NLin6Asym",
421
+ },
422
+ },
423
+ "replacements": ["subject"],
424
+ },
425
+ "confounds": {
426
+ "kind": "PatternDataGrabber",
427
+ "datadir": str(datasets["confounds"]),
428
+ "types": ["BOLD"],
429
+ "patterns": {
430
+ "BOLD": {
431
+ "confounds": {
432
+ "pattern": "{subject}/{subject}_task-{task}.tsv",
433
+ "format": "fmriprep",
434
+ },
435
+ },
436
+ },
437
+ "replacements": ["subject", "task"],
438
+ "partial_pattern_ok": True,
439
+ },
440
+ }
441
+
442
+
443
+ def _multiple(tmp_path: Path, names: list[str]) -> MultipleDataGrabber:
444
+ """Get a MultipleDataGrabber with datagrabbers of the local datasets.
445
+
446
+ Parameters
447
+ ----------
448
+ tmp_path : pathlib.Path
449
+ The path to the test directory.
450
+ names : list of str
451
+ The names of the datasets of the datagrabbers (see ``_datasets``).
452
+
453
+ Returns
454
+ -------
455
+ MultipleDataGrabber
456
+ The datagrabber.
457
+
458
+ """
459
+ configs = _configs(_datasets(tmp_path))
460
+ return PipelineComponentRegistry().build_component_instance(
461
+ step="datagrabber",
462
+ name="MultipleDataGrabber",
463
+ baseclass=BaseDataGrabber,
464
+ init_params={
465
+ "datagrabbers": [deepcopy(configs[name]) for name in names]
466
+ },
467
+ )
468
+
469
+
470
+ @pytest.mark.parametrize("names", [["fmriprep", "cat"], ["cat", "fmriprep"]])
471
+ def test_MultipleDataGrabber_element_keys(
472
+ tmp_path: Path, names: list[str]
473
+ ) -> None:
474
+ """Test MultipleDataGrabber with datagrabbers with different element keys.
475
+
476
+ Parameters
477
+ ----------
478
+ tmp_path : pathlib.Path
479
+ The path to the test directory.
480
+ names : list of str
481
+ The parametrized datasets of the datagrabbers.
482
+
483
+ """
484
+ dg = _multiple(tmp_path, names)
485
+ assert dg.get_element_keys() == ["subject", "task"]
486
+ # sub-03 has no BOLD data
487
+ assert dg.get_elements() == [
488
+ ("sub-01", "movie"),
489
+ ("sub-01", "rest"),
490
+ ("sub-02", "movie"),
491
+ ("sub-02", "rest"),
492
+ ]
493
+ out = dg[("sub-02", "rest")]
494
+ assert out["BOLD"]["path"] == (
495
+ tmp_path / "fmriprep/sub-02/func/sub-02_task-rest_bold.nii"
496
+ )
497
+ assert out["VBM_GM"]["path"] == tmp_path / "cat/sub-02/mri/mwp1sub-02.nii"
498
+ for data_type in ("BOLD", "VBM_GM"):
499
+ assert out[data_type]["meta"]["element"] == {
500
+ "subject": "sub-02",
501
+ "task": "rest",
502
+ }
503
+
504
+
505
+ @pytest.mark.parametrize(
506
+ "names", [["fmriprep", "confounds"], ["confounds", "fmriprep"]]
507
+ )
508
+ def test_MultipleDataGrabber_nested_types(
509
+ tmp_path: Path, names: list[str]
510
+ ) -> None:
511
+ """Test MultipleDataGrabber replacing nested data types.
512
+
513
+ Parameters
514
+ ----------
515
+ tmp_path : pathlib.Path
516
+ The path to the test directory.
517
+ names : list of str
518
+ The parametrized datasets of the datagrabbers.
519
+
520
+ """
521
+ dg = _multiple(tmp_path, names)
522
+ # The confounds dataset only has the rest task
523
+ assert dg.get_elements() == [("sub-01", "rest"), ("sub-02", "rest")]
524
+ out = dg[("sub-01", "rest")]
525
+ # The BOLD image of the first dataset with the confounds of the second
526
+ assert out["BOLD"]["path"] == (
527
+ tmp_path / "fmriprep/sub-01/func/sub-01_task-rest_bold.nii"
528
+ )
529
+ assert out["BOLD"]["confounds"]["path"] == (
530
+ tmp_path / "confounds/sub-01/sub-01_task-rest.tsv"
531
+ )
532
+
533
+
534
+ @pytest.mark.parametrize(
535
+ "names, match",
536
+ [
537
+ (["fmriprep", "fmriprep"], "`BOLD` is grabbed by more than one"),
538
+ (
539
+ ["confounds", "confounds"],
540
+ "`BOLD.confounds` is grabbed by more than one",
541
+ ),
542
+ ],
543
+ )
544
+ def test_MultipleDataGrabber_overlapping_types(
545
+ tmp_path: Path, names: list[str], match: str
546
+ ) -> None:
547
+ """Test MultipleDataGrabber errors with overlapping data types.
548
+
549
+ Parameters
550
+ ----------
551
+ tmp_path : pathlib.Path
552
+ The path to the test directory.
553
+ names : list of str
554
+ The parametrized datasets of the datagrabbers.
555
+ match : str
556
+ The parametrized error message.
557
+
558
+ """
559
+ with pytest.raises(RuntimeError, match=match):
560
+ _multiple(tmp_path, names)
561
+
562
+
563
+ def test_MultipleDataGrabber_three_datasets(tmp_path: Path) -> None:
564
+ """Test MultipleDataGrabber with the data of three datasets.
565
+
566
+ Parameters
567
+ ----------
568
+ tmp_path : pathlib.Path
569
+ The path to the test directory.
570
+
571
+ """
572
+ dg = _multiple(tmp_path, ["fmriprep", "cat", "confounds"])
573
+ assert dg.get_types() == ["BOLD", "VBM_GM"]
574
+ assert dg.get_elements() == [("sub-01", "rest"), ("sub-02", "rest")]
575
+ out = dg[("sub-02", "rest")]
576
+ assert out["BOLD"]["path"].parent == tmp_path / "fmriprep/sub-02/func"
577
+ assert out["BOLD"]["confounds"]["path"].parent == (
578
+ tmp_path / "confounds/sub-02"
579
+ )
580
+ assert out["VBM_GM"]["path"].parent == tmp_path / "cat/sub-02/mri"
581
+ # The element being processed, and the keys that the data depends on
582
+ for data_type in ("BOLD", "VBM_GM"):
583
+ assert out[data_type]["meta"]["element"] == {
584
+ "subject": "sub-02",
585
+ "task": "rest",
586
+ }
587
+ assert out["BOLD"]["meta"]["_element_keys"] == ["subject", "task"]
588
+ assert out["VBM_GM"]["meta"]["_element_keys"] == ["subject"]