brainles-preprocessing 0.6.13__tar.gz → 0.6.14__tar.gz
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.
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/PKG-INFO +2 -1
- brainles_preprocessing-0.6.14/brainles_preprocessing/utils/zenodo.py +352 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/pyproject.toml +1 -1
- brainles_preprocessing-0.6.13/brainles_preprocessing/utils/zenodo.py +0 -238
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/LICENSE +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/README.md +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/brain_extraction/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/brain_extraction/brain_extractor.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/brain_extraction/hd_bet.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/brain_extraction/synthstrip.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/brats/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/brats/t1_centric.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/brats/t1c_centric.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/cli.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/constants.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/defacing/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/defacing/defacer.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/defacing/quickshear/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/defacing/quickshear/nipy_quickshear.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/defacing/quickshear/quickshear.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/modality.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/n4_bias_correction/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/n4_bias_correction/n4_bias_corrector.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/n4_bias_correction/sitk/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/n4_bias_correction/sitk/sitk_n4_bias_corrector.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/normalization/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/normalization/normalizer_base.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/normalization/percentile_normalizer.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/normalization/windowing_normalizer.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/preprocessor/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/preprocessor/atlas_centric_preprocessor.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/preprocessor/native_space_preprocessor.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/preprocessor/preprocessor.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/ANTs/ANTs.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/ANTs/TODO_ANTs_parameters.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/ANTs/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/elastix/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/elastix/elastix.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/greedy/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/greedy/greedy.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/bin/groupwise_niftyreg_params.sh +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/bin/groupwise_niftyreg_run.sh +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/bin/reg_aladin +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/bin/reg_average +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/bin/reg_f3d +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/bin/reg_jacobian +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/bin/reg_measure +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/bin/reg_resample +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/bin/reg_tools +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/bin/reg_transform +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/AffineDeformationFieldKernel.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/AladinContent.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/BlockMatchingKernel.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/CPUAffineDeformationFieldKernel.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/CPUBlockMatchingKernel.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/CPUConvolutionKernel.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/CPUKernelFactory.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/CPUOptimiseKernel.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/CPUResampleImageKernel.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/ConvolutionKernel.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/Kernel.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/KernelFactory.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/OptimiseKernel.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/Platform.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/ResampleImageKernel.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_ReadWriteImage.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_ReadWriteMatrix.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_aladin.cpp +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_aladin.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_aladin_sym.cpp +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_aladin_sym.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_base.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_blockMatching.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_dti.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_f3d.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_f3d2.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_f3d_sym.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_femTrans.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_globalTrans.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_kld.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_lncc.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_localTrans.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_localTrans_jac.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_localTrans_regul.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_macros.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_maths.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_maths_eigen.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_measure.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_mind.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_nmi.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_optimiser.cpp +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_optimiser.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_resampling.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_splineBasis.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_ssd.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_stringFormat.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/_reg_tools.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/nifti1.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/nifti1_io.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/png.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/pngconf.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/pngdebug.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/pnginfo.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/pnglibconf.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/pngpriv.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/pngstruct.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/readpng.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/reg_png.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/zlib.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/znzlib.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/niftyreg_1.5.68/include/zutil.h +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/reg_aladin +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/reg_resample +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/rigid_reg.sh +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/niftyreg/niftyreg_scripts/transform.sh +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/registration/registrator.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/transform.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/utils/__init__.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/utils/citation_reminder.py +0 -0
- {brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/utils/generic.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brainles_preprocessing
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.14
|
|
4
4
|
Summary: Tool for preprocessing tasks in biomedical imaging, with a focus on (but not limited to) multi-modal brain MRI
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
21
22
|
Provides-Extra: all
|
|
22
23
|
Provides-Extra: itk-elastix
|
|
23
24
|
Provides-Extra: picsl-greedy
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
import tempfile
|
|
5
|
+
import zipfile
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import ClassVar, Dict, List, Tuple
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
from loguru import logger
|
|
11
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
12
|
+
|
|
13
|
+
# (connect, read) timeout in seconds for every request to Zenodo.
|
|
14
|
+
REQUEST_TIMEOUT = (10, 60)
|
|
15
|
+
|
|
16
|
+
ATLASES_FOLDER = Path(__file__).parent.parent / "registration" / "atlases"
|
|
17
|
+
ATLASES_RECORD_ID = "15236131"
|
|
18
|
+
|
|
19
|
+
SYNTHSTRIP_FOLDER = Path(__file__).parent.parent / "brain_extraction" / "weights"
|
|
20
|
+
SYNTHSTRIP_RECORD_ID = "16535633"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def fetch_atlases() -> Path:
|
|
24
|
+
"""
|
|
25
|
+
Ensure that the required atlases are available locally, downloading them if necessary.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Path: The path to the folder containing the atlases.
|
|
29
|
+
"""
|
|
30
|
+
record = ZenodoRecord(
|
|
31
|
+
record_id=ATLASES_RECORD_ID,
|
|
32
|
+
target_dir=ATLASES_FOLDER,
|
|
33
|
+
label="atlases",
|
|
34
|
+
)
|
|
35
|
+
return record.fetch()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def fetch_synthstrip() -> Path:
|
|
39
|
+
"""
|
|
40
|
+
Ensure that the SynthStrip weights are available locally, downloading them if necessary.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Path: The path to the folder containing the SynthStrip weights.
|
|
44
|
+
"""
|
|
45
|
+
record = ZenodoRecord(
|
|
46
|
+
record_id=SYNTHSTRIP_RECORD_ID,
|
|
47
|
+
target_dir=SYNTHSTRIP_FOLDER,
|
|
48
|
+
label="SynthStrip",
|
|
49
|
+
)
|
|
50
|
+
return record.fetch()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ZenodoException(Exception):
|
|
54
|
+
"""Raised when Zenodo cannot be reached or the request fails."""
|
|
55
|
+
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ZenodoRecord:
|
|
60
|
+
BASE_URL = "https://zenodo.org/api/records"
|
|
61
|
+
|
|
62
|
+
# Process-level cache: maps (record_id, target_dir) -> resolved Path
|
|
63
|
+
_cache: ClassVar[Dict[Tuple[str, str], Path]] = {}
|
|
64
|
+
|
|
65
|
+
def __init__(
|
|
66
|
+
self,
|
|
67
|
+
record_id: str,
|
|
68
|
+
target_dir: Path,
|
|
69
|
+
label: str = "asset",
|
|
70
|
+
):
|
|
71
|
+
self.record_id = record_id
|
|
72
|
+
self.target_dir = target_dir
|
|
73
|
+
self.label = label
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def clear_cache(cls) -> None:
|
|
77
|
+
"""Clear the process-level fetch cache. Primarily intended for testing."""
|
|
78
|
+
cls._cache.clear()
|
|
79
|
+
|
|
80
|
+
def _cache_key(self) -> Tuple[str, str]:
|
|
81
|
+
return (self.record_id, str(self.target_dir))
|
|
82
|
+
|
|
83
|
+
def fetch(self) -> Path:
|
|
84
|
+
"""Fetch the latest version of the record from Zenodo or from local storage.
|
|
85
|
+
|
|
86
|
+
Results are cached for the lifetime of the process so that repeated calls
|
|
87
|
+
(e.g. when processing many subjects in a loop) do not trigger redundant
|
|
88
|
+
Zenodo API requests.
|
|
89
|
+
"""
|
|
90
|
+
key = self._cache_key()
|
|
91
|
+
|
|
92
|
+
cached = ZenodoRecord._cache.get(key)
|
|
93
|
+
if cached is not None:
|
|
94
|
+
logger.debug(f"Using cached {self.label} path: {cached}")
|
|
95
|
+
return cached
|
|
96
|
+
|
|
97
|
+
result = self._fetch_uncached()
|
|
98
|
+
ZenodoRecord._cache[key] = result
|
|
99
|
+
return result
|
|
100
|
+
|
|
101
|
+
def _fetch_uncached(self) -> Path:
|
|
102
|
+
"""Perform the actual Zenodo check / download without consulting the cache."""
|
|
103
|
+
zenodo_response = self._get_metadata_and_files()
|
|
104
|
+
|
|
105
|
+
pattern = self._glob_pattern()
|
|
106
|
+
matching_folders = list(self.target_dir.glob(pattern))
|
|
107
|
+
latest_local = self._get_latest_version_folder_name(matching_folders)
|
|
108
|
+
|
|
109
|
+
if not latest_local:
|
|
110
|
+
if not zenodo_response:
|
|
111
|
+
msg = f"{self.label.title()} not found locally and Zenodo could not be reached."
|
|
112
|
+
logger.error(msg)
|
|
113
|
+
raise ZenodoException(msg)
|
|
114
|
+
|
|
115
|
+
logger.info(f"{self.label.title()} not found locally.")
|
|
116
|
+
metadata, files = zenodo_response
|
|
117
|
+
return self._download(metadata, files)
|
|
118
|
+
|
|
119
|
+
logger.info(f"Found local {self.label}: {latest_local}")
|
|
120
|
+
|
|
121
|
+
if not zenodo_response:
|
|
122
|
+
logger.warning(f"Zenodo unreachable. Using latest downloaded {self.label}.")
|
|
123
|
+
return self.target_dir / latest_local
|
|
124
|
+
|
|
125
|
+
metadata, files = zenodo_response
|
|
126
|
+
remote_version = metadata["version"]
|
|
127
|
+
local_version = latest_local.split("_v")[1]
|
|
128
|
+
|
|
129
|
+
if remote_version == local_version:
|
|
130
|
+
logger.info(f"Latest {self.label} ({remote_version}) already present.")
|
|
131
|
+
return self.target_dir / latest_local
|
|
132
|
+
|
|
133
|
+
logger.info(
|
|
134
|
+
f"New version of {self.label} available on Zenodo ({remote_version}). Replacing local copy..."
|
|
135
|
+
)
|
|
136
|
+
# The old version is only removed once the new one has been fully
|
|
137
|
+
# downloaded and staged, so a failed download never destroys a
|
|
138
|
+
# known-good local copy.
|
|
139
|
+
return self._download(
|
|
140
|
+
metadata,
|
|
141
|
+
files,
|
|
142
|
+
stale_folder=self.target_dir / latest_local,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def _glob_pattern(self) -> str:
|
|
146
|
+
return f"{self.record_id}_v*.*.*"
|
|
147
|
+
|
|
148
|
+
def _build_folder_path(
|
|
149
|
+
self,
|
|
150
|
+
version: str,
|
|
151
|
+
) -> Path:
|
|
152
|
+
return self.target_dir / f"{self.record_id}_v{version}"
|
|
153
|
+
|
|
154
|
+
def _get_latest_version_folder_name(
|
|
155
|
+
self,
|
|
156
|
+
folders: List[Path],
|
|
157
|
+
) -> str | None:
|
|
158
|
+
if not folders:
|
|
159
|
+
return None
|
|
160
|
+
latest = sorted(
|
|
161
|
+
folders,
|
|
162
|
+
reverse=True,
|
|
163
|
+
key=lambda x: tuple(map(int, str(x.name).split("_v")[1].split("."))),
|
|
164
|
+
)[0]
|
|
165
|
+
if not list(latest.glob("*")):
|
|
166
|
+
return None
|
|
167
|
+
return latest.name
|
|
168
|
+
|
|
169
|
+
def _get_metadata_and_files(self) -> Tuple[Dict, List[Dict]] | None:
|
|
170
|
+
"""Retrieve the record metadata and its file list from Zenodo.
|
|
171
|
+
|
|
172
|
+
Zenodo's API used to expose a single ``links["archive"]`` URL pointing to
|
|
173
|
+
a zip of all files. That link has been removed, so the record's ``files``
|
|
174
|
+
list is used instead: each entry provides a ``key`` and a ``links["self"]``
|
|
175
|
+
download URL.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
A ``(metadata, files)`` tuple, or ``None`` if Zenodo is unreachable or
|
|
179
|
+
returns an unexpected response.
|
|
180
|
+
"""
|
|
181
|
+
try:
|
|
182
|
+
response = requests.get(
|
|
183
|
+
f"{self.BASE_URL}/{self.record_id}", timeout=REQUEST_TIMEOUT
|
|
184
|
+
)
|
|
185
|
+
if response.status_code != 200:
|
|
186
|
+
error_msg = (
|
|
187
|
+
f"Cannot find record '{self.record_id}' on Zenodo "
|
|
188
|
+
f"({response.status_code=})."
|
|
189
|
+
)
|
|
190
|
+
logger.error(error_msg)
|
|
191
|
+
raise ZenodoException(error_msg)
|
|
192
|
+
|
|
193
|
+
data = response.json()
|
|
194
|
+
metadata = data["metadata"]
|
|
195
|
+
if not isinstance(metadata, dict) or not isinstance(
|
|
196
|
+
metadata.get("version"), str
|
|
197
|
+
):
|
|
198
|
+
raise ValueError("record metadata is missing a string 'version'")
|
|
199
|
+
|
|
200
|
+
files = data.get("files")
|
|
201
|
+
if files:
|
|
202
|
+
for entry in files:
|
|
203
|
+
if (
|
|
204
|
+
not isinstance(entry, dict)
|
|
205
|
+
or not isinstance(entry.get("key"), str)
|
|
206
|
+
or not isinstance(entry.get("links", {}).get("self"), str)
|
|
207
|
+
):
|
|
208
|
+
raise ValueError(
|
|
209
|
+
f"invalid file descriptor in record: {entry!r}"
|
|
210
|
+
)
|
|
211
|
+
return metadata, files
|
|
212
|
+
|
|
213
|
+
# Fallback for the deprecated archive link (kept for compatibility).
|
|
214
|
+
archive_url = data.get("links", {}).get("archive")
|
|
215
|
+
if isinstance(archive_url, str):
|
|
216
|
+
return metadata, [
|
|
217
|
+
{"key": f"{self.record_id}.zip", "links": {"self": archive_url}}
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
logger.warning(
|
|
221
|
+
f"Zenodo record '{self.record_id}' does not expose any downloadable files."
|
|
222
|
+
)
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
except requests.exceptions.RequestException as e:
|
|
226
|
+
logger.warning(f"Failed to fetch metadata from Zenodo: {e}")
|
|
227
|
+
return None
|
|
228
|
+
except (KeyError, ValueError) as e:
|
|
229
|
+
logger.warning(
|
|
230
|
+
f"Unexpected Zenodo response for record '{self.record_id}': {e}"
|
|
231
|
+
)
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
def _download(
|
|
235
|
+
self,
|
|
236
|
+
metadata: Dict,
|
|
237
|
+
files: List[Dict],
|
|
238
|
+
stale_folder: Path | None = None,
|
|
239
|
+
) -> Path:
|
|
240
|
+
folder = self._build_folder_path(metadata["version"])
|
|
241
|
+
self.target_dir.mkdir(parents=True, exist_ok=True)
|
|
242
|
+
|
|
243
|
+
# Stage everything in a temporary directory first, so a failed download
|
|
244
|
+
# never leaves a partial install or destroys an existing good version.
|
|
245
|
+
temp_folder = Path(
|
|
246
|
+
tempfile.mkdtemp(dir=self.target_dir, prefix=f".{folder.name}.tmp.")
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
logger.info(f"Downloading {self.label} from Zenodo. This may take a while...")
|
|
250
|
+
|
|
251
|
+
try:
|
|
252
|
+
for file in files:
|
|
253
|
+
key = file["key"]
|
|
254
|
+
file_url = file["links"]["self"]
|
|
255
|
+
destination = self._safe_destination(temp_folder, key)
|
|
256
|
+
self._download_file(file_url, destination)
|
|
257
|
+
|
|
258
|
+
if destination.suffix == ".zip":
|
|
259
|
+
self._extract_zip(destination, temp_folder)
|
|
260
|
+
|
|
261
|
+
if folder.exists():
|
|
262
|
+
shutil.rmtree(folder, ignore_errors=True)
|
|
263
|
+
|
|
264
|
+
# Install the fully staged version before removing the previous one,
|
|
265
|
+
# so an interrupted/failed swap can never leave us with neither.
|
|
266
|
+
temp_folder.rename(folder)
|
|
267
|
+
|
|
268
|
+
if (
|
|
269
|
+
stale_folder is not None
|
|
270
|
+
and stale_folder != folder
|
|
271
|
+
and stale_folder.exists()
|
|
272
|
+
):
|
|
273
|
+
shutil.rmtree(
|
|
274
|
+
stale_folder,
|
|
275
|
+
onerror=lambda func, path, excinfo: logger.warning(
|
|
276
|
+
f"Failed to delete {path}: {excinfo}"
|
|
277
|
+
),
|
|
278
|
+
)
|
|
279
|
+
except BaseException:
|
|
280
|
+
shutil.rmtree(temp_folder, ignore_errors=True)
|
|
281
|
+
raise
|
|
282
|
+
|
|
283
|
+
logger.info(f"{self.label.title()} downloaded to {folder}")
|
|
284
|
+
return folder
|
|
285
|
+
|
|
286
|
+
@staticmethod
|
|
287
|
+
def _safe_destination(base: Path, key: str) -> Path:
|
|
288
|
+
"""Join ``key`` onto ``base`` and reject paths that escape it.
|
|
289
|
+
|
|
290
|
+
``key`` comes from the (remote) record metadata, so it must not be able
|
|
291
|
+
to write outside the staging directory via ``..`` or an absolute path.
|
|
292
|
+
"""
|
|
293
|
+
base_resolved = base.resolve()
|
|
294
|
+
destination = (base_resolved / key).resolve()
|
|
295
|
+
if destination != base_resolved and base_resolved not in destination.parents:
|
|
296
|
+
raise ZenodoException(f"Unsafe file key in record: {key!r}")
|
|
297
|
+
return destination
|
|
298
|
+
|
|
299
|
+
def _download_file(self, url: str, destination: Path) -> None:
|
|
300
|
+
chunk_size = 1024 * 1024 # 1 MiB
|
|
301
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
302
|
+
|
|
303
|
+
try:
|
|
304
|
+
response = requests.get(url, stream=True, timeout=REQUEST_TIMEOUT)
|
|
305
|
+
if response.status_code != 200:
|
|
306
|
+
msg = (
|
|
307
|
+
f"Failed to download {self.label} file '{destination.name}'. "
|
|
308
|
+
f"Status code: {response.status_code}"
|
|
309
|
+
)
|
|
310
|
+
logger.error(msg)
|
|
311
|
+
raise ZenodoException(msg)
|
|
312
|
+
|
|
313
|
+
with Progress(
|
|
314
|
+
SpinnerColumn(),
|
|
315
|
+
TextColumn(f"[cyan]Downloading {destination.name}..."),
|
|
316
|
+
TextColumn("[cyan]{task.completed:.2f} MB"),
|
|
317
|
+
transient=True,
|
|
318
|
+
) as progress:
|
|
319
|
+
task = progress.add_task("", total=None)
|
|
320
|
+
with open(destination, "wb") as f:
|
|
321
|
+
for chunk in response.iter_content(chunk_size=chunk_size):
|
|
322
|
+
if not chunk:
|
|
323
|
+
continue
|
|
324
|
+
f.write(chunk)
|
|
325
|
+
progress.update(task, advance=len(chunk) / (1024**2))
|
|
326
|
+
except requests.exceptions.RequestException as e:
|
|
327
|
+
msg = f"Failed to download {self.label} file '{destination.name}': {e}"
|
|
328
|
+
logger.error(msg)
|
|
329
|
+
raise ZenodoException(msg) from e
|
|
330
|
+
|
|
331
|
+
def _extract_zip(
|
|
332
|
+
self,
|
|
333
|
+
zip_path: Path,
|
|
334
|
+
folder: Path,
|
|
335
|
+
) -> None:
|
|
336
|
+
with zipfile.ZipFile(zip_path) as z:
|
|
337
|
+
names = z.namelist()
|
|
338
|
+
with Progress(transient=True) as progress:
|
|
339
|
+
task = progress.add_task(
|
|
340
|
+
f"[cyan]Extracting {zip_path.name}...", total=len(names)
|
|
341
|
+
)
|
|
342
|
+
for i, name in enumerate(names):
|
|
343
|
+
self._safe_destination(folder, name)
|
|
344
|
+
z.extract(name, folder)
|
|
345
|
+
progress.update(task, completed=i + 1)
|
|
346
|
+
zip_path.unlink() # Remove the archive after extraction
|
|
347
|
+
|
|
348
|
+
# Extract any archives nested inside this one (e.g. a record whose
|
|
349
|
+
# archive contains the SynthStrip weights zip).
|
|
350
|
+
for nested in folder.rglob("*.zip"):
|
|
351
|
+
if nested.is_file():
|
|
352
|
+
self._extract_zip(nested, folder)
|
|
@@ -14,7 +14,7 @@ exclude = [
|
|
|
14
14
|
]
|
|
15
15
|
|
|
16
16
|
name = "brainles_preprocessing"
|
|
17
|
-
version = "0.6.
|
|
17
|
+
version = "0.6.14" # will be overwritten by poetry-dynamic-versioning but needs to be set
|
|
18
18
|
description = "Tool for preprocessing tasks in biomedical imaging, with a focus on (but not limited to) multi-modal brain MRI"
|
|
19
19
|
license = "Apache-2.0"
|
|
20
20
|
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import shutil
|
|
4
|
-
import zipfile
|
|
5
|
-
from io import BytesIO
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import ClassVar, Dict, List, Tuple
|
|
8
|
-
|
|
9
|
-
import requests
|
|
10
|
-
from loguru import logger
|
|
11
|
-
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
12
|
-
|
|
13
|
-
ATLASES_FOLDER = Path(__file__).parent.parent / "registration" / "atlases"
|
|
14
|
-
ATLASES_RECORD_ID = "15236131"
|
|
15
|
-
|
|
16
|
-
SYNTHSTRIP_FOLDER = Path(__file__).parent.parent / "brain_extraction" / "weights"
|
|
17
|
-
SYNTHSTRIP_RECORD_ID = "16535633"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def fetch_atlases() -> Path:
|
|
21
|
-
"""
|
|
22
|
-
Ensure that the required atlases are available locally, downloading them if necessary.
|
|
23
|
-
|
|
24
|
-
Returns:
|
|
25
|
-
Path: The path to the folder containing the atlases.
|
|
26
|
-
"""
|
|
27
|
-
record = ZenodoRecord(
|
|
28
|
-
record_id=ATLASES_RECORD_ID,
|
|
29
|
-
target_dir=ATLASES_FOLDER,
|
|
30
|
-
label="atlases",
|
|
31
|
-
)
|
|
32
|
-
return record.fetch()
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def fetch_synthstrip() -> Path:
|
|
36
|
-
"""
|
|
37
|
-
Ensure that the SynthStrip weights are available locally, downloading them if necessary.
|
|
38
|
-
|
|
39
|
-
Returns:
|
|
40
|
-
Path: The path to the folder containing the SynthStrip weights.
|
|
41
|
-
"""
|
|
42
|
-
record = ZenodoRecord(
|
|
43
|
-
record_id=SYNTHSTRIP_RECORD_ID,
|
|
44
|
-
target_dir=SYNTHSTRIP_FOLDER,
|
|
45
|
-
label="SynthStrip",
|
|
46
|
-
)
|
|
47
|
-
return record.fetch()
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class ZenodoException(Exception):
|
|
51
|
-
"""Raised when Zenodo cannot be reached or the request fails."""
|
|
52
|
-
|
|
53
|
-
pass
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class ZenodoRecord:
|
|
57
|
-
BASE_URL = "https://zenodo.org/api/records"
|
|
58
|
-
|
|
59
|
-
# Process-level cache: maps (record_id, target_dir) -> resolved Path
|
|
60
|
-
_cache: ClassVar[Dict[Tuple[str, str], Path]] = {}
|
|
61
|
-
|
|
62
|
-
def __init__(
|
|
63
|
-
self,
|
|
64
|
-
record_id: str,
|
|
65
|
-
target_dir: Path,
|
|
66
|
-
label: str = "asset",
|
|
67
|
-
):
|
|
68
|
-
self.record_id = record_id
|
|
69
|
-
self.target_dir = target_dir
|
|
70
|
-
self.label = label
|
|
71
|
-
|
|
72
|
-
@classmethod
|
|
73
|
-
def clear_cache(cls) -> None:
|
|
74
|
-
"""Clear the process-level fetch cache. Primarily intended for testing."""
|
|
75
|
-
cls._cache.clear()
|
|
76
|
-
|
|
77
|
-
def _cache_key(self) -> Tuple[str, str]:
|
|
78
|
-
return (self.record_id, str(self.target_dir))
|
|
79
|
-
|
|
80
|
-
def fetch(self) -> Path:
|
|
81
|
-
"""Fetch the latest version of the record from Zenodo or from local storage.
|
|
82
|
-
|
|
83
|
-
Results are cached for the lifetime of the process so that repeated calls
|
|
84
|
-
(e.g. when processing many subjects in a loop) do not trigger redundant
|
|
85
|
-
Zenodo API requests.
|
|
86
|
-
"""
|
|
87
|
-
key = self._cache_key()
|
|
88
|
-
|
|
89
|
-
cached = ZenodoRecord._cache.get(key)
|
|
90
|
-
if cached is not None:
|
|
91
|
-
logger.debug(f"Using cached {self.label} path: {cached}")
|
|
92
|
-
return cached
|
|
93
|
-
|
|
94
|
-
result = self._fetch_uncached()
|
|
95
|
-
ZenodoRecord._cache[key] = result
|
|
96
|
-
return result
|
|
97
|
-
|
|
98
|
-
def _fetch_uncached(self) -> Path:
|
|
99
|
-
"""Perform the actual Zenodo check / download without consulting the cache."""
|
|
100
|
-
zenodo_response = self._get_metadata_and_archive_url()
|
|
101
|
-
|
|
102
|
-
pattern = self._glob_pattern()
|
|
103
|
-
matching_folders = list(self.target_dir.glob(pattern))
|
|
104
|
-
latest_local = self._get_latest_version_folder_name(matching_folders)
|
|
105
|
-
|
|
106
|
-
if not latest_local:
|
|
107
|
-
if not zenodo_response:
|
|
108
|
-
msg = f"{self.label.title()} not found locally and Zenodo could not be reached."
|
|
109
|
-
logger.error(msg)
|
|
110
|
-
raise ZenodoException(msg)
|
|
111
|
-
|
|
112
|
-
logger.info(f"{self.label.title()} not found locally.")
|
|
113
|
-
metadata, archive_url = zenodo_response
|
|
114
|
-
return self._download(metadata, archive_url)
|
|
115
|
-
|
|
116
|
-
logger.info(f"Found local {self.label}: {latest_local}")
|
|
117
|
-
|
|
118
|
-
if not zenodo_response:
|
|
119
|
-
logger.warning(f"Zenodo unreachable. Using latest downloaded {self.label}.")
|
|
120
|
-
return self.target_dir / latest_local
|
|
121
|
-
|
|
122
|
-
metadata, archive_url = zenodo_response
|
|
123
|
-
remote_version = metadata["version"]
|
|
124
|
-
local_version = latest_local.split("_v")[1]
|
|
125
|
-
|
|
126
|
-
if remote_version == local_version:
|
|
127
|
-
logger.info(f"Latest {self.label} ({remote_version}) already present.")
|
|
128
|
-
return self.target_dir / latest_local
|
|
129
|
-
|
|
130
|
-
logger.info(
|
|
131
|
-
f"New version of {self.label} available on Zenodo ({remote_version}). Replacing local copy..."
|
|
132
|
-
)
|
|
133
|
-
shutil.rmtree(
|
|
134
|
-
self.target_dir / latest_local,
|
|
135
|
-
onerror=lambda func, path, excinfo: logger.warning(
|
|
136
|
-
f"Failed to delete {path}: {excinfo}"
|
|
137
|
-
),
|
|
138
|
-
)
|
|
139
|
-
return self._download(metadata, archive_url)
|
|
140
|
-
|
|
141
|
-
def _glob_pattern(self) -> str:
|
|
142
|
-
return f"{self.record_id}_v*.*.*"
|
|
143
|
-
|
|
144
|
-
def _build_folder_path(
|
|
145
|
-
self,
|
|
146
|
-
version: str,
|
|
147
|
-
) -> Path:
|
|
148
|
-
return self.target_dir / f"{self.record_id}_v{version}"
|
|
149
|
-
|
|
150
|
-
def _get_latest_version_folder_name(
|
|
151
|
-
self,
|
|
152
|
-
folders: List[Path],
|
|
153
|
-
) -> str | None:
|
|
154
|
-
if not folders:
|
|
155
|
-
return None
|
|
156
|
-
latest = sorted(
|
|
157
|
-
folders,
|
|
158
|
-
reverse=True,
|
|
159
|
-
key=lambda x: tuple(map(int, str(x.name).split("_v")[1].split("."))),
|
|
160
|
-
)[0]
|
|
161
|
-
if not list(latest.glob("*")):
|
|
162
|
-
return None
|
|
163
|
-
return latest.name
|
|
164
|
-
|
|
165
|
-
def _get_metadata_and_archive_url(self) -> Tuple[Dict, str] | None:
|
|
166
|
-
try:
|
|
167
|
-
response = requests.get(f"{self.BASE_URL}/{self.record_id}")
|
|
168
|
-
if response.status_code != 200:
|
|
169
|
-
error_msg = (
|
|
170
|
-
f"Cannot find record '{self.record_id}' on Zenodo "
|
|
171
|
-
f"({response.status_code=})."
|
|
172
|
-
)
|
|
173
|
-
logger.error(error_msg)
|
|
174
|
-
raise ZenodoException(error_msg)
|
|
175
|
-
|
|
176
|
-
data = response.json()
|
|
177
|
-
return data["metadata"], data["links"]["archive"]
|
|
178
|
-
|
|
179
|
-
except requests.exceptions.RequestException as e:
|
|
180
|
-
logger.warning(f"Failed to fetch metadata from Zenodo: {e}")
|
|
181
|
-
return None
|
|
182
|
-
|
|
183
|
-
def _download(
|
|
184
|
-
self,
|
|
185
|
-
metadata: Dict,
|
|
186
|
-
archive_url: str,
|
|
187
|
-
) -> Path:
|
|
188
|
-
folder = self._build_folder_path(metadata["version"])
|
|
189
|
-
folder.mkdir(parents=True, exist_ok=True)
|
|
190
|
-
|
|
191
|
-
logger.info(f"Downloading {self.label} from Zenodo. This may take a while...")
|
|
192
|
-
|
|
193
|
-
response = requests.get(archive_url, stream=True)
|
|
194
|
-
if response.status_code != 200:
|
|
195
|
-
msg = (
|
|
196
|
-
f"Failed to download {self.label}. Status code: {response.status_code}"
|
|
197
|
-
)
|
|
198
|
-
logger.error(msg)
|
|
199
|
-
raise ZenodoException(msg)
|
|
200
|
-
|
|
201
|
-
self._extract_archive(response, folder)
|
|
202
|
-
logger.info(f"{self.label.title()} extracted to {folder}")
|
|
203
|
-
return folder
|
|
204
|
-
|
|
205
|
-
def _extract_archive(
|
|
206
|
-
self,
|
|
207
|
-
response: requests.Response,
|
|
208
|
-
folder: Path,
|
|
209
|
-
):
|
|
210
|
-
chunk_size = 1024 # 1KB
|
|
211
|
-
buffer = BytesIO()
|
|
212
|
-
|
|
213
|
-
with Progress(
|
|
214
|
-
SpinnerColumn(),
|
|
215
|
-
TextColumn(f"[cyan]Downloading {self.label}..."),
|
|
216
|
-
TextColumn("[cyan]{task.completed:.2f} MB"),
|
|
217
|
-
transient=True,
|
|
218
|
-
) as progress:
|
|
219
|
-
task = progress.add_task("", total=None)
|
|
220
|
-
for chunk in response.iter_content(chunk_size=chunk_size):
|
|
221
|
-
buffer.write(chunk)
|
|
222
|
-
progress.update(task, advance=len(chunk) / (chunk_size**2))
|
|
223
|
-
|
|
224
|
-
with zipfile.ZipFile(buffer) as z:
|
|
225
|
-
z.extractall(folder)
|
|
226
|
-
|
|
227
|
-
for file in folder.iterdir():
|
|
228
|
-
if file.is_file() and file.suffix == ".zip":
|
|
229
|
-
with zipfile.ZipFile(file) as inner_zip:
|
|
230
|
-
files = inner_zip.namelist()
|
|
231
|
-
with Progress(transient=True) as progress:
|
|
232
|
-
task = progress.add_task(
|
|
233
|
-
f"[cyan]Extracting inner zip...", total=len(files)
|
|
234
|
-
)
|
|
235
|
-
for i, f in enumerate(files):
|
|
236
|
-
inner_zip.extract(f, folder)
|
|
237
|
-
progress.update(task, completed=i + 1)
|
|
238
|
-
file.unlink() # Remove inner zip after extraction
|
|
File without changes
|
|
File without changes
|
{brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/cli.py
RENAMED
|
File without changes
|
{brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/constants.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/modality.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{brainles_preprocessing-0.6.13 → brainles_preprocessing-0.6.14}/brainles_preprocessing/transform.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|