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
@@ -7,64 +7,50 @@
7
7
 
8
8
  from pathlib import Path
9
9
 
10
+ import datalad.api as dl
10
11
  import pytest
12
+ from pydantic import AnyUrl
11
13
 
12
- from junifer.datagrabber import PatternDataladDataGrabber
14
+ from junifer.datagrabber import DataType, PatternDataladDataGrabber
13
15
 
14
16
 
15
17
  _testing_dataset = {
16
18
  "example_bids": {
17
- "uri": "https://gin.g-node.org/juaml/datalad-example-bids",
19
+ "uri": (
20
+ "https://cerebra.fz-juelich.de/junifer/datalad-example-bids.git"
21
+ ),
18
22
  "commit": "3f288c8725207ae0c9b3616e093e78cda192b570",
19
23
  "id": "8fddff30-6993-420a-9d1e-b5b028c59468",
20
24
  },
21
25
  "example_bids_ses": {
22
- "uri": "https://gin.g-node.org/juaml/datalad-example-bids-ses",
26
+ "uri": (
27
+ "https://cerebra.fz-juelich.de/junifer/datalad-example-bids-ses.git"
28
+ ),
23
29
  "commit": "6b163aa98af76a9eac0272273c27e14127850181",
24
30
  "id": "715c17cf-a1b9-42d6-9af8-9f74c1a4a724",
25
31
  },
26
32
  }
27
33
 
28
34
 
29
- def test_bids_PatternDataladDataGrabber_missing_uri() -> None:
30
- """Test check of missing URI in PatternDataladDataGrabber."""
31
- with pytest.raises(ValueError, match=r"`uri` must be provided"):
32
- PatternDataladDataGrabber(
33
- datadir=None,
34
- types=[],
35
- patterns={},
36
- replacements=[],
37
- )
38
-
39
-
40
35
  def test_bids_PatternDataladDataGrabber() -> None:
41
36
  """Test subject-based BIDS PatternDataladDataGrabber."""
42
- # Define types
43
- types = ["T1w", "BOLD"]
44
- # Define patterns
45
- patterns = {
46
- "T1w": {
47
- "pattern": "{subject}/anat/{subject}_T1w.nii.gz",
48
- "space": "MNI152NLin6Asym",
49
- },
50
- "BOLD": {
51
- "pattern": "{subject}/func/{subject}_task-rest_bold.nii.gz",
52
- "space": "MNI152NLin6Asym",
53
- },
54
- }
55
- # Define replacements
56
- replacements = ["subject"]
57
-
58
- repo_uri = _testing_dataset["example_bids"]["uri"]
59
- rootdir = "example_bids"
60
37
  repo_commit = _testing_dataset["example_bids"]["commit"]
61
-
38
+ repo_uri = _testing_dataset["example_bids"]["uri"]
62
39
  with PatternDataladDataGrabber(
63
- rootdir=rootdir,
64
- uri=repo_uri,
65
- types=types,
66
- patterns=patterns,
67
- replacements=replacements,
40
+ uri=AnyUrl(repo_uri),
41
+ types=[DataType.T1w, DataType.BOLD],
42
+ patterns={
43
+ "T1w": {
44
+ "pattern": "{subject}/anat/{subject}_T1w.nii.gz",
45
+ "space": "MNI152NLin6Asym",
46
+ },
47
+ "BOLD": {
48
+ "pattern": "{subject}/func/{subject}_task-rest_bold.nii.gz",
49
+ "space": "MNI152NLin6Asym",
50
+ },
51
+ },
52
+ replacements=["subject"],
53
+ rootdir=Path("example_bids"),
68
54
  ) as dg:
69
55
  subs = list(dg)
70
56
  expected_subs = [f"sub-{i:02d}" for i in range(1, 10)]
@@ -74,11 +60,11 @@ def test_bids_PatternDataladDataGrabber() -> None:
74
60
  t_sub = dg[elem]
75
61
  assert "path" in t_sub["T1w"]
76
62
  assert t_sub["T1w"]["path"] == (
77
- dg.datadir / f"{elem}/anat/{elem}_T1w.nii.gz"
63
+ dg.fulldir / f"{elem}/anat/{elem}_T1w.nii.gz"
78
64
  )
79
65
  assert "path" in t_sub["BOLD"]
80
66
  assert t_sub["BOLD"]["path"] == (
81
- dg.datadir / f"{elem}/func/{elem}_task-rest_bold.nii.gz"
67
+ dg.fulldir / f"{elem}/func/{elem}_task-rest_bold.nii.gz"
82
68
  )
83
69
 
84
70
  assert "meta" in t_sub["BOLD"]
@@ -88,7 +74,7 @@ def test_bids_PatternDataladDataGrabber() -> None:
88
74
  assert "class" in dg_meta
89
75
  assert dg_meta["class"] == "PatternDataladDataGrabber"
90
76
  assert "uri" in dg_meta
91
- assert dg_meta["uri"] == repo_uri
77
+ assert str(dg_meta["uri"]) == repo_uri
92
78
  assert "datalad_commit_id" in dg_meta
93
79
  assert dg_meta["datalad_commit_id"] == repo_commit
94
80
 
@@ -98,80 +84,67 @@ def test_bids_PatternDataladDataGrabber() -> None:
98
84
 
99
85
  def test_bids_PatternDataladDataGrabber_datadir() -> None:
100
86
  """Test PatternDataladDataGrabber with a datadir set to a relative path."""
101
- # Define patterns
102
- patterns = {
103
- "T1w": {
104
- "pattern": "{subject}/anat/{subject}_T*w.nii.gz",
105
- "space": "MNI152NLin6Asym",
106
- },
107
- "BOLD": {
108
- "pattern": "{subject}/func/{subject}_task-rest_*.nii.gz",
109
- "space": "MNI152NLin6Asym",
110
- },
111
- }
112
- # Define datadir
113
- datadir = "dataset" # use string and not absolute path
87
+ datadir = Path("dataset") # use string and not absolute path
114
88
  with PatternDataladDataGrabber(
115
- uri=_testing_dataset["example_bids"]["uri"],
116
- types=["T1w", "BOLD"],
117
- patterns=patterns,
118
- datadir=datadir,
119
- rootdir="example_bids",
89
+ uri=AnyUrl(_testing_dataset["example_bids"]["uri"]),
90
+ types=[DataType.T1w, DataType.BOLD],
91
+ patterns={
92
+ "T1w": {
93
+ "pattern": "{subject}/anat/{subject}_T*w.nii.gz",
94
+ "space": "MNI152NLin6Asym",
95
+ },
96
+ "BOLD": {
97
+ "pattern": "{subject}/func/{subject}_task-rest_*bold.nii.gz",
98
+ "space": "MNI152NLin6Asym",
99
+ },
100
+ },
120
101
  replacements=["subject"],
102
+ datadir=datadir,
103
+ rootdir=Path("example_bids"),
121
104
  ) as dg:
122
- assert dg.datadir == Path(datadir) / "example_bids"
105
+ assert dg.fulldir == Path(datadir) / "example_bids"
106
+ assert len(dg.get_elements()) > 0
123
107
  for elem in dg:
124
108
  t_sub = dg[elem]
109
+ # The paths are absolute
125
110
  assert "path" in t_sub["T1w"]
126
111
  assert t_sub["T1w"]["path"] == (
127
- dg.datadir / f"{elem}/anat/{elem}_T1w.nii.gz"
112
+ dg.fulldir.absolute() / f"{elem}/anat/{elem}_T1w.nii.gz"
128
113
  )
129
114
  assert "path" in t_sub["BOLD"]
130
115
  assert t_sub["BOLD"]["path"] == (
131
- dg.datadir / f"{elem}/func/{elem}_task-rest_bold.nii.gz"
116
+ dg.fulldir.absolute()
117
+ / f"{elem}/func/{elem}_task-rest_bold.nii.gz"
132
118
  )
133
119
 
134
120
 
135
121
  def test_bids_PatternDataladDataGrabber_session():
136
122
  """Test a subject and session-based BIDS PatternDataladDataGrabber."""
137
- types = ["T1w", "BOLD"]
138
- patterns = {
139
- "T1w": {
140
- "pattern": (
141
- "{subject}/{session}/anat/{subject}_{session}_T1w.nii.gz"
142
- ),
143
- "space": "MNI152NLin6Asym",
144
- },
145
- "BOLD": {
146
- "pattern": (
147
- "{subject}/{session}/func/"
148
- "{subject}_{session}_task-rest_bold.nii.gz"
149
- ),
150
- "space": "MNI152NLin6Asym",
151
- },
152
- }
153
- replacements = ["subject", "session"]
154
-
155
- # Check error
156
- with pytest.raises(ValueError, match=r"`uri` must be provided"):
157
- PatternDataladDataGrabber(
158
- datadir=None,
159
- types=types,
160
- patterns=patterns,
161
- replacements=replacements,
162
- )
163
-
164
123
  # Set parameters
165
- repo_uri = _testing_dataset["example_bids_ses"]["uri"]
166
- rootdir = "example_bids_ses"
167
-
124
+ repo_uri = AnyUrl(_testing_dataset["example_bids_ses"]["uri"])
125
+ rootdir = Path("example_bids_ses")
126
+ replacements = ["subject", "session"]
168
127
  # With T1W and bold, only 2 sessions are available
169
128
  with PatternDataladDataGrabber(
170
- rootdir=rootdir,
171
129
  uri=repo_uri,
172
- types=types,
173
- patterns=patterns,
130
+ types=[DataType.T1w, DataType.BOLD],
131
+ patterns={
132
+ "T1w": {
133
+ "pattern": (
134
+ "{subject}/{session}/anat/{subject}_{session}_T1w.nii.gz"
135
+ ),
136
+ "space": "MNI152NLin6Asym",
137
+ },
138
+ "BOLD": {
139
+ "pattern": (
140
+ "{subject}/{session}/func/"
141
+ "{subject}_{session}_task-rest_bold.nii.gz"
142
+ ),
143
+ "space": "MNI152NLin6Asym",
144
+ },
145
+ },
174
146
  replacements=replacements,
147
+ rootdir=rootdir,
175
148
  ) as dg:
176
149
  subs = list(dg.get_elements())
177
150
  expected_subs = [
@@ -182,21 +155,19 @@ def test_bids_PatternDataladDataGrabber_session():
182
155
  assert set(subs) == set(expected_subs)
183
156
 
184
157
  # Test with a different T1w only, it should have 3 sessions
185
- types = ["T1w"]
186
- patterns = {
187
- "T1w": {
188
- "pattern": (
189
- "{subject}/{session}/anat/{subject}_{session}_T1w.nii.gz"
190
- ),
191
- "space": "MNI152NLin6Asym",
192
- },
193
- }
194
158
  with PatternDataladDataGrabber(
195
- rootdir=rootdir,
196
159
  uri=repo_uri,
197
- types=types,
198
- patterns=patterns,
160
+ types=DataType.T1w,
161
+ patterns={
162
+ "T1w": {
163
+ "pattern": (
164
+ "{subject}/{session}/anat/{subject}_{session}_T1w.nii.gz"
165
+ ),
166
+ "space": "MNI152NLin6Asym",
167
+ },
168
+ },
199
169
  replacements=replacements,
170
+ rootdir=rootdir,
200
171
  ) as dg:
201
172
  subs = list(dg)
202
173
  expected_subs = [
@@ -205,3 +176,102 @@ def test_bids_PatternDataladDataGrabber_session():
205
176
  for i in range(1, 10)
206
177
  ]
207
178
  assert set(subs) == set(expected_subs)
179
+
180
+
181
+ @pytest.fixture
182
+ def subdatasets_uri(tmp_path: Path) -> AnyUrl:
183
+ """Return the URI of a dataset with a sub-dataset for each subject.
184
+
185
+ Parameters
186
+ ----------
187
+ tmp_path : pathlib.Path
188
+ The path to the test directory.
189
+
190
+ """
191
+ dataset = dl.create(tmp_path / "origin", result_renderer="disabled")
192
+ for subject in ("sub-01", "sub-02"):
193
+ subdataset = dataset.create(subject, result_renderer="disabled")
194
+ (subdataset.pathobj / "T1w.nii").write_text(subject)
195
+ (dataset.pathobj / f"{subject}_T1w.nii").write_text(subject)
196
+ dataset.save(recursive=True, result_renderer="disabled")
197
+ return AnyUrl(f"file://{dataset.path}")
198
+
199
+
200
+ @pytest.mark.parametrize(
201
+ "pattern",
202
+ ["{subject}/T1w.nii", "{subject}/T1*.nii"],
203
+ )
204
+ def test_PatternDataladDataGrabber_subdatasets_search(
205
+ tmp_path: Path, subdatasets_uri: AnyUrl, pattern: str
206
+ ) -> None:
207
+ """Test searching the files of the sub-datasets raises an error.
208
+
209
+ Parameters
210
+ ----------
211
+ tmp_path : pathlib.Path
212
+ The path to the test directory.
213
+ subdatasets_uri : pydantic.AnyUrl
214
+ The URI of the dataset with a sub-dataset for each subject.
215
+ pattern : str
216
+ The parametrized pattern, which searches the files of the
217
+ sub-datasets.
218
+
219
+ """
220
+ dg = PatternDataladDataGrabber(
221
+ uri=subdatasets_uri,
222
+ datadir=tmp_path / "clone",
223
+ types=["T1w"],
224
+ patterns={"T1w": {"pattern": pattern, "space": "native"}},
225
+ replacements=["subject"],
226
+ )
227
+ with pytest.raises(RuntimeError, match="searches the files of the"):
228
+ with dg:
229
+ pass
230
+ # The cloned dataset is removed
231
+ assert not (tmp_path / "clone").exists() or not any(
232
+ (tmp_path / "clone").iterdir()
233
+ )
234
+
235
+
236
+ def test_PatternDataladDataGrabber_subdatasets_files(
237
+ tmp_path: Path, subdatasets_uri: AnyUrl
238
+ ) -> None:
239
+ """Test the patterns of a dataset with sub-datasets.
240
+
241
+ The files of the dataset can be searched, and the patterns without
242
+ wildcards can have files in the sub-datasets.
243
+
244
+ Parameters
245
+ ----------
246
+ tmp_path : pathlib.Path
247
+ The path to the test directory.
248
+ subdatasets_uri : pydantic.AnyUrl
249
+ The URI of the dataset with a sub-dataset for each subject.
250
+
251
+ """
252
+ dg = PatternDataladDataGrabber(
253
+ uri=subdatasets_uri,
254
+ datadir=tmp_path / "clone",
255
+ types=["T1w"],
256
+ patterns={"T1w": {"pattern": "{subject}_T1w.nii", "space": "native"}},
257
+ replacements=["subject"],
258
+ )
259
+ with dg:
260
+ assert dg.get_elements() == ["sub-01", "sub-02"]
261
+ assert dg["sub-02"]["T1w"]["path"].read_text() == "sub-02"
262
+ # A pattern without wildcards in a sub-dataset
263
+ dg = PatternDataladDataGrabber(
264
+ uri=subdatasets_uri,
265
+ datadir=tmp_path / "clone",
266
+ types=["T1w"],
267
+ patterns={
268
+ "T1w": {
269
+ "pattern": "{subject}_T1w.nii",
270
+ "space": "native",
271
+ "mask": {"pattern": "sub-01/T1w.nii", "space": "native"},
272
+ }
273
+ },
274
+ replacements=["subject"],
275
+ )
276
+ with dg:
277
+ assert dg.get_elements() == ["sub-01", "sub-02"]
@@ -5,11 +5,11 @@
5
5
  # License: AGPL
6
6
 
7
7
  from contextlib import AbstractContextManager, nullcontext
8
- from typing import Union
9
8
 
10
9
  import pytest
11
10
 
12
- from junifer.datagrabber.pattern_validation_mixin import (
11
+ from junifer.datagrabber import (
12
+ DataType,
13
13
  DataTypeManager,
14
14
  DataTypeSchema,
15
15
  PatternValidationMixin,
@@ -80,7 +80,7 @@ def test_dtype_mgr(dtype: DataTypeSchema) -> None:
80
80
 
81
81
  Parameters
82
82
  ----------
83
- dtype : DataTypeSchema
83
+ dtype : ``DataTypeSchema``
84
84
  The parametrized schema.
85
85
 
86
86
  """
@@ -111,33 +111,15 @@ def test_register_data_type() -> None:
111
111
  )
112
112
 
113
113
  assert "dtype" in DataTypeManager()
114
+ assert "dtype" in list(DataType)
114
115
  _ = DataTypeManager().pop("dtype")
115
- assert "dumb" not in DataTypeManager()
116
+ assert "dtype" not in DataTypeManager()
117
+ assert "dtype" in list(DataType)
116
118
 
117
119
 
118
120
  @pytest.mark.parametrize(
119
121
  "types, replacements, patterns, expect",
120
122
  [
121
- (
122
- "wrong",
123
- [],
124
- {},
125
- pytest.raises(TypeError, match="`types` must be a list"),
126
- ),
127
- (
128
- [1],
129
- [],
130
- {},
131
- pytest.raises(
132
- TypeError, match="`types` must be a list of strings"
133
- ),
134
- ),
135
- (
136
- ["BOLD"],
137
- [],
138
- "wrong",
139
- pytest.raises(TypeError, match="`patterns` must be a dict"),
140
- ),
141
123
  (
142
124
  ["T1w", "BOLD"],
143
125
  "",
@@ -205,30 +187,6 @@ def test_register_data_type() -> None:
205
187
  },
206
188
  pytest.raises(ValueError, match="following a replacement"),
207
189
  ),
208
- (
209
- ["T1w"],
210
- "wrong",
211
- {
212
- "T1w": {
213
- "pattern": "{subject}/anat/{subject}_T1w.nii",
214
- "space": "native",
215
- },
216
- },
217
- pytest.raises(TypeError, match="`replacements` must be a list"),
218
- ),
219
- (
220
- ["T1w"],
221
- [1],
222
- {
223
- "T1w": {
224
- "pattern": "{subject}/anat/{subject}_T1w.nii",
225
- "space": "native",
226
- },
227
- },
228
- pytest.raises(
229
- TypeError, match="`replacements` must be a list of strings"
230
- ),
231
- ),
232
190
  (
233
191
  ["T1w", "BOLD"],
234
192
  ["subject", "session"],
@@ -289,9 +247,9 @@ def test_register_data_type() -> None:
289
247
  ],
290
248
  )
291
249
  def test_PatternValidationMixin(
292
- types: Union[str, list[str], list[int]],
293
- replacements: Union[str, list[str], list[int]],
294
- patterns: Union[str, dict[str, dict[str, str]]],
250
+ types: str | list[str] | list[int],
251
+ replacements: str | list[str] | list[int],
252
+ patterns: str | dict[str, dict[str, str]],
295
253
  expect: AbstractContextManager,
296
254
  ) -> None:
297
255
  """Test validation.
@@ -5,18 +5,21 @@
5
5
  # License: AGPL
6
6
 
7
7
  from pathlib import Path
8
- from typing import Optional, Union
9
8
 
10
9
  import nibabel as nib
11
10
  import pandas as pd
11
+ import structlog
12
+ from pydantic import BaseModel
12
13
 
13
14
  from ..api.decorators import register_datareader
14
15
  from ..pipeline import PipelineStepMixin, UpdateMetaMixin
15
- from ..utils.logging import logger, warn_with_log
16
+ from ..utils.logging import warn_with_log
16
17
 
17
18
 
18
19
  __all__ = ["DefaultDataReader"]
19
20
 
21
+ _log = structlog.get_logger("junifer")
22
+ logger = _log.bind(pkg="datareader", step="datareader")
20
23
 
21
24
  # Map each file extension to a type
22
25
  _extensions = {
@@ -34,7 +37,7 @@ _readers["TSV"] = {"func": pd.read_csv, "params": {"sep": "\t"}}
34
37
 
35
38
 
36
39
  @register_datareader
37
- class DefaultDataReader(PipelineStepMixin, UpdateMetaMixin):
40
+ class DefaultDataReader(BaseModel, PipelineStepMixin, UpdateMetaMixin):
38
41
  """Concrete implementation for common data reading."""
39
42
 
40
43
  def validate_input(self, input: list[str]) -> list[str]:
@@ -59,7 +62,7 @@ class DefaultDataReader(PipelineStepMixin, UpdateMetaMixin):
59
62
  def _fit_transform(
60
63
  self,
61
64
  input: dict[str, dict],
62
- params: Optional[dict] = None,
65
+ params: dict | None = None,
63
66
  ) -> dict:
64
67
  """Fit and transform.
65
68
 
@@ -149,7 +152,7 @@ class DefaultDataReader(PipelineStepMixin, UpdateMetaMixin):
149
152
 
150
153
  def _read_data(
151
154
  data_type: str, path: Path, read_params: dict
152
- ) -> Union[nib.Nifti1Image, pd.DataFrame, None]:
155
+ ) -> nib.Nifti1Image | pd.DataFrame | None:
153
156
  """Read data for data type.
154
157
 
155
158
  Parameters
@@ -30,7 +30,7 @@ def test_DefaultDataReader_validation(type_) -> None:
30
30
  """
31
31
  reader = DefaultDataReader()
32
32
  assert reader.validate_input(type_) == type_
33
- assert reader.validate(type_) == type_
33
+ assert reader.validate_component(type_) == type_
34
34
 
35
35
 
36
36
  def test_DefaultDataReader_meta() -> None:
@@ -0,0 +1,2 @@
1
+ github_checks:
2
+ annotations: false
@@ -0,0 +1,2 @@
1
+ coo
2
+ daty
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: Bug Report
3
+ about: Create a bug report to help us improve BrainPrint
4
+ title: ''
5
+ labels: bug
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ ## Description
11
+ <!-- A clear and concise description of the observed bug and/or error -->
12
+ ....
13
+
14
+ ## Steps to Reproduce
15
+ <!--
16
+ Steps to reproduce the observed behaviour, in as much detail as possible:
17
+ 1. Go to '...'
18
+ 2. Checkout version '...'
19
+ 3. Run '....'
20
+ 4. See error
21
+ -->
22
+
23
+ <!-- If possible, provide error messages, stack traces, and any snippets useful in describing the bug here -->
24
+ ...
25
+
26
+ ## Expected Behavior
27
+ <!-- A clear and concise description of what you expected to happen. -->
28
+ ...
29
+
30
+ ## Screenshots
31
+ <!-- If applicable and desired, add screenshots to help illustrate your problem. -->
32
+ ...
33
+
34
+ ## Environment
35
+ - BrainPrint Version: ...
36
+ - OS: ...
37
+ - CPU: ...
38
+
39
+ <!-- Add any other relevant information on the environment here -->
40
+
41
+ <!-- If possible, include a snapshot of the python software package versions you have (for e.g., using pip freeze or conda list) here -->
42
+
43
+ ## Additional Context
44
+ <!-- Add any other context and comments about the problem here. -->
45
+ ...
@@ -0,0 +1,12 @@
1
+ ---
2
+ name: Documentation
3
+ about: Report an issue or make a suggestion related to BrainPrint documentation
4
+ title: ''
5
+ labels: documentation
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ ## Documentation
11
+ <!-- A clear and concise description of an issue you observed in the documentation, or a suggestion you wish to make. -->
12
+ ...
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: Feature Request
3
+ about: Submit a proposal/request for a new BrainPrint feature
4
+ title: ''
5
+ labels: enhancement
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ ## Feature Description
11
+ <!-- A clear and concise description of the feature you would like to propose -->
12
+ ...
13
+
14
+ ## Motivation
15
+ <!-- A clear and concise description of the problem that the proposed feature would address. For e.g., I'm always frustrated when [...]. If this is related to a specific issue, refer to it here -->
16
+ ...
17
+
18
+ ## Alternatives
19
+ <!-- A clear and concise description of any alternative solutions or features you've considered, if any. -->
20
+ ...
21
+
22
+ ## Additional Context
23
+ <!-- Add any other context or screenshots about the feature request here. -->
24
+ ...
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: Questions/Help/Support
3
+ about: Submit a request for support or a question
4
+ title: ''
5
+ labels: question
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ ## Question/Support Request
11
+ <!-- A clear and concise description of a question you may have or a problem for which you would like to request support -->
12
+ ...
13
+
14
+ ## Screenshots
15
+ <!-- If possible, add screenshots to help illustrate your problem. -->
16
+ ...
17
+
18
+ <!-- If applicable, provide error messages, stack traces, and any snippets useful in describing your problem here -->
19
+
20
+ ## Environment
21
+ - BrainPrint Version: ...
22
+ - OS: ...
23
+ - CPU: ...
24
+
25
+ <!-- Add any other relevant information on the environment here -->
26
+
27
+ <!-- If possible, include a snapshot of the python software package versions you have (for e.g., using pip freeze or conda list) here -->
28
+
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"