junifer 0.0.4.dev829__py3-none-any.whl → 0.0.5__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 (206) hide show
  1. junifer/__init__.py +17 -0
  2. junifer/_version.py +2 -2
  3. junifer/api/__init__.py +4 -1
  4. junifer/api/cli.py +91 -1
  5. junifer/api/decorators.py +9 -0
  6. junifer/api/functions.py +56 -10
  7. junifer/api/parser.py +3 -0
  8. junifer/api/queue_context/__init__.py +4 -1
  9. junifer/api/queue_context/gnu_parallel_local_adapter.py +16 -6
  10. junifer/api/queue_context/htcondor_adapter.py +16 -5
  11. junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +41 -12
  12. junifer/api/queue_context/tests/test_htcondor_adapter.py +48 -15
  13. junifer/api/res/afni/run_afni_docker.sh +1 -1
  14. junifer/api/res/ants/run_ants_docker.sh +1 -1
  15. junifer/api/res/freesurfer/mri_binarize +3 -0
  16. junifer/api/res/freesurfer/mri_mc +3 -0
  17. junifer/api/res/freesurfer/mri_pretess +3 -0
  18. junifer/api/res/freesurfer/mris_convert +3 -0
  19. junifer/api/res/freesurfer/run_freesurfer_docker.sh +61 -0
  20. junifer/api/res/fsl/run_fsl_docker.sh +1 -1
  21. junifer/api/res/{run_conda.sh → run_conda.bash} +1 -1
  22. junifer/api/res/run_conda.zsh +23 -0
  23. junifer/api/res/run_venv.bash +22 -0
  24. junifer/api/res/{run_venv.sh → run_venv.zsh} +1 -1
  25. junifer/api/tests/test_api_utils.py +4 -2
  26. junifer/api/tests/test_cli.py +83 -0
  27. junifer/api/tests/test_functions.py +27 -2
  28. junifer/configs/__init__.py +1 -1
  29. junifer/configs/juseless/__init__.py +4 -1
  30. junifer/configs/juseless/datagrabbers/__init__.py +10 -1
  31. junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +4 -3
  32. junifer/configs/juseless/datagrabbers/camcan_vbm.py +3 -0
  33. junifer/configs/juseless/datagrabbers/ixi_vbm.py +4 -3
  34. junifer/configs/juseless/datagrabbers/tests/test_ucla.py +1 -3
  35. junifer/configs/juseless/datagrabbers/ucla.py +12 -9
  36. junifer/configs/juseless/datagrabbers/ukb_vbm.py +3 -0
  37. junifer/data/__init__.py +21 -1
  38. junifer/data/coordinates.py +10 -19
  39. junifer/data/masks/ukb/UKB_15K_GM_template.nii.gz +0 -0
  40. junifer/data/masks.py +58 -87
  41. junifer/data/parcellations.py +14 -3
  42. junifer/data/template_spaces.py +4 -1
  43. junifer/data/tests/test_masks.py +26 -37
  44. junifer/data/utils.py +3 -0
  45. junifer/datagrabber/__init__.py +18 -1
  46. junifer/datagrabber/aomic/__init__.py +3 -0
  47. junifer/datagrabber/aomic/id1000.py +70 -37
  48. junifer/datagrabber/aomic/piop1.py +69 -36
  49. junifer/datagrabber/aomic/piop2.py +71 -38
  50. junifer/datagrabber/aomic/tests/test_id1000.py +44 -100
  51. junifer/datagrabber/aomic/tests/test_piop1.py +65 -108
  52. junifer/datagrabber/aomic/tests/test_piop2.py +45 -102
  53. junifer/datagrabber/base.py +13 -6
  54. junifer/datagrabber/datalad_base.py +13 -1
  55. junifer/datagrabber/dmcc13_benchmark.py +36 -53
  56. junifer/datagrabber/hcp1200/__init__.py +3 -0
  57. junifer/datagrabber/hcp1200/datalad_hcp1200.py +3 -0
  58. junifer/datagrabber/hcp1200/hcp1200.py +4 -1
  59. junifer/datagrabber/multiple.py +45 -6
  60. junifer/datagrabber/pattern.py +170 -62
  61. junifer/datagrabber/pattern_datalad.py +25 -12
  62. junifer/datagrabber/pattern_validation_mixin.py +388 -0
  63. junifer/datagrabber/tests/test_datalad_base.py +4 -4
  64. junifer/datagrabber/tests/test_dmcc13_benchmark.py +46 -19
  65. junifer/datagrabber/tests/test_multiple.py +161 -84
  66. junifer/datagrabber/tests/test_pattern.py +45 -0
  67. junifer/datagrabber/tests/test_pattern_datalad.py +4 -4
  68. junifer/datagrabber/tests/test_pattern_validation_mixin.py +249 -0
  69. junifer/datareader/__init__.py +4 -1
  70. junifer/datareader/default.py +95 -43
  71. junifer/external/BrainPrint/brainprint/__init__.py +4 -0
  72. junifer/external/BrainPrint/brainprint/_version.py +3 -0
  73. junifer/external/BrainPrint/brainprint/asymmetry.py +91 -0
  74. junifer/external/BrainPrint/brainprint/brainprint.py +441 -0
  75. junifer/external/BrainPrint/brainprint/surfaces.py +258 -0
  76. junifer/external/BrainPrint/brainprint/utils/__init__.py +1 -0
  77. junifer/external/BrainPrint/brainprint/utils/_config.py +112 -0
  78. junifer/external/BrainPrint/brainprint/utils/utils.py +188 -0
  79. junifer/external/__init__.py +1 -1
  80. junifer/external/nilearn/__init__.py +5 -1
  81. junifer/external/nilearn/junifer_connectivity_measure.py +483 -0
  82. junifer/external/nilearn/junifer_nifti_spheres_masker.py +23 -9
  83. junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +1089 -0
  84. junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +76 -1
  85. junifer/markers/__init__.py +23 -1
  86. junifer/markers/base.py +68 -28
  87. junifer/markers/brainprint.py +459 -0
  88. junifer/markers/collection.py +10 -2
  89. junifer/markers/complexity/__init__.py +10 -0
  90. junifer/markers/complexity/complexity_base.py +26 -43
  91. junifer/markers/complexity/hurst_exponent.py +3 -0
  92. junifer/markers/complexity/multiscale_entropy_auc.py +3 -0
  93. junifer/markers/complexity/perm_entropy.py +3 -0
  94. junifer/markers/complexity/range_entropy.py +3 -0
  95. junifer/markers/complexity/range_entropy_auc.py +3 -0
  96. junifer/markers/complexity/sample_entropy.py +3 -0
  97. junifer/markers/complexity/tests/test_hurst_exponent.py +11 -3
  98. junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +11 -3
  99. junifer/markers/complexity/tests/test_perm_entropy.py +11 -3
  100. junifer/markers/complexity/tests/test_range_entropy.py +11 -3
  101. junifer/markers/complexity/tests/test_range_entropy_auc.py +11 -3
  102. junifer/markers/complexity/tests/test_sample_entropy.py +11 -3
  103. junifer/markers/complexity/tests/test_weighted_perm_entropy.py +11 -3
  104. junifer/markers/complexity/weighted_perm_entropy.py +3 -0
  105. junifer/markers/ets_rss.py +27 -42
  106. junifer/markers/falff/__init__.py +3 -0
  107. junifer/markers/falff/_afni_falff.py +5 -2
  108. junifer/markers/falff/_junifer_falff.py +3 -0
  109. junifer/markers/falff/falff_base.py +20 -46
  110. junifer/markers/falff/falff_parcels.py +56 -27
  111. junifer/markers/falff/falff_spheres.py +60 -29
  112. junifer/markers/falff/tests/test_falff_parcels.py +39 -23
  113. junifer/markers/falff/tests/test_falff_spheres.py +39 -23
  114. junifer/markers/functional_connectivity/__init__.py +9 -0
  115. junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +63 -60
  116. junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +45 -32
  117. junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +49 -36
  118. junifer/markers/functional_connectivity/functional_connectivity_base.py +71 -70
  119. junifer/markers/functional_connectivity/functional_connectivity_parcels.py +34 -25
  120. junifer/markers/functional_connectivity/functional_connectivity_spheres.py +40 -30
  121. junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +11 -7
  122. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +27 -7
  123. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +28 -12
  124. junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +35 -11
  125. junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +36 -62
  126. junifer/markers/parcel_aggregation.py +47 -61
  127. junifer/markers/reho/__init__.py +3 -0
  128. junifer/markers/reho/_afni_reho.py +5 -2
  129. junifer/markers/reho/_junifer_reho.py +4 -1
  130. junifer/markers/reho/reho_base.py +8 -27
  131. junifer/markers/reho/reho_parcels.py +28 -17
  132. junifer/markers/reho/reho_spheres.py +27 -18
  133. junifer/markers/reho/tests/test_reho_parcels.py +8 -3
  134. junifer/markers/reho/tests/test_reho_spheres.py +8 -3
  135. junifer/markers/sphere_aggregation.py +43 -59
  136. junifer/markers/temporal_snr/__init__.py +3 -0
  137. junifer/markers/temporal_snr/temporal_snr_base.py +23 -32
  138. junifer/markers/temporal_snr/temporal_snr_parcels.py +9 -6
  139. junifer/markers/temporal_snr/temporal_snr_spheres.py +9 -6
  140. junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +6 -3
  141. junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +6 -3
  142. junifer/markers/tests/test_brainprint.py +58 -0
  143. junifer/markers/tests/test_collection.py +9 -8
  144. junifer/markers/tests/test_ets_rss.py +15 -9
  145. junifer/markers/tests/test_markers_base.py +17 -18
  146. junifer/markers/tests/test_parcel_aggregation.py +93 -32
  147. junifer/markers/tests/test_sphere_aggregation.py +72 -19
  148. junifer/onthefly/__init__.py +4 -1
  149. junifer/onthefly/read_transform.py +3 -0
  150. junifer/pipeline/__init__.py +9 -1
  151. junifer/pipeline/pipeline_step_mixin.py +21 -4
  152. junifer/pipeline/registry.py +3 -0
  153. junifer/pipeline/singleton.py +3 -0
  154. junifer/pipeline/tests/test_registry.py +1 -1
  155. junifer/pipeline/update_meta_mixin.py +3 -0
  156. junifer/pipeline/utils.py +67 -1
  157. junifer/pipeline/workdir_manager.py +3 -0
  158. junifer/preprocess/__init__.py +10 -2
  159. junifer/preprocess/base.py +6 -3
  160. junifer/preprocess/confounds/__init__.py +3 -0
  161. junifer/preprocess/confounds/fmriprep_confound_remover.py +47 -60
  162. junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +72 -113
  163. junifer/preprocess/smoothing/__init__.py +9 -0
  164. junifer/preprocess/smoothing/_afni_smoothing.py +119 -0
  165. junifer/preprocess/smoothing/_fsl_smoothing.py +116 -0
  166. junifer/preprocess/smoothing/_nilearn_smoothing.py +69 -0
  167. junifer/preprocess/smoothing/smoothing.py +174 -0
  168. junifer/preprocess/smoothing/tests/test_smoothing.py +94 -0
  169. junifer/preprocess/warping/__init__.py +3 -0
  170. junifer/preprocess/warping/_ants_warper.py +3 -0
  171. junifer/preprocess/warping/_fsl_warper.py +3 -0
  172. junifer/stats.py +4 -1
  173. junifer/storage/__init__.py +9 -1
  174. junifer/storage/base.py +40 -1
  175. junifer/storage/hdf5.py +71 -9
  176. junifer/storage/pandas_base.py +3 -0
  177. junifer/storage/sqlite.py +3 -0
  178. junifer/storage/tests/test_hdf5.py +82 -10
  179. junifer/storage/utils.py +9 -0
  180. junifer/testing/__init__.py +4 -1
  181. junifer/testing/datagrabbers.py +13 -6
  182. junifer/testing/tests/test_partlycloudytesting_datagrabber.py +7 -7
  183. junifer/testing/utils.py +3 -0
  184. junifer/utils/__init__.py +13 -2
  185. junifer/utils/fs.py +3 -0
  186. junifer/utils/helpers.py +32 -1
  187. junifer/utils/logging.py +33 -4
  188. junifer/utils/tests/test_logging.py +8 -0
  189. {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/METADATA +17 -16
  190. junifer-0.0.5.dist-info/RECORD +275 -0
  191. {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/WHEEL +1 -1
  192. junifer/datagrabber/tests/test_datagrabber_utils.py +0 -218
  193. junifer/datagrabber/utils.py +0 -230
  194. junifer/preprocess/ants/__init__.py +0 -4
  195. junifer/preprocess/ants/ants_apply_transforms_warper.py +0 -185
  196. junifer/preprocess/ants/tests/test_ants_apply_transforms_warper.py +0 -56
  197. junifer/preprocess/bold_warper.py +0 -265
  198. junifer/preprocess/fsl/__init__.py +0 -4
  199. junifer/preprocess/fsl/apply_warper.py +0 -179
  200. junifer/preprocess/fsl/tests/test_apply_warper.py +0 -45
  201. junifer/preprocess/tests/test_bold_warper.py +0 -159
  202. junifer-0.0.4.dev829.dist-info/RECORD +0 -257
  203. {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/AUTHORS.rst +0 -0
  204. {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/LICENSE.md +0 -0
  205. {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/entry_points.txt +0 -0
  206. {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/top_level.txt +0 -0
@@ -25,7 +25,12 @@ from junifer.storage.utils import (
25
25
  def test_get_valid_inputs() -> None:
26
26
  """Test valid inputs."""
27
27
  storage = HDF5FeatureStorage(uri="/tmp")
28
- assert storage.get_valid_inputs() == ["matrix", "vector", "timeseries"]
28
+ assert storage.get_valid_inputs() == [
29
+ "matrix",
30
+ "vector",
31
+ "timeseries",
32
+ "scalar_table",
33
+ ]
29
34
 
30
35
 
31
36
  def test_single_output(tmp_path: Path) -> None:
@@ -808,7 +813,7 @@ def test_store_timeseries(tmp_path: Path) -> None:
808
813
  data = np.array([[10], [20], [30], [40], [50]])
809
814
  col_names = ["signal"]
810
815
 
811
- # Store vector
816
+ # Store timeseries
812
817
  storage.store_timeseries(
813
818
  meta_md5=meta_md5,
814
819
  element=element_to_store,
@@ -822,6 +827,53 @@ def test_store_timeseries(tmp_path: Path) -> None:
822
827
  assert_array_equal(read_df.values, data)
823
828
 
824
829
 
830
+ def test_store_scalar_table(tmp_path: Path) -> None:
831
+ """Test scalar table store.
832
+
833
+ Parameters
834
+ ----------
835
+ tmp_path : pathlib.Path
836
+ The path to the test directory.
837
+
838
+ """
839
+ uri = tmp_path / "test_store_scalar_table.hdf5"
840
+ storage = HDF5FeatureStorage(uri=uri)
841
+ # Metadata to store
842
+ element = {"subject": "test"}
843
+ meta = {
844
+ "element": element,
845
+ "dependencies": ["numpy"],
846
+ "marker": {"name": "brainprint"},
847
+ "type": "FreeSurfer",
848
+ }
849
+ # Process the metadata
850
+ meta_md5, meta_to_store, element_to_store = process_meta(meta)
851
+ # Store metadata
852
+ storage.store_metadata(
853
+ meta_md5=meta_md5, element=element_to_store, meta=meta_to_store
854
+ )
855
+
856
+ # Data to store
857
+ data = np.array([[10, 20], [30, 40], [50, 60]])
858
+ col_names = ["roi1", "roi2"]
859
+ row_names = ["ev1", "ev2", "ev3"]
860
+
861
+ # Store timeseries
862
+ storage.store_scalar_table(
863
+ meta_md5=meta_md5,
864
+ element=element_to_store,
865
+ data=data,
866
+ col_names=col_names,
867
+ row_names=row_names,
868
+ row_header_col_name="eigenvalue",
869
+ )
870
+
871
+ # Read into dataframe
872
+ read_df = storage.read_df(feature_md5=meta_md5)
873
+ # Check if data are equal
874
+ assert_array_equal(read_df.values, data)
875
+
876
+
825
877
  def _create_data_to_store(n_elements: int, kind: str) -> Tuple[str, Dict]:
826
878
  """Create data to store.
827
879
 
@@ -854,13 +906,19 @@ def _create_data_to_store(n_elements: int, kind: str) -> Tuple[str, Dict]:
854
906
  "col_names": [f"col-{i}" for i in range(10)],
855
907
  "matrix_kind": "full",
856
908
  }
857
- elif kind == "timeseries":
909
+ elif kind in "timeseries":
858
910
  data_to_store = {
859
911
  "data": np.arange(20).reshape(2, 10),
860
912
  "col_names": [f"col-{i}" for i in range(10)],
861
913
  }
862
- else:
863
- raise ValueError(f"Unknown kind {kind}.")
914
+ elif kind in "scalar_table":
915
+ data_to_store = {
916
+ "data": np.arange(50).reshape(5, 10),
917
+ "row_names": [f"row-{i}" for i in range(5)],
918
+ "col_names": [f"col-{i}" for i in range(10)],
919
+ "row_header_col_name": "row",
920
+ }
921
+
864
922
  for i in range(n_elements):
865
923
  element = {"subject": f"sub-{i // 2}", "session": f"ses-{i % 2}"}
866
924
  meta = {
@@ -903,6 +961,7 @@ def _create_data_to_store(n_elements: int, kind: str) -> Tuple[str, Dict]:
903
961
  (10, 3, "matrix"),
904
962
  (10, 5, "matrix"),
905
963
  (10, 5, "timeseries"),
964
+ (10, 5, "scalar_table"),
906
965
  ],
907
966
  )
908
967
  def test_multi_output_store_and_collect(
@@ -930,21 +989,20 @@ def test_multi_output_store_and_collect(
930
989
  meta_md5, all_data = _create_data_to_store(n_elements, kind)
931
990
 
932
991
  for t_data in all_data:
933
- # Store metadata for tables
992
+ # Store metadata
934
993
  storage.store_metadata(
935
994
  meta_md5=meta_md5,
936
995
  element=t_data["element"],
937
996
  meta=t_data["meta"],
938
997
  )
998
+ # Store data
939
999
  if kind == "vector":
940
- # Store tables
941
1000
  storage.store_vector(
942
1001
  meta_md5=meta_md5,
943
1002
  element=t_data["element"],
944
1003
  **t_data["data"],
945
1004
  )
946
1005
  elif kind == "matrix":
947
- # Store tables
948
1006
  storage.store_matrix(
949
1007
  meta_md5=meta_md5,
950
1008
  element=t_data["element"],
@@ -956,11 +1014,17 @@ def test_multi_output_store_and_collect(
956
1014
  element=t_data["element"],
957
1015
  **t_data["data"],
958
1016
  )
1017
+ elif kind == "scalar_table":
1018
+ storage.store_scalar_table(
1019
+ meta_md5=meta_md5,
1020
+ element=t_data["element"],
1021
+ **t_data["data"],
1022
+ )
959
1023
  # Check that base URI does not exist yet
960
1024
  assert not uri.exists()
961
1025
 
962
1026
  for t_data in all_data:
963
- # Convert element to preifx
1027
+ # Convert element to prefix
964
1028
  prefix = element_to_prefix(t_data["element"])
965
1029
  # URIs for data storage
966
1030
  elem_uri = uri.parent / f"{prefix}{uri.name}"
@@ -977,7 +1041,7 @@ def test_multi_output_store_and_collect(
977
1041
  # Check that base URI exists now
978
1042
  assert uri.exists()
979
1043
 
980
- # # Read unified metadata
1044
+ # Read unified metadata
981
1045
  read_unified_meta = storage.list_features()
982
1046
  assert meta_md5 in read_unified_meta
983
1047
 
@@ -989,6 +1053,10 @@ def test_multi_output_store_and_collect(
989
1053
  data_size = np.sum([x["data"]["data"].shape[0] for x in all_data])
990
1054
  assert len(all_df) == data_size
991
1055
  idx_names = [x for x in all_df.index.names if x != "timepoint"]
1056
+ elif kind == "scalar_table":
1057
+ data_size = np.sum([x["data"]["data"].shape[0] for x in all_data])
1058
+ assert len(all_df) == data_size
1059
+ idx_names = [x for x in all_df.index.names if x != "row"]
992
1060
  else:
993
1061
  assert len(all_df) == len(all_data)
994
1062
  idx_names = all_df.index.names
@@ -1013,6 +1081,10 @@ def test_multi_output_store_and_collect(
1013
1081
  assert_array_equal(t_series.values, t_data["data"]["data"])
1014
1082
  series_names = t_series.columns.values.tolist()
1015
1083
  assert series_names == t_data["data"]["col_names"]
1084
+ elif kind == "scalar_table":
1085
+ assert_array_equal(t_series.values, t_data["data"]["data"])
1086
+ series_names = t_series.columns.values.tolist()
1087
+ assert series_names == t_data["data"]["col_names"]
1016
1088
 
1017
1089
 
1018
1090
  def test_collect_error_single_output() -> None:
junifer/storage/utils.py CHANGED
@@ -14,6 +14,15 @@ import numpy as np
14
14
  from ..utils.logging import logger, raise_error
15
15
 
16
16
 
17
+ __all__ = [
18
+ "get_dependency_version",
19
+ "process_meta",
20
+ "element_to_prefix",
21
+ "store_matrix_checks",
22
+ "matrix_to_vector",
23
+ ]
24
+
25
+
17
26
  def get_dependency_version(dependency: str) -> str:
18
27
  """Get dependency version.
19
28
 
@@ -1,4 +1,4 @@
1
- """Provide imports for testing sub-package."""
1
+ """Testing components."""
2
2
 
3
3
  # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
4
  # Synchon Mandal <s.mandal@fz-juelich.de>
@@ -6,3 +6,6 @@
6
6
 
7
7
  from . import datagrabbers
8
8
  from .utils import get_testing_data
9
+
10
+
11
+ __all__ = ["datagrabbers", "get_testing_data"]
@@ -1,4 +1,4 @@
1
- """Provide testing DataGrabbers."""
1
+ """Testing DataGrabbers."""
2
2
 
3
3
  # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
4
  # Synchon Mandal <s.mandal@fz-juelich.de>
@@ -14,6 +14,13 @@ from nilearn import datasets, image
14
14
  from ..datagrabber.base import BaseDataGrabber
15
15
 
16
16
 
17
+ __all__ = [
18
+ "OasisVBMTestingDataGrabber",
19
+ "SPMAuditoryTestingDataGrabber",
20
+ "PartlyCloudyTestingDataGrabber",
21
+ ]
22
+
23
+
17
24
  class OasisVBMTestingDataGrabber(BaseDataGrabber):
18
25
  """DataGrabber for Oasis VBM testing data.
19
26
 
@@ -181,7 +188,7 @@ class PartlyCloudyTestingDataGrabber(BaseDataGrabber):
181
188
  """Initialize the class."""
182
189
  datadir = tempfile.mkdtemp()
183
190
  # Define types
184
- types = ["BOLD", "BOLD_confounds"]
191
+ types = ["BOLD"]
185
192
  self.reduce_confounds = reduce_confounds
186
193
  self.age_group = age_group
187
194
  super().__init__(types=types, datadir=datadir)
@@ -242,10 +249,10 @@ class PartlyCloudyTestingDataGrabber(BaseDataGrabber):
242
249
  out["BOLD"] = {
243
250
  "path": Path(self._dataset["func"][i_sub]),
244
251
  "space": "MNI152NLin2009cAsym",
245
- }
246
- out["BOLD_confounds"] = {
247
- "path": Path(self._dataset["confounds"][i_sub]),
248
- "format": "fmriprep",
252
+ "confounds": {
253
+ "path": Path(self._dataset["confounds"][i_sub]),
254
+ "format": "fmriprep",
255
+ },
249
256
  }
250
257
 
251
258
  return out
@@ -28,13 +28,13 @@ def test_PartlyCloudyTestingDataGrabber() -> None:
28
28
  assert out["BOLD"]["path"].exists()
29
29
  assert out["BOLD"]["path"].is_file()
30
30
 
31
- assert "BOLD_confounds" in out
32
- assert out["BOLD_confounds"]["path"].exists()
33
- assert out["BOLD_confounds"]["path"].is_file()
34
- assert "format" in out["BOLD_confounds"]
35
- assert "fmriprep" == out["BOLD_confounds"]["format"]
31
+ assert "confounds" in out["BOLD"]
32
+ assert out["BOLD"]["confounds"]["path"].exists()
33
+ assert out["BOLD"]["confounds"]["path"].is_file()
34
+ assert "format" in out["BOLD"]["confounds"]
35
+ assert "fmriprep" == out["BOLD"]["confounds"]["format"]
36
36
 
37
37
  with PartlyCloudyTestingDataGrabber(reduce_confounds=False) as dg:
38
38
  out = dg["sub-01"]
39
- assert "format" in out["BOLD_confounds"]
40
- assert "fmriprep" == out["BOLD_confounds"]["format"]
39
+ assert "format" in out["BOLD"]["confounds"]
40
+ assert "fmriprep" == out["BOLD"]["confounds"]["format"]
junifer/testing/utils.py CHANGED
@@ -6,6 +6,9 @@
6
6
  from pathlib import Path
7
7
 
8
8
 
9
+ __all__ = ["get_testing_data"]
10
+
11
+
9
12
  def get_testing_data(fname: str) -> Path:
10
13
  """Get the path to a testing data file.
11
14
 
junifer/utils/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- """Provide imports for utils sub-package."""
1
+ """General utilities and helpers."""
2
2
 
3
3
  # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
4
  # Synchon Mandal <s.mandal@fz-juelich.de>
@@ -6,4 +6,15 @@
6
6
 
7
7
  from .fs import make_executable
8
8
  from .logging import configure_logging, logger, raise_error, warn_with_log
9
- from .helpers import run_ext_cmd
9
+ from .helpers import run_ext_cmd, deep_update
10
+
11
+
12
+ __all__ = [
13
+ "make_executable",
14
+ "configure_logging",
15
+ "logger",
16
+ "raise_error",
17
+ "warn_with_log",
18
+ "run_ext_cmd",
19
+ "deep_update",
20
+ ]
junifer/utils/fs.py CHANGED
@@ -8,6 +8,9 @@ import stat
8
8
  from pathlib import Path
9
9
 
10
10
 
11
+ __all__ = ["make_executable"]
12
+
13
+
11
14
  def make_executable(path: Path) -> None:
12
15
  """Make ``path`` executable.
13
16
 
junifer/utils/helpers.py CHANGED
@@ -3,12 +3,16 @@
3
3
  # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
4
  # License: AGPL
5
5
 
6
+ import collections.abc
6
7
  import subprocess
7
- from typing import List
8
+ from typing import Dict, List
8
9
 
9
10
  from .logging import logger, raise_error
10
11
 
11
12
 
13
+ __all__ = ["run_ext_cmd", "deep_update"]
14
+
15
+
12
16
  def run_ext_cmd(name: str, cmd: List[str]) -> None:
13
17
  """Run external command via subprocess.
14
18
 
@@ -51,3 +55,30 @@ def run_ext_cmd(name: str, cmd: List[str]) -> None:
51
55
  ),
52
56
  klass=RuntimeError,
53
57
  )
58
+
59
+
60
+ def deep_update(d: Dict, u: Dict) -> Dict:
61
+ """Deep update `d` with `u`.
62
+
63
+ From: "https://stackoverflow.com/questions/3232943/update-value-of-a-nested
64
+ -dictionary-of-varying-depth"
65
+
66
+ Parameters
67
+ ----------
68
+ d : dict
69
+ The dictionary to deep-update.
70
+ u : dict
71
+ The dictionary to deep-update `d` with.
72
+
73
+ Returns
74
+ -------
75
+ dict
76
+ The updated dictionary.
77
+
78
+ """
79
+ for k, v in u.items():
80
+ if isinstance(v, collections.abc.Mapping):
81
+ d[k] = deep_update(d.get(k, {}), v)
82
+ else:
83
+ d[k] = v
84
+ return d
junifer/utils/logging.py CHANGED
@@ -4,13 +4,16 @@
4
4
  # Synchon Mandal <s.mandal@fz-juelich.de>
5
5
  # License: AGPL
6
6
 
7
- try:
7
+ import sys
8
+
9
+
10
+ if sys.version_info < (3, 12):
8
11
  from distutils.version import LooseVersion
9
- except ImportError: # pragma: no cover
12
+ else: # pragma: no cover
10
13
  from looseversion import LooseVersion
11
14
 
12
15
  import logging
13
- import sys
16
+ import warnings
14
17
  from pathlib import Path
15
18
  from subprocess import PIPE, Popen, TimeoutExpired
16
19
  from typing import Dict, NoReturn, Optional, Type, Union
@@ -19,6 +22,16 @@ from warnings import warn
19
22
  import datalad
20
23
 
21
24
 
25
+ __all__ = [
26
+ "WrapStdOut",
27
+ "get_versions",
28
+ "log_versions",
29
+ "configure_logging",
30
+ "raise_error",
31
+ "warn_with_log",
32
+ ]
33
+
34
+
22
35
  logger = logging.getLogger("JUNIFER")
23
36
 
24
37
  # Set up datalad logger level to warning by default
@@ -32,6 +45,23 @@ _logging_types = {
32
45
  }
33
46
 
34
47
 
48
+ # Copied over from stdlib and tweaked to our use-case.
49
+ def _showwarning(message, category, filename, lineno, file=None, line=None):
50
+ s = warnings.formatwarning(message, category, filename, lineno, line)
51
+ logger.warning(str(s))
52
+
53
+
54
+ # Overwrite warnings display to integrate with logging
55
+
56
+
57
+ def capture_warnings():
58
+ """Capture warnings and log them."""
59
+ warnings.showwarning = _showwarning
60
+
61
+
62
+ capture_warnings()
63
+
64
+
35
65
  class WrapStdOut(logging.StreamHandler):
36
66
  """Dynamically wrap to sys.stdout.
37
67
 
@@ -313,5 +343,4 @@ def warn_with_log(
313
343
  The warning subclass (default RuntimeWarning).
314
344
 
315
345
  """
316
- logger.warning(msg)
317
346
  warn(msg, category=category, stacklevel=2)
@@ -145,8 +145,16 @@ def test_log_file(tmp_path: Path) -> None:
145
145
  assert any("Warn3 message" in line for line in lines)
146
146
  assert any("Error3 message" in line for line in lines)
147
147
 
148
+ # This should raise a warning (test that it was raised)
148
149
  with pytest.warns(RuntimeWarning, match=r"Warn raised"):
149
150
  warn_with_log("Warn raised")
151
+
152
+ # This should log the warning (workaround for pytest messing with logging)
153
+ from junifer.utils.logging import capture_warnings
154
+
155
+ capture_warnings()
156
+
157
+ warn_with_log("Warn raised 2")
150
158
  with pytest.raises(ValueError, match=r"Error raised"):
151
159
  raise_error("Error raised")
152
160
  with open(tmp_path / "test4.log") as f:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: junifer
3
- Version: 0.0.4.dev829
3
+ Version: 0.0.5
4
4
  Summary: JUelich NeuroImaging FEature extractoR
5
5
  Author-email: Fede Raimondo <f.raimondo@fz-juelich.de>, Synchon Mandal <s.mandal@fz-juelich.de>
6
6
  Maintainer-email: Fede Raimondo <f.raimondo@fz-juelich.de>, Synchon Mandal <s.mandal@fz-juelich.de>
@@ -29,19 +29,20 @@ Description-Content-Type: text/markdown
29
29
  License-File: LICENSE.md
30
30
  License-File: AUTHORS.rst
31
31
  Requires-Dist: click <8.2,>=8.1.3
32
- Requires-Dist: numpy <1.27,>=1.24
33
- Requires-Dist: scipy <=1.11.4,>=1.9.0
34
- Requires-Dist: datalad <0.20,>=0.15.4
35
- Requires-Dist: pandas <2.2,>=1.4.0
36
- Requires-Dist: nibabel <5.11,>=3.2.0
37
- Requires-Dist: nilearn <=0.10.2,>=0.9.0
38
- Requires-Dist: sqlalchemy <=2.1.0,>=1.4.27
32
+ Requires-Dist: numpy <2.0.0,>=1.24.0
33
+ Requires-Dist: scipy <=1.14.0,>=1.10.0
34
+ Requires-Dist: datalad <1.2.0,>=1.0.0
35
+ Requires-Dist: pandas <2.3.0,>=2.0.0
36
+ Requires-Dist: nibabel <5.3.0,>=5.2.0
37
+ Requires-Dist: nilearn <=0.10.4,>=0.10.3
38
+ Requires-Dist: sqlalchemy <=2.1.0,>=2.0.25
39
39
  Requires-Dist: ruamel.yaml <0.18,>=0.17
40
40
  Requires-Dist: h5py >=3.10
41
41
  Requires-Dist: httpx[http2] ==0.26.0
42
42
  Requires-Dist: tqdm ==4.66.1
43
43
  Requires-Dist: templateflow >=23.0.0
44
- Requires-Dist: importlib-metadata ; python_version < "3.10"
44
+ Requires-Dist: lapy <2.0.0,>=1.0.0
45
+ Requires-Dist: importlib-metadata ; python_version < "3.9"
45
46
  Requires-Dist: looseversion ==1.3.0 ; python_version >= "3.12"
46
47
  Provides-Extra: all
47
48
  Requires-Dist: bctpy ==0.6.0 ; extra == 'all'
@@ -52,14 +53,14 @@ Provides-Extra: dev
52
53
  Requires-Dist: tox ; extra == 'dev'
53
54
  Requires-Dist: pre-commit ; extra == 'dev'
54
55
  Provides-Extra: docs
55
- Requires-Dist: seaborn <0.13,>=0.11.2 ; extra == 'docs'
56
- Requires-Dist: sphinx <7.3,>=5.3.0 ; extra == 'docs'
57
- Requires-Dist: sphinx-gallery <0.15.0,>=0.11.0 ; extra == 'docs'
58
- Requires-Dist: furo <2023.10.0,>=2022.9.29 ; extra == 'docs'
59
- Requires-Dist: numpydoc <1.6,>=1.5.0 ; extra == 'docs'
60
- Requires-Dist: julearn <0.4,>=0.3.0 ; extra == 'docs'
56
+ Requires-Dist: seaborn <0.14.0,>=0.13.0 ; extra == 'docs'
57
+ Requires-Dist: sphinx <7.4.0,>=7.3.0 ; extra == 'docs'
58
+ Requires-Dist: sphinx-gallery <0.17.0,>=0.15.0 ; extra == 'docs'
59
+ Requires-Dist: furo <2024.6.0,>=2024.4.27 ; extra == 'docs'
60
+ Requires-Dist: numpydoc <1.8.0,>=1.6.0 ; extra == 'docs'
61
+ Requires-Dist: julearn ==0.3.3 ; extra == 'docs'
61
62
  Requires-Dist: sphinx-copybutton <0.5.3,>=0.5.1 ; extra == 'docs'
62
- Requires-Dist: towncrier <23.7,>=22.12.0 ; extra == 'docs'
63
+ Requires-Dist: towncrier <23.12.0,>=23.10.0 ; extra == 'docs'
63
64
  Requires-Dist: sphinxcontrib-mermaid <0.10,>=0.8.1 ; extra == 'docs'
64
65
  Provides-Extra: neurokit2
65
66
  Requires-Dist: neurokit2 >=0.1.7 ; extra == 'neurokit2'