junifer 0.0.3.dev186__py3-none-any.whl → 0.0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- junifer/_version.py +14 -2
- junifer/api/cli.py +162 -17
- junifer/api/functions.py +87 -419
- junifer/api/parser.py +24 -0
- junifer/api/queue_context/__init__.py +8 -0
- junifer/api/queue_context/gnu_parallel_local_adapter.py +258 -0
- junifer/api/queue_context/htcondor_adapter.py +365 -0
- junifer/api/queue_context/queue_context_adapter.py +60 -0
- junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +192 -0
- junifer/api/queue_context/tests/test_htcondor_adapter.py +257 -0
- junifer/api/res/afni/run_afni_docker.sh +6 -6
- junifer/api/res/ants/ResampleImage +3 -0
- junifer/api/res/ants/antsApplyTransforms +3 -0
- junifer/api/res/ants/antsApplyTransformsToPoints +3 -0
- junifer/api/res/ants/run_ants_docker.sh +39 -0
- junifer/api/res/fsl/applywarp +3 -0
- junifer/api/res/fsl/flirt +3 -0
- junifer/api/res/fsl/img2imgcoord +3 -0
- junifer/api/res/fsl/run_fsl_docker.sh +39 -0
- junifer/api/res/fsl/std2imgcoord +3 -0
- junifer/api/res/run_conda.sh +4 -4
- junifer/api/res/run_venv.sh +22 -0
- junifer/api/tests/data/partly_cloudy_agg_mean_tian.yml +16 -0
- junifer/api/tests/test_api_utils.py +21 -3
- junifer/api/tests/test_cli.py +232 -9
- junifer/api/tests/test_functions.py +211 -439
- junifer/api/tests/test_parser.py +1 -1
- junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +6 -1
- junifer/configs/juseless/datagrabbers/camcan_vbm.py +6 -1
- junifer/configs/juseless/datagrabbers/ixi_vbm.py +6 -1
- junifer/configs/juseless/datagrabbers/tests/test_ucla.py +8 -8
- junifer/configs/juseless/datagrabbers/ucla.py +44 -26
- junifer/configs/juseless/datagrabbers/ukb_vbm.py +6 -1
- junifer/data/VOIs/meta/AutobiographicalMemory_VOIs.txt +23 -0
- junifer/data/VOIs/meta/Power2013_MNI_VOIs.tsv +264 -0
- junifer/data/__init__.py +4 -0
- junifer/data/coordinates.py +298 -31
- junifer/data/masks.py +360 -28
- junifer/data/parcellations.py +621 -188
- junifer/data/template_spaces.py +190 -0
- junifer/data/tests/test_coordinates.py +34 -3
- junifer/data/tests/test_data_utils.py +1 -0
- junifer/data/tests/test_masks.py +202 -86
- junifer/data/tests/test_parcellations.py +266 -55
- junifer/data/tests/test_template_spaces.py +104 -0
- junifer/data/utils.py +4 -2
- junifer/datagrabber/__init__.py +1 -0
- junifer/datagrabber/aomic/id1000.py +111 -70
- junifer/datagrabber/aomic/piop1.py +116 -53
- junifer/datagrabber/aomic/piop2.py +116 -53
- junifer/datagrabber/aomic/tests/test_id1000.py +27 -27
- junifer/datagrabber/aomic/tests/test_piop1.py +27 -27
- junifer/datagrabber/aomic/tests/test_piop2.py +27 -27
- junifer/datagrabber/base.py +62 -10
- junifer/datagrabber/datalad_base.py +0 -2
- junifer/datagrabber/dmcc13_benchmark.py +372 -0
- junifer/datagrabber/hcp1200/datalad_hcp1200.py +5 -0
- junifer/datagrabber/hcp1200/hcp1200.py +30 -13
- junifer/datagrabber/pattern.py +133 -27
- junifer/datagrabber/pattern_datalad.py +111 -13
- junifer/datagrabber/tests/test_base.py +57 -6
- junifer/datagrabber/tests/test_datagrabber_utils.py +204 -76
- junifer/datagrabber/tests/test_datalad_base.py +0 -6
- junifer/datagrabber/tests/test_dmcc13_benchmark.py +256 -0
- junifer/datagrabber/tests/test_multiple.py +43 -10
- junifer/datagrabber/tests/test_pattern.py +125 -178
- junifer/datagrabber/tests/test_pattern_datalad.py +44 -25
- junifer/datagrabber/utils.py +151 -16
- junifer/datareader/default.py +36 -10
- junifer/external/nilearn/junifer_nifti_spheres_masker.py +6 -0
- junifer/markers/base.py +25 -16
- junifer/markers/collection.py +35 -16
- junifer/markers/complexity/__init__.py +27 -0
- junifer/markers/complexity/complexity_base.py +149 -0
- junifer/markers/complexity/hurst_exponent.py +136 -0
- junifer/markers/complexity/multiscale_entropy_auc.py +140 -0
- junifer/markers/complexity/perm_entropy.py +132 -0
- junifer/markers/complexity/range_entropy.py +136 -0
- junifer/markers/complexity/range_entropy_auc.py +145 -0
- junifer/markers/complexity/sample_entropy.py +134 -0
- junifer/markers/complexity/tests/test_complexity_base.py +19 -0
- junifer/markers/complexity/tests/test_hurst_exponent.py +69 -0
- junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +68 -0
- junifer/markers/complexity/tests/test_perm_entropy.py +68 -0
- junifer/markers/complexity/tests/test_range_entropy.py +69 -0
- junifer/markers/complexity/tests/test_range_entropy_auc.py +69 -0
- junifer/markers/complexity/tests/test_sample_entropy.py +68 -0
- junifer/markers/complexity/tests/test_weighted_perm_entropy.py +68 -0
- junifer/markers/complexity/weighted_perm_entropy.py +133 -0
- junifer/markers/falff/_afni_falff.py +153 -0
- junifer/markers/falff/_junifer_falff.py +142 -0
- junifer/markers/falff/falff_base.py +91 -84
- junifer/markers/falff/falff_parcels.py +61 -45
- junifer/markers/falff/falff_spheres.py +64 -48
- junifer/markers/falff/tests/test_falff_parcels.py +89 -121
- junifer/markers/falff/tests/test_falff_spheres.py +92 -127
- junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +1 -0
- junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +1 -0
- junifer/markers/functional_connectivity/functional_connectivity_base.py +1 -0
- junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +46 -44
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +34 -39
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +40 -52
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +62 -70
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +99 -85
- junifer/markers/parcel_aggregation.py +60 -38
- junifer/markers/reho/_afni_reho.py +192 -0
- junifer/markers/reho/_junifer_reho.py +281 -0
- junifer/markers/reho/reho_base.py +69 -34
- junifer/markers/reho/reho_parcels.py +26 -16
- junifer/markers/reho/reho_spheres.py +23 -9
- junifer/markers/reho/tests/test_reho_parcels.py +93 -92
- junifer/markers/reho/tests/test_reho_spheres.py +88 -86
- junifer/markers/sphere_aggregation.py +54 -9
- junifer/markers/temporal_snr/temporal_snr_base.py +1 -0
- junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +38 -37
- junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +34 -38
- junifer/markers/tests/test_collection.py +43 -42
- junifer/markers/tests/test_ets_rss.py +29 -37
- junifer/markers/tests/test_parcel_aggregation.py +587 -468
- junifer/markers/tests/test_sphere_aggregation.py +209 -157
- junifer/markers/utils.py +2 -40
- junifer/onthefly/read_transform.py +13 -6
- junifer/pipeline/__init__.py +1 -0
- junifer/pipeline/pipeline_step_mixin.py +105 -41
- junifer/pipeline/registry.py +17 -0
- junifer/pipeline/singleton.py +45 -0
- junifer/pipeline/tests/test_pipeline_step_mixin.py +139 -51
- junifer/pipeline/tests/test_update_meta_mixin.py +1 -0
- junifer/pipeline/tests/test_workdir_manager.py +104 -0
- junifer/pipeline/update_meta_mixin.py +8 -2
- junifer/pipeline/utils.py +154 -15
- junifer/pipeline/workdir_manager.py +246 -0
- junifer/preprocess/__init__.py +3 -0
- junifer/preprocess/ants/__init__.py +4 -0
- junifer/preprocess/ants/ants_apply_transforms_warper.py +185 -0
- junifer/preprocess/ants/tests/test_ants_apply_transforms_warper.py +56 -0
- junifer/preprocess/base.py +96 -69
- junifer/preprocess/bold_warper.py +265 -0
- junifer/preprocess/confounds/fmriprep_confound_remover.py +91 -134
- junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +106 -111
- junifer/preprocess/fsl/__init__.py +4 -0
- junifer/preprocess/fsl/apply_warper.py +179 -0
- junifer/preprocess/fsl/tests/test_apply_warper.py +45 -0
- junifer/preprocess/tests/test_bold_warper.py +159 -0
- junifer/preprocess/tests/test_preprocess_base.py +6 -6
- junifer/preprocess/warping/__init__.py +6 -0
- junifer/preprocess/warping/_ants_warper.py +167 -0
- junifer/preprocess/warping/_fsl_warper.py +109 -0
- junifer/preprocess/warping/space_warper.py +213 -0
- junifer/preprocess/warping/tests/test_space_warper.py +198 -0
- junifer/stats.py +18 -4
- junifer/storage/base.py +9 -1
- junifer/storage/hdf5.py +8 -3
- junifer/storage/pandas_base.py +2 -1
- junifer/storage/sqlite.py +1 -0
- junifer/storage/tests/test_hdf5.py +2 -1
- junifer/storage/tests/test_sqlite.py +8 -8
- junifer/storage/tests/test_utils.py +6 -6
- junifer/storage/utils.py +1 -0
- junifer/testing/datagrabbers.py +11 -7
- junifer/testing/utils.py +1 -0
- junifer/tests/test_stats.py +2 -0
- junifer/utils/__init__.py +1 -0
- junifer/utils/helpers.py +53 -0
- junifer/utils/logging.py +14 -3
- junifer/utils/tests/test_helpers.py +35 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/METADATA +59 -28
- junifer-0.0.4.dist-info/RECORD +257 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/WHEEL +1 -1
- junifer/markers/falff/falff_estimator.py +0 -334
- junifer/markers/falff/tests/test_falff_estimator.py +0 -238
- junifer/markers/reho/reho_estimator.py +0 -515
- junifer/markers/reho/tests/test_reho_estimator.py +0 -260
- junifer-0.0.3.dev186.dist-info/RECORD +0 -199
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/top_level.txt +0 -0
@@ -44,7 +44,7 @@ class PipelineStepMixin:
|
|
44
44
|
raise_error(
|
45
45
|
msg="Concrete classes need to implement validate_input().",
|
46
46
|
klass=NotImplementedError,
|
47
|
-
)
|
47
|
+
) # pragma: no cover
|
48
48
|
|
49
49
|
def get_output_type(self, input_type: str) -> str:
|
50
50
|
"""Get output type.
|
@@ -63,10 +63,11 @@ class PipelineStepMixin:
|
|
63
63
|
raise_error(
|
64
64
|
msg="Concrete classes need to implement get_output_type().",
|
65
65
|
klass=NotImplementedError,
|
66
|
-
)
|
66
|
+
) # pragma: no cover
|
67
67
|
|
68
68
|
def _fit_transform(
|
69
|
-
self,
|
69
|
+
self,
|
70
|
+
input: Dict[str, Dict],
|
70
71
|
) -> Dict[str, Dict]:
|
71
72
|
"""Fit and transform.
|
72
73
|
|
@@ -74,8 +75,6 @@ class PipelineStepMixin:
|
|
74
75
|
----------
|
75
76
|
input : dict
|
76
77
|
The Junifer Data object.
|
77
|
-
**kwargs : dict
|
78
|
-
Extra keyword arguments.
|
79
78
|
|
80
79
|
Returns
|
81
80
|
-------
|
@@ -86,7 +85,7 @@ class PipelineStepMixin:
|
|
86
85
|
raise_error(
|
87
86
|
msg="Concrete classes need to implement _fit_transform().",
|
88
87
|
klass=NotImplementedError,
|
89
|
-
)
|
88
|
+
) # pragma: no cover
|
90
89
|
|
91
90
|
def validate(self, input: List[str]) -> List[str]:
|
92
91
|
"""Validate the the pipeline step.
|
@@ -101,44 +100,109 @@ class PipelineStepMixin:
|
|
101
100
|
list of str
|
102
101
|
The output of the pipeline step.
|
103
102
|
|
104
|
-
Raises
|
105
|
-
------
|
106
|
-
ValueError
|
107
|
-
If the pipeline step object is missing dependencies required for
|
108
|
-
its working or if the input does not have the required data.
|
109
|
-
|
110
103
|
"""
|
111
|
-
# Check if _DEPENDENCIES attribute is found;
|
112
|
-
# (markers and preprocessors will have them but not datareaders
|
113
|
-
# as of now)
|
114
|
-
dependencies_not_found = []
|
115
|
-
if hasattr(self, "_DEPENDENCIES"):
|
116
|
-
# Check if dependencies are importable
|
117
|
-
for dependency in self._DEPENDENCIES: # type: ignore
|
118
|
-
# First perform an easy check
|
119
|
-
if find_spec(dependency) is None:
|
120
|
-
# Then check mapped names
|
121
|
-
if dependency not in list(
|
122
|
-
chain.from_iterable(packages_distributions().values())
|
123
|
-
):
|
124
|
-
dependencies_not_found.append(dependency)
|
125
|
-
# Raise error if any dependency is not found
|
126
|
-
if dependencies_not_found:
|
127
|
-
raise_error(
|
128
|
-
msg=f"{dependencies_not_found} are not installed but are "
|
129
|
-
"required for using {self.name}.",
|
130
|
-
klass=ImportError,
|
131
|
-
)
|
132
|
-
# Check if _EXT_DEPENDENCIES attribute is found;
|
133
|
-
# (some markers might have them like ReHo-family)
|
134
|
-
if hasattr(self, "_EXT_DEPENDENCIES"):
|
135
|
-
for dependency in self._EXT_DEPENDENCIES: # type: ignore
|
136
|
-
out = check_ext_dependencies(**dependency)
|
137
|
-
if getattr(self, f"use_{dependency['name']}", None) is None:
|
138
|
-
# Set attribute for using external tools
|
139
|
-
setattr(self, f"use_{dependency['name']}", out)
|
140
104
|
|
105
|
+
def _check_dependencies(obj) -> None:
|
106
|
+
"""Check obj._DEPENDENCIES.
|
107
|
+
|
108
|
+
Parameters
|
109
|
+
----------
|
110
|
+
obj : object
|
111
|
+
Object to check _DEPENDENCIES of.
|
112
|
+
|
113
|
+
Raises
|
114
|
+
------
|
115
|
+
ImportError
|
116
|
+
If the pipeline step object is missing dependencies required
|
117
|
+
for its working.
|
118
|
+
|
119
|
+
"""
|
120
|
+
# Check if _DEPENDENCIES attribute is found;
|
121
|
+
# (markers and preprocessors will have them but not datareaders
|
122
|
+
# as of now)
|
123
|
+
dependencies_not_found = []
|
124
|
+
if hasattr(obj, "_DEPENDENCIES"):
|
125
|
+
# Check if dependencies are importable
|
126
|
+
for dependency in obj._DEPENDENCIES:
|
127
|
+
# First perform an easy check
|
128
|
+
if find_spec(dependency) is None:
|
129
|
+
# Then check mapped names
|
130
|
+
if dependency not in list(
|
131
|
+
chain.from_iterable(
|
132
|
+
packages_distributions().values()
|
133
|
+
)
|
134
|
+
):
|
135
|
+
dependencies_not_found.append(dependency)
|
136
|
+
# Raise error if any dependency is not found
|
137
|
+
if dependencies_not_found:
|
138
|
+
raise_error(
|
139
|
+
msg=(
|
140
|
+
f"{dependencies_not_found} are not installed but are "
|
141
|
+
f"required for using {obj.__class__.__name__}."
|
142
|
+
),
|
143
|
+
klass=ImportError,
|
144
|
+
)
|
145
|
+
|
146
|
+
def _check_ext_dependencies(obj) -> None:
|
147
|
+
"""Check obj._EXT_DEPENDENCIES.
|
148
|
+
|
149
|
+
Parameters
|
150
|
+
----------
|
151
|
+
obj : object
|
152
|
+
Object to check _EXT_DEPENDENCIES of.
|
153
|
+
|
154
|
+
"""
|
155
|
+
# Check if _EXT_DEPENDENCIES attribute is found;
|
156
|
+
# (some markers and preprocessors might have them)
|
157
|
+
if hasattr(obj, "_EXT_DEPENDENCIES"):
|
158
|
+
for dependency in obj._EXT_DEPENDENCIES:
|
159
|
+
check_ext_dependencies(**dependency)
|
160
|
+
|
161
|
+
def _check_conditional_dependencies(obj) -> None:
|
162
|
+
"""Check obj._CONDITIONAL_DEPENDENCIES.
|
163
|
+
|
164
|
+
Parameters
|
165
|
+
----------
|
166
|
+
obj : object
|
167
|
+
Object to check _CONDITIONAL_DEPENDENCIES of.
|
168
|
+
|
169
|
+
Raises
|
170
|
+
------
|
171
|
+
AttributeError
|
172
|
+
If the pipeline step object does not have `using` as a
|
173
|
+
constructor parameter.
|
174
|
+
|
175
|
+
"""
|
176
|
+
# Check if _CONDITIONAL_DEPENDENCIES attribute is found;
|
177
|
+
# (some markers and preprocessors might have them)
|
178
|
+
if hasattr(obj, "_CONDITIONAL_DEPENDENCIES"):
|
179
|
+
if not hasattr(obj, "using"):
|
180
|
+
raise_error(
|
181
|
+
msg=(
|
182
|
+
f"The pipeline step: {obj.__class__.__name__} has "
|
183
|
+
"`_CONDITIONAL_DEPENDENCIES` but does not have "
|
184
|
+
"`using` as a constructor parameter"
|
185
|
+
),
|
186
|
+
klass=AttributeError,
|
187
|
+
)
|
188
|
+
else:
|
189
|
+
for dependency in obj._CONDITIONAL_DEPENDENCIES:
|
190
|
+
if dependency["using"] == obj.using:
|
191
|
+
depends_on = dependency["depends_on"]
|
192
|
+
# Check dependencies
|
193
|
+
_check_dependencies(depends_on)
|
194
|
+
# Check external dependencies
|
195
|
+
_check_ext_dependencies(depends_on)
|
196
|
+
|
197
|
+
# Check dependencies
|
198
|
+
_check_dependencies(self)
|
199
|
+
# Check external dependencies
|
200
|
+
_check_ext_dependencies(self)
|
201
|
+
# Check conditional dependencies
|
202
|
+
_check_conditional_dependencies(self)
|
203
|
+
# Validate input
|
141
204
|
fit_input = self.validate_input(input=input)
|
205
|
+
# Validate output type
|
142
206
|
outputs = [self.get_output_type(t_input) for t_input in fit_input]
|
143
207
|
return outputs
|
144
208
|
|
junifer/pipeline/registry.py
CHANGED
@@ -41,6 +41,11 @@ def register(step: str, name: str, klass: type) -> None:
|
|
41
41
|
klass : class
|
42
42
|
Class to be registered.
|
43
43
|
|
44
|
+
Raises
|
45
|
+
------
|
46
|
+
ValueError
|
47
|
+
If the ``step`` is invalid.
|
48
|
+
|
44
49
|
"""
|
45
50
|
# Verify step
|
46
51
|
if step not in _VALID_STEPS:
|
@@ -63,6 +68,11 @@ def get_step_names(step: str) -> List[str]:
|
|
63
68
|
list
|
64
69
|
List of registered function names.
|
65
70
|
|
71
|
+
Raises
|
72
|
+
------
|
73
|
+
ValueError
|
74
|
+
If the ``step`` is invalid.
|
75
|
+
|
66
76
|
"""
|
67
77
|
# Verify step
|
68
78
|
if step not in _VALID_STEPS:
|
@@ -86,6 +96,11 @@ def get_class(step: str, name: str) -> type:
|
|
86
96
|
class
|
87
97
|
Registered function class.
|
88
98
|
|
99
|
+
Raises
|
100
|
+
------
|
101
|
+
ValueError
|
102
|
+
If the ``step`` or ``name`` is invalid.
|
103
|
+
|
89
104
|
"""
|
90
105
|
# Verify step
|
91
106
|
if step not in _VALID_STEPS:
|
@@ -123,6 +138,8 @@ def build(
|
|
123
138
|
|
124
139
|
Raises
|
125
140
|
------
|
141
|
+
RuntimeError
|
142
|
+
If there is a problem creating the instance.
|
126
143
|
ValueError
|
127
144
|
If the created object with the given name is not an instance of the
|
128
145
|
base class.
|
@@ -0,0 +1,45 @@
|
|
1
|
+
"""Provide a singleton class to be used by pipeline components."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
from typing import Any, Dict, Type
|
7
|
+
|
8
|
+
|
9
|
+
def singleton(cls: Type) -> Type:
|
10
|
+
"""Make a class singleton.
|
11
|
+
|
12
|
+
Parameters
|
13
|
+
----------
|
14
|
+
cls : class
|
15
|
+
The class to designate as singleton.
|
16
|
+
|
17
|
+
Returns
|
18
|
+
-------
|
19
|
+
class
|
20
|
+
The only instance of the class.
|
21
|
+
|
22
|
+
"""
|
23
|
+
instances: Dict = {}
|
24
|
+
|
25
|
+
def get_instance(*args: Any, **kwargs: Any) -> Type:
|
26
|
+
"""Get the only instance for a class.
|
27
|
+
|
28
|
+
Parameters
|
29
|
+
----------
|
30
|
+
*args : tuple
|
31
|
+
The positional arguments to pass to the class.
|
32
|
+
**kwargs : dict
|
33
|
+
The keyword arguments to pass to the class.
|
34
|
+
|
35
|
+
Returns
|
36
|
+
-------
|
37
|
+
class
|
38
|
+
The only instance of the class.
|
39
|
+
|
40
|
+
"""
|
41
|
+
if cls not in instances:
|
42
|
+
instances[cls] = cls(*args, **kwargs)
|
43
|
+
return instances[cls]
|
44
|
+
|
45
|
+
return get_instance
|
@@ -1,11 +1,11 @@
|
|
1
|
-
"""Provide tests for
|
1
|
+
"""Provide tests for PipelineStepMixin."""
|
2
2
|
|
3
3
|
# Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
|
4
4
|
# Synchon Mandal <s.mandal@fz-juelich.de>
|
5
5
|
# License: AGPL
|
6
6
|
|
7
7
|
import warnings
|
8
|
-
from typing import ClassVar, Dict, List, Set, Union
|
8
|
+
from typing import ClassVar, Dict, List, Set, Type, Union
|
9
9
|
|
10
10
|
import pytest
|
11
11
|
|
@@ -13,24 +13,13 @@ from junifer.pipeline.pipeline_step_mixin import PipelineStepMixin
|
|
13
13
|
from junifer.pipeline.utils import _check_afni
|
14
14
|
|
15
15
|
|
16
|
-
def
|
17
|
-
"""Test
|
18
|
-
mixin = PipelineStepMixin()
|
19
|
-
with pytest.raises(NotImplementedError):
|
20
|
-
mixin.validate_input([])
|
21
|
-
with pytest.raises(NotImplementedError):
|
22
|
-
mixin.get_output_type("")
|
23
|
-
with pytest.raises(NotImplementedError):
|
24
|
-
mixin._fit_transform({})
|
25
|
-
|
26
|
-
|
27
|
-
def test_pipeline_step_mixin_validate_correct_dependencies() -> None:
|
28
|
-
"""Test validate with correct dependencies."""
|
16
|
+
def test_PipelineStepMixin_correct_dependencies() -> None:
|
17
|
+
"""Test fit-transform with correct dependencies."""
|
29
18
|
|
30
19
|
class CorrectMixer(PipelineStepMixin):
|
31
20
|
"""Test class for validation."""
|
32
21
|
|
33
|
-
_DEPENDENCIES: ClassVar[Set[str]] = {"
|
22
|
+
_DEPENDENCIES: ClassVar[Set[str]] = {"math"}
|
34
23
|
|
35
24
|
def validate_input(self, input: List[str]) -> List[str]:
|
36
25
|
return input
|
@@ -38,15 +27,15 @@ def test_pipeline_step_mixin_validate_correct_dependencies() -> None:
|
|
38
27
|
def get_output_type(self, input_type: str) -> str:
|
39
28
|
return input_type
|
40
29
|
|
41
|
-
def
|
30
|
+
def _fit_transform(self, input: Dict[str, Dict]) -> Dict[str, Dict]:
|
42
31
|
return {"input": input}
|
43
32
|
|
44
33
|
mixer = CorrectMixer()
|
45
|
-
mixer.
|
34
|
+
mixer.fit_transform({})
|
46
35
|
|
47
36
|
|
48
|
-
def
|
49
|
-
"""Test
|
37
|
+
def test_PipelineStepMixin_incorrect_dependencies() -> None:
|
38
|
+
"""Test fit-transform with incorrect dependencies."""
|
50
39
|
|
51
40
|
class IncorrectMixer(PipelineStepMixin):
|
52
41
|
"""Test class for validation."""
|
@@ -59,26 +48,24 @@ def test_pipeline_step_mixin_validate_incorrect_dependencies() -> None:
|
|
59
48
|
def get_output_type(self, input_type: str) -> str:
|
60
49
|
return input_type
|
61
50
|
|
62
|
-
def
|
51
|
+
def _fit_transform(self, input: Dict[str, Dict]) -> Dict[str, Dict]:
|
63
52
|
return {"input": input}
|
64
53
|
|
65
54
|
mixer = IncorrectMixer()
|
66
55
|
with pytest.raises(ImportError, match="not installed"):
|
67
|
-
mixer.
|
56
|
+
mixer.fit_transform({})
|
68
57
|
|
69
58
|
|
70
59
|
@pytest.mark.skipif(
|
71
|
-
_check_afni() is False, reason="requires
|
60
|
+
_check_afni() is False, reason="requires AFNI to be in PATH"
|
72
61
|
)
|
73
|
-
def
|
74
|
-
"""Test
|
62
|
+
def test_PipelineStepMixin_correct_ext_dependencies() -> None:
|
63
|
+
"""Test fit-transform with correct external dependencies."""
|
75
64
|
|
76
65
|
class CorrectMixer(PipelineStepMixin):
|
77
66
|
"""Test class for validation."""
|
78
67
|
|
79
|
-
_EXT_DEPENDENCIES: ClassVar[List[Dict[str,
|
80
|
-
{"name": "afni", "optional": False}
|
81
|
-
]
|
68
|
+
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, str]]] = [{"name": "afni"}]
|
82
69
|
|
83
70
|
def validate_input(self, input: List[str]) -> List[str]:
|
84
71
|
return input
|
@@ -86,25 +73,25 @@ def test_pipeline_step_mixin_validate_correct_ext_dependencies() -> None:
|
|
86
73
|
def get_output_type(self, input_type: str) -> str:
|
87
74
|
return input_type
|
88
75
|
|
89
|
-
def
|
76
|
+
def _fit_transform(self, input: Dict[str, Dict]) -> Dict[str, Dict]:
|
90
77
|
return {"input": input}
|
91
78
|
|
92
79
|
mixer = CorrectMixer()
|
93
|
-
mixer.
|
80
|
+
mixer.fit_transform({})
|
94
81
|
|
95
82
|
|
96
83
|
@pytest.mark.skipif(
|
97
|
-
_check_afni() is False, reason="requires
|
84
|
+
_check_afni() is False, reason="requires AFNI to be in PATH"
|
98
85
|
)
|
99
|
-
def
|
100
|
-
"""Test
|
86
|
+
def test_PipelineStepMixin_ext_deps_correct_commands() -> None:
|
87
|
+
"""Test fit-transform with correct external dependency commands."""
|
101
88
|
|
102
89
|
class CorrectMixer(PipelineStepMixin):
|
103
90
|
"""Test class for validation."""
|
104
91
|
|
105
|
-
_EXT_DEPENDENCIES: ClassVar[
|
106
|
-
|
107
|
-
]
|
92
|
+
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
|
93
|
+
{"name": "afni", "commands": ["3dReHo"]}
|
94
|
+
]
|
108
95
|
|
109
96
|
def validate_input(self, input: List[str]) -> List[str]:
|
110
97
|
return input
|
@@ -112,27 +99,27 @@ def test_pipeline_step_mixin_validate_ext_deps_correct_commands() -> None:
|
|
112
99
|
def get_output_type(self, input_type: str) -> str:
|
113
100
|
return input_type
|
114
101
|
|
115
|
-
def
|
102
|
+
def _fit_transform(self, input: Dict[str, Dict]) -> Dict[str, Dict]:
|
116
103
|
return {"input": input}
|
117
104
|
|
118
105
|
mixer = CorrectMixer()
|
119
106
|
with warnings.catch_warnings():
|
120
107
|
warnings.simplefilter("error")
|
121
|
-
mixer.
|
108
|
+
mixer.fit_transform({})
|
122
109
|
|
123
110
|
|
124
111
|
@pytest.mark.skipif(
|
125
|
-
_check_afni() is False, reason="requires
|
112
|
+
_check_afni() is False, reason="requires AFNI to be in PATH"
|
126
113
|
)
|
127
|
-
def
|
128
|
-
"""Test
|
114
|
+
def test_PipelineStepMixin_ext_deps_incorrect_commands() -> None:
|
115
|
+
"""Test fit-transform with inccorrect external dependency commands."""
|
129
116
|
|
130
117
|
class CorrectMixer(PipelineStepMixin):
|
131
118
|
"""Test class for validation."""
|
132
119
|
|
133
|
-
_EXT_DEPENDENCIES: ClassVar[
|
134
|
-
|
135
|
-
]
|
120
|
+
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
|
121
|
+
{"name": "afni", "commands": ["3d"]}
|
122
|
+
]
|
136
123
|
|
137
124
|
def validate_input(self, input: List[str]) -> List[str]:
|
138
125
|
return input
|
@@ -140,16 +127,16 @@ def test_pipeline_step_mixin_validate_ext_deps_incorrect_commands() -> None:
|
|
140
127
|
def get_output_type(self, input_type: str) -> str:
|
141
128
|
return input_type
|
142
129
|
|
143
|
-
def
|
130
|
+
def _fit_transform(self, input: Dict[str, Dict]) -> Dict[str, Dict]:
|
144
131
|
return {"input": input}
|
145
132
|
|
146
133
|
mixer = CorrectMixer()
|
147
134
|
with pytest.warns(RuntimeWarning, match="AFNI is installed"):
|
148
|
-
mixer.
|
135
|
+
mixer.fit_transform({})
|
149
136
|
|
150
137
|
|
151
|
-
def
|
152
|
-
"""Test
|
138
|
+
def test_PipelineStepMixin_incorrect_ext_dependencies() -> None:
|
139
|
+
"""Test fit-transform with incorrect external dependencies."""
|
153
140
|
|
154
141
|
class IncorrectMixer(PipelineStepMixin):
|
155
142
|
"""Test class for validation."""
|
@@ -164,9 +151,110 @@ def test_pipeline_step_mixin_validate_incorrect_ext_dependencies() -> None:
|
|
164
151
|
def get_output_type(self, input_type: str) -> str:
|
165
152
|
return input_type
|
166
153
|
|
167
|
-
def
|
154
|
+
def _fit_transform(self, input: Dict[str, Dict]) -> Dict[str, Dict]:
|
155
|
+
return {"input": input}
|
156
|
+
|
157
|
+
mixer = IncorrectMixer()
|
158
|
+
with pytest.raises(ValueError, match="Invalid value"):
|
159
|
+
mixer.fit_transform({})
|
160
|
+
|
161
|
+
|
162
|
+
def test_PipelineStepMixin_correct_conditional_dependencies() -> None:
|
163
|
+
"""Test fit-transform with correct conditional dependencies."""
|
164
|
+
|
165
|
+
class Dependency:
|
166
|
+
_DEPENDENCIES: ClassVar[Set[str]] = {"math"}
|
167
|
+
|
168
|
+
class CorrectMixer(PipelineStepMixin):
|
169
|
+
"""Test class for validation."""
|
170
|
+
|
171
|
+
_CONDITIONAL_DEPENDENCIES: ClassVar[
|
172
|
+
List[Dict[str, Union[str, Type]]]
|
173
|
+
] = [
|
174
|
+
{
|
175
|
+
"using": "math",
|
176
|
+
"depends_on": Dependency,
|
177
|
+
},
|
178
|
+
]
|
179
|
+
|
180
|
+
using = "math"
|
181
|
+
|
182
|
+
def validate_input(self, input: List[str]) -> List[str]:
|
183
|
+
return input
|
184
|
+
|
185
|
+
def get_output_type(self, input_type: str) -> str:
|
186
|
+
return input_type
|
187
|
+
|
188
|
+
def _fit_transform(self, input: Dict[str, Dict]) -> Dict[str, Dict]:
|
189
|
+
return {"input": input}
|
190
|
+
|
191
|
+
mixer = CorrectMixer()
|
192
|
+
mixer.fit_transform({})
|
193
|
+
|
194
|
+
|
195
|
+
def test_PipelineStepMixin_incorrect_conditional_dependencies() -> None:
|
196
|
+
"""Test fit-transform with incorrect conditional dependencies."""
|
197
|
+
|
198
|
+
class Dependency:
|
199
|
+
_DEPENDENCIES: ClassVar[Set[str]] = {"math"}
|
200
|
+
|
201
|
+
class IncorrectMixer(PipelineStepMixin):
|
202
|
+
"""Test class for validation."""
|
203
|
+
|
204
|
+
_CONDITIONAL_DEPENDENCIES: ClassVar[
|
205
|
+
List[Dict[str, Union[str, Type]]]
|
206
|
+
] = [
|
207
|
+
{
|
208
|
+
"using": "math",
|
209
|
+
"depends_on": Dependency,
|
210
|
+
},
|
211
|
+
]
|
212
|
+
|
213
|
+
def validate_input(self, input: List[str]) -> List[str]:
|
214
|
+
return input
|
215
|
+
|
216
|
+
def get_output_type(self, input_type: str) -> str:
|
217
|
+
return input_type
|
218
|
+
|
219
|
+
def _fit_transform(self, input: Dict[str, Dict]) -> Dict[str, Dict]:
|
168
220
|
return {"input": input}
|
169
221
|
|
170
222
|
mixer = IncorrectMixer()
|
171
|
-
with pytest.raises(
|
172
|
-
mixer.
|
223
|
+
with pytest.raises(AttributeError, match="`using`"):
|
224
|
+
mixer.fit_transform({})
|
225
|
+
|
226
|
+
|
227
|
+
@pytest.mark.skipif(
|
228
|
+
_check_afni() is False, reason="requires AFNI to be in PATH"
|
229
|
+
)
|
230
|
+
def test_PipelineStepMixin_correct_conditional_ext_dependencies() -> None:
|
231
|
+
"""Test fit-transform with correct conditional external dependencies."""
|
232
|
+
|
233
|
+
class ExternalDependency:
|
234
|
+
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, str]]] = [{"name": "afni"}]
|
235
|
+
|
236
|
+
class CorrectMixer(PipelineStepMixin):
|
237
|
+
"""Test class for validation."""
|
238
|
+
|
239
|
+
_CONDITIONAL_DEPENDENCIES: ClassVar[
|
240
|
+
List[Dict[str, Union[str, Type]]]
|
241
|
+
] = [
|
242
|
+
{
|
243
|
+
"using": "afni",
|
244
|
+
"depends_on": ExternalDependency,
|
245
|
+
},
|
246
|
+
]
|
247
|
+
|
248
|
+
using = "afni"
|
249
|
+
|
250
|
+
def validate_input(self, input: List[str]) -> List[str]:
|
251
|
+
return input
|
252
|
+
|
253
|
+
def get_output_type(self, input_type: str) -> str:
|
254
|
+
return input_type
|
255
|
+
|
256
|
+
def _fit_transform(self, input: Dict[str, Dict]) -> Dict[str, Dict]:
|
257
|
+
return {"input": input}
|
258
|
+
|
259
|
+
mixer = CorrectMixer()
|
260
|
+
mixer.fit_transform({})
|
@@ -0,0 +1,104 @@
|
|
1
|
+
"""Provide tests for WorkDirManager."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
from pathlib import Path
|
7
|
+
|
8
|
+
from junifer.pipeline import WorkDirManager
|
9
|
+
|
10
|
+
|
11
|
+
def test_workdir_manager_singleton() -> None:
|
12
|
+
"""Test that WorkDirManager is a singleton."""
|
13
|
+
workdir_mgr_1 = WorkDirManager()
|
14
|
+
workdir_mgr_2 = WorkDirManager()
|
15
|
+
assert id(workdir_mgr_1) == id(workdir_mgr_2)
|
16
|
+
|
17
|
+
|
18
|
+
def test_workdir_manager_workdir(tmp_path: Path) -> None:
|
19
|
+
"""Test WorkDirManager correctly sets workdir.
|
20
|
+
|
21
|
+
Parameters
|
22
|
+
----------
|
23
|
+
tmp_path : pathlib.Path
|
24
|
+
The path to the test directory.
|
25
|
+
|
26
|
+
"""
|
27
|
+
workdir_mgr = WorkDirManager()
|
28
|
+
workdir_mgr.workdir = tmp_path
|
29
|
+
assert workdir_mgr.workdir == tmp_path
|
30
|
+
|
31
|
+
|
32
|
+
def test_workdir_manager_get_and_delete_element_tempdir(
|
33
|
+
tmp_path: Path,
|
34
|
+
) -> None:
|
35
|
+
"""Test WorkDirManager gets and deletes element tempdirs correctly.
|
36
|
+
|
37
|
+
Parameters
|
38
|
+
----------
|
39
|
+
tmp_path : pathlib.Path
|
40
|
+
The path to the test directory.
|
41
|
+
|
42
|
+
"""
|
43
|
+
workdir_mgr = WorkDirManager()
|
44
|
+
workdir_mgr.workdir = tmp_path
|
45
|
+
# Check no element directory
|
46
|
+
assert workdir_mgr.elementdir is None
|
47
|
+
|
48
|
+
element_tempdir = workdir_mgr.get_element_tempdir()
|
49
|
+
# Should create a temporary directory
|
50
|
+
assert workdir_mgr.elementdir is not None
|
51
|
+
|
52
|
+
workdir_mgr.delete_element_tempdir(element_tempdir)
|
53
|
+
workdir_mgr._cleanup()
|
54
|
+
# Should remove temporary directory
|
55
|
+
assert workdir_mgr.elementdir is None
|
56
|
+
|
57
|
+
|
58
|
+
def test_workdir_manager_cleanup_elementdir(
|
59
|
+
tmp_path: Path,
|
60
|
+
) -> None:
|
61
|
+
"""Test WorkDirManager cleans up element directory correctly.
|
62
|
+
|
63
|
+
Parameters
|
64
|
+
----------
|
65
|
+
tmp_path : pathlib.Path
|
66
|
+
The path to the test directory.
|
67
|
+
|
68
|
+
"""
|
69
|
+
workdir_mgr = WorkDirManager()
|
70
|
+
workdir_mgr.workdir = tmp_path
|
71
|
+
# Check no element directory
|
72
|
+
assert workdir_mgr.elementdir is None
|
73
|
+
|
74
|
+
workdir_mgr.get_element_tempdir()
|
75
|
+
# Should create a temporary directory
|
76
|
+
assert workdir_mgr.elementdir is not None
|
77
|
+
|
78
|
+
workdir_mgr.cleanup_elementdir()
|
79
|
+
# Should remove temporary directory
|
80
|
+
assert workdir_mgr.elementdir is None
|
81
|
+
|
82
|
+
|
83
|
+
def test_workdir_manager_get_and_delete_tempdir(tmp_path: Path) -> None:
|
84
|
+
"""Test WorkDirManager gets and deletes temporary directories correctly.
|
85
|
+
|
86
|
+
Parameters
|
87
|
+
----------
|
88
|
+
tmp_path : pathlib.Path
|
89
|
+
The path to the test directory.
|
90
|
+
|
91
|
+
"""
|
92
|
+
workdir_mgr = WorkDirManager()
|
93
|
+
workdir_mgr.workdir = tmp_path
|
94
|
+
# Check no root temporary directory
|
95
|
+
assert workdir_mgr.root_tempdir is None
|
96
|
+
|
97
|
+
tempdir = workdir_mgr.get_tempdir()
|
98
|
+
# Should create a temporary directory
|
99
|
+
assert workdir_mgr.root_tempdir is not None
|
100
|
+
|
101
|
+
workdir_mgr.delete_tempdir(tempdir)
|
102
|
+
workdir_mgr._cleanup()
|
103
|
+
# Should remove temporary directory
|
104
|
+
assert workdir_mgr.root_tempdir is None
|