pineuro 1.0.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.
- pineuro-1.0.0/LICENSE +13 -0
- pineuro-1.0.0/PKG-INFO +121 -0
- pineuro-1.0.0/README.md +72 -0
- pineuro-1.0.0/pyproject.toml +145 -0
- pineuro-1.0.0/setup.cfg +4 -0
- pineuro-1.0.0/src/pineuro/__init__.py +284 -0
- pineuro-1.0.0/src/pineuro/atlas_registry.py +49 -0
- pineuro-1.0.0/src/pineuro/backup.py +210 -0
- pineuro-1.0.0/src/pineuro/collection_qc.py +377 -0
- pineuro-1.0.0/src/pineuro/config.py +90 -0
- pineuro-1.0.0/src/pineuro/display.py +130 -0
- pineuro-1.0.0/src/pineuro/feedback.py +4177 -0
- pineuro-1.0.0/src/pineuro/gui/__init__.py +94 -0
- pineuro-1.0.0/src/pineuro/gui/app.py +702 -0
- pineuro-1.0.0/src/pineuro/gui/components/__init__.py +142 -0
- pineuro-1.0.0/src/pineuro/gui/components/activation_plot.py +377 -0
- pineuro-1.0.0/src/pineuro/gui/components/base_dialog.py +60 -0
- pineuro-1.0.0/src/pineuro/gui/components/collection_progress.py +304 -0
- pineuro-1.0.0/src/pineuro/gui/components/csv_poller.py +272 -0
- pineuro-1.0.0/src/pineuro/gui/components/disk_space_bar.py +154 -0
- pineuro-1.0.0/src/pineuro/gui/components/feedback_summary.py +486 -0
- pineuro-1.0.0/src/pineuro/gui/components/manual_masks.py +98 -0
- pineuro-1.0.0/src/pineuro/gui/components/mask_selection_dialog.py +1759 -0
- pineuro-1.0.0/src/pineuro/gui/components/pipeline_runner.py +2817 -0
- pineuro-1.0.0/src/pineuro/gui/components/qc_plot_viewer.py +224 -0
- pineuro-1.0.0/src/pineuro/gui/components/qc_plots.py +492 -0
- pineuro-1.0.0/src/pineuro/gui/components/qc_viewer.py +346 -0
- pineuro-1.0.0/src/pineuro/gui/components/scanner_connection_dialog.py +225 -0
- pineuro-1.0.0/src/pineuro/gui/components/session_selection_dialog.py +345 -0
- pineuro-1.0.0/src/pineuro/gui/components/task_mirror.py +113 -0
- pineuro-1.0.0/src/pineuro/gui/components/unified_attempt_dialog.py +337 -0
- pineuro-1.0.0/src/pineuro/gui/config_manager.py +500 -0
- pineuro-1.0.0/src/pineuro/gui/config_snapshot.py +206 -0
- pineuro-1.0.0/src/pineuro/gui/dispatch.py +103 -0
- pineuro-1.0.0/src/pineuro/gui/models.py +272 -0
- pineuro-1.0.0/src/pineuro/gui/screens/__init__.py +6 -0
- pineuro-1.0.0/src/pineuro/gui/screens/_feedback_tab.py +1198 -0
- pineuro-1.0.0/src/pineuro/gui/screens/_localizer_tab.py +1081 -0
- pineuro-1.0.0/src/pineuro/gui/screens/participant_workflow.py +8253 -0
- pineuro-1.0.0/src/pineuro/gui/screens/study_editor.py +2200 -0
- pineuro-1.0.0/src/pineuro/gui/styles.py +406 -0
- pineuro-1.0.0/src/pineuro/gui/utils.py +1092 -0
- pineuro-1.0.0/src/pineuro/mask_extraction.py +4399 -0
- pineuro-1.0.0/src/pineuro/preprocessing.py +1605 -0
- pineuro-1.0.0/src/pineuro/py.typed +0 -0
- pineuro-1.0.0/src/pineuro/qc.py +4082 -0
- pineuro-1.0.0/src/pineuro/scanner_setup.py +466 -0
- pineuro-1.0.0/src/pineuro/script_generator.py +401 -0
- pineuro-1.0.0/src/pineuro/session.py +2991 -0
- pineuro-1.0.0/src/pineuro/sham.py +447 -0
- pineuro-1.0.0/src/pineuro/simulator.py +746 -0
- pineuro-1.0.0/src/pineuro/storage.py +123 -0
- pineuro-1.0.0/src/pineuro/tasks/__init__.py +276 -0
- pineuro-1.0.0/src/pineuro/tasks/ball.py +1334 -0
- pineuro-1.0.0/src/pineuro/tasks/base.py +661 -0
- pineuro-1.0.0/src/pineuro/tasks/designs.py +233 -0
- pineuro-1.0.0/src/pineuro/tasks/factory.py +222 -0
- pineuro-1.0.0/src/pineuro/tasks/gauge.py +858 -0
- pineuro-1.0.0/src/pineuro/tasks/localizer_base.py +255 -0
- pineuro-1.0.0/src/pineuro/tasks/protocol.py +102 -0
- pineuro-1.0.0/src/pineuro/tasks/rest.py +129 -0
- pineuro-1.0.0/src/pineuro/tasks/rit.py +1158 -0
- pineuro-1.0.0/src/pineuro/tasks/rit_experience_sampling.py +1408 -0
- pineuro-1.0.0/src/pineuro/tasks/rit_triggered_ball.py +1507 -0
- pineuro-1.0.0/src/pineuro/tasks/self_other.py +525 -0
- pineuro-1.0.0/src/pineuro/tasks/state.py +492 -0
- pineuro-1.0.0/src/pineuro/tasks/subprocess_runner.py +438 -0
- pineuro-1.0.0/src/pineuro/tasks/task_base.py +1495 -0
- pineuro-1.0.0/src/pineuro/tasks/task_registry.py +611 -0
- pineuro-1.0.0/src/pineuro/tasks/thermometer.py +1371 -0
- pineuro-1.0.0/src/pineuro/tasks/ui_helpers.py +364 -0
- pineuro-1.0.0/src/pineuro/tasks/utils.py +222 -0
- pineuro-1.0.0/src/pineuro/utils.py +1971 -0
- pineuro-1.0.0/src/pineuro.egg-info/PKG-INFO +121 -0
- pineuro-1.0.0/src/pineuro.egg-info/SOURCES.txt +97 -0
- pineuro-1.0.0/src/pineuro.egg-info/dependency_links.txt +1 -0
- pineuro-1.0.0/src/pineuro.egg-info/entry_points.txt +2 -0
- pineuro-1.0.0/src/pineuro.egg-info/requires.txt +29 -0
- pineuro-1.0.0/src/pineuro.egg-info/top_level.txt +1 -0
- pineuro-1.0.0/tests/test_atlas_registry.py +70 -0
- pineuro-1.0.0/tests/test_backup.py +147 -0
- pineuro-1.0.0/tests/test_collection_qc.py +212 -0
- pineuro-1.0.0/tests/test_config.py +53 -0
- pineuro-1.0.0/tests/test_discard_attempt.py +226 -0
- pineuro-1.0.0/tests/test_display.py +84 -0
- pineuro-1.0.0/tests/test_feedback.py +712 -0
- pineuro-1.0.0/tests/test_mask_extraction.py +585 -0
- pineuro-1.0.0/tests/test_preprocessing.py +471 -0
- pineuro-1.0.0/tests/test_qc.py +258 -0
- pineuro-1.0.0/tests/test_scanner_setup.py +360 -0
- pineuro-1.0.0/tests/test_script_generator.py +263 -0
- pineuro-1.0.0/tests/test_session_config.py +641 -0
- pineuro-1.0.0/tests/test_session_orchestration.py +374 -0
- pineuro-1.0.0/tests/test_sham.py +199 -0
- pineuro-1.0.0/tests/test_simulator.py +345 -0
- pineuro-1.0.0/tests/test_smoke.py +7 -0
- pineuro-1.0.0/tests/test_storage.py +83 -0
- pineuro-1.0.0/tests/test_subprocess_runner.py +319 -0
- pineuro-1.0.0/tests/test_utils.py +740 -0
pineuro-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2026, Marlee M Vandewouw
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
pineuro-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pineuro
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python for Individualized Neurofeedback
|
|
5
|
+
Author-email: Marlee M Vandewouw <marlee.vandewouw@outlook.com>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/WhitfieldGabrieli-EPICBrainLab/pineuro
|
|
8
|
+
Project-URL: Documentation, https://github.com/WhitfieldGabrieli-EPICBrainLab/pineuro/tree/main/docs
|
|
9
|
+
Project-URL: Issues, https://github.com/WhitfieldGabrieli-EPICBrainLab/pineuro/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/WhitfieldGabrieli-EPICBrainLab/pineuro/blob/main/CHANGELOG.md
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
19
|
+
Requires-Python: <3.12,>=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: antspyx==0.6.2
|
|
23
|
+
Requires-Dist: dcm2niix
|
|
24
|
+
Requires-Dist: evdev; sys_platform == "linux"
|
|
25
|
+
Requires-Dist: flet==0.80.5
|
|
26
|
+
Requires-Dist: flet-charts>=0.1.0
|
|
27
|
+
Requires-Dist: matplotlib
|
|
28
|
+
Requires-Dist: nibabel>=5.3.0
|
|
29
|
+
Requires-Dist: nilearn==0.13.1
|
|
30
|
+
Requires-Dist: numpy>=2.0.0
|
|
31
|
+
Requires-Dist: pandas>=2.0.0
|
|
32
|
+
Requires-Dist: psutil>=5.9
|
|
33
|
+
Requires-Dist: psychopy>=2025.2.0
|
|
34
|
+
Requires-Dist: pygame>=2.6.0
|
|
35
|
+
Requires-Dist: pydicom
|
|
36
|
+
Requires-Dist: python-xlib; sys_platform == "linux"
|
|
37
|
+
Requires-Dist: pyyaml
|
|
38
|
+
Requires-Dist: scipy>=1.14.0
|
|
39
|
+
Requires-Dist: screeninfo>=0.8
|
|
40
|
+
Requires-Dist: templateflow
|
|
41
|
+
Requires-Dist: watchdog
|
|
42
|
+
Provides-Extra: dev
|
|
43
|
+
Requires-Dist: build; extra == "dev"
|
|
44
|
+
Requires-Dist: mypy==1.19.1; extra == "dev"
|
|
45
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
46
|
+
Requires-Dist: ruff==0.15.2; extra == "dev"
|
|
47
|
+
Requires-Dist: types-PyYAML; extra == "dev"
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
|
|
50
|
+
# pineuro
|
|
51
|
+
|
|
52
|
+
**Python for Individualized Neurofeedback**
|
|
53
|
+
|
|
54
|
+
A Python library for real-time fMRI neurofeedback research. pineuro provides a complete pipeline from DICOM acquisition through preprocessing, network mask extraction, and moment-to-moment BOLD activation estimation, with a desktop GUI for study management and participant workflows.
|
|
55
|
+
|
|
56
|
+
## Features
|
|
57
|
+
|
|
58
|
+
**Core pipeline**
|
|
59
|
+
- **DICOM processing**: Watch for incoming scanner DICOMs and process volumes as they arrive
|
|
60
|
+
- **Preprocessing**: Motion correction, brain extraction, and multi-run coregistration (ANTsPy)
|
|
61
|
+
- **ICA mask extraction**: Participant-specific network masks via ICA and Yeo atlas
|
|
62
|
+
- **GLM mask extraction**: Network masks from task localizer contrasts
|
|
63
|
+
- **MNI atlas masks**: Atlas-based ROIs without a localizer run
|
|
64
|
+
- **Incremental GLM**: Online activation estimation using Gentleman's algorithm
|
|
65
|
+
- **Connectivity feedback**: Real-time functional connectivity between network pairs
|
|
66
|
+
|
|
67
|
+
**Study management**
|
|
68
|
+
- **Desktop GUI**: Study configuration and participant workflows (`pineuro-gui`)
|
|
69
|
+
- **Session orchestration**: Multi-run and multi-session protocols
|
|
70
|
+
- **Sham / control conditions**: Sham feedback, replay providers, and randomization
|
|
71
|
+
- **QC reports**: HTML quality reports for each pipeline stage
|
|
72
|
+
|
|
73
|
+
**Task framework**
|
|
74
|
+
- **Built-in tasks**: Gauge (dual-network block design), ball (dual-network adaptive), thermometer (single-network), and RIT variants
|
|
75
|
+
- **Task plugin system**: `@pineuro_task` decorator and entry-point registration
|
|
76
|
+
- **Simulation mode**: Offline DICOM replay at configurable speed
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
**Requires Python 3.10 or 3.11** (`>=3.10,<3.12`). All dependencies are pure pip; no external tools required.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git clone https://github.com/WhitfieldGabrieli-EPICBrainLab/pineuro.git
|
|
84
|
+
cd pineuro
|
|
85
|
+
pip install -e .
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
See the [Installation Guide](docs/getting-started/installation.md) for platform-specific notes and troubleshooting.
|
|
89
|
+
|
|
90
|
+
## GUI
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pineuro-gui
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The desktop GUI provides study configuration, participant workflows, live activation plots, and QC reports. See the [First Session Tutorial](docs/getting-started/first-session.md) for a complete walkthrough.
|
|
97
|
+
|
|
98
|
+
## Documentation
|
|
99
|
+
|
|
100
|
+
**Getting started**
|
|
101
|
+
- [Installation](docs/getting-started/installation.md): Setup and dependencies
|
|
102
|
+
- [Quick Start](docs/getting-started/quickstart.md): GUI quick start (with an API snippet)
|
|
103
|
+
- [First Session](docs/getting-started/first-session.md): End-to-end GUI walkthrough
|
|
104
|
+
|
|
105
|
+
**User guide**: [Full index](docs/index.md)
|
|
106
|
+
|
|
107
|
+
[Pipeline Overview](docs/user-guide/pipeline-overview.md) · [Study Configuration](docs/user-guide/study-configuration.md) · [Preprocessing](docs/user-guide/preprocessing.md) · [Mask Extraction](docs/user-guide/mask-extraction.md) · [Feedback Tasks](docs/user-guide/feedback-tasks.md) · [Tasks](docs/user-guide/tasks.md) · [Sham & Blinding](docs/user-guide/sham-and-blinding.md) · [Multi-Session](docs/user-guide/multi-session.md) · [Display Setup](docs/user-guide/display-setup.md) · [Simulation Mode](docs/user-guide/simulation-mode.md) · [QC Reports](docs/user-guide/qc-reports.md) · [Network Testing](docs/user-guide/network-testing.md)
|
|
108
|
+
|
|
109
|
+
## Citation
|
|
110
|
+
|
|
111
|
+
If you use pineuro in your research, please cite:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
Vandewouw, M. M. (2026). pineuro: Python for Individualized Neurofeedback.
|
|
115
|
+
https://github.com/WhitfieldGabrieli-EPICBrainLab/pineuro
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
Apache License 2.0. See [LICENSE](LICENSE) for details.
|
|
121
|
+
|
pineuro-1.0.0/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# pineuro
|
|
2
|
+
|
|
3
|
+
**Python for Individualized Neurofeedback**
|
|
4
|
+
|
|
5
|
+
A Python library for real-time fMRI neurofeedback research. pineuro provides a complete pipeline from DICOM acquisition through preprocessing, network mask extraction, and moment-to-moment BOLD activation estimation, with a desktop GUI for study management and participant workflows.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
**Core pipeline**
|
|
10
|
+
- **DICOM processing**: Watch for incoming scanner DICOMs and process volumes as they arrive
|
|
11
|
+
- **Preprocessing**: Motion correction, brain extraction, and multi-run coregistration (ANTsPy)
|
|
12
|
+
- **ICA mask extraction**: Participant-specific network masks via ICA and Yeo atlas
|
|
13
|
+
- **GLM mask extraction**: Network masks from task localizer contrasts
|
|
14
|
+
- **MNI atlas masks**: Atlas-based ROIs without a localizer run
|
|
15
|
+
- **Incremental GLM**: Online activation estimation using Gentleman's algorithm
|
|
16
|
+
- **Connectivity feedback**: Real-time functional connectivity between network pairs
|
|
17
|
+
|
|
18
|
+
**Study management**
|
|
19
|
+
- **Desktop GUI**: Study configuration and participant workflows (`pineuro-gui`)
|
|
20
|
+
- **Session orchestration**: Multi-run and multi-session protocols
|
|
21
|
+
- **Sham / control conditions**: Sham feedback, replay providers, and randomization
|
|
22
|
+
- **QC reports**: HTML quality reports for each pipeline stage
|
|
23
|
+
|
|
24
|
+
**Task framework**
|
|
25
|
+
- **Built-in tasks**: Gauge (dual-network block design), ball (dual-network adaptive), thermometer (single-network), and RIT variants
|
|
26
|
+
- **Task plugin system**: `@pineuro_task` decorator and entry-point registration
|
|
27
|
+
- **Simulation mode**: Offline DICOM replay at configurable speed
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
**Requires Python 3.10 or 3.11** (`>=3.10,<3.12`). All dependencies are pure pip; no external tools required.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
git clone https://github.com/WhitfieldGabrieli-EPICBrainLab/pineuro.git
|
|
35
|
+
cd pineuro
|
|
36
|
+
pip install -e .
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
See the [Installation Guide](docs/getting-started/installation.md) for platform-specific notes and troubleshooting.
|
|
40
|
+
|
|
41
|
+
## GUI
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pineuro-gui
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The desktop GUI provides study configuration, participant workflows, live activation plots, and QC reports. See the [First Session Tutorial](docs/getting-started/first-session.md) for a complete walkthrough.
|
|
48
|
+
|
|
49
|
+
## Documentation
|
|
50
|
+
|
|
51
|
+
**Getting started**
|
|
52
|
+
- [Installation](docs/getting-started/installation.md): Setup and dependencies
|
|
53
|
+
- [Quick Start](docs/getting-started/quickstart.md): GUI quick start (with an API snippet)
|
|
54
|
+
- [First Session](docs/getting-started/first-session.md): End-to-end GUI walkthrough
|
|
55
|
+
|
|
56
|
+
**User guide**: [Full index](docs/index.md)
|
|
57
|
+
|
|
58
|
+
[Pipeline Overview](docs/user-guide/pipeline-overview.md) · [Study Configuration](docs/user-guide/study-configuration.md) · [Preprocessing](docs/user-guide/preprocessing.md) · [Mask Extraction](docs/user-guide/mask-extraction.md) · [Feedback Tasks](docs/user-guide/feedback-tasks.md) · [Tasks](docs/user-guide/tasks.md) · [Sham & Blinding](docs/user-guide/sham-and-blinding.md) · [Multi-Session](docs/user-guide/multi-session.md) · [Display Setup](docs/user-guide/display-setup.md) · [Simulation Mode](docs/user-guide/simulation-mode.md) · [QC Reports](docs/user-guide/qc-reports.md) · [Network Testing](docs/user-guide/network-testing.md)
|
|
59
|
+
|
|
60
|
+
## Citation
|
|
61
|
+
|
|
62
|
+
If you use pineuro in your research, please cite:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
Vandewouw, M. M. (2026). pineuro: Python for Individualized Neurofeedback.
|
|
66
|
+
https://github.com/WhitfieldGabrieli-EPICBrainLab/pineuro
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
Apache License 2.0. See [LICENSE](LICENSE) for details.
|
|
72
|
+
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=45", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pineuro"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Python for Individualized Neurofeedback"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10,<3.12"
|
|
11
|
+
authors = [{name = "Marlee M Vandewouw", email = "marlee.vandewouw@outlook.com"}]
|
|
12
|
+
license = "Apache-2.0"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 5 - Production/Stable",
|
|
15
|
+
"Intended Audience :: Science/Research",
|
|
16
|
+
"Topic :: Scientific/Engineering :: Medical Science Apps.",
|
|
17
|
+
"Programming Language :: Python :: 3.10",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Operating System :: POSIX :: Linux",
|
|
20
|
+
"Operating System :: MacOS",
|
|
21
|
+
"Operating System :: Microsoft :: Windows",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
# antspyx/nilearn are exact-pinned: the validation suite asserts their
|
|
25
|
+
# output via exact / first-principles checks that only reproduce when the
|
|
26
|
+
# iterative optimizers run deterministically (single-threaded ITK/BLAS; see
|
|
27
|
+
# tests/validation/conftest.py). A version bump can change that output, so it
|
|
28
|
+
# must be a deliberate, re-verified change -- see tests/validation/README.md.
|
|
29
|
+
"antspyx==0.6.2",
|
|
30
|
+
"dcm2niix",
|
|
31
|
+
# Linux-only: reads /dev/input/event* for global key polling under Wayland,
|
|
32
|
+
# where XQueryKeymap is starved of events when the stimulus window is
|
|
33
|
+
# unfocused (see tasks/task_base.py:_poll_keys_evdev). Compiles against
|
|
34
|
+
# linux/input.h, so it must not install on Windows/macOS.
|
|
35
|
+
"evdev; sys_platform == 'linux'",
|
|
36
|
+
"flet==0.80.5",
|
|
37
|
+
"flet-charts>=0.1.0",
|
|
38
|
+
"matplotlib",
|
|
39
|
+
"nibabel>=5.3.0",
|
|
40
|
+
"nilearn==0.13.1", # exact-pinned -- see the antspyx note above
|
|
41
|
+
"numpy>=2.0.0",
|
|
42
|
+
"pandas>=2.0.0",
|
|
43
|
+
"psutil>=5.9", # Terminate the ICA subprocess tree on GUI Stop (mask_extraction)
|
|
44
|
+
"psychopy>=2025.2.0",
|
|
45
|
+
"pygame>=2.6.0", # Cross-platform audio backend for PsychoPy
|
|
46
|
+
"pydicom",
|
|
47
|
+
# Linux-only: Xlib window positioning (_move_window_xlib) and the Xorg
|
|
48
|
+
# unfocused-key poll path (XQueryKeymap). Present transitively via PsychoPy
|
|
49
|
+
# today, but pineuro uses it directly, so depend on it explicitly.
|
|
50
|
+
"python-xlib; sys_platform == 'linux'",
|
|
51
|
+
"pyyaml",
|
|
52
|
+
"scipy>=1.14.0",
|
|
53
|
+
"screeninfo>=0.8",
|
|
54
|
+
"templateflow",
|
|
55
|
+
"watchdog",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[project.urls]
|
|
59
|
+
Homepage = "https://github.com/WhitfieldGabrieli-EPICBrainLab/pineuro"
|
|
60
|
+
Documentation = "https://github.com/WhitfieldGabrieli-EPICBrainLab/pineuro/tree/main/docs"
|
|
61
|
+
Issues = "https://github.com/WhitfieldGabrieli-EPICBrainLab/pineuro/issues"
|
|
62
|
+
Changelog = "https://github.com/WhitfieldGabrieli-EPICBrainLab/pineuro/blob/main/CHANGELOG.md"
|
|
63
|
+
|
|
64
|
+
[project.optional-dependencies]
|
|
65
|
+
# ruff and mypy are exact-pinned so the checks CI runs match a local
|
|
66
|
+
# `pip install -e ".[dev]"`: a new release can change their output on unchanged
|
|
67
|
+
# code (ruff 0.17 formats code blocks in Markdown files; 0.15 did not). Upgrade them
|
|
68
|
+
# deliberately, together with the ruff install in .github/workflows/ci.yml.
|
|
69
|
+
dev = ["build", "mypy==1.19.1", "pytest>=8.0", "ruff==0.15.2", "types-PyYAML"]
|
|
70
|
+
|
|
71
|
+
[project.scripts]
|
|
72
|
+
pineuro-gui = "pineuro.gui.app:main"
|
|
73
|
+
|
|
74
|
+
# Third-party task plugins register under this entry-point group.
|
|
75
|
+
# In a plugin package's pyproject.toml:
|
|
76
|
+
# [project.entry-points."pineuro.tasks"]
|
|
77
|
+
# my_task = "my_package.tasks"
|
|
78
|
+
# pineuro auto-discovers all registered plugins at GUI startup.
|
|
79
|
+
[project.entry-points."pineuro.tasks"]
|
|
80
|
+
|
|
81
|
+
# ---------------------------------------------------------------------------
|
|
82
|
+
# Ruff — linter + formatter
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
[tool.ruff]
|
|
86
|
+
line-length = 120
|
|
87
|
+
target-version = "py310"
|
|
88
|
+
exclude = [".git", ".venv", "__pycache__", "*.egg-info", "build", "dist"]
|
|
89
|
+
|
|
90
|
+
[tool.ruff.lint]
|
|
91
|
+
select = ["E", "W", "F", "I", "UP", "B", "SIM"]
|
|
92
|
+
ignore = [
|
|
93
|
+
"E501", # line too long — formatter handles this
|
|
94
|
+
"E402", # module-level import not at top — lazy PsychoPy imports are intentional
|
|
95
|
+
"UP007", # Use X | Y for Union — noisy to enforce retroactively
|
|
96
|
+
"B007", # loop variable not used — common in NumPy loops (for _ in range(n))
|
|
97
|
+
"B008", # function call in default arg — legitimate for np.array() defaults
|
|
98
|
+
"SIM108", # use ternary — hurts readability in numerical code
|
|
99
|
+
]
|
|
100
|
+
fixable = ["I", "UP", "E", "W", "F401"]
|
|
101
|
+
|
|
102
|
+
[tool.ruff.lint.per-file-ignores]
|
|
103
|
+
"__init__.py" = ["F401"]
|
|
104
|
+
"tests/*.py" = ["T20", "S101", "INP001", "E402"]
|
|
105
|
+
"tests/**/*.py" = ["T20", "S101", "INP001", "E402"]
|
|
106
|
+
"analysis/*.py" = ["T20", "S101", "INP001", "E402"]
|
|
107
|
+
"src/pineuro/gui/**/*.py" = ["B006"]
|
|
108
|
+
"src/pineuro/tasks/*.py" = ["E402"]
|
|
109
|
+
|
|
110
|
+
[tool.ruff.lint.isort]
|
|
111
|
+
known-first-party = ["pineuro"]
|
|
112
|
+
force-sort-within-sections = false
|
|
113
|
+
split-on-trailing-comma = true
|
|
114
|
+
|
|
115
|
+
[tool.ruff.format]
|
|
116
|
+
quote-style = "double"
|
|
117
|
+
indent-style = "space"
|
|
118
|
+
skip-magic-trailing-comma = false
|
|
119
|
+
docstring-code-format = false
|
|
120
|
+
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
# pytest
|
|
123
|
+
# ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
[tool.pytest.ini_options]
|
|
126
|
+
testpaths = ["tests"]
|
|
127
|
+
markers = [
|
|
128
|
+
"slow: marks tests as slow (>10s)",
|
|
129
|
+
"validation: scientific-correctness validation (real ANTs/nilearn/dcm2niix, synthetic ground truth)",
|
|
130
|
+
]
|
|
131
|
+
addopts = "-m 'not validation'"
|
|
132
|
+
|
|
133
|
+
# ---------------------------------------------------------------------------
|
|
134
|
+
# mypy — basic type checking
|
|
135
|
+
# ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
[tool.mypy]
|
|
138
|
+
# Keep in step with the Python of the lint-and-typecheck job in .github/workflows/ci.yml.
|
|
139
|
+
python_version = "3.10"
|
|
140
|
+
warn_return_any = true
|
|
141
|
+
warn_unused_configs = true
|
|
142
|
+
ignore_missing_imports = true
|
|
143
|
+
no_implicit_optional = false
|
|
144
|
+
disable_error_code = ["import-untyped"]
|
|
145
|
+
|
pineuro-1.0.0/setup.cfg
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"""pineuro - Python Real-Time fMRI Neurofeedback
|
|
2
|
+
|
|
3
|
+
A complete pipeline for real-time fMRI neurofeedback research, from volume acquisition
|
|
4
|
+
through preprocessing, network mask extraction, and moment-to-moment BOLD activation estimation.
|
|
5
|
+
|
|
6
|
+
Main Functions
|
|
7
|
+
--------------
|
|
8
|
+
run_feedback_task : Run a complete real-time feedback task
|
|
9
|
+
preprocessing : DICOM conversion, motion correction, brain extraction
|
|
10
|
+
collect_volumes : Watch for and collect incoming scanner volumes (DICOM or NIfTI)
|
|
11
|
+
mask_extraction_rest : Extract network masks using ICA and Yeo atlas
|
|
12
|
+
mask_extraction_mni : Extract network masks directly from MNI atlas (no localizer needed)
|
|
13
|
+
mask_extraction_task : Extract network masks using GLM contrasts from task localizer
|
|
14
|
+
coregister_reference : Coregister masks to real-time scanner position
|
|
15
|
+
|
|
16
|
+
Task Framework
|
|
17
|
+
--------------
|
|
18
|
+
BaseTask : Abstract base for custom neurofeedback tasks
|
|
19
|
+
BaseLocalizerTask : Abstract base for localizer/stimulus tasks
|
|
20
|
+
BallTask : Built-in dual-network ball game with adaptive difficulty
|
|
21
|
+
ThermometerTask : Built-in single-network thermometer feedback
|
|
22
|
+
create_task : Factory function to create tasks from registry
|
|
23
|
+
NetworkRole : Declare network slot semantics for tasks
|
|
24
|
+
ActivationSmoother : Exponential moving average smoothing
|
|
25
|
+
ActivationNormalizer : Percentile-based normalization
|
|
26
|
+
|
|
27
|
+
Session Orchestration
|
|
28
|
+
--------------------
|
|
29
|
+
SessionConfig : High-level session configuration
|
|
30
|
+
LocalizerConfig / FeedbackRunConfig : Per-localizer and per-feedback-run configuration
|
|
31
|
+
run_full_session : Run complete multi-run session
|
|
32
|
+
setup_session : Initialize session (collection, preprocessing, mask extraction)
|
|
33
|
+
run_single_task : Run one feedback/transfer task via task factory
|
|
34
|
+
TaskState : Persistent state across runs
|
|
35
|
+
|
|
36
|
+
Examples
|
|
37
|
+
--------
|
|
38
|
+
>>> from pineuro import preprocessing, mask_extraction_rest, run_feedback_task
|
|
39
|
+
>>>
|
|
40
|
+
>>> # Step 1: Preprocess resting-state data
|
|
41
|
+
>>> preproc_file, mask_file, ref_file = preprocessing(
|
|
42
|
+
... participant_id='sub-01', session_id='ses-01', task='rest',
|
|
43
|
+
... root_dir='/data/sub-01/ses-01/preprocessing',
|
|
44
|
+
... input_dicoms='/scanner/sub-01/rest',
|
|
45
|
+
... )
|
|
46
|
+
>>>
|
|
47
|
+
>>> # Step 2: Extract participant-specific network masks (ICA + Yeo atlas)
|
|
48
|
+
>>> mask_paths = mask_extraction_rest(
|
|
49
|
+
... participant_id='sub-01', session_id='ses-01',
|
|
50
|
+
... root_dir='/data/sub-01/ses-01/mask_extraction',
|
|
51
|
+
... input_file=preproc_file, mask_file=mask_file, ref_file=ref_file,
|
|
52
|
+
... networks=['ContA', 'DefaultA'], # returns a list of mask Paths
|
|
53
|
+
... )
|
|
54
|
+
>>>
|
|
55
|
+
>>> # Step 3: Run a real-time feedback task (here, simulated offline)
|
|
56
|
+
>>> result = run_feedback_task(
|
|
57
|
+
... mask_paths=mask_paths, subject='sub-01', session='ses-01',
|
|
58
|
+
... task='ball', run=1,
|
|
59
|
+
... output_dir='/data/sub-01/ses-01/feedback',
|
|
60
|
+
... collection_dir='/scanner/incoming',
|
|
61
|
+
... n_baseline=15, n_volumes=200, tr=2.0,
|
|
62
|
+
... simulator_source_dir='/data/sub-01/feedback_dicoms', simulator_speed=10.0,
|
|
63
|
+
... )
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
import importlib as _importlib
|
|
67
|
+
from importlib.metadata import PackageNotFoundError as _PackageNotFoundError
|
|
68
|
+
from importlib.metadata import version as _pkg_version
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
# Single source of truth: the version declared in pyproject.toml and
|
|
72
|
+
# recorded in the installed package metadata.
|
|
73
|
+
__version__ = _pkg_version("pineuro")
|
|
74
|
+
except _PackageNotFoundError: # pragma: no cover - package not installed
|
|
75
|
+
__version__ = "unknown"
|
|
76
|
+
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
# Lazy import system (PEP 562)
|
|
79
|
+
#
|
|
80
|
+
# Heavy neuroimaging libraries (ANTsPy, nilearn, scipy, nibabel) take 8-17s
|
|
81
|
+
# to import on cold start. By deferring submodule imports until first access,
|
|
82
|
+
# the GUI starts instantly and scripts only pay for what they actually use.
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
# Mapping: attribute_name -> (submodule_path, attribute_name_in_submodule)
|
|
86
|
+
_LAZY_IMPORTS: dict[str, tuple[str, str]] = {
|
|
87
|
+
# --- feedback.py (heavy: nibabel, scipy, watchdog, pydicom) ---
|
|
88
|
+
"run_feedback_task": (".feedback", "run_feedback_task"),
|
|
89
|
+
"ConnectivityEstimator": (".feedback", "ConnectivityEstimator"),
|
|
90
|
+
"get_next_run_number": (".feedback", "get_next_run_number"),
|
|
91
|
+
"get_slot_run_info": (".feedback", "get_slot_run_info"),
|
|
92
|
+
# --- preprocessing.py (heavy: nibabel, scipy) ---
|
|
93
|
+
"preprocessing": (".preprocessing", "preprocessing"),
|
|
94
|
+
"collect_volumes": (".preprocessing", "collect_volumes"),
|
|
95
|
+
# --- mask_extraction.py (heavy: nibabel, scipy, pandas) ---
|
|
96
|
+
"mask_extraction_rest": (".mask_extraction", "mask_extraction_rest"),
|
|
97
|
+
"mask_extraction_mni": (".mask_extraction", "mask_extraction_mni"),
|
|
98
|
+
"mask_extraction_task": (".mask_extraction", "mask_extraction_task"),
|
|
99
|
+
"run_mask_extraction_from_config": (".mask_extraction", "run_mask_extraction_from_config"),
|
|
100
|
+
"import_manual_masks": (".mask_extraction", "import_manual_masks"),
|
|
101
|
+
"coregister_reference": (".mask_extraction", "coregister_reference"),
|
|
102
|
+
"create_tissue_masks": (".mask_extraction", "create_tissue_masks"),
|
|
103
|
+
"ATLAS_SOURCES": (".atlas_registry", "ATLAS_SOURCES"),
|
|
104
|
+
"get_atlas_labels": (".mask_extraction", "get_atlas_labels"),
|
|
105
|
+
# --- tasks (lightweight but deferred to avoid loading task modules) ---
|
|
106
|
+
"BaseTask": (".tasks", "BaseTask"),
|
|
107
|
+
"BaseLocalizerTask": (".tasks", "BaseLocalizerTask"),
|
|
108
|
+
"BallTask": (".tasks", "BallTask"),
|
|
109
|
+
"ThermometerTask": (".tasks", "ThermometerTask"),
|
|
110
|
+
"ActivationSmoother": (".tasks", "ActivationSmoother"),
|
|
111
|
+
"ActivationNormalizer": (".tasks", "ActivationNormalizer"),
|
|
112
|
+
"NetworkRole": (".tasks", "NetworkRole"),
|
|
113
|
+
"create_task": (".tasks", "create_task"),
|
|
114
|
+
"create_localizer_task": (".tasks", "create_localizer_task"),
|
|
115
|
+
# --- tasks.state ---
|
|
116
|
+
"TaskState": (".tasks.state", "TaskState"),
|
|
117
|
+
"RunInfo": (".tasks.state", "RunInfo"),
|
|
118
|
+
# --- session.py (lightweight: stdlib only) ---
|
|
119
|
+
"SessionConfig": (".session", "SessionConfig"),
|
|
120
|
+
"PathConfig": (".session", "PathConfig"),
|
|
121
|
+
"LocalizerConfig": (".session", "LocalizerConfig"),
|
|
122
|
+
"LocalizerExtractionConfig": (".session", "LocalizerExtractionConfig"),
|
|
123
|
+
"FeedbackRunConfig": (".session", "FeedbackRunConfig"),
|
|
124
|
+
"QCConfig": (".session", "QCConfig"),
|
|
125
|
+
"GLMConfig": (".session", "GLMConfig"),
|
|
126
|
+
"ConnectivityConfig": (".session", "ConnectivityConfig"),
|
|
127
|
+
"IntermittentConfig": (".session", "IntermittentConfig"),
|
|
128
|
+
"SimulatorConfig": (".session", "SimulatorConfig"),
|
|
129
|
+
"ScannerConfig": (".session", "ScannerConfig"),
|
|
130
|
+
"ReferenceConfig": (".session", "ReferenceConfig"),
|
|
131
|
+
"SessionMetadata": (".session", "SessionMetadata"),
|
|
132
|
+
"ShamConfig": (".session", "ShamConfig"),
|
|
133
|
+
"ShamRunConfig": (".session", "ShamRunConfig"),
|
|
134
|
+
"SimulatorSourceMapping": (".session", "SimulatorSourceMapping"),
|
|
135
|
+
"setup_session": (".session", "setup_session"),
|
|
136
|
+
"run_single_task": (".session", "run_single_task"),
|
|
137
|
+
"run_full_session": (".session", "run_full_session"),
|
|
138
|
+
"run_next_task": (".session", "run_next_task"),
|
|
139
|
+
"reset_task_state": (".session", "reset_task_state"),
|
|
140
|
+
"resolve_localizer_masks": (".session", "resolve_localizer_masks"),
|
|
141
|
+
"detect_existing_sessions": (".session", "detect_existing_sessions"),
|
|
142
|
+
"next_session_id": (".session", "next_session_id"),
|
|
143
|
+
# --- sham.py ---
|
|
144
|
+
"ShamActivationProvider": (".sham", "ShamActivationProvider"),
|
|
145
|
+
"ReplayActivationProvider": (".sham", "ReplayActivationProvider"),
|
|
146
|
+
"load_randomization": (".sham", "load_randomization"),
|
|
147
|
+
"generate_randomization": (".sham", "generate_randomization"),
|
|
148
|
+
"get_condition_for_participant": (".sham", "get_condition_for_participant"),
|
|
149
|
+
"find_feedbacklog_for_replay": (".sham", "find_feedbacklog_for_replay"),
|
|
150
|
+
# --- utils.py (heavy: numpy, nibabel, scipy, templateflow) ---
|
|
151
|
+
"enable_timing": (".utils", "enable_timing"),
|
|
152
|
+
"disable_timing": (".utils", "disable_timing"),
|
|
153
|
+
"get_timing_data": (".utils", "get_timing_data"),
|
|
154
|
+
"save_timing_report": (".utils", "save_timing_report"),
|
|
155
|
+
"Timer": (".utils", "Timer"),
|
|
156
|
+
"check_platform_requirements": (".utils", "check_platform_requirements"),
|
|
157
|
+
"outputs_exist": (".utils", "outputs_exist"),
|
|
158
|
+
"count_volume_files": (".utils", "count_volume_files"),
|
|
159
|
+
"collection_is_complete": (".utils", "collection_is_complete"),
|
|
160
|
+
"ProgressEmitter": (".utils", "ProgressEmitter"),
|
|
161
|
+
"compute_hrf": (".utils", "compute_hrf"),
|
|
162
|
+
"compute_boxcar_task_regressor": (".utils", "compute_boxcar_task_regressor"),
|
|
163
|
+
# --- script_generator.py ---
|
|
164
|
+
"generate_mask_extraction_script": (".script_generator", "generate_mask_extraction_script"),
|
|
165
|
+
"save_script": (".script_generator", "save_script"),
|
|
166
|
+
# --- scanner_setup.py (lightweight: stdlib only) ---
|
|
167
|
+
"test_scanner_connection": (".scanner_setup", "test_scanner_connection"),
|
|
168
|
+
"ConnectionTestResult": (".scanner_setup", "ConnectionTestResult"),
|
|
169
|
+
"ping_scanner": (".scanner_setup", "ping_scanner"),
|
|
170
|
+
"check_watch_directory": (".scanner_setup", "check_watch_directory"),
|
|
171
|
+
"get_setup_instructions": (".scanner_setup", "get_setup_instructions"),
|
|
172
|
+
# --- config module ---
|
|
173
|
+
"config": (".config", ""), # Module import, not attribute
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def __getattr__(name: str):
|
|
178
|
+
if name in _LAZY_IMPORTS:
|
|
179
|
+
module_path, attr_name = _LAZY_IMPORTS[name]
|
|
180
|
+
module = _importlib.import_module(module_path, __name__)
|
|
181
|
+
if not attr_name:
|
|
182
|
+
# Module-level import (e.g., config)
|
|
183
|
+
val = module
|
|
184
|
+
else:
|
|
185
|
+
val = getattr(module, attr_name)
|
|
186
|
+
globals()[name] = val # Cache for subsequent access
|
|
187
|
+
return val
|
|
188
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
__all__ = [
|
|
192
|
+
# Version
|
|
193
|
+
"__version__",
|
|
194
|
+
# Core functions
|
|
195
|
+
"run_feedback_task",
|
|
196
|
+
"ConnectivityEstimator",
|
|
197
|
+
"get_next_run_number",
|
|
198
|
+
"get_slot_run_info",
|
|
199
|
+
"preprocessing",
|
|
200
|
+
"collect_volumes",
|
|
201
|
+
"mask_extraction_rest",
|
|
202
|
+
"mask_extraction_mni",
|
|
203
|
+
"mask_extraction_task",
|
|
204
|
+
"run_mask_extraction_from_config",
|
|
205
|
+
"import_manual_masks",
|
|
206
|
+
"coregister_reference",
|
|
207
|
+
"ATLAS_SOURCES",
|
|
208
|
+
"get_atlas_labels",
|
|
209
|
+
# Task classes
|
|
210
|
+
"BaseTask",
|
|
211
|
+
"BaseLocalizerTask",
|
|
212
|
+
"BallTask",
|
|
213
|
+
"ThermometerTask",
|
|
214
|
+
"ActivationSmoother",
|
|
215
|
+
"ActivationNormalizer",
|
|
216
|
+
# Task registry & factory
|
|
217
|
+
"NetworkRole",
|
|
218
|
+
"create_task",
|
|
219
|
+
"create_localizer_task",
|
|
220
|
+
# Task regressor computation
|
|
221
|
+
"compute_hrf",
|
|
222
|
+
"compute_boxcar_task_regressor",
|
|
223
|
+
# Task state
|
|
224
|
+
"TaskState",
|
|
225
|
+
"RunInfo",
|
|
226
|
+
# Configuration dataclasses
|
|
227
|
+
"SessionConfig",
|
|
228
|
+
"PathConfig",
|
|
229
|
+
"LocalizerConfig",
|
|
230
|
+
"LocalizerExtractionConfig",
|
|
231
|
+
"FeedbackRunConfig",
|
|
232
|
+
"QCConfig",
|
|
233
|
+
"GLMConfig",
|
|
234
|
+
"ConnectivityConfig",
|
|
235
|
+
"IntermittentConfig",
|
|
236
|
+
"SimulatorConfig",
|
|
237
|
+
"ScannerConfig",
|
|
238
|
+
"ReferenceConfig",
|
|
239
|
+
"SessionMetadata",
|
|
240
|
+
"ShamConfig",
|
|
241
|
+
"ShamRunConfig",
|
|
242
|
+
"SimulatorSourceMapping",
|
|
243
|
+
# Sham functionality
|
|
244
|
+
"ShamActivationProvider",
|
|
245
|
+
"ReplayActivationProvider",
|
|
246
|
+
"load_randomization",
|
|
247
|
+
"generate_randomization",
|
|
248
|
+
"get_condition_for_participant",
|
|
249
|
+
"find_feedbacklog_for_replay",
|
|
250
|
+
# Orchestration functions
|
|
251
|
+
"setup_session",
|
|
252
|
+
"run_single_task",
|
|
253
|
+
"run_full_session",
|
|
254
|
+
"run_next_task",
|
|
255
|
+
"reset_task_state",
|
|
256
|
+
# Multi-session support functions
|
|
257
|
+
"resolve_localizer_masks",
|
|
258
|
+
"detect_existing_sessions",
|
|
259
|
+
"next_session_id",
|
|
260
|
+
# Timing functions
|
|
261
|
+
"enable_timing",
|
|
262
|
+
"disable_timing",
|
|
263
|
+
"get_timing_data",
|
|
264
|
+
"save_timing_report",
|
|
265
|
+
"Timer",
|
|
266
|
+
# Platform checks
|
|
267
|
+
"check_platform_requirements",
|
|
268
|
+
# Utilities
|
|
269
|
+
"outputs_exist",
|
|
270
|
+
"count_volume_files",
|
|
271
|
+
"collection_is_complete",
|
|
272
|
+
"ProgressEmitter",
|
|
273
|
+
# Script generation
|
|
274
|
+
"generate_mask_extraction_script",
|
|
275
|
+
"save_script",
|
|
276
|
+
# Scanner setup
|
|
277
|
+
"test_scanner_connection",
|
|
278
|
+
"ConnectionTestResult",
|
|
279
|
+
"ping_scanner",
|
|
280
|
+
"check_watch_directory",
|
|
281
|
+
"get_setup_instructions",
|
|
282
|
+
# Configuration
|
|
283
|
+
"config",
|
|
284
|
+
]
|