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.
- junifer/_version.py +9 -19
- junifer/api/__init__.pyi +17 -2
- junifer/api/functions.py +222 -39
- junifer/api/queue_context/__init__.pyi +11 -3
- junifer/api/queue_context/gnu_parallel_local_adapter.py +81 -120
- junifer/api/queue_context/htcondor_adapter.py +118 -181
- junifer/api/queue_context/queue_context_adapter.py +51 -7
- junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +10 -35
- junifer/api/queue_context/tests/test_htcondor_adapter.py +13 -50
- junifer/api/res/fsl/run_fsl_docker.sh +4 -0
- junifer/api/tests/test_functions.py +863 -36
- junifer/cli/__init__.py +1 -7
- junifer/cli/cli.py +41 -22
- junifer/cli/parser.py +23 -147
- junifer/cli/tests/data/gmd_mean.yaml +6 -6
- junifer/cli/tests/data/gmd_mean_htcondor.yaml +6 -5
- junifer/cli/tests/data/partly_cloudy_agg_mean_tian.yml +1 -0
- junifer/cli/tests/test_cli.py +120 -4
- junifer/cli/tests/test_cli_utils.py +23 -6
- junifer/cli/utils.py +14 -9
- junifer/configs/__init__.py +5 -0
- junifer/configs/__init__.pyi +0 -0
- junifer/configs/juseless/datagrabbers/__init__.pyi +4 -2
- junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +18 -24
- junifer/configs/juseless/datagrabbers/camcan_vbm.py +20 -27
- junifer/configs/juseless/datagrabbers/ixi_vbm.py +44 -52
- junifer/configs/juseless/datagrabbers/tests/test_ixi_vbm.py +0 -7
- junifer/configs/juseless/datagrabbers/tests/test_ucla.py +2 -19
- junifer/configs/juseless/datagrabbers/ucla.py +111 -104
- junifer/configs/juseless/datagrabbers/ukb_vbm.py +19 -25
- junifer/configs/py.typed +0 -0
- junifer/conftest.py +63 -24
- junifer/data/_dispatch.py +9 -19
- junifer/data/coordinates/_ants_coordinates_warper.py +4 -1
- junifer/data/coordinates/_coordinates.py +51 -6
- junifer/data/coordinates/_fsl_coordinates_warper.py +4 -1
- junifer/data/coordinates/tests/test_coordinates.py +15 -3
- junifer/data/coordinates/tests/test_coordinates_names.py +37 -0
- junifer/data/maps/_ants_maps_warper.py +18 -12
- junifer/data/maps/_fsl_maps_warper.py +7 -3
- junifer/data/maps/_maps.py +12 -8
- junifer/data/maps/tests/test_maps.py +13 -21
- junifer/data/masks/_ants_mask_warper.py +18 -12
- junifer/data/masks/_fsl_mask_warper.py +7 -3
- junifer/data/masks/_masks.py +15 -12
- junifer/data/masks/tests/test_masks.py +20 -17
- junifer/data/parcellations/_ants_parcellation_warper.py +18 -12
- junifer/data/parcellations/_fsl_parcellation_warper.py +7 -5
- junifer/data/parcellations/_parcellations.py +125 -91
- junifer/data/parcellations/tests/test_parcellations.py +122 -1
- junifer/data/template_spaces.py +117 -50
- junifer/data/tests/test_template_spaces.py +10 -7
- junifer/data/utils.py +10 -8
- junifer/datagrabber/__init__.pyi +38 -5
- junifer/datagrabber/aomic/__init__.pyi +8 -1
- junifer/datagrabber/aomic/_types.py +22 -0
- junifer/datagrabber/aomic/id1000.py +193 -183
- junifer/datagrabber/aomic/piop1.py +215 -259
- junifer/datagrabber/aomic/piop2.py +211 -244
- junifer/datagrabber/aomic/tests/test_id1000.py +70 -53
- junifer/datagrabber/aomic/tests/test_piop1.py +98 -102
- junifer/datagrabber/aomic/tests/test_piop2.py +88 -85
- junifer/datagrabber/base.py +253 -79
- junifer/datagrabber/datalad_base.py +127 -76
- junifer/datagrabber/dmcc13_benchmark.py +204 -289
- junifer/datagrabber/hcp1200/__init__.pyi +7 -2
- junifer/datagrabber/hcp1200/datalad_hcp1200.py +61 -40
- junifer/datagrabber/hcp1200/hcp1200.py +151 -164
- junifer/datagrabber/hcp1200/tests/test_hcp1200.py +116 -128
- junifer/datagrabber/multiple.py +183 -63
- junifer/datagrabber/pattern.py +358 -227
- junifer/datagrabber/pattern_datalad.py +84 -124
- junifer/datagrabber/pattern_validation_mixin.py +22 -50
- junifer/datagrabber/tests/test_base.py +17 -4
- junifer/datagrabber/tests/test_datalad_base.py +20 -27
- junifer/datagrabber/tests/test_dmcc13_benchmark.py +110 -122
- junifer/datagrabber/tests/test_multiple.py +293 -31
- junifer/datagrabber/tests/test_pattern.py +577 -18
- junifer/datagrabber/tests/test_pattern_datalad.py +177 -107
- junifer/datagrabber/tests/test_pattern_validation_mixin.py +9 -51
- junifer/datareader/default.py +8 -5
- junifer/datareader/tests/test_default_reader.py +1 -1
- junifer/external/BrainPrint/.codecov.yml +2 -0
- junifer/external/BrainPrint/.codespellignore +2 -0
- junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/bug-report.md +45 -0
- junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/documentation.md +12 -0
- junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/feature-request.md +24 -0
- junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/questions-help-support.md +28 -0
- junifer/external/BrainPrint/.github/dependabot.yml +6 -0
- junifer/external/BrainPrint/.github/workflows/build.yml +53 -0
- junifer/external/BrainPrint/.github/workflows/code-style.yml +44 -0
- junifer/external/BrainPrint/.github/workflows/doc.yml +65 -0
- junifer/external/BrainPrint/.github/workflows/publish.yml +31 -0
- junifer/external/BrainPrint/.github/workflows/pytest.yml +54 -0
- junifer/external/BrainPrint/.gitignore +123 -0
- junifer/external/BrainPrint/LICENSE +21 -0
- junifer/external/BrainPrint/README.md +108 -0
- junifer/external/BrainPrint/brainprint/cli/__init__.py +11 -0
- junifer/external/BrainPrint/brainprint/cli/help_text.py +169 -0
- junifer/external/BrainPrint/brainprint/cli/parser.py +150 -0
- junifer/external/BrainPrint/brainprint/cli/utils.py +14 -0
- junifer/external/BrainPrint/brainprint/commands/__init__.py +0 -0
- junifer/external/BrainPrint/brainprint/commands/sys_info.py +18 -0
- junifer/external/BrainPrint/brainprint/utils/tests/__init__.py +0 -0
- junifer/external/BrainPrint/brainprint/utils/tests/test_config.py +33 -0
- junifer/external/BrainPrint/doc/Makefile +20 -0
- junifer/external/BrainPrint/doc/_static/.gitkeep +0 -0
- junifer/external/BrainPrint/doc/_templates/autosummary/class.rst +10 -0
- junifer/external/BrainPrint/doc/_templates/autosummary/function.rst +8 -0
- junifer/external/BrainPrint/doc/_templates/autosummary/module.rst +6 -0
- junifer/external/BrainPrint/doc/api/index.rst +11 -0
- junifer/external/BrainPrint/doc/conf.py +272 -0
- junifer/external/BrainPrint/doc/index.rst +140 -0
- junifer/external/BrainPrint/doc/links.inc +36 -0
- junifer/external/BrainPrint/doc/make.bat +35 -0
- junifer/external/BrainPrint/doc/references.bib +23 -0
- junifer/external/BrainPrint/pyproject.toml +170 -0
- junifer/external/BrainPrint/setup.py +3 -0
- junifer/external/__init__.py +5 -0
- junifer/external/__init__.pyi +0 -0
- junifer/external/nilearn/junifer_connectivity_measure.py +95 -39
- junifer/external/nilearn/junifer_nifti_spheres_masker.py +226 -67
- junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +39 -27
- junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +231 -21
- junifer/external/py.typed +0 -0
- junifer/markers/__init__.pyi +6 -3
- junifer/markers/base.py +126 -76
- junifer/markers/brainprint.py +30 -39
- junifer/markers/complexity/__init__.py +20 -3
- junifer/markers/complexity/complexity_base.py +40 -41
- junifer/markers/complexity/hurst_exponent.py +22 -35
- junifer/markers/complexity/multiscale_entropy_auc.py +53 -41
- junifer/markers/complexity/perm_entropy.py +22 -36
- junifer/markers/complexity/range_entropy.py +22 -35
- junifer/markers/complexity/range_entropy_auc.py +27 -36
- junifer/markers/complexity/sample_entropy.py +22 -36
- junifer/markers/complexity/tests/test_complexity_registry.py +48 -0
- junifer/markers/complexity/tests/test_hurst_exponent.py +47 -36
- junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +50 -36
- junifer/markers/complexity/tests/test_perm_entropy.py +48 -36
- junifer/markers/complexity/tests/test_range_entropy.py +47 -36
- junifer/markers/complexity/tests/test_range_entropy_auc.py +47 -36
- junifer/markers/complexity/tests/test_sample_entropy.py +48 -36
- junifer/markers/complexity/tests/test_weighted_perm_entropy.py +50 -36
- junifer/markers/complexity/weighted_perm_entropy.py +22 -36
- junifer/markers/ets_rss.py +35 -31
- junifer/markers/falff/__init__.pyi +7 -1
- junifer/markers/falff/_afni_falff.py +22 -9
- junifer/markers/falff/_junifer_falff.py +47 -31
- junifer/markers/falff/falff_base.py +80 -60
- junifer/markers/falff/falff_maps.py +17 -41
- junifer/markers/falff/falff_parcels.py +26 -51
- junifer/markers/falff/falff_spheres.py +34 -61
- junifer/markers/falff/tests/test_falff_maps.py +20 -23
- junifer/markers/falff/tests/test_falff_mask.py +99 -0
- junifer/markers/falff/tests/test_falff_parcels.py +14 -10
- junifer/markers/falff/tests/test_falff_spheres.py +14 -10
- junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +32 -34
- junifer/markers/functional_connectivity/edge_functional_connectivity_maps.py +12 -25
- junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +19 -34
- junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +24 -44
- junifer/markers/functional_connectivity/functional_connectivity_base.py +50 -40
- junifer/markers/functional_connectivity/functional_connectivity_maps.py +12 -25
- junifer/markers/functional_connectivity/functional_connectivity_parcels.py +19 -34
- junifer/markers/functional_connectivity/functional_connectivity_spheres.py +24 -45
- junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +21 -22
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_maps.py +8 -7
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +8 -4
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +8 -4
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_maps.py +8 -7
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +9 -5
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +8 -12
- junifer/markers/maps_aggregation.py +77 -55
- junifer/markers/parcel_aggregation.py +91 -70
- junifer/markers/reho/__init__.pyi +7 -1
- junifer/markers/reho/_afni_reho.py +23 -14
- junifer/markers/reho/_junifer_reho.py +112 -123
- junifer/markers/reho/reho_base.py +115 -37
- junifer/markers/reho/reho_maps.py +18 -32
- junifer/markers/reho/reho_parcels.py +26 -41
- junifer/markers/reho/reho_spheres.py +33 -54
- junifer/markers/reho/tests/test_junifer_reho.py +178 -0
- junifer/markers/reho/tests/test_reho_maps.py +27 -25
- junifer/markers/reho/tests/test_reho_parcels.py +28 -22
- junifer/markers/reho/tests/test_reho_spheres.py +16 -12
- junifer/markers/sphere_aggregation.py +88 -72
- junifer/markers/temporal_snr/temporal_snr_base.py +32 -30
- junifer/markers/temporal_snr/temporal_snr_maps.py +9 -18
- junifer/markers/temporal_snr/temporal_snr_parcels.py +19 -27
- junifer/markers/temporal_snr/temporal_snr_spheres.py +27 -41
- junifer/markers/temporal_snr/tests/test_temporal_snr_base.py +2 -2
- junifer/markers/temporal_snr/tests/test_temporal_snr_maps.py +17 -22
- junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +11 -3
- junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +9 -9
- junifer/markers/tests/test_brainprint.py +10 -9
- junifer/markers/tests/test_ets_rss.py +7 -3
- junifer/markers/tests/test_lost_regions.py +279 -0
- junifer/markers/tests/test_maps_aggregation.py +67 -114
- junifer/markers/tests/test_marker_utils.py +40 -1
- junifer/markers/tests/test_markers_base.py +275 -6
- junifer/markers/tests/test_parcel_aggregation.py +91 -53
- junifer/markers/tests/test_sphere_aggregation.py +53 -47
- junifer/markers/utils.py +267 -5
- junifer/onthefly/__init__.py +2 -3
- junifer/onthefly/__init__.pyi +4 -0
- junifer/onthefly/_brainprint.py +3 -5
- junifer/onthefly/py.typed +0 -0
- junifer/onthefly/read_transform.py +11 -9
- junifer/onthefly/tests/test_read_transform.py +7 -7
- junifer/pipeline/__init__.pyi +4 -2
- junifer/pipeline/marker_collection.py +14 -10
- junifer/pipeline/pipeline_component_registry.py +25 -6
- junifer/pipeline/pipeline_step_mixin.py +5 -12
- junifer/pipeline/tests/test_data_object_dumper.py +10 -10
- junifer/pipeline/tests/test_marker_collection.py +145 -168
- junifer/pipeline/tests/test_pipeline_component_registry.py +52 -8
- junifer/pipeline/tests/test_pipeline_step_mixin.py +12 -26
- junifer/pipeline/tests/test_update_meta_mixin.py +4 -5
- junifer/pipeline/tests/test_workdir_manager.py +39 -0
- junifer/pipeline/update_meta_mixin.py +13 -10
- junifer/pipeline/utils.py +22 -17
- junifer/pipeline/workdir_manager.py +41 -32
- junifer/preprocess/__init__.pyi +9 -4
- junifer/preprocess/_temporal_filter.py +34 -33
- junifer/preprocess/_temporal_slicer.py +17 -30
- junifer/preprocess/base.py +99 -55
- junifer/preprocess/confounds/__init__.pyi +10 -2
- junifer/preprocess/confounds/fmriprep_confound_remover.py +87 -109
- junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +64 -45
- junifer/preprocess/smoothing/__init__.pyi +2 -2
- junifer/preprocess/smoothing/_afni_smoothing.py +8 -7
- junifer/preprocess/smoothing/_fsl_smoothing.py +6 -4
- junifer/preprocess/smoothing/_nilearn_smoothing.py +7 -5
- junifer/preprocess/smoothing/smoothing.py +59 -40
- junifer/preprocess/smoothing/tests/test_smoothing.py +28 -8
- junifer/preprocess/tests/test_preprocess_base.py +17 -9
- junifer/preprocess/tests/test_temporal_filter.py +37 -16
- junifer/preprocess/tests/test_temporal_slicer.py +12 -12
- junifer/preprocess/warping/__init__.pyi +2 -2
- junifer/preprocess/warping/_ants_warper.py +56 -40
- junifer/preprocess/warping/_fsl_warper.py +49 -36
- junifer/preprocess/warping/_utils.py +106 -0
- junifer/preprocess/warping/space_warper.py +99 -98
- junifer/preprocess/warping/tests/test_space_warper.py +46 -41
- junifer/preprocess/warping/tests/test_warping_utils.py +163 -0
- junifer/stats.py +46 -34
- junifer/storage/__init__.pyi +8 -4
- junifer/storage/base.py +129 -64
- junifer/storage/hdf5.py +307 -199
- junifer/storage/pandas_base.py +46 -24
- junifer/storage/sqlite.py +99 -62
- junifer/storage/tests/data/collected_h5io_fork.hdf5 +0 -0
- junifer/storage/tests/test_hdf5.py +147 -12
- junifer/storage/tests/test_repeated_storage.py +293 -0
- junifer/storage/tests/test_sqlite.py +9 -20
- junifer/storage/tests/test_storage_base.py +18 -21
- junifer/storage/tests/test_utils.py +83 -22
- junifer/storage/utils.py +58 -38
- junifer/testing/__init__.pyi +2 -2
- junifer/testing/datagrabbers.py +480 -44
- junifer/testing/registry.py +12 -0
- junifer/testing/tests/test_adhdtesting_datagrabber.py +24 -0
- junifer/testing/tests/test_oasisvmbtesting_datagrabber.py +5 -0
- junifer/testing/tests/test_partlycloudytesting_datagrabber.py +52 -0
- junifer/testing/tests/test_spmauditory_datagrabber.py +16 -0
- junifer/testing/tests/test_testing_registry.py +31 -0
- junifer/testing/tests/test_testing_utils.py +35 -0
- junifer/testing/tests/test_uclacnpvbmtesting_datagrabber.py +81 -0
- junifer/testing/utils.py +33 -1
- junifer/tests/test_stats.py +1 -5
- junifer/typing/__init__.pyi +2 -0
- junifer/typing/_typing.py +34 -19
- junifer/typing/py.typed +0 -0
- junifer/utils/__init__.pyi +10 -1
- junifer/utils/_config.py +1 -2
- junifer/utils/helpers.py +68 -2
- junifer/utils/logging.py +31 -7
- junifer/utils/tests/test_helpers.py +36 -1
- {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/METADATA +43 -35
- junifer-0.0.8.dist-info/RECORD +402 -0
- {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/WHEEL +1 -1
- junifer/cli/tests/test_parser.py +0 -280
- junifer/external/h5io/h5io/__init__.py +0 -15
- junifer/external/h5io/h5io/_h5io.py +0 -767
- junifer/external/h5io/h5io/_version.py +0 -1
- junifer/external/h5io/h5io/chunked_array.py +0 -94
- junifer/external/h5io/h5io/chunked_list.py +0 -65
- junifer/markers/complexity/__init__.pyi +0 -17
- junifer-0.0.7.dev332.dist-info/RECORD +0 -353
- {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/licenses/AUTHORS.rst +0 -0
- {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/licenses/LICENSE.md +0 -0
- {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/top_level.txt +0 -0
|
@@ -5,8 +5,14 @@
|
|
|
5
5
|
# Synchon Mandal <s.mandal@fz-juelich.de>
|
|
6
6
|
# License: AGPL
|
|
7
7
|
|
|
8
|
+
from fnmatch import fnmatchcase
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from pydantic import ConfigDict
|
|
12
|
+
|
|
8
13
|
from ..api.decorators import register_datagrabber
|
|
9
|
-
from ..utils import
|
|
14
|
+
from ..utils import raise_error
|
|
15
|
+
from .base import logger
|
|
10
16
|
from .datalad_base import DataladDataGrabber
|
|
11
17
|
from .pattern import PatternDataGrabber
|
|
12
18
|
|
|
@@ -23,122 +29,24 @@ class PatternDataladDataGrabber(DataladDataGrabber, PatternDataGrabber):
|
|
|
23
29
|
|
|
24
30
|
Parameters
|
|
25
31
|
----------
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
{
|
|
36
|
-
"mandatory": ["pattern", "space"],
|
|
37
|
-
"optional": {
|
|
38
|
-
"mask": {
|
|
39
|
-
"mandatory": ["pattern", "space"],
|
|
40
|
-
"optional": []
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
* ``"T2w"`` :
|
|
46
|
-
|
|
47
|
-
.. code-block:: none
|
|
48
|
-
|
|
49
|
-
{
|
|
50
|
-
"mandatory": ["pattern", "space"],
|
|
51
|
-
"optional": {
|
|
52
|
-
"mask": {
|
|
53
|
-
"mandatory": ["pattern", "space"],
|
|
54
|
-
"optional": []
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
* ``"BOLD"`` :
|
|
60
|
-
|
|
61
|
-
.. code-block:: none
|
|
62
|
-
|
|
63
|
-
{
|
|
64
|
-
"mandatory": ["pattern", "space"],
|
|
65
|
-
"optional": {
|
|
66
|
-
"mask": {
|
|
67
|
-
"mandatory": ["pattern", "space"],
|
|
68
|
-
"optional": []
|
|
69
|
-
}
|
|
70
|
-
"confounds": {
|
|
71
|
-
"mandatory": ["pattern", "format"],
|
|
72
|
-
"optional": []
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
* ``"Warp"`` :
|
|
78
|
-
|
|
79
|
-
.. code-block:: none
|
|
80
|
-
|
|
81
|
-
{
|
|
82
|
-
"mandatory": ["pattern", "src", "dst"],
|
|
83
|
-
"optional": []
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
* ``"VBM_GM"`` :
|
|
87
|
-
|
|
88
|
-
.. code-block:: none
|
|
89
|
-
|
|
90
|
-
{
|
|
91
|
-
"mandatory": ["pattern", "space"],
|
|
92
|
-
"optional": []
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
* ``"VBM_WM"`` :
|
|
96
|
-
|
|
97
|
-
.. code-block:: none
|
|
98
|
-
|
|
99
|
-
{
|
|
100
|
-
"mandatory": ["pattern", "space"],
|
|
101
|
-
"optional": []
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
Basically, for each data type, one needs to provide ``mandatory`` keys
|
|
105
|
-
and can choose to also provide ``optional`` keys. The value for each
|
|
106
|
-
key is a string. So, one needs to provide necessary data types as a
|
|
107
|
-
dictionary, for example:
|
|
108
|
-
|
|
109
|
-
.. code-block:: none
|
|
110
|
-
|
|
111
|
-
{
|
|
112
|
-
"BOLD": {
|
|
113
|
-
"pattern": "...",
|
|
114
|
-
"space": "...",
|
|
115
|
-
},
|
|
116
|
-
"T1w": {
|
|
117
|
-
"pattern": "...",
|
|
118
|
-
"space": "...",
|
|
119
|
-
},
|
|
120
|
-
"Warp": {
|
|
121
|
-
"pattern": "...",
|
|
122
|
-
"src": "...",
|
|
123
|
-
"dst": "...",
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
taken from :class:`.HCP1200`.
|
|
128
|
-
replacements : str or list of str
|
|
129
|
-
Replacements in the ``pattern`` key of each data type. The value needs
|
|
130
|
-
to be a list of all possible replacements.
|
|
131
|
-
confounds_format : {"fmriprep", "adhoc"} or None, optional
|
|
132
|
-
The format of the confounds for the dataset (default None).
|
|
133
|
-
datadir : str or pathlib.Path or None, optional
|
|
134
|
-
That directory where the datalad dataset will be cloned. If None,
|
|
135
|
-
the datalad dataset will be cloned into a temporary directory
|
|
136
|
-
(default None).
|
|
137
|
-
rootdir : str or pathlib.Path, optional
|
|
32
|
+
uri : pydantic.AnyUrl
|
|
33
|
+
URI of the datalad sibling.
|
|
34
|
+
types : enum:`.DataType` or list of variants
|
|
35
|
+
The data type(s) to grab.
|
|
36
|
+
patterns : ``DataGrabberPatterns``
|
|
37
|
+
The datagrabber patterns. Check :class:`DataTypeSchema` for the schema.
|
|
38
|
+
replacements : list of str
|
|
39
|
+
All possible replacements in ``patterns.<data_type>.pattern``.
|
|
40
|
+
rootdir : pathlib.Path, optional
|
|
138
41
|
The path within the datalad dataset to the root directory
|
|
139
|
-
(default ".").
|
|
140
|
-
|
|
141
|
-
|
|
42
|
+
(default Path(".")).
|
|
43
|
+
confounds_format : :enum:`.ConfoundsFormat` or None, optional
|
|
44
|
+
The format of the confounds for the dataset (default None).
|
|
45
|
+
datadir : pathlib.Path, optional
|
|
46
|
+
That path where the datalad dataset will be cloned.
|
|
47
|
+
If not specified, the datalad dataset will be cloned into a temporary
|
|
48
|
+
directory.
|
|
49
|
+
|
|
142
50
|
|
|
143
51
|
See Also
|
|
144
52
|
--------
|
|
@@ -149,15 +57,67 @@ class PatternDataladDataGrabber(DataladDataGrabber, PatternDataGrabber):
|
|
|
149
57
|
|
|
150
58
|
"""
|
|
151
59
|
|
|
152
|
-
|
|
153
|
-
self,
|
|
154
|
-
**kwargs,
|
|
155
|
-
) -> None:
|
|
156
|
-
# TODO(synchon): needs to be reworked, DataladDataGrabber needs to be
|
|
157
|
-
# a mixin to avoid multiple inheritance wherever possible.
|
|
60
|
+
model_config = ConfigDict(extra="allow")
|
|
158
61
|
|
|
62
|
+
def validate_datagrabber_params(self) -> None:
|
|
63
|
+
"""Run extra logical validation for datagrabber."""
|
|
64
|
+
super().validate_datagrabber_params()
|
|
159
65
|
logger.debug("Initializing PatternDataladDataGrabber")
|
|
160
|
-
for key, val in
|
|
66
|
+
for key, val in self.__pydantic_extra__.items():
|
|
161
67
|
logger.debug(f"\t{key} = {val}")
|
|
162
68
|
|
|
163
|
-
|
|
69
|
+
def install(self) -> None:
|
|
70
|
+
"""Install the datalad dataset and check the patterns.
|
|
71
|
+
|
|
72
|
+
The elements are found by searching the files of the patterns, but the
|
|
73
|
+
files of a sub-dataset are only there once the sub-dataset is
|
|
74
|
+
installed, so the patterns cannot search the files of the
|
|
75
|
+
sub-datasets (e.g., with one sub-dataset per subject).
|
|
76
|
+
|
|
77
|
+
Raises
|
|
78
|
+
------
|
|
79
|
+
RuntimeError
|
|
80
|
+
If a pattern searches the files of a sub-dataset.
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
super().install()
|
|
84
|
+
subdatasets = [
|
|
85
|
+
Path(x)
|
|
86
|
+
for x in self._dataset.subdatasets(
|
|
87
|
+
result_xfm="relpaths",
|
|
88
|
+
result_renderer="disabled",
|
|
89
|
+
return_type="list",
|
|
90
|
+
)
|
|
91
|
+
]
|
|
92
|
+
if len(subdatasets) == 0:
|
|
93
|
+
return
|
|
94
|
+
for data_type in self.types:
|
|
95
|
+
for pattern in self._get_type_patterns(data_type):
|
|
96
|
+
_, glob_pattern, _ = self._replace_patterns_regex(pattern)
|
|
97
|
+
if not any(x in glob_pattern for x in "*?["):
|
|
98
|
+
continue
|
|
99
|
+
pattern_parts = (self.rootdir / glob_pattern).parts
|
|
100
|
+
for subdataset in subdatasets:
|
|
101
|
+
n_parts = len(subdataset.parts)
|
|
102
|
+
if len(pattern_parts) > n_parts and all(
|
|
103
|
+
fnmatchcase(x, y)
|
|
104
|
+
for x, y in zip(
|
|
105
|
+
subdataset.parts, pattern_parts, strict=False
|
|
106
|
+
)
|
|
107
|
+
):
|
|
108
|
+
# Remove the dataset if cloned, as the context is
|
|
109
|
+
# not entered
|
|
110
|
+
self.cleanup()
|
|
111
|
+
raise_error(
|
|
112
|
+
msg=(
|
|
113
|
+
f"The pattern {pattern} of {data_type} "
|
|
114
|
+
"searches the files of the sub-dataset "
|
|
115
|
+
f"{subdataset}, which are only there if it "
|
|
116
|
+
"is installed. A DataGrabber with files in "
|
|
117
|
+
"sub-datasets must list its elements and "
|
|
118
|
+
"build the paths of the files without "
|
|
119
|
+
"searching them (e.g., without wildcards), "
|
|
120
|
+
"as DataladHCP1200."
|
|
121
|
+
),
|
|
122
|
+
klass=RuntimeError,
|
|
123
|
+
)
|
|
@@ -3,11 +3,22 @@
|
|
|
3
3
|
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
|
4
4
|
# License: AGPL
|
|
5
5
|
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
if sys.version_info < (3, 12): # pragma: no cover
|
|
10
|
+
from typing_extensions import TypedDict
|
|
11
|
+
else:
|
|
12
|
+
from typing import TypedDict
|
|
13
|
+
|
|
14
|
+
|
|
6
15
|
from collections.abc import Iterator, MutableMapping
|
|
7
|
-
|
|
16
|
+
|
|
17
|
+
from aenum import extend_enum
|
|
8
18
|
|
|
9
19
|
from ..typing import DataGrabberPatterns
|
|
10
|
-
from ..utils import
|
|
20
|
+
from ..utils import raise_error, warn_with_log
|
|
21
|
+
from .base import DataType, logger
|
|
11
22
|
|
|
12
23
|
|
|
13
24
|
__all__ = [
|
|
@@ -191,37 +202,17 @@ def register_data_type(name: str, schema: DataTypeSchema) -> None:
|
|
|
191
202
|
----------
|
|
192
203
|
name : str
|
|
193
204
|
The data type name.
|
|
194
|
-
schema : DataTypeSchema
|
|
205
|
+
schema : ``DataTypeSchema``
|
|
195
206
|
The data type schema.
|
|
196
207
|
|
|
197
208
|
"""
|
|
198
209
|
DataTypeManager()[name] = schema
|
|
210
|
+
extend_enum(DataType, name, name)
|
|
199
211
|
|
|
200
212
|
|
|
201
213
|
class PatternValidationMixin:
|
|
202
214
|
"""Mixin class for pattern validation."""
|
|
203
215
|
|
|
204
|
-
def _validate_types(self, types: list[str]) -> None:
|
|
205
|
-
"""Validate the types.
|
|
206
|
-
|
|
207
|
-
Parameters
|
|
208
|
-
----------
|
|
209
|
-
types : list of str
|
|
210
|
-
The data types to validate.
|
|
211
|
-
|
|
212
|
-
Raises
|
|
213
|
-
------
|
|
214
|
-
TypeError
|
|
215
|
-
If ``types`` is not a list or if the values are not string.
|
|
216
|
-
|
|
217
|
-
"""
|
|
218
|
-
if not isinstance(types, list):
|
|
219
|
-
raise_error(msg="`types` must be a list", klass=TypeError)
|
|
220
|
-
if any(not isinstance(x, str) for x in types):
|
|
221
|
-
raise_error(
|
|
222
|
-
msg="`types` must be a list of strings", klass=TypeError
|
|
223
|
-
)
|
|
224
|
-
|
|
225
216
|
def _validate_replacements(
|
|
226
217
|
self,
|
|
227
218
|
replacements: list[str],
|
|
@@ -234,15 +225,13 @@ class PatternValidationMixin:
|
|
|
234
225
|
----------
|
|
235
226
|
replacements : list of str
|
|
236
227
|
The replacements to validate.
|
|
237
|
-
patterns :
|
|
238
|
-
The patterns to validate replacements against.
|
|
228
|
+
patterns : ``DataGrabberPatterns``
|
|
229
|
+
The patterns to validate ``replacements`` against.
|
|
239
230
|
partial_pattern_ok : bool
|
|
240
231
|
Whether to raise error if partial pattern for a data type is found.
|
|
241
232
|
|
|
242
233
|
Raises
|
|
243
234
|
------
|
|
244
|
-
TypeError
|
|
245
|
-
If ``replacements`` is not a list or if the values are not string.
|
|
246
235
|
ValueError
|
|
247
236
|
If a value in ``replacements`` is not part of a data type pattern
|
|
248
237
|
and ``partial_pattern_ok=False`` or
|
|
@@ -256,15 +245,6 @@ class PatternValidationMixin:
|
|
|
256
245
|
and ``partial_pattern_ok=True``.
|
|
257
246
|
|
|
258
247
|
"""
|
|
259
|
-
if not isinstance(replacements, list):
|
|
260
|
-
raise_error(msg="`replacements` must be a list.", klass=TypeError)
|
|
261
|
-
|
|
262
|
-
if any(not isinstance(x, str) for x in replacements):
|
|
263
|
-
raise_error(
|
|
264
|
-
msg="`replacements` must be a list of strings",
|
|
265
|
-
klass=TypeError,
|
|
266
|
-
)
|
|
267
|
-
|
|
268
248
|
# Make a list of all patterns recursively
|
|
269
249
|
all_patterns = []
|
|
270
250
|
for dtype_val in patterns.values():
|
|
@@ -390,7 +370,7 @@ class PatternValidationMixin:
|
|
|
390
370
|
|
|
391
371
|
def validate_patterns(
|
|
392
372
|
self,
|
|
393
|
-
types: list[
|
|
373
|
+
types: list[DataType],
|
|
394
374
|
replacements: list[str],
|
|
395
375
|
patterns: DataGrabberPatterns,
|
|
396
376
|
partial_pattern_ok: bool = False,
|
|
@@ -399,11 +379,11 @@ class PatternValidationMixin:
|
|
|
399
379
|
|
|
400
380
|
Parameters
|
|
401
381
|
----------
|
|
402
|
-
types : list of
|
|
403
|
-
The data
|
|
382
|
+
types : list of :enum:`.DataType`
|
|
383
|
+
The data type(s) to check patterns of.
|
|
404
384
|
replacements : list of str
|
|
405
|
-
The replacements to be replaced in the patterns
|
|
406
|
-
patterns :
|
|
385
|
+
The replacements to be replaced in the ``patterns``.
|
|
386
|
+
patterns : ``DataGrabberPatterns``
|
|
407
387
|
The patterns to validate.
|
|
408
388
|
partial_pattern_ok : bool, optional
|
|
409
389
|
Whether to raise error if partial pattern for a data type is found.
|
|
@@ -412,8 +392,6 @@ class PatternValidationMixin:
|
|
|
412
392
|
|
|
413
393
|
Raises
|
|
414
394
|
------
|
|
415
|
-
TypeError
|
|
416
|
-
If ``patterns`` is not a dictionary.
|
|
417
395
|
ValueError
|
|
418
396
|
If length of ``types`` and ``patterns`` are different or
|
|
419
397
|
if ``patterns`` is missing entries from ``types`` or
|
|
@@ -421,12 +399,6 @@ class PatternValidationMixin:
|
|
|
421
399
|
if data type pattern key contains '*' as value.
|
|
422
400
|
|
|
423
401
|
"""
|
|
424
|
-
# Validate types
|
|
425
|
-
self._validate_types(types=types)
|
|
426
|
-
|
|
427
|
-
# Validate patterns
|
|
428
|
-
if not isinstance(patterns, dict):
|
|
429
|
-
raise_error(msg="`patterns` must be a dict", klass=TypeError)
|
|
430
402
|
# Unequal length of objects
|
|
431
403
|
if len(types) > len(patterns):
|
|
432
404
|
raise_error(
|
|
@@ -26,7 +26,7 @@ def test_BaseDataGrabber() -> None:
|
|
|
26
26
|
def get_element_keys(self):
|
|
27
27
|
return ["subject"]
|
|
28
28
|
|
|
29
|
-
dg = MyDataGrabber(datadir="/tmp", types=
|
|
29
|
+
dg = MyDataGrabber(datadir="/tmp", types="BOLD")
|
|
30
30
|
elem = dg["sub01"]
|
|
31
31
|
assert "BOLD" in elem
|
|
32
32
|
assert "meta" in elem["BOLD"]
|
|
@@ -55,7 +55,7 @@ def test_BaseDataGrabber() -> None:
|
|
|
55
55
|
def get_element_keys(self):
|
|
56
56
|
return super().get_element_keys()
|
|
57
57
|
|
|
58
|
-
dg = MyDataGrabber2(datadir="/tmp", types=
|
|
58
|
+
dg = MyDataGrabber2(datadir="/tmp", types="BOLD")
|
|
59
59
|
with pytest.raises(NotImplementedError):
|
|
60
60
|
dg.get_element_keys()
|
|
61
61
|
|
|
@@ -77,7 +77,7 @@ def test_BaseDataGrabber_filter_single() -> None:
|
|
|
77
77
|
def get_element_keys(self):
|
|
78
78
|
return ["subject"]
|
|
79
79
|
|
|
80
|
-
dg = FilterDataGrabber(datadir="/tmp", types=
|
|
80
|
+
dg = FilterDataGrabber(datadir="/tmp", types="BOLD")
|
|
81
81
|
with dg:
|
|
82
82
|
assert "sub01" in list(dg.filter(["sub01"]))
|
|
83
83
|
assert "sub02" not in list(dg.filter(["sub01"]))
|
|
@@ -104,7 +104,7 @@ def test_BaseDataGrabber_filter_multi() -> None:
|
|
|
104
104
|
def get_element_keys(self):
|
|
105
105
|
return ["subject", "task"]
|
|
106
106
|
|
|
107
|
-
dg = FilterDataGrabber(datadir="/tmp", types=
|
|
107
|
+
dg = FilterDataGrabber(datadir="/tmp", types="BOLD")
|
|
108
108
|
with dg:
|
|
109
109
|
assert ("sub01", "rest") in list(
|
|
110
110
|
dg.filter([("sub01", "rest")]) # type: ignore
|
|
@@ -118,3 +118,16 @@ def test_BaseDataGrabber_filter_multi() -> None:
|
|
|
118
118
|
assert ("sub02", "movie") not in list(
|
|
119
119
|
dg.filter([("sub01", "rest")]) # type: ignore
|
|
120
120
|
)
|
|
121
|
+
# Partial selectors as values or tuples
|
|
122
|
+
assert list(dg.filter(["sub01"])) == [
|
|
123
|
+
("sub01", "rest"),
|
|
124
|
+
("sub01", "movie"),
|
|
125
|
+
]
|
|
126
|
+
assert list(dg.filter([("rest",)])) == [
|
|
127
|
+
("sub01", "rest"),
|
|
128
|
+
("sub02", "rest"),
|
|
129
|
+
("sub03", "rest"),
|
|
130
|
+
]
|
|
131
|
+
# The selectors must have the same number of values
|
|
132
|
+
with pytest.raises(ValueError, match="same number of values"):
|
|
133
|
+
list(dg.filter(["sub01", ("sub02", "rest")])) # type: ignore
|
|
@@ -9,18 +9,22 @@ from pathlib import Path
|
|
|
9
9
|
import datalad.api as dl
|
|
10
10
|
import pytest
|
|
11
11
|
|
|
12
|
-
from junifer.datagrabber import DataladDataGrabber
|
|
12
|
+
from junifer.datagrabber import DataladDataGrabber, DataType
|
|
13
13
|
from junifer.utils import config
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
_testing_dataset = {
|
|
17
17
|
"example_bids": {
|
|
18
|
-
"uri":
|
|
18
|
+
"uri": (
|
|
19
|
+
"https://cerebra.fz-juelich.de/junifer/datalad-example-bids.git"
|
|
20
|
+
),
|
|
19
21
|
"commit": "3f288c8725207ae0c9b3616e093e78cda192b570",
|
|
20
22
|
"id": "582b9696-f13f-42e4-9587-b4e62aa2a8e7",
|
|
21
23
|
},
|
|
22
24
|
"example_bids_ses": {
|
|
23
|
-
"uri":
|
|
25
|
+
"uri": (
|
|
26
|
+
"https://cerebra.fz-juelich.de/junifer/datalad-example-bids-ses.git"
|
|
27
|
+
),
|
|
24
28
|
"commit": "6b163aa98af76a9eac0272273c27e14127850181",
|
|
25
29
|
"id": "715c17cf-a1b9-42d6-9af8-9f74c1a4a724",
|
|
26
30
|
},
|
|
@@ -38,23 +42,18 @@ def concrete_datagrabber() -> type[DataladDataGrabber]:
|
|
|
38
42
|
|
|
39
43
|
"""
|
|
40
44
|
|
|
41
|
-
class MyDataGrabber(DataladDataGrabber):
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
datadir=datadir,
|
|
45
|
-
rootdir="example_bids",
|
|
46
|
-
uri=uri,
|
|
47
|
-
types=["T1w", "BOLD"],
|
|
48
|
-
)
|
|
45
|
+
class MyDataGrabber(DataladDataGrabber):
|
|
46
|
+
types: list[DataType] = [DataType.T1w, DataType.BOLD] # noqa: RUF012
|
|
47
|
+
rootdir: Path = Path("example_bids")
|
|
49
48
|
|
|
50
49
|
def get_item(self, subject):
|
|
51
50
|
out = {
|
|
52
51
|
"T1w": {
|
|
53
|
-
"path": self.
|
|
52
|
+
"path": self.fulldir
|
|
54
53
|
/ f"{subject}/anat/{subject}_T1w.nii.gz"
|
|
55
54
|
},
|
|
56
55
|
"BOLD": {
|
|
57
|
-
"path": self.
|
|
56
|
+
"path": self.fulldir
|
|
58
57
|
/ f"{subject}/func/{subject}_task-rest_bold.nii.gz"
|
|
59
58
|
},
|
|
60
59
|
}
|
|
@@ -91,7 +90,7 @@ def test_DataladDataGrabber_install_errors(
|
|
|
91
90
|
# Files are not there
|
|
92
91
|
assert datadir.exists() is False
|
|
93
92
|
# Clone dataset
|
|
94
|
-
dl.clone(uri, datadir)
|
|
93
|
+
dl.clone(uri, datadir)
|
|
95
94
|
dg = concrete_datagrabber(datadir=datadir, uri=uri2)
|
|
96
95
|
with pytest.raises(ValueError, match=r"different ID"):
|
|
97
96
|
with dg:
|
|
@@ -160,7 +159,6 @@ def test_DataladDataGrabber_clone_cleanup(
|
|
|
160
159
|
assert "datagrabber" in meta
|
|
161
160
|
assert "datalad_dirty" in meta["datagrabber"]
|
|
162
161
|
assert meta["datagrabber"]["datalad_dirty"] is False
|
|
163
|
-
assert hasattr(dg, "_got_files") is False
|
|
164
162
|
assert datadir.exists() is True
|
|
165
163
|
assert elem1_bold.is_file() is True
|
|
166
164
|
assert elem1_bold.is_symlink() is True
|
|
@@ -185,8 +183,8 @@ def test_DataladDataGrabber_clone_create_cleanup(
|
|
|
185
183
|
|
|
186
184
|
# Clone whole dataset
|
|
187
185
|
uri = _testing_dataset["example_bids"]["uri"]
|
|
188
|
-
with concrete_datagrabber(
|
|
189
|
-
datadir = dg.
|
|
186
|
+
with concrete_datagrabber(uri=uri) as dg:
|
|
187
|
+
datadir = dg._repodir
|
|
190
188
|
elem1_bold = (
|
|
191
189
|
datadir / "example_bids/sub-01/func/sub-01_task-rest_bold.nii.gz"
|
|
192
190
|
)
|
|
@@ -206,7 +204,6 @@ def test_DataladDataGrabber_clone_create_cleanup(
|
|
|
206
204
|
assert "datagrabber" in meta
|
|
207
205
|
assert "datalad_dirty" in meta["datagrabber"]
|
|
208
206
|
assert meta["datagrabber"]["datalad_dirty"] is False
|
|
209
|
-
assert hasattr(dg, "_got_files") is False
|
|
210
207
|
assert datadir.exists() is True
|
|
211
208
|
assert elem1_bold.is_file() is True
|
|
212
209
|
assert elem1_bold.is_symlink() is True
|
|
@@ -246,7 +243,7 @@ def test_DataladDataGrabber_previously_cloned(
|
|
|
246
243
|
assert elem1_t1w.exists() is False
|
|
247
244
|
|
|
248
245
|
# Clone dataset
|
|
249
|
-
dl.clone(uri, datadir, result_renderer="disabled")
|
|
246
|
+
dl.clone(uri, datadir, result_renderer="disabled")
|
|
250
247
|
|
|
251
248
|
# Files are there, but are empty symbolic links
|
|
252
249
|
assert datadir.exists() is True
|
|
@@ -316,7 +313,7 @@ def test_DataladDataGrabber_previously_cloned_and_get(
|
|
|
316
313
|
assert elem1_t1w.exists() is False
|
|
317
314
|
|
|
318
315
|
# Clone dataset
|
|
319
|
-
dl.clone(uri, datadir, result_renderer="disabled")
|
|
316
|
+
dl.clone(uri, datadir, result_renderer="disabled")
|
|
320
317
|
|
|
321
318
|
# Files are there, but are empty symbolic links
|
|
322
319
|
assert datadir.exists() is True
|
|
@@ -325,9 +322,7 @@ def test_DataladDataGrabber_previously_cloned_and_get(
|
|
|
325
322
|
assert elem1_t1w.is_symlink() is True
|
|
326
323
|
assert elem1_t1w.is_file() is False
|
|
327
324
|
|
|
328
|
-
dl.get(
|
|
329
|
-
elem1_t1w, dataset=datadir, result_renderer="disabled"
|
|
330
|
-
)
|
|
325
|
+
dl.get(elem1_t1w, dataset=datadir, result_renderer="disabled")
|
|
331
326
|
|
|
332
327
|
assert elem1_bold.is_symlink() is True
|
|
333
328
|
assert elem1_bold.is_file() is False
|
|
@@ -399,7 +394,7 @@ def test_DataladDataGrabber_previously_cloned_and_get_dirty(
|
|
|
399
394
|
assert elem1_t1w.exists() is False
|
|
400
395
|
|
|
401
396
|
# Clone dataset
|
|
402
|
-
dl.clone(uri, datadir, result_renderer="disabled")
|
|
397
|
+
dl.clone(uri, datadir, result_renderer="disabled")
|
|
403
398
|
|
|
404
399
|
# Files are there, but are empty symbolic links
|
|
405
400
|
assert datadir.exists() is True
|
|
@@ -408,9 +403,7 @@ def test_DataladDataGrabber_previously_cloned_and_get_dirty(
|
|
|
408
403
|
assert elem1_t1w.is_symlink() is True
|
|
409
404
|
assert elem1_t1w.is_file() is False
|
|
410
405
|
|
|
411
|
-
dl.get(
|
|
412
|
-
elem1_t1w, dataset=datadir, result_renderer="disabled"
|
|
413
|
-
)
|
|
406
|
+
dl.get(elem1_t1w, dataset=datadir, result_renderer="disabled")
|
|
414
407
|
|
|
415
408
|
assert elem1_bold.is_symlink() is True
|
|
416
409
|
assert elem1_bold.is_file() is False
|