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
@@ -5,8 +5,14 @@
5
5
  # Synchon Mandal <s.mandal@fz-juelich.de>
6
6
  # License: AGPL
7
7
 
8
+ from fnmatch import fnmatchcase
9
+ from pathlib import Path
10
+
11
+ from pydantic import ConfigDict
12
+
8
13
  from ..api.decorators import register_datagrabber
9
- from ..utils import logger
14
+ from ..utils import raise_error
15
+ from .base import logger
10
16
  from .datalad_base import DataladDataGrabber
11
17
  from .pattern import PatternDataGrabber
12
18
 
@@ -23,122 +29,24 @@ class PatternDataladDataGrabber(DataladDataGrabber, PatternDataGrabber):
23
29
 
24
30
  Parameters
25
31
  ----------
26
- types : list of str
27
- The types of data to be grabbed.
28
- patterns : dict
29
- Data type patterns as a dictionary. It has the following schema:
30
-
31
- * ``"T1w"`` :
32
-
33
- .. code-block:: none
34
-
35
- {
36
- "mandatory": ["pattern", "space"],
37
- "optional": {
38
- "mask": {
39
- "mandatory": ["pattern", "space"],
40
- "optional": []
41
- }
42
- }
43
- }
44
-
45
- * ``"T2w"`` :
46
-
47
- .. code-block:: none
48
-
49
- {
50
- "mandatory": ["pattern", "space"],
51
- "optional": {
52
- "mask": {
53
- "mandatory": ["pattern", "space"],
54
- "optional": []
55
- }
56
- }
57
- }
58
-
59
- * ``"BOLD"`` :
60
-
61
- .. code-block:: none
62
-
63
- {
64
- "mandatory": ["pattern", "space"],
65
- "optional": {
66
- "mask": {
67
- "mandatory": ["pattern", "space"],
68
- "optional": []
69
- }
70
- "confounds": {
71
- "mandatory": ["pattern", "format"],
72
- "optional": []
73
- }
74
- }
75
- }
76
-
77
- * ``"Warp"`` :
78
-
79
- .. code-block:: none
80
-
81
- {
82
- "mandatory": ["pattern", "src", "dst"],
83
- "optional": []
84
- }
85
-
86
- * ``"VBM_GM"`` :
87
-
88
- .. code-block:: none
89
-
90
- {
91
- "mandatory": ["pattern", "space"],
92
- "optional": []
93
- }
94
-
95
- * ``"VBM_WM"`` :
96
-
97
- .. code-block:: none
98
-
99
- {
100
- "mandatory": ["pattern", "space"],
101
- "optional": []
102
- }
103
-
104
- Basically, for each data type, one needs to provide ``mandatory`` keys
105
- and can choose to also provide ``optional`` keys. The value for each
106
- key is a string. So, one needs to provide necessary data types as a
107
- dictionary, for example:
108
-
109
- .. code-block:: none
110
-
111
- {
112
- "BOLD": {
113
- "pattern": "...",
114
- "space": "...",
115
- },
116
- "T1w": {
117
- "pattern": "...",
118
- "space": "...",
119
- },
120
- "Warp": {
121
- "pattern": "...",
122
- "src": "...",
123
- "dst": "...",
124
- }
125
- }
126
-
127
- taken from :class:`.HCP1200`.
128
- replacements : str or list of str
129
- Replacements in the ``pattern`` key of each data type. The value needs
130
- to be a list of all possible replacements.
131
- confounds_format : {"fmriprep", "adhoc"} or None, optional
132
- The format of the confounds for the dataset (default None).
133
- datadir : str or pathlib.Path or None, optional
134
- That directory where the datalad dataset will be cloned. If None,
135
- the datalad dataset will be cloned into a temporary directory
136
- (default None).
137
- rootdir : str or pathlib.Path, optional
32
+ uri : pydantic.AnyUrl
33
+ URI of the datalad sibling.
34
+ types : enum:`.DataType` or list of variants
35
+ The data type(s) to grab.
36
+ patterns : ``DataGrabberPatterns``
37
+ The datagrabber patterns. Check :class:`DataTypeSchema` for the schema.
38
+ replacements : list of str
39
+ All possible replacements in ``patterns.<data_type>.pattern``.
40
+ rootdir : pathlib.Path, optional
138
41
  The path within the datalad dataset to the root directory
139
- (default ".").
140
- uri : str or None, optional
141
- URI of the datalad sibling (default None).
42
+ (default Path(".")).
43
+ confounds_format : :enum:`.ConfoundsFormat` or None, optional
44
+ The format of the confounds for the dataset (default None).
45
+ datadir : pathlib.Path, optional
46
+ That path where the datalad dataset will be cloned.
47
+ If not specified, the datalad dataset will be cloned into a temporary
48
+ directory.
49
+
142
50
 
143
51
  See Also
144
52
  --------
@@ -149,15 +57,67 @@ class PatternDataladDataGrabber(DataladDataGrabber, PatternDataGrabber):
149
57
 
150
58
  """
151
59
 
152
- def __init__(
153
- self,
154
- **kwargs,
155
- ) -> None:
156
- # TODO(synchon): needs to be reworked, DataladDataGrabber needs to be
157
- # a mixin to avoid multiple inheritance wherever possible.
60
+ model_config = ConfigDict(extra="allow")
158
61
 
62
+ def validate_datagrabber_params(self) -> None:
63
+ """Run extra logical validation for datagrabber."""
64
+ super().validate_datagrabber_params()
159
65
  logger.debug("Initializing PatternDataladDataGrabber")
160
- for key, val in kwargs.items():
66
+ for key, val in self.__pydantic_extra__.items():
161
67
  logger.debug(f"\t{key} = {val}")
162
68
 
163
- super().__init__(**kwargs)
69
+ def install(self) -> None:
70
+ """Install the datalad dataset and check the patterns.
71
+
72
+ The elements are found by searching the files of the patterns, but the
73
+ files of a sub-dataset are only there once the sub-dataset is
74
+ installed, so the patterns cannot search the files of the
75
+ sub-datasets (e.g., with one sub-dataset per subject).
76
+
77
+ Raises
78
+ ------
79
+ RuntimeError
80
+ If a pattern searches the files of a sub-dataset.
81
+
82
+ """
83
+ super().install()
84
+ subdatasets = [
85
+ Path(x)
86
+ for x in self._dataset.subdatasets(
87
+ result_xfm="relpaths",
88
+ result_renderer="disabled",
89
+ return_type="list",
90
+ )
91
+ ]
92
+ if len(subdatasets) == 0:
93
+ return
94
+ for data_type in self.types:
95
+ for pattern in self._get_type_patterns(data_type):
96
+ _, glob_pattern, _ = self._replace_patterns_regex(pattern)
97
+ if not any(x in glob_pattern for x in "*?["):
98
+ continue
99
+ pattern_parts = (self.rootdir / glob_pattern).parts
100
+ for subdataset in subdatasets:
101
+ n_parts = len(subdataset.parts)
102
+ if len(pattern_parts) > n_parts and all(
103
+ fnmatchcase(x, y)
104
+ for x, y in zip(
105
+ subdataset.parts, pattern_parts, strict=False
106
+ )
107
+ ):
108
+ # Remove the dataset if cloned, as the context is
109
+ # not entered
110
+ self.cleanup()
111
+ raise_error(
112
+ msg=(
113
+ f"The pattern {pattern} of {data_type} "
114
+ "searches the files of the sub-dataset "
115
+ f"{subdataset}, which are only there if it "
116
+ "is installed. A DataGrabber with files in "
117
+ "sub-datasets must list its elements and "
118
+ "build the paths of the files without "
119
+ "searching them (e.g., without wildcards), "
120
+ "as DataladHCP1200."
121
+ ),
122
+ klass=RuntimeError,
123
+ )
@@ -3,11 +3,22 @@
3
3
  # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
4
  # License: AGPL
5
5
 
6
+ import sys
7
+
8
+
9
+ if sys.version_info < (3, 12): # pragma: no cover
10
+ from typing_extensions import TypedDict
11
+ else:
12
+ from typing import TypedDict
13
+
14
+
6
15
  from collections.abc import Iterator, MutableMapping
7
- from typing import TypedDict
16
+
17
+ from aenum import extend_enum
8
18
 
9
19
  from ..typing import DataGrabberPatterns
10
- from ..utils import logger, raise_error, warn_with_log
20
+ from ..utils import raise_error, warn_with_log
21
+ from .base import DataType, logger
11
22
 
12
23
 
13
24
  __all__ = [
@@ -191,37 +202,17 @@ def register_data_type(name: str, schema: DataTypeSchema) -> None:
191
202
  ----------
192
203
  name : str
193
204
  The data type name.
194
- schema : DataTypeSchema
205
+ schema : ``DataTypeSchema``
195
206
  The data type schema.
196
207
 
197
208
  """
198
209
  DataTypeManager()[name] = schema
210
+ extend_enum(DataType, name, name)
199
211
 
200
212
 
201
213
  class PatternValidationMixin:
202
214
  """Mixin class for pattern validation."""
203
215
 
204
- def _validate_types(self, types: list[str]) -> None:
205
- """Validate the types.
206
-
207
- Parameters
208
- ----------
209
- types : list of str
210
- The data types to validate.
211
-
212
- Raises
213
- ------
214
- TypeError
215
- If ``types`` is not a list or if the values are not string.
216
-
217
- """
218
- if not isinstance(types, list):
219
- raise_error(msg="`types` must be a list", klass=TypeError)
220
- if any(not isinstance(x, str) for x in types):
221
- raise_error(
222
- msg="`types` must be a list of strings", klass=TypeError
223
- )
224
-
225
216
  def _validate_replacements(
226
217
  self,
227
218
  replacements: list[str],
@@ -234,15 +225,13 @@ class PatternValidationMixin:
234
225
  ----------
235
226
  replacements : list of str
236
227
  The replacements to validate.
237
- patterns : dict
238
- The patterns to validate replacements against.
228
+ patterns : ``DataGrabberPatterns``
229
+ The patterns to validate ``replacements`` against.
239
230
  partial_pattern_ok : bool
240
231
  Whether to raise error if partial pattern for a data type is found.
241
232
 
242
233
  Raises
243
234
  ------
244
- TypeError
245
- If ``replacements`` is not a list or if the values are not string.
246
235
  ValueError
247
236
  If a value in ``replacements`` is not part of a data type pattern
248
237
  and ``partial_pattern_ok=False`` or
@@ -256,15 +245,6 @@ class PatternValidationMixin:
256
245
  and ``partial_pattern_ok=True``.
257
246
 
258
247
  """
259
- if not isinstance(replacements, list):
260
- raise_error(msg="`replacements` must be a list.", klass=TypeError)
261
-
262
- if any(not isinstance(x, str) for x in replacements):
263
- raise_error(
264
- msg="`replacements` must be a list of strings",
265
- klass=TypeError,
266
- )
267
-
268
248
  # Make a list of all patterns recursively
269
249
  all_patterns = []
270
250
  for dtype_val in patterns.values():
@@ -390,7 +370,7 @@ class PatternValidationMixin:
390
370
 
391
371
  def validate_patterns(
392
372
  self,
393
- types: list[str],
373
+ types: list[DataType],
394
374
  replacements: list[str],
395
375
  patterns: DataGrabberPatterns,
396
376
  partial_pattern_ok: bool = False,
@@ -399,11 +379,11 @@ class PatternValidationMixin:
399
379
 
400
380
  Parameters
401
381
  ----------
402
- types : list of str
403
- The data types to check patterns of.
382
+ types : list of :enum:`.DataType`
383
+ The data type(s) to check patterns of.
404
384
  replacements : list of str
405
- The replacements to be replaced in the patterns.
406
- patterns : dict
385
+ The replacements to be replaced in the ``patterns``.
386
+ patterns : ``DataGrabberPatterns``
407
387
  The patterns to validate.
408
388
  partial_pattern_ok : bool, optional
409
389
  Whether to raise error if partial pattern for a data type is found.
@@ -412,8 +392,6 @@ class PatternValidationMixin:
412
392
 
413
393
  Raises
414
394
  ------
415
- TypeError
416
- If ``patterns`` is not a dictionary.
417
395
  ValueError
418
396
  If length of ``types`` and ``patterns`` are different or
419
397
  if ``patterns`` is missing entries from ``types`` or
@@ -421,12 +399,6 @@ class PatternValidationMixin:
421
399
  if data type pattern key contains '*' as value.
422
400
 
423
401
  """
424
- # Validate types
425
- self._validate_types(types=types)
426
-
427
- # Validate patterns
428
- if not isinstance(patterns, dict):
429
- raise_error(msg="`patterns` must be a dict", klass=TypeError)
430
402
  # Unequal length of objects
431
403
  if len(types) > len(patterns):
432
404
  raise_error(
@@ -26,7 +26,7 @@ def test_BaseDataGrabber() -> None:
26
26
  def get_element_keys(self):
27
27
  return ["subject"]
28
28
 
29
- dg = MyDataGrabber(datadir="/tmp", types=["BOLD"])
29
+ dg = MyDataGrabber(datadir="/tmp", types="BOLD")
30
30
  elem = dg["sub01"]
31
31
  assert "BOLD" in elem
32
32
  assert "meta" in elem["BOLD"]
@@ -55,7 +55,7 @@ def test_BaseDataGrabber() -> None:
55
55
  def get_element_keys(self):
56
56
  return super().get_element_keys()
57
57
 
58
- dg = MyDataGrabber2(datadir="/tmp", types=["BOLD"])
58
+ dg = MyDataGrabber2(datadir="/tmp", types="BOLD")
59
59
  with pytest.raises(NotImplementedError):
60
60
  dg.get_element_keys()
61
61
 
@@ -77,7 +77,7 @@ def test_BaseDataGrabber_filter_single() -> None:
77
77
  def get_element_keys(self):
78
78
  return ["subject"]
79
79
 
80
- dg = FilterDataGrabber(datadir="/tmp", types=["BOLD"])
80
+ dg = FilterDataGrabber(datadir="/tmp", types="BOLD")
81
81
  with dg:
82
82
  assert "sub01" in list(dg.filter(["sub01"]))
83
83
  assert "sub02" not in list(dg.filter(["sub01"]))
@@ -104,7 +104,7 @@ def test_BaseDataGrabber_filter_multi() -> None:
104
104
  def get_element_keys(self):
105
105
  return ["subject", "task"]
106
106
 
107
- dg = FilterDataGrabber(datadir="/tmp", types=["BOLD"])
107
+ dg = FilterDataGrabber(datadir="/tmp", types="BOLD")
108
108
  with dg:
109
109
  assert ("sub01", "rest") in list(
110
110
  dg.filter([("sub01", "rest")]) # type: ignore
@@ -118,3 +118,16 @@ def test_BaseDataGrabber_filter_multi() -> None:
118
118
  assert ("sub02", "movie") not in list(
119
119
  dg.filter([("sub01", "rest")]) # type: ignore
120
120
  )
121
+ # Partial selectors as values or tuples
122
+ assert list(dg.filter(["sub01"])) == [
123
+ ("sub01", "rest"),
124
+ ("sub01", "movie"),
125
+ ]
126
+ assert list(dg.filter([("rest",)])) == [
127
+ ("sub01", "rest"),
128
+ ("sub02", "rest"),
129
+ ("sub03", "rest"),
130
+ ]
131
+ # The selectors must have the same number of values
132
+ with pytest.raises(ValueError, match="same number of values"):
133
+ list(dg.filter(["sub01", ("sub02", "rest")])) # type: ignore
@@ -9,18 +9,22 @@ from pathlib import Path
9
9
  import datalad.api as dl
10
10
  import pytest
11
11
 
12
- from junifer.datagrabber import DataladDataGrabber
12
+ from junifer.datagrabber import DataladDataGrabber, DataType
13
13
  from junifer.utils import config
14
14
 
15
15
 
16
16
  _testing_dataset = {
17
17
  "example_bids": {
18
- "uri": "https://gin.g-node.org/juaml/datalad-example-bids",
18
+ "uri": (
19
+ "https://cerebra.fz-juelich.de/junifer/datalad-example-bids.git"
20
+ ),
19
21
  "commit": "3f288c8725207ae0c9b3616e093e78cda192b570",
20
22
  "id": "582b9696-f13f-42e4-9587-b4e62aa2a8e7",
21
23
  },
22
24
  "example_bids_ses": {
23
- "uri": "https://gin.g-node.org/juaml/datalad-example-bids-ses",
25
+ "uri": (
26
+ "https://cerebra.fz-juelich.de/junifer/datalad-example-bids-ses.git"
27
+ ),
24
28
  "commit": "6b163aa98af76a9eac0272273c27e14127850181",
25
29
  "id": "715c17cf-a1b9-42d6-9af8-9f74c1a4a724",
26
30
  },
@@ -38,23 +42,18 @@ def concrete_datagrabber() -> type[DataladDataGrabber]:
38
42
 
39
43
  """
40
44
 
41
- class MyDataGrabber(DataladDataGrabber): # type: ignore
42
- def __init__(self, datadir, uri):
43
- super().__init__(
44
- datadir=datadir,
45
- rootdir="example_bids",
46
- uri=uri,
47
- types=["T1w", "BOLD"],
48
- )
45
+ class MyDataGrabber(DataladDataGrabber):
46
+ types: list[DataType] = [DataType.T1w, DataType.BOLD] # noqa: RUF012
47
+ rootdir: Path = Path("example_bids")
49
48
 
50
49
  def get_item(self, subject):
51
50
  out = {
52
51
  "T1w": {
53
- "path": self.datadir
52
+ "path": self.fulldir
54
53
  / f"{subject}/anat/{subject}_T1w.nii.gz"
55
54
  },
56
55
  "BOLD": {
57
- "path": self.datadir
56
+ "path": self.fulldir
58
57
  / f"{subject}/func/{subject}_task-rest_bold.nii.gz"
59
58
  },
60
59
  }
@@ -91,7 +90,7 @@ def test_DataladDataGrabber_install_errors(
91
90
  # Files are not there
92
91
  assert datadir.exists() is False
93
92
  # Clone dataset
94
- dl.clone(uri, datadir) # type: ignore
93
+ dl.clone(uri, datadir)
95
94
  dg = concrete_datagrabber(datadir=datadir, uri=uri2)
96
95
  with pytest.raises(ValueError, match=r"different ID"):
97
96
  with dg:
@@ -160,7 +159,6 @@ def test_DataladDataGrabber_clone_cleanup(
160
159
  assert "datagrabber" in meta
161
160
  assert "datalad_dirty" in meta["datagrabber"]
162
161
  assert meta["datagrabber"]["datalad_dirty"] is False
163
- assert hasattr(dg, "_got_files") is False
164
162
  assert datadir.exists() is True
165
163
  assert elem1_bold.is_file() is True
166
164
  assert elem1_bold.is_symlink() is True
@@ -185,8 +183,8 @@ def test_DataladDataGrabber_clone_create_cleanup(
185
183
 
186
184
  # Clone whole dataset
187
185
  uri = _testing_dataset["example_bids"]["uri"]
188
- with concrete_datagrabber(datadir=None, uri=uri) as dg:
189
- datadir = dg._tmpdir / "datadir"
186
+ with concrete_datagrabber(uri=uri) as dg:
187
+ datadir = dg._repodir
190
188
  elem1_bold = (
191
189
  datadir / "example_bids/sub-01/func/sub-01_task-rest_bold.nii.gz"
192
190
  )
@@ -206,7 +204,6 @@ def test_DataladDataGrabber_clone_create_cleanup(
206
204
  assert "datagrabber" in meta
207
205
  assert "datalad_dirty" in meta["datagrabber"]
208
206
  assert meta["datagrabber"]["datalad_dirty"] is False
209
- assert hasattr(dg, "_got_files") is False
210
207
  assert datadir.exists() is True
211
208
  assert elem1_bold.is_file() is True
212
209
  assert elem1_bold.is_symlink() is True
@@ -246,7 +243,7 @@ def test_DataladDataGrabber_previously_cloned(
246
243
  assert elem1_t1w.exists() is False
247
244
 
248
245
  # Clone dataset
249
- dl.clone(uri, datadir, result_renderer="disabled") # type: ignore
246
+ dl.clone(uri, datadir, result_renderer="disabled")
250
247
 
251
248
  # Files are there, but are empty symbolic links
252
249
  assert datadir.exists() is True
@@ -316,7 +313,7 @@ def test_DataladDataGrabber_previously_cloned_and_get(
316
313
  assert elem1_t1w.exists() is False
317
314
 
318
315
  # Clone dataset
319
- dl.clone(uri, datadir, result_renderer="disabled") # type: ignore
316
+ dl.clone(uri, datadir, result_renderer="disabled")
320
317
 
321
318
  # Files are there, but are empty symbolic links
322
319
  assert datadir.exists() is True
@@ -325,9 +322,7 @@ def test_DataladDataGrabber_previously_cloned_and_get(
325
322
  assert elem1_t1w.is_symlink() is True
326
323
  assert elem1_t1w.is_file() is False
327
324
 
328
- dl.get( # type: ignore
329
- elem1_t1w, dataset=datadir, result_renderer="disabled"
330
- )
325
+ dl.get(elem1_t1w, dataset=datadir, result_renderer="disabled")
331
326
 
332
327
  assert elem1_bold.is_symlink() is True
333
328
  assert elem1_bold.is_file() is False
@@ -399,7 +394,7 @@ def test_DataladDataGrabber_previously_cloned_and_get_dirty(
399
394
  assert elem1_t1w.exists() is False
400
395
 
401
396
  # Clone dataset
402
- dl.clone(uri, datadir, result_renderer="disabled") # type: ignore
397
+ dl.clone(uri, datadir, result_renderer="disabled")
403
398
 
404
399
  # Files are there, but are empty symbolic links
405
400
  assert datadir.exists() is True
@@ -408,9 +403,7 @@ def test_DataladDataGrabber_previously_cloned_and_get_dirty(
408
403
  assert elem1_t1w.is_symlink() is True
409
404
  assert elem1_t1w.is_file() is False
410
405
 
411
- dl.get( # type: ignore
412
- elem1_t1w, dataset=datadir, result_renderer="disabled"
413
- )
406
+ dl.get(elem1_t1w, dataset=datadir, result_renderer="disabled")
414
407
 
415
408
  assert elem1_bold.is_symlink() is True
416
409
  assert elem1_bold.is_file() is False