qsiplan 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.
- qsiplan-0.1.0/.github/workflows/lint.yml +41 -0
- qsiplan-0.1.0/.github/workflows/release.yml +46 -0
- qsiplan-0.1.0/.github/workflows/reusable-tests.yml +34 -0
- qsiplan-0.1.0/.github/workflows/tests.yml +15 -0
- qsiplan-0.1.0/.gitignore +35 -0
- qsiplan-0.1.0/LICENSE +27 -0
- qsiplan-0.1.0/PKG-INFO +61 -0
- qsiplan-0.1.0/README.md +18 -0
- qsiplan-0.1.0/codecov.yml +20 -0
- qsiplan-0.1.0/pyproject.toml +94 -0
- qsiplan-0.1.0/qsiplan/__init__.py +220 -0
- qsiplan-0.1.0/qsiplan/__main__.py +8 -0
- qsiplan-0.1.0/qsiplan/_version.py +24 -0
- qsiplan-0.1.0/qsiplan/adapters.py +438 -0
- qsiplan-0.1.0/qsiplan/cli.py +194 -0
- qsiplan-0.1.0/qsiplan/data/__init__.py +13 -0
- qsiplan-0.1.0/qsiplan/data/pipeline_viewer.css +16 -0
- qsiplan-0.1.0/qsiplan/data/pipeline_viewer.js +348 -0
- qsiplan-0.1.0/qsiplan/data/qspace_viewer.css +47 -0
- qsiplan-0.1.0/qsiplan/data/qspace_viewer.js +516 -0
- qsiplan-0.1.0/qsiplan/inference.py +1233 -0
- qsiplan-0.1.0/qsiplan/integrity.py +275 -0
- qsiplan-0.1.0/qsiplan/interactive.py +773 -0
- qsiplan-0.1.0/qsiplan/metadata.py +379 -0
- qsiplan-0.1.0/qsiplan/methods.py +359 -0
- qsiplan-0.1.0/qsiplan/models.py +571 -0
- qsiplan-0.1.0/qsiplan/plan.py +613 -0
- qsiplan-0.1.0/qsiplan/report.py +977 -0
- qsiplan-0.1.0/qsiplan/validation.py +327 -0
- qsiplan-0.1.0/qsiplan/viz/__init__.py +1 -0
- qsiplan-0.1.0/qsiplan/viz/pipeline.py +156 -0
- qsiplan-0.1.0/qsiplan/viz/qspace.py +93 -0
- qsiplan-0.1.0/tests/conftest.py +1 -0
- qsiplan-0.1.0/tests/data/grouping_reports/abcd_style.txt +52 -0
- qsiplan-0.1.0/tests/data/grouping_reports/cross_axis_b0field.txt +66 -0
- qsiplan-0.1.0/tests/data/grouping_reports/cross_axis_unpaired.txt +56 -0
- qsiplan-0.1.0/tests/data/grouping_reports/fieldmapless_t1w_only_syn.txt +46 -0
- qsiplan-0.1.0/tests/data/grouping_reports/fieldmapless_t1w_only_synb0.txt +48 -0
- qsiplan-0.1.0/tests/data/grouping_reports/fieldmapless_t2w.txt +47 -0
- qsiplan-0.1.0/tests/data/grouping_reports/hcp_style.txt +57 -0
- qsiplan-0.1.0/tests/data/grouping_reports/missing_pedir.txt +81 -0
- qsiplan-0.1.0/tests/data/grouping_reports/multi_readout.txt +66 -0
- qsiplan-0.1.0/tests/data/grouping_reports/multi_session_b0field_reused.txt +145 -0
- qsiplan-0.1.0/tests/data/grouping_reports/multi_session_curated_multipart.txt +90 -0
- qsiplan-0.1.0/tests/data/grouping_reports/multi_session_shared_fmap.txt +88 -0
- qsiplan-0.1.0/tests/data/grouping_reports/multipart_splits_estimation.txt +105 -0
- qsiplan-0.1.0/tests/data/grouping_reports/nonshelled_pair.txt +55 -0
- qsiplan-0.1.0/tests/data/grouping_reports/partial_curation.txt +118 -0
- qsiplan-0.1.0/tests/data/grouping_reports/partial_pair.txt +64 -0
- qsiplan-0.1.0/tests/data/grouping_reports/shell_mix.txt +60 -0
- qsiplan-0.1.0/tests/data/grouping_reports/t2w_hcp_synb0.txt +56 -0
- qsiplan-0.1.0/tests/data/grouping_reports/two_gre_fmaps.txt +70 -0
- qsiplan-0.1.0/tests/data/grouping_reports/virtual_acq_isolated_fields.txt +151 -0
- qsiplan-0.1.0/tests/data/grouping_reports/virtual_acq_multipart.txt +93 -0
- qsiplan-0.1.0/tests/data/skeleton_complex_relpaths.yml +38 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_abcd_style.yml +20 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_abcd_t2w.yml +21 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_acq_multipart.yml +37 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_b0only_fmap_with_bvals.yml +25 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_bidsuri_intendedfor.yml +19 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_cluster_multipart.yml +65 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_cluster_nomultipart.yml +54 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_cross_axis_b0field.yml +36 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_cross_axis_unpaired.yml +19 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_curated_b0field.yml +21 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_curated_t2wreg.yml +17 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_fieldmapless_t1w_only.yml +13 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_fieldmapless_t2w.yml +14 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_fov_grid.yml +17 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_fov_oblique.yml +18 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_fov_shift.yml +18 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_gre_phasediff.yml +25 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_hcp_style.yml +31 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_intendedfor_superseded.yml +35 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_maxb_mismatch.yml +17 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_missing_pedir.yml +16 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_mixed_trt.yml +18 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_multi_readout.yml +40 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_multi_session.yml +29 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_multi_session_b0field_reused.yml +37 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_multi_session_curated_multipart.yml +43 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_multi_session_shared_fmap.yml +36 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_multipart_splits_estimation.yml +35 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_name_collision.yml +36 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_name_collision_inferred.yml +36 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_nonshelled_pair.yml +18 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_partial_curation.yml +35 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_partial_curation_stranded.yml +32 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_partial_intendedfor.yml +26 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_partial_multipart.yml +34 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_partial_pair.yml +34 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_reshim.yml +29 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_same_ped_own_fmaps.yml +35 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_shell_mix.yml +20 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_t2w_hcp.yml +20 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_two_gre_fmaps.yml +42 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_unlinked_fmap.yml +24 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_virtual_acq_isolated_fields.yml +51 -0
- qsiplan-0.1.0/tests/data/skeleton_grouping_virtual_acq_multipart.yml +26 -0
- qsiplan-0.1.0/tests/data/skeleton_simple_multiped.yml +22 -0
- qsiplan-0.1.0/tests/grouping_scenarios.py +190 -0
- qsiplan-0.1.0/tests/test_cli.py +60 -0
- qsiplan-0.1.0/tests/test_grouping_adapters.py +335 -0
- qsiplan-0.1.0/tests/test_grouping_inference.py +1048 -0
- qsiplan-0.1.0/tests/test_grouping_integrity.py +54 -0
- qsiplan-0.1.0/tests/test_grouping_methods.py +136 -0
- qsiplan-0.1.0/tests/test_grouping_models.py +237 -0
- qsiplan-0.1.0/tests/test_grouping_plan.py +294 -0
- qsiplan-0.1.0/tests/test_grouping_report.py +158 -0
- qsiplan-0.1.0/tests/test_grouping_viz.py +287 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Contribution checks
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
branches:
|
|
9
|
+
- main
|
|
10
|
+
|
|
11
|
+
defaults:
|
|
12
|
+
run:
|
|
13
|
+
shell: bash
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
17
|
+
cancel-in-progress: true
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
style:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v7
|
|
27
|
+
# Keep this pin in sync with the ruff rev in .pre-commit-config.yaml
|
|
28
|
+
# and the dev pin in pyproject.toml. Unpinned, CI silently follows new
|
|
29
|
+
# ruff releases and goes red without a code change.
|
|
30
|
+
- run: pipx run --spec ruff==0.15.21 ruff check .
|
|
31
|
+
- run: pipx run --spec ruff==0.15.21 ruff format --diff .
|
|
32
|
+
|
|
33
|
+
codespell:
|
|
34
|
+
name: Check for spelling errors
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
|
|
37
|
+
steps:
|
|
38
|
+
- name: Checkout
|
|
39
|
+
uses: actions/checkout@v7
|
|
40
|
+
- name: Codespell
|
|
41
|
+
uses: codespell-project/actions-codespell@v2
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
# The release runs the same suite as every push/PR, re-run on the exact
|
|
12
|
+
# tagged commit. publish depends on it, so a red tag never reaches PyPI.
|
|
13
|
+
tests:
|
|
14
|
+
uses: ./.github/workflows/reusable-tests.yml
|
|
15
|
+
secrets: inherit
|
|
16
|
+
|
|
17
|
+
build:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
with:
|
|
22
|
+
fetch-depth: 0 # hatch-vcs derives the version from the tag
|
|
23
|
+
- uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: '3.11'
|
|
26
|
+
- run: pipx run build
|
|
27
|
+
- run: pipx run twine check dist/*
|
|
28
|
+
- uses: actions/upload-artifact@v4
|
|
29
|
+
with:
|
|
30
|
+
name: dist
|
|
31
|
+
path: dist/
|
|
32
|
+
|
|
33
|
+
publish:
|
|
34
|
+
needs: [tests, build]
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
environment:
|
|
37
|
+
name: pypi
|
|
38
|
+
url: https://pypi.org/p/qsiplan
|
|
39
|
+
permissions:
|
|
40
|
+
id-token: write # OIDC token for Trusted Publishing (no stored secret)
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/download-artifact@v4
|
|
43
|
+
with:
|
|
44
|
+
name: dist
|
|
45
|
+
path: dist/
|
|
46
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: reusable tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
secrets:
|
|
6
|
+
CODECOV_TOKEN:
|
|
7
|
+
required: false
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
pytest:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ['3.11', '3.12', '3.13']
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
cache: pip
|
|
22
|
+
- name: Install with test extras
|
|
23
|
+
run: pip install -e '.[tests]' pytest-cov
|
|
24
|
+
- name: Run the suite with coverage
|
|
25
|
+
run: pytest tests --cov=qsiplan --cov-report=term --cov-report=xml --cov-fail-under=85
|
|
26
|
+
- name: Smoke the CLI entry point
|
|
27
|
+
run: qsiplan --help
|
|
28
|
+
- name: Upload coverage to Codecov
|
|
29
|
+
uses: codecov/codecov-action@v5
|
|
30
|
+
with:
|
|
31
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
32
|
+
files: ./coverage.xml
|
|
33
|
+
flags: py${{ matrix.python-version }}
|
|
34
|
+
fail_ci_if_error: false
|
qsiplan-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Byte-compiled / caches
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.pytest_cache/
|
|
5
|
+
.hypothesis/
|
|
6
|
+
|
|
7
|
+
# Distribution / packaging
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
.eggs/
|
|
12
|
+
|
|
13
|
+
# Version file, if hatch-vcs versioning is adopted
|
|
14
|
+
qsiplan/_version.py
|
|
15
|
+
|
|
16
|
+
# Coverage
|
|
17
|
+
htmlcov/
|
|
18
|
+
.coverage
|
|
19
|
+
.coverage.*
|
|
20
|
+
coverage.xml
|
|
21
|
+
|
|
22
|
+
# Environments
|
|
23
|
+
.venv/
|
|
24
|
+
env/
|
|
25
|
+
.tox/
|
|
26
|
+
|
|
27
|
+
# Editors / OS
|
|
28
|
+
.idea/
|
|
29
|
+
.vscode/
|
|
30
|
+
.ipynb_checkpoints/
|
|
31
|
+
*.swp
|
|
32
|
+
*.DS_Store
|
|
33
|
+
|
|
34
|
+
# Rendered preview pages from local qsiplan runs
|
|
35
|
+
grouping*.html
|
qsiplan-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Copyright (c) 2015-2018, the PennLINC developers team.
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
8
|
+
list of conditions and the following disclaimer.
|
|
9
|
+
|
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
and/or other materials provided with the distribution.
|
|
13
|
+
|
|
14
|
+
* Neither the name of qsiprep nor the names of its
|
|
15
|
+
contributors may be used to endorse or promote products derived from
|
|
16
|
+
this software without specific prior written permission.
|
|
17
|
+
|
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
19
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
20
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
22
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
23
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
24
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
25
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
26
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
qsiplan-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: qsiplan
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Plan and explain QSIPrep's diffusion-MRI preprocessing from BIDS metadata
|
|
5
|
+
Project-URL: Homepage, https://github.com/PennLINC/qsiprep
|
|
6
|
+
Author: The PennLINC developers
|
|
7
|
+
License: Copyright (c) 2015-2018, the PennLINC developers team.
|
|
8
|
+
All rights reserved.
|
|
9
|
+
|
|
10
|
+
Redistribution and use in source and binary forms, with or without
|
|
11
|
+
modification, are permitted provided that the following conditions are met:
|
|
12
|
+
|
|
13
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
14
|
+
list of conditions and the following disclaimer.
|
|
15
|
+
|
|
16
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
17
|
+
this list of conditions and the following disclaimer in the documentation
|
|
18
|
+
and/or other materials provided with the distribution.
|
|
19
|
+
|
|
20
|
+
* Neither the name of qsiprep nor the names of its
|
|
21
|
+
contributors may be used to endorse or promote products derived from
|
|
22
|
+
this software without specific prior written permission.
|
|
23
|
+
|
|
24
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
25
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
26
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
27
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
28
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
29
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
30
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
31
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
32
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
33
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
34
|
+
License-File: LICENSE
|
|
35
|
+
Requires-Python: >=3.11
|
|
36
|
+
Requires-Dist: nibabel
|
|
37
|
+
Requires-Dist: numpy
|
|
38
|
+
Requires-Dist: pybids
|
|
39
|
+
Provides-Extra: tests
|
|
40
|
+
Requires-Dist: niworkflows; extra == 'tests'
|
|
41
|
+
Requires-Dist: pytest; extra == 'tests'
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
|
|
44
|
+
# QSIPlan
|
|
45
|
+
|
|
46
|
+
[](https://github.com/PennLINC/QSIPlan/actions/workflows/tests.yml)
|
|
47
|
+
[](https://codecov.io/gh/PennLINC/QSIPlan)
|
|
48
|
+
|
|
49
|
+
Plan and explain [qsiprep](https://github.com/PennLINC/qsiprep)'s
|
|
50
|
+
diffusion-MRI preprocessing from BIDS metadata and configuration flags.
|
|
51
|
+
|
|
52
|
+
`qsiplan` groups a subject's DWI scans the way qsiprep will (distortion
|
|
53
|
+
groups, fieldmap estimations, correction units, outputs), validates the
|
|
54
|
+
data and curation (missing phase-encoding metadata, IntendedFor conflicts,
|
|
55
|
+
shim walls, b-value mismatches...), compiles the execution plan for any
|
|
56
|
+
combination of head-motion- and susceptibility-correction methods, and
|
|
57
|
+
renders it all as text or as a self-contained interactive HTML page:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
qsiplan /path/to/bids --html grouping.html
|
|
61
|
+
```
|
qsiplan-0.1.0/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# QSIPlan
|
|
2
|
+
|
|
3
|
+
[](https://github.com/PennLINC/QSIPlan/actions/workflows/tests.yml)
|
|
4
|
+
[](https://codecov.io/gh/PennLINC/QSIPlan)
|
|
5
|
+
|
|
6
|
+
Plan and explain [qsiprep](https://github.com/PennLINC/qsiprep)'s
|
|
7
|
+
diffusion-MRI preprocessing from BIDS metadata and configuration flags.
|
|
8
|
+
|
|
9
|
+
`qsiplan` groups a subject's DWI scans the way qsiprep will (distortion
|
|
10
|
+
groups, fieldmap estimations, correction units, outputs), validates the
|
|
11
|
+
data and curation (missing phase-encoding metadata, IntendedFor conflicts,
|
|
12
|
+
shim walls, b-value mismatches...), compiles the execution plan for any
|
|
13
|
+
combination of head-motion- and susceptibility-correction methods, and
|
|
14
|
+
renders it all as text or as a self-contained interactive HTML page:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
qsiplan /path/to/bids --html grouping.html
|
|
18
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Codecov behaves as a reporter here, never a gate: the enforced floor is
|
|
2
|
+
# pytest's --cov-fail-under in CI, so both statuses are informational and
|
|
3
|
+
# nothing goes red because of an upload or rounding quirk.
|
|
4
|
+
coverage:
|
|
5
|
+
status:
|
|
6
|
+
project:
|
|
7
|
+
default:
|
|
8
|
+
informational: true
|
|
9
|
+
patch:
|
|
10
|
+
default:
|
|
11
|
+
informational: true
|
|
12
|
+
|
|
13
|
+
comment:
|
|
14
|
+
layout: "condensed_header, diff, flags"
|
|
15
|
+
# One comment after all three matrix uploads, not three drafts.
|
|
16
|
+
after_n_builds: 3
|
|
17
|
+
|
|
18
|
+
codecov:
|
|
19
|
+
notify:
|
|
20
|
+
after_n_builds: 3
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "qsiplan"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Plan and explain QSIPrep's diffusion-MRI preprocessing from BIDS metadata"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { file = "LICENSE" }
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
authors = [{ name = "The PennLINC developers" }]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"pybids",
|
|
15
|
+
"nibabel",
|
|
16
|
+
"numpy",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.optional-dependencies]
|
|
20
|
+
tests = [
|
|
21
|
+
"pytest",
|
|
22
|
+
"niworkflows",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
qsiplan = "qsiplan.cli:main"
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/PennLINC/qsiprep"
|
|
30
|
+
|
|
31
|
+
[tool.hatch.build.targets.wheel]
|
|
32
|
+
packages = ["qsiplan"]
|
|
33
|
+
|
|
34
|
+
[tool.hatch.version]
|
|
35
|
+
source = "vcs"
|
|
36
|
+
fallback-version = "0.1.0"
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.hooks.vcs]
|
|
39
|
+
version-file = "qsiplan/_version.py"
|
|
40
|
+
|
|
41
|
+
[tool.pytest.ini_options]
|
|
42
|
+
testpaths = ["tests"]
|
|
43
|
+
|
|
44
|
+
[tool.coverage.run]
|
|
45
|
+
branch = true
|
|
46
|
+
source = ["qsiplan"]
|
|
47
|
+
|
|
48
|
+
[tool.coverage.report]
|
|
49
|
+
show_missing = true
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
line-length = 99
|
|
53
|
+
extend-exclude = ["qsiplan/_version.py"]
|
|
54
|
+
|
|
55
|
+
[tool.ruff.lint]
|
|
56
|
+
extend-select = [
|
|
57
|
+
"F",
|
|
58
|
+
"E",
|
|
59
|
+
"W",
|
|
60
|
+
"I",
|
|
61
|
+
"UP",
|
|
62
|
+
"YTT",
|
|
63
|
+
"S",
|
|
64
|
+
"BLE",
|
|
65
|
+
"B",
|
|
66
|
+
"A",
|
|
67
|
+
"C4",
|
|
68
|
+
"DTZ",
|
|
69
|
+
"T10",
|
|
70
|
+
"EXE",
|
|
71
|
+
"FA",
|
|
72
|
+
"ISC",
|
|
73
|
+
"ICN",
|
|
74
|
+
"PT",
|
|
75
|
+
"Q",
|
|
76
|
+
]
|
|
77
|
+
ignore = [
|
|
78
|
+
"ISC001", # conflicts with the formatter
|
|
79
|
+
"S311", # random is never used for cryptography here
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
[tool.ruff.lint.flake8-quotes]
|
|
83
|
+
inline-quotes = "single"
|
|
84
|
+
|
|
85
|
+
[tool.ruff.lint.extend-per-file-ignores]
|
|
86
|
+
"tests/*.py" = ["S101"]
|
|
87
|
+
|
|
88
|
+
[tool.ruff.format]
|
|
89
|
+
quote-style = "single"
|
|
90
|
+
|
|
91
|
+
[tool.codespell]
|
|
92
|
+
skip = '.git,*.html,*.svg,*.lock'
|
|
93
|
+
# te: the TE (echo time) abbreviation in prose and golden reports
|
|
94
|
+
ignore-words-list = 'te'
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""BIDS-native grouping of DWI scans for preprocessing.
|
|
2
|
+
|
|
3
|
+
This package decides, per subject, from BIDS metadata alone:
|
|
4
|
+
|
|
5
|
+
1. Which DWI files share susceptibility distortions (distortion groups).
|
|
6
|
+
2. Which files jointly estimate each fieldmap (``B0FieldIdentifier``).
|
|
7
|
+
3. Which fieldmap corrects which DWI file (``B0FieldSource``).
|
|
8
|
+
4. Which files are concatenated in the outputs (``MultipartID``).
|
|
9
|
+
|
|
10
|
+
Curated sidecar metadata is used verbatim; everything the user did not curate
|
|
11
|
+
is inferred and tagged with its provenance. The output,
|
|
12
|
+
:class:`~.models.DWIGrouping`, describes the *data* - how any given
|
|
13
|
+
processing backend arranges that data is the business of adapters and the
|
|
14
|
+
previews in :mod:`~.report`.
|
|
15
|
+
|
|
16
|
+
Run ``qsiplan /path/to/bids`` to print the grouping and
|
|
17
|
+
per-selection processing previews for a dataset.
|
|
18
|
+
|
|
19
|
+
Adding a new estimation method
|
|
20
|
+
------------------------------
|
|
21
|
+
The (method x selection) behavior matrix is deliberately spelled out as prose
|
|
22
|
+
branches rather than a rules table, because the preview text is the product.
|
|
23
|
+
The cost is that a new :class:`~.models.CorrectionMethod` touches five places
|
|
24
|
+
(SYNB0 is the worked example in each):
|
|
25
|
+
|
|
26
|
+
1. :class:`~.models.CorrectionMethod` - add the member.
|
|
27
|
+
2. ``inference.py`` - produce it: ``_classify_method`` for curated sources
|
|
28
|
+
and/or a step in ``resolve_fieldmapless``; rank it in ``_METHOD_RANK``.
|
|
29
|
+
3. ``methods.py`` - which :class:`~.methods.SdcTool` consumes it (the
|
|
30
|
+
capability registries), and ``plan.py`` - its stage sequence per HMC
|
|
31
|
+
method, and whether an infeasible selection is an error or a degradation.
|
|
32
|
+
4. ``report.py`` - ``_METHOD_LABELS``, ``MethodGroups``/``_ids_by_kind``,
|
|
33
|
+
a narration branch in each ``_describe_*`` function, and its
|
|
34
|
+
``_stage_text`` sentence.
|
|
35
|
+
5. Tests - a scenario skeleton, inference assertions, plan-stage assertions,
|
|
36
|
+
and regenerated golden reports (``QSIPREP_REGEN_GROUPING_REPORTS=1``).
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
from ._version import __version__
|
|
41
|
+
except ImportError: # pragma: no cover - not yet built from a git checkout
|
|
42
|
+
__version__ = '0+unknown'
|
|
43
|
+
|
|
44
|
+
from .adapters import (
|
|
45
|
+
PreprocUnit,
|
|
46
|
+
assembly_to_sidecar,
|
|
47
|
+
concatenation_scheme,
|
|
48
|
+
to_preproc_units,
|
|
49
|
+
unit_to_sidecar,
|
|
50
|
+
)
|
|
51
|
+
from .inference import build_grouping
|
|
52
|
+
from .interactive import render_html, render_report_segment
|
|
53
|
+
from .metadata import index_subject
|
|
54
|
+
from .methods import (
|
|
55
|
+
HMC_CAPABILITIES,
|
|
56
|
+
SDC_CAPABILITIES,
|
|
57
|
+
HmcMethod,
|
|
58
|
+
MethodSelection,
|
|
59
|
+
SdcTool,
|
|
60
|
+
canonical_selection,
|
|
61
|
+
selection_for_config,
|
|
62
|
+
)
|
|
63
|
+
from .models import (
|
|
64
|
+
ConcatenationGroup,
|
|
65
|
+
CorrectionMethod,
|
|
66
|
+
DistortionGroup,
|
|
67
|
+
DistortionSignature,
|
|
68
|
+
DWIGrouping,
|
|
69
|
+
FieldmapEstimation,
|
|
70
|
+
FileRecord,
|
|
71
|
+
GroupingPolicy,
|
|
72
|
+
Provenance,
|
|
73
|
+
)
|
|
74
|
+
from .plan import ExecutionPlan, OutputAssembly, PlanStage, ProcessingRun, compile_plan
|
|
75
|
+
from .report import describe_processing, full_report, report_text
|
|
76
|
+
from .validation import BACKENDS, GroupingError, GroupingIssue, check_backend, raise_for_errors
|
|
77
|
+
|
|
78
|
+
__all__ = [
|
|
79
|
+
'BACKENDS',
|
|
80
|
+
'HMC_CAPABILITIES',
|
|
81
|
+
'SDC_CAPABILITIES',
|
|
82
|
+
'ConcatenationGroup',
|
|
83
|
+
'DistortionGroup',
|
|
84
|
+
'DistortionSignature',
|
|
85
|
+
'DWIGrouping',
|
|
86
|
+
'CorrectionMethod',
|
|
87
|
+
'FieldmapEstimation',
|
|
88
|
+
'FileRecord',
|
|
89
|
+
'GroupingError',
|
|
90
|
+
'GroupingIssue',
|
|
91
|
+
'GroupingPolicy',
|
|
92
|
+
'HmcMethod',
|
|
93
|
+
'MethodSelection',
|
|
94
|
+
'PreprocUnit',
|
|
95
|
+
'Provenance',
|
|
96
|
+
'SdcTool',
|
|
97
|
+
'assembly_to_sidecar',
|
|
98
|
+
'build_dwi_grouping',
|
|
99
|
+
'canonical_selection',
|
|
100
|
+
'check_backend',
|
|
101
|
+
'compile_plan',
|
|
102
|
+
'ExecutionPlan',
|
|
103
|
+
'OutputAssembly',
|
|
104
|
+
'PlanStage',
|
|
105
|
+
'ProcessingRun',
|
|
106
|
+
'concatenation_scheme',
|
|
107
|
+
'describe_processing',
|
|
108
|
+
'full_report',
|
|
109
|
+
'index_subject',
|
|
110
|
+
'render_html',
|
|
111
|
+
'render_report_segment',
|
|
112
|
+
'report_text',
|
|
113
|
+
'selection_for_config',
|
|
114
|
+
'to_preproc_units',
|
|
115
|
+
'unit_to_sidecar',
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def build_dwi_grouping(
|
|
120
|
+
layout,
|
|
121
|
+
subject_data,
|
|
122
|
+
separate_all_dwis=False,
|
|
123
|
+
ignore_fieldmaps=False,
|
|
124
|
+
ignore_shims=False,
|
|
125
|
+
ignore_fov=False,
|
|
126
|
+
ignore_sdc=False,
|
|
127
|
+
force_t2wreg=False,
|
|
128
|
+
use_synb0=False,
|
|
129
|
+
use_nipreps_syn_sdc=False,
|
|
130
|
+
distortion_group_merge='concat',
|
|
131
|
+
b0_threshold=None,
|
|
132
|
+
strict=True,
|
|
133
|
+
):
|
|
134
|
+
"""Group one subject's DWI scans.
|
|
135
|
+
|
|
136
|
+
Parameters
|
|
137
|
+
----------
|
|
138
|
+
layout : :class:`bids.BIDSLayout`
|
|
139
|
+
Layout of the input dataset; used only for sidecar metadata reads.
|
|
140
|
+
subject_data : dict
|
|
141
|
+
As returned by :func:`qsiprep.utils.bids.collect_data`: must contain
|
|
142
|
+
a ``'dwi'`` key listing the subject's DWI files (``'fmap'``,
|
|
143
|
+
``'t1w'``, and ``'t2w'`` are optional - they are discovered from the
|
|
144
|
+
layout when absent).
|
|
145
|
+
separate_all_dwis : bool
|
|
146
|
+
Every DWI series becomes its own output. Fieldmap estimation still
|
|
147
|
+
happens at session scope, so single series keep their SDC.
|
|
148
|
+
ignore_fieldmaps : bool
|
|
149
|
+
Do not index ``fmap/``. The reverse phase-encoding DWI heuristic
|
|
150
|
+
still applies.
|
|
151
|
+
ignore_shims : bool
|
|
152
|
+
Treat all ShimSetting values as compatible. Use when data were
|
|
153
|
+
re-shimmed but distortion correction across shims is wanted anyway.
|
|
154
|
+
ignore_fov : bool
|
|
155
|
+
Downgrade the differing-orientation field-of-view error to a warning
|
|
156
|
+
and proceed, accepting misapplied distortion corrections. Grid-size
|
|
157
|
+
mismatches remain errors: they cannot be stacked at all.
|
|
158
|
+
ignore_sdc : bool
|
|
159
|
+
Disable susceptibility distortion correction entirely: no fieldmaps, no
|
|
160
|
+
reverse-PE heuristic, and no fieldmap-less fallback. Every series is
|
|
161
|
+
left uncorrected but is still grouped and concatenated for HMC. Stronger
|
|
162
|
+
than ``ignore_fieldmaps`` (which only skips ``fmap/``).
|
|
163
|
+
force_t2wreg : bool
|
|
164
|
+
Correct every DWI series by registering its b=0 to the subject's T2w
|
|
165
|
+
(TORTOISE T2Wreg), overriding any fieldmaps. Errors if no T2w exists.
|
|
166
|
+
use_synb0 : bool
|
|
167
|
+
Give series that have no fieldmap a SyNb0 synthetic-b=0 estimation
|
|
168
|
+
synthesized from the T1w. Never overrides a real fieldmap. Errors if
|
|
169
|
+
no T1w exists or a target series lacks PhaseEncodingDirection.
|
|
170
|
+
distortion_group_merge : str or None
|
|
171
|
+
How the corrected results of a final output's correction units are
|
|
172
|
+
combined: ``'concat'`` (default) concatenates them, ``'average'``
|
|
173
|
+
averages matched volumes (opposite-PE duplicate schemes), and
|
|
174
|
+
``'none'`` keeps every correction unit as its own output (``None``
|
|
175
|
+
is treated as ``'concat'``).
|
|
176
|
+
use_nipreps_syn_sdc : bool
|
|
177
|
+
Correct series that have no fieldmap with the standalone niworkflows
|
|
178
|
+
SyN-SDC: a constrained ANTs SyN registration of an inverted T1w (or a
|
|
179
|
+
synthetic b=0) to a fieldmap atlas. Never overrides a real fieldmap and
|
|
180
|
+
is never combined with SyNb0 (SyNb0 wins if both are set). Errors if no
|
|
181
|
+
T1w exists or a target series lacks PhaseEncodingDirection.
|
|
182
|
+
b0_threshold : float
|
|
183
|
+
Diffusion-weighting at or below this is treated as b=0 when
|
|
184
|
+
classifying sampling schemes (default:
|
|
185
|
+
:data:`~.metadata.B0_THRESHOLD`).
|
|
186
|
+
strict : bool
|
|
187
|
+
Raise :class:`~.validation.GroupingError` if any error-severity issue
|
|
188
|
+
is found. With ``strict=False`` the grouping is returned with its
|
|
189
|
+
``issues`` intact, which is what reports and previews want.
|
|
190
|
+
|
|
191
|
+
Returns
|
|
192
|
+
-------
|
|
193
|
+
:class:`~.models.DWIGrouping`
|
|
194
|
+
"""
|
|
195
|
+
from bids.layout import parse_file_entities
|
|
196
|
+
|
|
197
|
+
records, index_issues = index_subject(
|
|
198
|
+
layout,
|
|
199
|
+
subject_data,
|
|
200
|
+
ignore_fieldmaps=ignore_fieldmaps,
|
|
201
|
+
b0_threshold=b0_threshold,
|
|
202
|
+
)
|
|
203
|
+
subject_id = parse_file_entities(records[0].path)['subject']
|
|
204
|
+
grouping = build_grouping(
|
|
205
|
+
records,
|
|
206
|
+
subject_id=subject_id,
|
|
207
|
+
separate_all_dwis=separate_all_dwis,
|
|
208
|
+
ignore_fieldmaps=ignore_fieldmaps,
|
|
209
|
+
ignore_shims=ignore_shims,
|
|
210
|
+
ignore_fov=ignore_fov,
|
|
211
|
+
ignore_sdc=ignore_sdc,
|
|
212
|
+
force_t2wreg=force_t2wreg,
|
|
213
|
+
use_synb0=use_synb0,
|
|
214
|
+
use_nipreps_syn_sdc=use_nipreps_syn_sdc,
|
|
215
|
+
distortion_group_merge=distortion_group_merge,
|
|
216
|
+
extra_issues=index_issues,
|
|
217
|
+
)
|
|
218
|
+
if strict:
|
|
219
|
+
raise_for_errors(grouping)
|
|
220
|
+
return grouping
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.1.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|