voxelops 0.1.0__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.
- voxelops-0.1.0/.claude/settings.local.json +18 -0
- voxelops-0.1.0/.github/workflows/ci.yml +47 -0
- voxelops-0.1.0/.gitignore +56 -0
- voxelops-0.1.0/.pre-commit-config.yaml +18 -0
- voxelops-0.1.0/.python-version +1 -0
- voxelops-0.1.0/.readthedocs.yaml +20 -0
- voxelops-0.1.0/.uvignore +13 -0
- voxelops-0.1.0/ARCHITECTURE.md +217 -0
- voxelops-0.1.0/LICENSE +21 -0
- voxelops-0.1.0/Makefile +130 -0
- voxelops-0.1.0/PKG-INFO +221 -0
- voxelops-0.1.0/README.rst +176 -0
- voxelops-0.1.0/UV_QUICKSTART.md +381 -0
- voxelops-0.1.0/docs/Makefile +20 -0
- voxelops-0.1.0/docs/_build/doctrees/contributing.doctree +0 -0
- voxelops-0.1.0/docs/_build/doctrees/environment.pickle +0 -0
- voxelops-0.1.0/docs/_build/doctrees/getting_started.doctree +0 -0
- voxelops-0.1.0/docs/_build/doctrees/index.doctree +0 -0
- voxelops-0.1.0/docs/_build/doctrees/source/modules.doctree +0 -0
- voxelops-0.1.0/docs/_build/doctrees/source/voxelops.doctree +0 -0
- voxelops-0.1.0/docs/_build/doctrees/source/voxelops.runners.doctree +0 -0
- voxelops-0.1.0/docs/_build/doctrees/source/voxelops.schemas.doctree +0 -0
- voxelops-0.1.0/docs/_build/doctrees/source/voxelops.utils.doctree +0 -0
- voxelops-0.1.0/docs/_build/doctrees/tutorials.doctree +0 -0
- voxelops-0.1.0/docs/_build/doctrees/workflows.doctree +0 -0
- voxelops-0.1.0/docs/_build/html/.buildinfo +4 -0
- voxelops-0.1.0/docs/_build/html/_images/Gemini_Generated_Image_m9bi47m9bi47m9bi.png +0 -0
- voxelops-0.1.0/docs/_build/html/_modules/index.html +121 -0
- voxelops-0.1.0/docs/_build/html/_modules/voxelops/exceptions.html +295 -0
- voxelops-0.1.0/docs/_build/html/_modules/voxelops/runners/_base.html +311 -0
- voxelops-0.1.0/docs/_build/html/_modules/voxelops/runners/heudiconv.html +296 -0
- voxelops-0.1.0/docs/_build/html/_modules/voxelops/runners/qsiparc.html +263 -0
- voxelops-0.1.0/docs/_build/html/_modules/voxelops/runners/qsiprep.html +287 -0
- voxelops-0.1.0/docs/_build/html/_modules/voxelops/runners/qsirecon.html +285 -0
- voxelops-0.1.0/docs/_build/html/_modules/voxelops/schemas/heudiconv.html +247 -0
- voxelops-0.1.0/docs/_build/html/_modules/voxelops/schemas/qsiparc.html +230 -0
- voxelops-0.1.0/docs/_build/html/_modules/voxelops/schemas/qsiprep.html +266 -0
- voxelops-0.1.0/docs/_build/html/_modules/voxelops/schemas/qsirecon.html +280 -0
- voxelops-0.1.0/docs/_build/html/_modules/voxelops/utils/bids.html +584 -0
- voxelops-0.1.0/docs/_build/html/_sources/contributing.rst.txt +211 -0
- voxelops-0.1.0/docs/_build/html/_sources/getting_started.rst.txt +83 -0
- voxelops-0.1.0/docs/_build/html/_sources/index.rst.txt +105 -0
- voxelops-0.1.0/docs/_build/html/_sources/source/modules.rst.txt +7 -0
- voxelops-0.1.0/docs/_build/html/_sources/source/voxelops.rst.txt +27 -0
- voxelops-0.1.0/docs/_build/html/_sources/source/voxelops.runners.rst.txt +54 -0
- voxelops-0.1.0/docs/_build/html/_sources/source/voxelops.schemas.rst.txt +46 -0
- voxelops-0.1.0/docs/_build/html/_sources/source/voxelops.utils.rst.txt +23 -0
- voxelops-0.1.0/docs/_build/html/_sources/tutorials.rst.txt +23 -0
- voxelops-0.1.0/docs/_build/html/_sources/workflows.rst.txt +77 -0
- voxelops-0.1.0/docs/_build/html/_static/Gemini_Generated_Image_m9bi47m9bi47m9bi.png +0 -0
- voxelops-0.1.0/docs/_build/html/_static/_sphinx_javascript_frameworks_compat.js +123 -0
- voxelops-0.1.0/docs/_build/html/_static/basic.css +925 -0
- voxelops-0.1.0/docs/_build/html/_static/css/badge_only.css +1 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/fontawesome-webfont.eot +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/fontawesome-webfont.svg +2671 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/fontawesome-webfont.ttf +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff2 +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/lato-bold-italic.woff +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/lato-bold-italic.woff2 +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/lato-bold.woff +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/lato-bold.woff2 +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/lato-normal-italic.woff +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/lato-normal-italic.woff2 +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/lato-normal.woff +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/fonts/lato-normal.woff2 +0 -0
- voxelops-0.1.0/docs/_build/html/_static/css/theme.css +4 -0
- voxelops-0.1.0/docs/_build/html/_static/doctools.js +156 -0
- voxelops-0.1.0/docs/_build/html/_static/documentation_options.js +13 -0
- voxelops-0.1.0/docs/_build/html/_static/file.png +0 -0
- voxelops-0.1.0/docs/_build/html/_static/jquery.js +2 -0
- voxelops-0.1.0/docs/_build/html/_static/js/badge_only.js +1 -0
- voxelops-0.1.0/docs/_build/html/_static/js/html5shiv-printshiv.min.js +4 -0
- voxelops-0.1.0/docs/_build/html/_static/js/html5shiv.min.js +4 -0
- voxelops-0.1.0/docs/_build/html/_static/js/theme.js +1 -0
- voxelops-0.1.0/docs/_build/html/_static/language_data.js +199 -0
- voxelops-0.1.0/docs/_build/html/_static/minus.png +0 -0
- voxelops-0.1.0/docs/_build/html/_static/plus.png +0 -0
- voxelops-0.1.0/docs/_build/html/_static/pygments.css +75 -0
- voxelops-0.1.0/docs/_build/html/_static/searchtools.js +620 -0
- voxelops-0.1.0/docs/_build/html/_static/sphinx_highlight.js +154 -0
- voxelops-0.1.0/docs/_build/html/contributing.html +306 -0
- voxelops-0.1.0/docs/_build/html/genindex.html +683 -0
- voxelops-0.1.0/docs/_build/html/getting_started.html +197 -0
- voxelops-0.1.0/docs/_build/html/index.html +233 -0
- voxelops-0.1.0/docs/_build/html/objects.inv +0 -0
- voxelops-0.1.0/docs/_build/html/py-modindex.html +200 -0
- voxelops-0.1.0/docs/_build/html/search.html +130 -0
- voxelops-0.1.0/docs/_build/html/searchindex.js +1 -0
- voxelops-0.1.0/docs/_build/html/source/modules.html +167 -0
- voxelops-0.1.0/docs/_build/html/source/voxelops.html +470 -0
- voxelops-0.1.0/docs/_build/html/source/voxelops.runners.html +452 -0
- voxelops-0.1.0/docs/_build/html/source/voxelops.schemas.html +846 -0
- voxelops-0.1.0/docs/_build/html/source/voxelops.utils.html +251 -0
- voxelops-0.1.0/docs/_build/html/tutorials.html +136 -0
- voxelops-0.1.0/docs/_build/html/workflows.html +186 -0
- voxelops-0.1.0/docs/conf.py +74 -0
- voxelops-0.1.0/docs/contributing.rst +211 -0
- voxelops-0.1.0/docs/getting_started.rst +83 -0
- voxelops-0.1.0/docs/images/Gemini_Generated_Image_m9bi47m9bi47m9bi.png +0 -0
- voxelops-0.1.0/docs/index.rst +109 -0
- voxelops-0.1.0/docs/make.bat +35 -0
- voxelops-0.1.0/docs/requirements.txt +3 -0
- voxelops-0.1.0/docs/source/modules.rst +7 -0
- voxelops-0.1.0/docs/source/voxelops.rst +27 -0
- voxelops-0.1.0/docs/source/voxelops.runners.rst +54 -0
- voxelops-0.1.0/docs/source/voxelops.schemas.rst +46 -0
- voxelops-0.1.0/docs/source/voxelops.utils.rst +23 -0
- voxelops-0.1.0/docs/tutorials.rst +23 -0
- voxelops-0.1.0/docs/workflows.rst +77 -0
- voxelops-0.1.0/examples/dicom_to_bids.py +35 -0
- voxelops-0.1.0/examples/qsiparc.py +37 -0
- voxelops-0.1.0/examples/qsiprep.py +35 -0
- voxelops-0.1.0/examples/qsirecon.py +36 -0
- voxelops-0.1.0/notebooks/.ruff.toml +2 -0
- voxelops-0.1.0/notebooks/01_heudiconv_basics.ipynb +370 -0
- voxelops-0.1.0/notebooks/02_qsiprep_basics.ipynb +570 -0
- voxelops-0.1.0/notebooks/03_qsirecon_basics.ipynb +586 -0
- voxelops-0.1.0/notebooks/04_qsiparc_basics.ipynb +521 -0
- voxelops-0.1.0/notebooks/05_full_pipeline.ipynb +555 -0
- voxelops-0.1.0/notebooks/README.md +99 -0
- voxelops-0.1.0/notebooks/generate_atlas_dataset.ipynb +239 -0
- voxelops-0.1.0/pyproject.toml +107 -0
- voxelops-0.1.0/src/voxelops/__init__.py +98 -0
- voxelops-0.1.0/src/voxelops/exceptions.py +158 -0
- voxelops-0.1.0/src/voxelops/runners/__init__.py +13 -0
- voxelops-0.1.0/src/voxelops/runners/_base.py +191 -0
- voxelops-0.1.0/src/voxelops/runners/heudiconv.py +202 -0
- voxelops-0.1.0/src/voxelops/runners/qsiparc.py +150 -0
- voxelops-0.1.0/src/voxelops/runners/qsiprep.py +187 -0
- voxelops-0.1.0/src/voxelops/runners/qsirecon.py +173 -0
- voxelops-0.1.0/src/voxelops/schemas/__init__.py +41 -0
- voxelops-0.1.0/src/voxelops/schemas/heudiconv.py +121 -0
- voxelops-0.1.0/src/voxelops/schemas/qsiparc.py +107 -0
- voxelops-0.1.0/src/voxelops/schemas/qsiprep.py +140 -0
- voxelops-0.1.0/src/voxelops/schemas/qsirecon.py +154 -0
- voxelops-0.1.0/src/voxelops/utils/__init__.py +1 -0
- voxelops-0.1.0/src/voxelops/utils/bids.py +486 -0
- voxelops-0.1.0/tests/conftest.py +144 -0
- voxelops-0.1.0/tests/test_exceptions.py +143 -0
- voxelops-0.1.0/tests/test_init.py +40 -0
- voxelops-0.1.0/tests/test_runners_base.py +177 -0
- voxelops-0.1.0/tests/test_runners_heudiconv.py +277 -0
- voxelops-0.1.0/tests/test_runners_qsiparc.py +189 -0
- voxelops-0.1.0/tests/test_runners_qsiprep.py +187 -0
- voxelops-0.1.0/tests/test_runners_qsirecon.py +223 -0
- voxelops-0.1.0/tests/test_schemas_heudiconv.py +80 -0
- voxelops-0.1.0/tests/test_schemas_qsiparc.py +73 -0
- voxelops-0.1.0/tests/test_schemas_qsiprep.py +88 -0
- voxelops-0.1.0/tests/test_schemas_qsirecon.py +115 -0
- voxelops-0.1.0/tests/test_utils_bids.py +449 -0
- voxelops-0.1.0/uv.lock +3965 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(tree:*)",
|
|
5
|
+
"Bash(uv lock:*)",
|
|
6
|
+
"Bash(make:*)",
|
|
7
|
+
"Bash(python -m py_compile:*)",
|
|
8
|
+
"Bash(python:*)",
|
|
9
|
+
"Bash(git add:*)",
|
|
10
|
+
"Bash(git commit:*)",
|
|
11
|
+
"Bash(git push)",
|
|
12
|
+
"Bash(git push:*)",
|
|
13
|
+
"Bash(.venv/bin/pip list:*)",
|
|
14
|
+
"Bash(git pull:*)",
|
|
15
|
+
"Bash(git stash:*)"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v3
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v4
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install --upgrade pip
|
|
27
|
+
pip install ".[dev]"
|
|
28
|
+
|
|
29
|
+
- name: Lint with ruff
|
|
30
|
+
run: |
|
|
31
|
+
pip install ruff
|
|
32
|
+
ruff check .
|
|
33
|
+
|
|
34
|
+
- name: Test with pytest
|
|
35
|
+
run: |
|
|
36
|
+
pytest
|
|
37
|
+
|
|
38
|
+
- name: Upload coverage reports to Codecov
|
|
39
|
+
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de
|
|
40
|
+
with:
|
|
41
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
42
|
+
slug: GalKepler/VoxelOps
|
|
43
|
+
|
|
44
|
+
- name: Build documentation
|
|
45
|
+
run: |
|
|
46
|
+
pip install ".[docs]"
|
|
47
|
+
make -C docs html
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
|
|
23
|
+
# Testing
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
.coverage
|
|
26
|
+
htmlcov/
|
|
27
|
+
.tox/
|
|
28
|
+
|
|
29
|
+
# Virtual environments
|
|
30
|
+
venv/
|
|
31
|
+
env/
|
|
32
|
+
ENV/
|
|
33
|
+
.venv
|
|
34
|
+
|
|
35
|
+
# IDE
|
|
36
|
+
.vscode/
|
|
37
|
+
.idea/
|
|
38
|
+
*.swp
|
|
39
|
+
*.swo
|
|
40
|
+
*~
|
|
41
|
+
|
|
42
|
+
# OS
|
|
43
|
+
.DS_Store
|
|
44
|
+
Thumbs.db
|
|
45
|
+
|
|
46
|
+
# Logs
|
|
47
|
+
*.log
|
|
48
|
+
logs/
|
|
49
|
+
|
|
50
|
+
# Development
|
|
51
|
+
.mypy_cache/
|
|
52
|
+
.ruff_cache/
|
|
53
|
+
|
|
54
|
+
heuristic.py
|
|
55
|
+
bids_filters.json
|
|
56
|
+
qsirecon_spec.yaml
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v4.6.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-added-large-files
|
|
9
|
+
- repo: https://github.com/psf/black
|
|
10
|
+
rev: 24.3.0
|
|
11
|
+
hooks:
|
|
12
|
+
- id: black
|
|
13
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
14
|
+
rev: v0.3.4
|
|
15
|
+
hooks:
|
|
16
|
+
- id: ruff
|
|
17
|
+
args: [--fix, --exit-non-zero-on-fix]
|
|
18
|
+
- id: ruff-format
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.11
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
build:
|
|
4
|
+
os: ubuntu-24.04
|
|
5
|
+
tools:
|
|
6
|
+
python: "3.12"
|
|
7
|
+
|
|
8
|
+
sphinx:
|
|
9
|
+
configuration: docs/conf.py
|
|
10
|
+
|
|
11
|
+
python:
|
|
12
|
+
install:
|
|
13
|
+
- requirements: docs/requirements.txt
|
|
14
|
+
- method: pip
|
|
15
|
+
path: .
|
|
16
|
+
|
|
17
|
+
formats:
|
|
18
|
+
- htmlzip
|
|
19
|
+
- pdf
|
|
20
|
+
- epub
|
voxelops-0.1.0/.uvignore
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# VoxelOps Architecture
|
|
2
|
+
|
|
3
|
+
## Design Principles
|
|
4
|
+
|
|
5
|
+
1. **Simplicity** -- Functions, not classes. Dicts, not complex objects.
|
|
6
|
+
2. **Clarity** -- Typed dataclass schemas for every input, output, and configuration.
|
|
7
|
+
3. **Reproducibility** -- The exact Docker command is stored in every execution record.
|
|
8
|
+
4. **Database-Friendly** -- Results are plain dicts, trivial to persist anywhere.
|
|
9
|
+
5. **Extensibility** -- New procedures follow the same pattern, easy to add.
|
|
10
|
+
|
|
11
|
+
## Directory Structure
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
VoxelOps/
|
|
15
|
+
src/voxelops/
|
|
16
|
+
__init__.py # Package exports, version
|
|
17
|
+
exceptions.py # Exception hierarchy
|
|
18
|
+
runners/
|
|
19
|
+
__init__.py # Runner exports
|
|
20
|
+
_base.py # Shared: run_docker, validate_input_dir, validate_participant
|
|
21
|
+
heudiconv.py # DICOM -> BIDS
|
|
22
|
+
qsiprep.py # Diffusion preprocessing
|
|
23
|
+
qsirecon.py # Diffusion reconstruction
|
|
24
|
+
qsiparc.py # Parcellation (via parcellate, not Docker)
|
|
25
|
+
schemas/
|
|
26
|
+
__init__.py # Schema exports
|
|
27
|
+
heudiconv.py # HeudiconvInputs / Outputs / Defaults
|
|
28
|
+
qsiprep.py # QSIPrepInputs / Outputs / Defaults
|
|
29
|
+
qsirecon.py # QSIReconInputs / Outputs / Defaults
|
|
30
|
+
qsiparc.py # QSIParcInputs / Outputs / Defaults
|
|
31
|
+
utils/
|
|
32
|
+
__init__.py
|
|
33
|
+
bids.py # BIDS post-processing (IntendedFor, fmap cleanup)
|
|
34
|
+
examples/
|
|
35
|
+
dicom_to_bids.py # HeudiConv example
|
|
36
|
+
qsiprep.py # QSIPrep example
|
|
37
|
+
qsirecon.py # QSIRecon example
|
|
38
|
+
qsiparc.py # QSIParc example
|
|
39
|
+
notebooks/
|
|
40
|
+
01_heudiconv_basics.ipynb # Step-by-step tutorials
|
|
41
|
+
02_qsiprep_basics.ipynb
|
|
42
|
+
03_qsirecon_basics.ipynb
|
|
43
|
+
04_qsiparc_basics.ipynb
|
|
44
|
+
05_full_pipeline.ipynb
|
|
45
|
+
tests/ # Pytest test suite
|
|
46
|
+
docs/ # Sphinx documentation source
|
|
47
|
+
pyproject.toml # Build config, dependencies
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Code Organization
|
|
51
|
+
|
|
52
|
+
### Runners (`runners/`)
|
|
53
|
+
|
|
54
|
+
Each runner is a function that:
|
|
55
|
+
|
|
56
|
+
1. Accepts an `Inputs` dataclass and optional `Defaults` config
|
|
57
|
+
2. Validates inputs (directory exists, participant found)
|
|
58
|
+
3. Builds a Docker command (or calls `parcellate` directly for QSIParc)
|
|
59
|
+
4. Executes via `run_docker()` (or `run_parcellations()`)
|
|
60
|
+
5. Returns an execution record dict with expected outputs
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
def run_procedure(
|
|
64
|
+
inputs: ProcedureInputs,
|
|
65
|
+
config: Optional[ProcedureDefaults] = None,
|
|
66
|
+
**overrides,
|
|
67
|
+
) -> Dict[str, Any]:
|
|
68
|
+
config = config or ProcedureDefaults()
|
|
69
|
+
|
|
70
|
+
for key, value in overrides.items():
|
|
71
|
+
if hasattr(config, key):
|
|
72
|
+
setattr(config, key, value)
|
|
73
|
+
|
|
74
|
+
validate_input_dir(inputs.input_dir)
|
|
75
|
+
validate_participant(inputs.input_dir, inputs.participant)
|
|
76
|
+
|
|
77
|
+
output_dir = inputs.output_dir or default_path
|
|
78
|
+
expected_outputs = ProcedureOutputs.from_inputs(inputs, output_dir)
|
|
79
|
+
|
|
80
|
+
cmd = ["docker", "run", "--rm", ...]
|
|
81
|
+
result = run_docker(cmd, "tool_name", inputs.participant, log_dir)
|
|
82
|
+
|
|
83
|
+
result["inputs"] = inputs
|
|
84
|
+
result["config"] = config
|
|
85
|
+
result["expected_outputs"] = expected_outputs
|
|
86
|
+
return result
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Schemas (`schemas/`)
|
|
90
|
+
|
|
91
|
+
Each procedure defines three dataclasses:
|
|
92
|
+
|
|
93
|
+
**Inputs** -- required parameters with `__post_init__` path coercion:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
@dataclass
|
|
97
|
+
class ProcedureInputs:
|
|
98
|
+
input_dir: Path
|
|
99
|
+
participant: str
|
|
100
|
+
output_dir: Optional[Path] = None
|
|
101
|
+
|
|
102
|
+
def __post_init__(self):
|
|
103
|
+
self.input_dir = Path(self.input_dir)
|
|
104
|
+
if self.output_dir:
|
|
105
|
+
self.output_dir = Path(self.output_dir)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Outputs** -- generated from inputs via `from_inputs()` classmethod:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
@dataclass
|
|
112
|
+
class ProcedureOutputs:
|
|
113
|
+
output_dir: Path
|
|
114
|
+
participant_dir: Path
|
|
115
|
+
|
|
116
|
+
@classmethod
|
|
117
|
+
def from_inputs(cls, inputs, output_dir):
|
|
118
|
+
return cls(
|
|
119
|
+
output_dir=output_dir,
|
|
120
|
+
participant_dir=output_dir / f"sub-{inputs.participant}",
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Defaults** -- configuration with brain bank standard values:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
@dataclass
|
|
128
|
+
class ProcedureDefaults:
|
|
129
|
+
nprocs: int = 8
|
|
130
|
+
mem_mb: int = 16000
|
|
131
|
+
docker_image: str = "tool/image:latest"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Base Utilities (`runners/_base.py`)
|
|
135
|
+
|
|
136
|
+
Three shared functions:
|
|
137
|
+
|
|
138
|
+
- `validate_input_dir(path, dir_type)` -- raises `InputValidationError` if missing or not a directory
|
|
139
|
+
- `validate_participant(input_dir, participant, prefix)` -- raises `InputValidationError` if participant subdir not found
|
|
140
|
+
- `run_docker(cmd, tool_name, participant, log_dir, capture_output)` -- executes subprocess, builds execution record, writes JSON log, raises `ProcedureExecutionError` on failure
|
|
141
|
+
|
|
142
|
+
### BIDS Utilities (`utils/bids.py`)
|
|
143
|
+
|
|
144
|
+
Post-processing for HeudiConv output:
|
|
145
|
+
|
|
146
|
+
- `post_process_heudiconv_output()` -- orchestrates three steps after DICOM conversion
|
|
147
|
+
- `verify_fmap_epi_files()` -- checks fieldmap NIfTI + JSON exist
|
|
148
|
+
- `add_intended_for_to_fmaps()` -- writes `IntendedFor` into fmap JSON sidecars
|
|
149
|
+
- `remove_bval_bvec_from_fmaps()` -- hides spurious `.bvec`/`.bval` files from fmap dirs
|
|
150
|
+
|
|
151
|
+
### Exceptions (`exceptions.py`)
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
YALabProcedureError (base, aliased as ProcedureError)
|
|
155
|
+
ProcedureExecutionError
|
|
156
|
+
DockerExecutionError
|
|
157
|
+
ProcedureConfigurationError
|
|
158
|
+
FreeSurferLicenseError
|
|
159
|
+
InputValidationError
|
|
160
|
+
OutputCollectionError
|
|
161
|
+
BIDSValidationError
|
|
162
|
+
DependencyError
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Execution Flow
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
User calls run_procedure(inputs, config)
|
|
169
|
+
-> Validate inputs
|
|
170
|
+
-> Generate expected outputs
|
|
171
|
+
-> Build Docker command
|
|
172
|
+
-> run_docker() executes subprocess
|
|
173
|
+
-> Build execution record dict
|
|
174
|
+
-> Attach inputs, config, expected_outputs
|
|
175
|
+
-> Return dict
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Execution Record Structure
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
{
|
|
182
|
+
"tool": str,
|
|
183
|
+
"participant": str,
|
|
184
|
+
"command": List[str],
|
|
185
|
+
"exit_code": int,
|
|
186
|
+
"start_time": str, # ISO format
|
|
187
|
+
"end_time": str,
|
|
188
|
+
"duration_seconds": float,
|
|
189
|
+
"duration_human": str,
|
|
190
|
+
"success": bool,
|
|
191
|
+
"log_file": str, # Path to JSON log (if log_dir provided)
|
|
192
|
+
"stdout": str, # If capture_output=True
|
|
193
|
+
"stderr": str,
|
|
194
|
+
"error": str, # If failed
|
|
195
|
+
"inputs": ProcedureInputs,
|
|
196
|
+
"config": ProcedureDefaults,
|
|
197
|
+
"expected_outputs": ProcedureOutputs,
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Adding a New Procedure
|
|
202
|
+
|
|
203
|
+
1. **Schema** -- create `schemas/your_procedure.py` with `Inputs`, `Outputs`, `Defaults`
|
|
204
|
+
2. **Runner** -- create `runners/your_procedure.py` with `run_your_procedure()`
|
|
205
|
+
3. **Exports** -- add to `schemas/__init__.py`, `runners/__init__.py`, and `__init__.py`
|
|
206
|
+
4. **Tests** -- create `tests/test_runners_your_procedure.py` and `tests/test_schemas_your_procedure.py`
|
|
207
|
+
5. **Example** -- add `examples/your_procedure.py`
|
|
208
|
+
|
|
209
|
+
## Key Design Decisions
|
|
210
|
+
|
|
211
|
+
| Decision | Rationale |
|
|
212
|
+
|----------|-----------|
|
|
213
|
+
| Dataclasses for schemas | Type hints for IDE support, automatic `__init__` and `__repr__` |
|
|
214
|
+
| Functions, not classes | Simpler, no state, no inheritance, easier to test |
|
|
215
|
+
| Command stored in record | Perfect reproducibility -- just rerun the command |
|
|
216
|
+
| Minimal dependencies | Faster installs, fewer breaking changes, no Nipype lock-in |
|
|
217
|
+
| No output validation | Docker container handles that; keep this package simple |
|
voxelops-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 YALab DevOps
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
voxelops-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
.PHONY: help install install-dev install-all test test-cov format lint clean lock upgrade venv
|
|
2
|
+
|
|
3
|
+
help: ## Show this help message
|
|
4
|
+
@echo "Usage: make [target]"
|
|
5
|
+
@echo ""
|
|
6
|
+
@echo "Targets:"
|
|
7
|
+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'
|
|
8
|
+
|
|
9
|
+
venv: ## Create virtual environment
|
|
10
|
+
uv venv
|
|
11
|
+
@echo ""
|
|
12
|
+
@echo "Virtual environment created. Activate with:"
|
|
13
|
+
@echo " source .venv/bin/activate # macOS/Linux"
|
|
14
|
+
@echo " .venv\\Scripts\\activate # Windows"
|
|
15
|
+
|
|
16
|
+
install: ## Install package in editable mode
|
|
17
|
+
uv pip install -e .
|
|
18
|
+
|
|
19
|
+
install-dev: ## Install package with dev dependencies
|
|
20
|
+
uv pip install -e ".[dev]"
|
|
21
|
+
|
|
22
|
+
install-all: ## Install package with all optional dependencies
|
|
23
|
+
uv pip install -e ".[dev,notebooks,config]"
|
|
24
|
+
|
|
25
|
+
test: ## Run tests
|
|
26
|
+
pytest
|
|
27
|
+
|
|
28
|
+
test-cov: ## Run tests with coverage report
|
|
29
|
+
pytest --cov=yalab_procedures --cov-report=term-missing --cov-report=html
|
|
30
|
+
@echo ""
|
|
31
|
+
@echo "Coverage report generated in htmlcov/index.html"
|
|
32
|
+
|
|
33
|
+
format: ## Format code with black
|
|
34
|
+
black src tests examples
|
|
35
|
+
|
|
36
|
+
format-check: ## Check code formatting without changes
|
|
37
|
+
black --check src tests examples
|
|
38
|
+
|
|
39
|
+
lint: ## Lint code with ruff
|
|
40
|
+
ruff check src tests examples
|
|
41
|
+
|
|
42
|
+
lint-fix: ## Lint and auto-fix issues
|
|
43
|
+
ruff check src tests examples --fix
|
|
44
|
+
|
|
45
|
+
qa: format-check lint test ## Run all quality checks
|
|
46
|
+
|
|
47
|
+
lock: ## Update lock file
|
|
48
|
+
uv lock
|
|
49
|
+
|
|
50
|
+
upgrade: ## Upgrade all dependencies
|
|
51
|
+
uv lock --upgrade
|
|
52
|
+
@echo ""
|
|
53
|
+
@echo "Dependencies upgraded. Run 'make install-dev' to install updates."
|
|
54
|
+
|
|
55
|
+
sync: ## Sync environment with lock file
|
|
56
|
+
uv pip sync
|
|
57
|
+
|
|
58
|
+
clean: ## Remove build artifacts and cache files
|
|
59
|
+
rm -rf build/
|
|
60
|
+
rm -rf dist/
|
|
61
|
+
rm -rf *.egg-info
|
|
62
|
+
rm -rf .pytest_cache
|
|
63
|
+
rm -rf .coverage
|
|
64
|
+
rm -rf htmlcov/
|
|
65
|
+
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
|
66
|
+
find . -type f -name "*.pyc" -delete
|
|
67
|
+
|
|
68
|
+
clean-venv: ## Remove virtual environment
|
|
69
|
+
rm -rf .venv
|
|
70
|
+
|
|
71
|
+
notebook: ## Start Jupyter notebook server
|
|
72
|
+
jupyter notebook notebooks/
|
|
73
|
+
|
|
74
|
+
lab: ## Start Jupyter lab server
|
|
75
|
+
jupyter lab notebooks/
|
|
76
|
+
|
|
77
|
+
docs: ## Open main documentation files
|
|
78
|
+
@echo "Documentation files:"
|
|
79
|
+
@echo " README.md - Main documentation"
|
|
80
|
+
@echo " DEVELOPMENT.md - Development guide"
|
|
81
|
+
@echo " UV_QUICKSTART.md - Quick start with uv"
|
|
82
|
+
@echo " ARCHITECTURE.md - Architecture documentation"
|
|
83
|
+
@echo " notebooks/README.md - Notebook examples"
|
|
84
|
+
|
|
85
|
+
setup: venv install-dev ## Complete setup: create venv and install dev dependencies
|
|
86
|
+
@echo ""
|
|
87
|
+
@echo "Setup complete! Don't forget to activate the virtual environment:"
|
|
88
|
+
@echo " source .venv/bin/activate"
|
|
89
|
+
|
|
90
|
+
# Development workflow targets
|
|
91
|
+
dev-start: ## Start development session (activate venv, show status)
|
|
92
|
+
@echo "Development environment ready!"
|
|
93
|
+
@echo ""
|
|
94
|
+
@echo "Quick commands:"
|
|
95
|
+
@echo " make test - Run tests"
|
|
96
|
+
@echo " make format - Format code"
|
|
97
|
+
@echo " make lint - Lint code"
|
|
98
|
+
@echo " make qa - Run all quality checks"
|
|
99
|
+
@echo ""
|
|
100
|
+
|
|
101
|
+
# CI targets
|
|
102
|
+
ci: format-check lint test ## Run CI checks (format, lint, test)
|
|
103
|
+
@echo ""
|
|
104
|
+
@echo "✅ All CI checks passed!"
|
|
105
|
+
|
|
106
|
+
# Build targets
|
|
107
|
+
build: ## Build distribution packages
|
|
108
|
+
uv build
|
|
109
|
+
|
|
110
|
+
publish-test: ## Publish to Test PyPI
|
|
111
|
+
uv publish --publish-url https://test.pypi.org/legacy/
|
|
112
|
+
|
|
113
|
+
publish: ## Publish to PyPI
|
|
114
|
+
uv publish
|
|
115
|
+
|
|
116
|
+
# Info targets
|
|
117
|
+
info: ## Show package and environment info
|
|
118
|
+
@echo "Package: yalab-procedures"
|
|
119
|
+
@echo "Version: $$(grep '^version = ' pyproject.toml | cut -d'"' -f2)"
|
|
120
|
+
@echo ""
|
|
121
|
+
@echo "Python: $$(python --version 2>&1)"
|
|
122
|
+
@echo "uv: $$(uv --version 2>&1)"
|
|
123
|
+
@echo ""
|
|
124
|
+
@echo "Virtual environment:"
|
|
125
|
+
@if [ -d .venv ]; then \
|
|
126
|
+
echo " Status: ✅ Exists"; \
|
|
127
|
+
echo " Path: $$(pwd)/.venv"; \
|
|
128
|
+
else \
|
|
129
|
+
echo " Status: ❌ Not created (run 'make venv')"; \
|
|
130
|
+
fi
|