ddalab 1.2.11__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ddalab-1.2.11.dist-info/METADATA +277 -0
- ddalab-1.2.11.dist-info/RECORD +116 -0
- ddalab-1.2.11.dist-info/WHEEL +5 -0
- ddalab-1.2.11.dist-info/entry_points.txt +4 -0
- ddalab-1.2.11.dist-info/top_level.txt +1 -0
- ddalab_app/__init__.py +5 -0
- ddalab_app/__main__.py +4 -0
- ddalab_app/app/__init__.py +14 -0
- ddalab_app/app/core/__init__.py +1 -0
- ddalab_app/app/core/analysis_input.py +44 -0
- ddalab_app/app/core/snapshot_payload.py +84 -0
- ddalab_app/app/integrations/__init__.py +1 -0
- ddalab_app/app/integrations/cdr.py +297 -0
- ddalab_app/app/integrations/cdr_simulation.py +196 -0
- ddalab_app/app/integrations/dda_export_utils.py +421 -0
- ddalab_app/app/runtime/__init__.py +1 -0
- ddalab_app/app/runtime/perf_logging.py +70 -0
- ddalab_app/app/runtime/runtime_logging.py +230 -0
- ddalab_app/app/workbench/__init__.py +4 -0
- ddalab_app/app/workbench/controller.py +1971 -0
- ddalab_app/app/workbench/models.py +117 -0
- ddalab_app/app/workbench/runtime.py +45 -0
- ddalab_app/app/workbench/tasks.py +50 -0
- ddalab_app/assets/fonts/ibm_plex_sans_medium.ttf +0 -0
- ddalab_app/assets/fonts/ibm_plex_sans_regular.ttf +0 -0
- ddalab_app/assets/fonts/ibm_plex_sans_semibold.ttf +0 -0
- ddalab_app/assets/icons/icon.icns +0 -0
- ddalab_app/assets/icons/icon.ico +0 -0
- ddalab_app/assets/icons/icon.png +0 -0
- ddalab_app/assets/icons/icon.svg +49 -0
- ddalab_app/backend/__init__.py +10 -0
- ddalab_app/backend/contracts.py +174 -0
- ddalab_app/backend/dda/__init__.py +73 -0
- ddalab_app/backend/dda/engine.py +800 -0
- ddalab_app/backend/dda/motifs.py +135 -0
- ddalab_app/backend/dda/scalar.py +709 -0
- ddalab_app/backend/dda/sidecar.py +224 -0
- ddalab_app/backend/dda/sparse.py +960 -0
- ddalab_app/backend/local/__init__.py +11 -0
- ddalab_app/backend/local/client.py +256 -0
- ddalab_app/backend/local/dda.py +1034 -0
- ddalab_app/backend/local/filesystem.py +131 -0
- ddalab_app/backend/local/results.py +264 -0
- ddalab_app/backend/local/runtime.py +175 -0
- ddalab_app/backend/readers/__init__.py +13 -0
- ddalab_app/backend/readers/common.py +297 -0
- ddalab_app/backend/readers/delimited.py +300 -0
- ddalab_app/backend/readers/local.py +96 -0
- ddalab_app/backend/readers/mne.py +179 -0
- ddalab_app/backend/readers/nifti.py +225 -0
- ddalab_app/backend/readers/nwb.py +189 -0
- ddalab_app/backend/readers/xdf.py +221 -0
- ddalab_app/backend/services/__init__.py +10 -0
- ddalab_app/backend/services/ica.py +166 -0
- ddalab_app/backend/services/nsg.py +696 -0
- ddalab_app/backend/services/openneuro.py +150 -0
- ddalab_app/cli/__init__.py +1 -0
- ddalab_app/cli/commands.py +336 -0
- ddalab_app/cli/constants.py +47 -0
- ddalab_app/cli/parser.py +294 -0
- ddalab_app/cli/plot_style.py +37 -0
- ddalab_app/cli/runtime.py +500 -0
- ddalab_app/cli_main.py +25 -0
- ddalab_app/domain/__init__.py +1 -0
- ddalab_app/domain/file_types.py +246 -0
- ddalab_app/domain/models.py +902 -0
- ddalab_app/gui_main.py +77 -0
- ddalab_app/persistence/__init__.py +3 -0
- ddalab_app/persistence/state_db.py +969 -0
- ddalab_app/runtime/bin/ddalab-backend.exe +0 -0
- ddalab_app/runtime_binary_names.py +10 -0
- ddalab_app/runtime_paths.py +93 -0
- ddalab_app/ui/__init__.py +13 -0
- ddalab_app/ui/plot_benchmarks.py +163 -0
- ddalab_app/ui/plot_data.py +75 -0
- ddalab_app/ui/plot_data_common.py +162 -0
- ddalab_app/ui/plot_heatmap_data.py +71 -0
- ddalab_app/ui/plot_layers.py +12 -0
- ddalab_app/ui/plot_matrix_data.py +362 -0
- ddalab_app/ui/plot_waveform_data.py +628 -0
- ddalab_app/ui/qml/QuickPlotSurface.qml +248 -0
- ddalab_app/ui/qml/QuickWaveformSurface.qml +316 -0
- ddalab_app/ui/qml/__init__.py +1 -0
- ddalab_app/ui/qml/workbench/Main.qml +382 -0
- ddalab_app/ui/qml/workbench/__init__.py +1 -0
- ddalab_app/ui/qml/workbench/components/ActivitySpinner.qml +39 -0
- ddalab_app/ui/qml/workbench/components/AnalysisInputSummary.qml +80 -0
- ddalab_app/ui/qml/workbench/components/AnalysisInspector.qml +206 -0
- ddalab_app/ui/qml/workbench/components/CdrHeatmap.qml +82 -0
- ddalab_app/ui/qml/workbench/components/CdrLineChart.qml +199 -0
- ddalab_app/ui/qml/workbench/components/CdrResultsView.qml +89 -0
- ddalab_app/ui/qml/workbench/components/InspectorPanel.qml +96 -0
- ddalab_app/ui/qml/workbench/components/LibraryRail.qml +152 -0
- ddalab_app/ui/qml/workbench/components/LoadingOverlay.qml +32 -0
- ddalab_app/ui/qml/workbench/components/NavButton.qml +34 -0
- ddalab_app/ui/qml/workbench/components/ResultHistoryList.qml +74 -0
- ddalab_app/ui/qml/workbench/components/ResultsInspector.qml +151 -0
- ddalab_app/ui/qml/workbench/components/SectionLabel.qml +10 -0
- ddalab_app/ui/qml/workbench/components/SettingsInspector.qml +134 -0
- ddalab_app/ui/qml/workbench/components/Toggle.qml +43 -0
- ddalab_app/ui/qml/workbench/components/WorkbenchButton.qml +52 -0
- ddalab_app/ui/qml/workbench/components/WorkbenchField.qml +24 -0
- ddalab_app/ui/qml/workbench/components/WorkspaceInspector.qml +124 -0
- ddalab_app/ui/qml/workbench/components/__init__.py +1 -0
- ddalab_app/ui/qml/workbench/pages/AnalysisPage.qml +220 -0
- ddalab_app/ui/qml/workbench/pages/ResultsPage.qml +254 -0
- ddalab_app/ui/qml/workbench/pages/SettingsPage.qml +51 -0
- ddalab_app/ui/qml/workbench/pages/WorkspacePage.qml +229 -0
- ddalab_app/ui/qml/workbench/pages/__init__.py +1 -0
- ddalab_app/ui/qt_plot_renderer.py +216 -0
- ddalab_app/ui/quick_plot_surface.py +776 -0
- ddalab_app/ui/quick_waveform_surface.py +633 -0
- ddalab_app/ui/render_cache.py +32 -0
- ddalab_app/ui/style.py +205 -0
- ddalab_app/update_manager.py +439 -0
- ddalab_app/version.py +38 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ddalab
|
|
3
|
+
Version: 1.2.11
|
|
4
|
+
Summary: DDALAB desktop application and command-line interface
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/sdraeger/DDALAB
|
|
7
|
+
Project-URL: Documentation, https://github.com/sdraeger/DDALAB/tree/main/packages/ddalab
|
|
8
|
+
Project-URL: Repository, https://github.com/sdraeger/DDALAB
|
|
9
|
+
Project-URL: Issues, https://github.com/sdraeger/DDALAB/issues
|
|
10
|
+
Requires-Python: <3.13,>=3.11
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: PySide6==6.8.2.1
|
|
13
|
+
Requires-Dist: requests<3,>=2.32.0
|
|
14
|
+
Requires-Dist: urllib3<2
|
|
15
|
+
Requires-Dist: numpy==1.26.4
|
|
16
|
+
Requires-Dist: matplotlib==3.10.8
|
|
17
|
+
Requires-Dist: scipy==1.13.1
|
|
18
|
+
Requires-Dist: scikit-learn==1.5.2
|
|
19
|
+
Requires-Dist: mne==1.8.0
|
|
20
|
+
Requires-Dist: defusedxml<1,>=0.7
|
|
21
|
+
Requires-Dist: pymatreader<1,>=0.0.32
|
|
22
|
+
Requires-Dist: mffpy<1,>=0.10
|
|
23
|
+
Requires-Dist: nibabel==5.3.3
|
|
24
|
+
Requires-Dist: pyxdf==1.16.8
|
|
25
|
+
Requires-Dist: pynwb==2.8.3
|
|
26
|
+
Requires-Dist: packaging<25,>=24
|
|
27
|
+
|
|
28
|
+
# DDALAB
|
|
29
|
+
|
|
30
|
+
`ddalab` is the unified Python package for DDALAB.
|
|
31
|
+
|
|
32
|
+
It ships:
|
|
33
|
+
|
|
34
|
+
- the command-line interface
|
|
35
|
+
- the Qt desktop application
|
|
36
|
+
- the shared local runtime and dataset readers
|
|
37
|
+
- the bundled `dda-rs` backend binary used for local DDA analysis
|
|
38
|
+
|
|
39
|
+
## Entry Points
|
|
40
|
+
|
|
41
|
+
Installing this package provides:
|
|
42
|
+
|
|
43
|
+
- `ddalab`
|
|
44
|
+
- `ddalab-cli`
|
|
45
|
+
- `ddalab-gui`
|
|
46
|
+
|
|
47
|
+
Use `ddalab open` to open the desktop application. Other `ddalab` commands use
|
|
48
|
+
the command-line interface. `ddalab gui` remains an alias for `ddalab open`,
|
|
49
|
+
and `ddalab-gui` launches the application directly.
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
python -m pip install ddalab
|
|
55
|
+
ddalab open
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For a source checkout:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
cd packages/ddalab
|
|
62
|
+
python -m pip install .
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Published wheels and desktop installers bundle the local `dda-rs` backend, so DDA works offline without a separate network service.
|
|
66
|
+
|
|
67
|
+
For editable or source installs, local DDA analysis also requires a working Rust toolchain with `cargo` so the bundled `dda-rs` binary can be built for your platform.
|
|
68
|
+
|
|
69
|
+
## CLI Examples
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
ddalab health
|
|
73
|
+
ddalab dataset info --file data/MG100_Seizure1.edf
|
|
74
|
+
ddalab dda info --json
|
|
75
|
+
ddalab dda validate data/MG100_Seizure1.edf --json
|
|
76
|
+
ddalab dda run data/MG100_Seizure1.edf --channels 0 1 2 --variants ST SY --end 30
|
|
77
|
+
ddalab dda run data/MG100_Seizure1.edf --channels 0 1 2 --variants ST --device cuda
|
|
78
|
+
ddalab dda batch --bids-dir data/ds003029 --variants ST --continue-on-error
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
CPU execution is the default. The desktop DDA setup and CLI accept CUDA on
|
|
82
|
+
systems with an NVIDIA driver and cuBLAS runtime; `cuda:N` selects a specific
|
|
83
|
+
GPU. Analyses that use Rust-only CCD extensions continue those extension
|
|
84
|
+
computations on the CPU.
|
|
85
|
+
|
|
86
|
+
## Python DDA Estimators
|
|
87
|
+
|
|
88
|
+
The bundled Python backend includes scalar estimators for derivative-form and
|
|
89
|
+
weak-form DDA. Derivative-form DDA builds delayed polynomial features
|
|
90
|
+
`Phi(t)` and regresses a numerical derivative of `x(t)` on those features.
|
|
91
|
+
Weak-form DDA uses the same delayed polynomial dictionary, but fits integrated
|
|
92
|
+
increments instead:
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
x(t + Delta) - x(t) ~= integral_t^{t+Delta} Phi(s) ds @ beta
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This avoids pointwise derivative estimation and can be preferable when
|
|
99
|
+
observation noise would be amplified by finite differences. Coefficients remain
|
|
100
|
+
on the same differential-equation scale as derivative-form DDA because both the
|
|
101
|
+
response and design matrix are integrated over the same window.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from ddalab_app.backend.dda import DerivativeDDA, WeakFormDDA
|
|
105
|
+
|
|
106
|
+
vanilla = DerivativeDDA(degree=3, delays=[0], derivative="finite_difference")
|
|
107
|
+
vanilla.fit(x, dt=0.01)
|
|
108
|
+
|
|
109
|
+
weak = WeakFormDDA(
|
|
110
|
+
degree=3,
|
|
111
|
+
delays=[0],
|
|
112
|
+
integration_window=9,
|
|
113
|
+
stride=1,
|
|
114
|
+
quadrature="trapezoid",
|
|
115
|
+
regression="ridge",
|
|
116
|
+
ridge_alpha=1e-6,
|
|
117
|
+
standardize_features=True,
|
|
118
|
+
)
|
|
119
|
+
weak.fit(x, dt=0.01)
|
|
120
|
+
print(weak.term_names_)
|
|
121
|
+
print(weak.coefficients_)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`integration_window` and `stride` are specified in samples. Use
|
|
125
|
+
`integration_window_seconds` or `stride_seconds` with `dt`/`sample_rate` when
|
|
126
|
+
time-based settings are more convenient. The default weak-form window is
|
|
127
|
+
conservative: half of the smallest positive delay, with a lower bound of three
|
|
128
|
+
samples, or five samples for nondelayed scalar systems. Too-small windows behave
|
|
129
|
+
more like derivative DDA; too-large windows can blur fast dynamics.
|
|
130
|
+
|
|
131
|
+
The available quadrature rules are `trapezoid`, `left_rectangle`,
|
|
132
|
+
`right_rectangle`, `midpoint`, and `simpson` when SciPy is available.
|
|
133
|
+
`trapezoid` is the default because it is the natural continuous-time weak-form
|
|
134
|
+
choice for sampled trajectories. `left_rectangle` can match an Euler-stepped
|
|
135
|
+
simulator more closely and is useful as a discretization diagnostic, but it is
|
|
136
|
+
not the recommended default for continuous-time estimation.
|
|
137
|
+
|
|
138
|
+
Weak-form DDA does not universally dominate derivative DDA. It trades derivative
|
|
139
|
+
noise sensitivity for integration-window bias. In practice, choose
|
|
140
|
+
`integration_window` on a held-out validation interval by minimizing weak-form
|
|
141
|
+
interval prediction error, then report the selected-window result. Oracle-best
|
|
142
|
+
windows, selected using known synthetic coefficients, are useful for experiments
|
|
143
|
+
but should be treated only as upper bounds.
|
|
144
|
+
|
|
145
|
+
For noisy or ill-conditioned windows, the recommended weak-form setting is
|
|
146
|
+
`regression="ridge"` with `standardize_features=True`. Feature standardization is
|
|
147
|
+
used only internally for numerical conditioning; exposed coefficients are
|
|
148
|
+
back-transformed to the original feature scale, so they remain comparable to
|
|
149
|
+
ordinary derivative-form DDA coefficients.
|
|
150
|
+
|
|
151
|
+
## Weak-Form Sparse Additive DDA
|
|
152
|
+
|
|
153
|
+
The currently supported reliable Python estimator is polynomial `WeakFormDDA`.
|
|
154
|
+
`SparseAdditiveWeakFormDDA` is experimental infrastructure for nonlinear
|
|
155
|
+
main-effect delay discovery; use it for method development and diagnostics, not
|
|
156
|
+
as a replacement for polynomial weak-form DDA in production analyses.
|
|
157
|
+
|
|
158
|
+
Fixed polynomial dictionaries are interpretable but can be brittle when the
|
|
159
|
+
true delayed response is saturating, threshold-like, or otherwise smooth but not
|
|
160
|
+
well approximated by low-order monomials. `SparseAdditiveWeakFormDDA` replaces
|
|
161
|
+
polynomial terms with cubic B-spline response functions over candidate delays:
|
|
162
|
+
|
|
163
|
+
```text
|
|
164
|
+
dx/dt = c + sum_tau f_tau(x(t - tau))
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Each response function is fit in weak form by integrating spline basis functions
|
|
168
|
+
over the same interval used for the observed increment. The first implementation
|
|
169
|
+
is scalar and main-effect only; it does not yet fit pairwise delay surfaces.
|
|
170
|
+
Delay selection is statistically meaningful only with group sparsity, because
|
|
171
|
+
all spline basis functions for one delay must enter or leave the model together.
|
|
172
|
+
Neighboring candidate delays can also be nearly duplicate predictors, so delay
|
|
173
|
+
dictionary thinning and state-coverage diagnostics are part of the estimator.
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from ddalab_app.backend.dda import (
|
|
177
|
+
SparseAdditiveWeakFormDDA,
|
|
178
|
+
stability_select_sparse_additive_weak_form,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
model = SparseAdditiveWeakFormDDA(
|
|
182
|
+
candidate_delays=[0, 10, 11, 24, 25, 40],
|
|
183
|
+
n_knots=7,
|
|
184
|
+
spline_degree=3,
|
|
185
|
+
integration_window=9,
|
|
186
|
+
stride=1,
|
|
187
|
+
quadrature="trapezoid",
|
|
188
|
+
regression="group_lasso",
|
|
189
|
+
group_lasso_alpha=0.004,
|
|
190
|
+
ridge_alpha=1e-6,
|
|
191
|
+
standardize_features=True,
|
|
192
|
+
thin_delays=True,
|
|
193
|
+
max_delay_correlation=0.98,
|
|
194
|
+
)
|
|
195
|
+
model.fit([x_trial_1, x_trial_2, x_trial_3], dt=0.01)
|
|
196
|
+
print(model.selected_delays_)
|
|
197
|
+
print(model.group_strengths_)
|
|
198
|
+
print(model.delay_block_correlation_)
|
|
199
|
+
print(model.state_coverage_)
|
|
200
|
+
|
|
201
|
+
grid, effect = model.effect_curves_[10]
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
`fit` accepts either one scalar trajectory or a list of scalar trajectories. For
|
|
205
|
+
list input, spline bases are shared across trajectories and weak-form windows
|
|
206
|
+
are built within each trajectory only, never across trajectory boundaries.
|
|
207
|
+
|
|
208
|
+
Delay ranking is based on the L2 norm of each delay-specific spline coefficient
|
|
209
|
+
block. With `regression="group_lasso"`, inactive delay blocks can be driven to
|
|
210
|
+
zero directly. For more conservative delay selection, use stability selection
|
|
211
|
+
with null calibration:
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
scores = stability_select_sparse_additive_weak_form(
|
|
215
|
+
[x_trial_1, x_trial_2, x_trial_3],
|
|
216
|
+
dt=0.01,
|
|
217
|
+
candidate_delays=model.candidate_delays_,
|
|
218
|
+
integration_window=9,
|
|
219
|
+
regression="group_lasso",
|
|
220
|
+
group_lasso_alpha=0.004,
|
|
221
|
+
n_repeats=20,
|
|
222
|
+
null_repeats=10,
|
|
223
|
+
calibrate=True,
|
|
224
|
+
)
|
|
225
|
+
print(scores.selection_frequency)
|
|
226
|
+
print(scores.null_selection_frequency)
|
|
227
|
+
print(scores.calibrated_selected_delays)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Prefer polynomial weak-form DDA when a low-order dictionary is scientifically
|
|
231
|
+
meaningful and coefficient-level interpretation is the primary output. Prefer
|
|
232
|
+
sparse additive weak-form DDA only when the main question is whether a small set
|
|
233
|
+
of candidate delays has smooth nonlinear influence and the shape of that
|
|
234
|
+
influence is itself informative. Current limitations are scalar input, main
|
|
235
|
+
effects only, sensitivity to candidate-delay collinearity, dependence on broad
|
|
236
|
+
state coverage, and validation-dependent choices of spline, group penalty, and
|
|
237
|
+
stability thresholds.
|
|
238
|
+
|
|
239
|
+
## Choosing Between WeakFormDDA and SparseAdditiveWeakFormDDA
|
|
240
|
+
|
|
241
|
+
Use `WeakFormDDA` as the default estimator when the goal is robust recovery of
|
|
242
|
+
polynomial DDA coefficients or when the analysis must be stable without
|
|
243
|
+
additional model-selection diagnostics. It is the reliable general-purpose
|
|
244
|
+
Python estimator.
|
|
245
|
+
|
|
246
|
+
Use `SparseAdditiveWeakFormDDA` only as an experimental nonlinear-delay
|
|
247
|
+
estimator. A defensible sparse-additive analysis should use group-lasso delay
|
|
248
|
+
blocks, candidate-delay thinning, stability selection, null calibration, and
|
|
249
|
+
state-coverage checks. Sparse-additive delay selections should not be trusted
|
|
250
|
+
when instantaneous null diagnostics select delayed terms, even if interval
|
|
251
|
+
prediction error is competitive. The current estimator also cannot represent
|
|
252
|
+
pairwise or interacting nonlinear delayed effects; those require a future
|
|
253
|
+
pairwise-surface model rather than the current main-effect spline model.
|
|
254
|
+
|
|
255
|
+
## Desktop Development
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
cd packages/ddalab
|
|
259
|
+
./start.sh
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The script provisions a local virtual environment, installs the unified package in editable mode, and launches the Qt application.
|
|
263
|
+
|
|
264
|
+
The desktop application uses a Qt Quick/QML workbench. Waveform and DDA-result
|
|
265
|
+
plots use viewport-aware, cached Qt Quick scene-graph surfaces; there is no
|
|
266
|
+
separate QWidget rendering mode.
|
|
267
|
+
|
|
268
|
+
See [ARCHITECTURE.md](ARCHITECTURE.md) for the desktop boundaries and data flow.
|
|
269
|
+
|
|
270
|
+
If you are working from source, `./start.sh` expects `cargo` to be available so it can build or refresh the bundled `dda-rs` runtime.
|
|
271
|
+
|
|
272
|
+
## Smoke Test
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
cd packages/ddalab
|
|
276
|
+
./start.sh --smoke-test
|
|
277
|
+
```
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
ddalab_app/__init__.py,sha256=YKRhpInhcIqFLhs3fSFrUy_o9HMgwFO3lPDxXG4363k,103
|
|
2
|
+
ddalab_app/__main__.py,sha256=Jk9YMe4tVRadNwNcM9mxdW1_tN_pkg26sis3mkW00kk,88
|
|
3
|
+
ddalab_app/cli_main.py,sha256=1UlDcblfWrYsuW_CmqaiF0w0vywCkK3HdRyYvJbQPGs,695
|
|
4
|
+
ddalab_app/gui_main.py,sha256=_8jOoBTTh3QVRR5dSGyfdtwtuCPojDHD4ddRybJhmuQ,2496
|
|
5
|
+
ddalab_app/runtime_binary_names.py,sha256=WwNOibNAYTXYUfOQYX52FAtZOoQMtTHjKFCJurszXH4,232
|
|
6
|
+
ddalab_app/runtime_paths.py,sha256=4p3bzh_-XdFMKrU7qfJwWK58JqTJUpqmQWAIa0jZpt8,2984
|
|
7
|
+
ddalab_app/update_manager.py,sha256=8wk_L_XW2WZC6pC1UvGr9wvXZTAvN42cp_kJaKxy1Fo,16212
|
|
8
|
+
ddalab_app/version.py,sha256=wr0yVXCX4U6wjc3AcG1itFbxKxKRePJCNBF5ZYM8PjI,1156
|
|
9
|
+
ddalab_app/app/__init__.py,sha256=Nl-gYPu5IVo422-S41L_qbnJMXULzeUlL33E2WDy4Wo,394
|
|
10
|
+
ddalab_app/app/core/__init__.py,sha256=LBHvoYmCg4kz2wVRNj692IFMjqEuJ3Y-VTOdz7uBdCs,33
|
|
11
|
+
ddalab_app/app/core/analysis_input.py,sha256=8gyK65SARoZi48pqcNLCawzOxhULgXKQbYQZGDHtIk8,1287
|
|
12
|
+
ddalab_app/app/core/snapshot_payload.py,sha256=yu_QBDm_3H4gI9_qNa-UMXSivl_8RUU-uDgGCnMPFjo,2778
|
|
13
|
+
ddalab_app/app/integrations/__init__.py,sha256=hV2ircjsAHrXw2AKsMlFMNSq4pAP09Gn5QBkbQyNhHA,48
|
|
14
|
+
ddalab_app/app/integrations/cdr.py,sha256=EwQkZYBEgWo21eEgKACp44suJdWPurdBo67YYijHrQs,10406
|
|
15
|
+
ddalab_app/app/integrations/cdr_simulation.py,sha256=rlyMabtNOIzWvISjFSUsoZHcyipKJt1S-7TYVpKltco,6713
|
|
16
|
+
ddalab_app/app/integrations/dda_export_utils.py,sha256=LO22y84MibdHfwq6Pdu4pHZaXPFT5xB7gmt8A8h50BU,14706
|
|
17
|
+
ddalab_app/app/runtime/__init__.py,sha256=LX6v7hqHEUCcFIanbkd6faYDpHkF4sme-TFgZHFUiC4,48
|
|
18
|
+
ddalab_app/app/runtime/perf_logging.py,sha256=Q_OXeCqHNtCjTSAwCqK2kLMMZD0gu_AL1DwJzovFdOI,2277
|
|
19
|
+
ddalab_app/app/runtime/runtime_logging.py,sha256=gPrFqC1-tMLXk2pvs7oWUC3ayE5fF2FSFk4-CrYq7q8,7792
|
|
20
|
+
ddalab_app/app/workbench/__init__.py,sha256=yBvhosFpyzoveWVbN610c8Yb7G2hEPaWQj-5Qgz5y7A,139
|
|
21
|
+
ddalab_app/app/workbench/controller.py,sha256=V640Fj6P8yTxzLuZQl2TwyuiqxLNaj4Kktxf1Xn-HGk,72893
|
|
22
|
+
ddalab_app/app/workbench/models.py,sha256=hwcJbfSLI1ltEqIxiZOXhIY8hQyaeBGD7ksY4Yn_AR8,3985
|
|
23
|
+
ddalab_app/app/workbench/runtime.py,sha256=ZXn1z6DVmN1H8D2oiM6z9cFHwejNc5cEyhqq_oQ6S8s,1440
|
|
24
|
+
ddalab_app/app/workbench/tasks.py,sha256=O1P9IwAWo0dhBjHvhvhpWeA_rF71bOtcPdedThOtRy0,1580
|
|
25
|
+
ddalab_app/assets/fonts/ibm_plex_sans_medium.ttf,sha256=MxyGOddZiyzeYqkRpx2xleMMtlXNa98uMkp-mElV-Qc,202460
|
|
26
|
+
ddalab_app/assets/fonts/ibm_plex_sans_regular.ttf,sha256=l13No32A8Djc0UPCLjPKLZegzFqSmqzhx0kVOw_hr6U,200500
|
|
27
|
+
ddalab_app/assets/fonts/ibm_plex_sans_semibold.ttf,sha256=ogyvgoYCOmp6heQLHSpK6fw-Ox-e2o9MVC3UmGr2e7E,202632
|
|
28
|
+
ddalab_app/assets/icons/icon.icns,sha256=-jt9ZDzQJZ2lmiWe13uVbZKYxBhCWgBrBOuyb5D6x-0,719367
|
|
29
|
+
ddalab_app/assets/icons/icon.ico,sha256=pFdwmDiX4Y_w_F40aH5El-AWb0MdoP5fvDe54T3UA20,99678
|
|
30
|
+
ddalab_app/assets/icons/icon.png,sha256=gcEZiLqUi0i3xiKOOci7I2Mvx6zhxAzPivzTw7ztr0Y,32619
|
|
31
|
+
ddalab_app/assets/icons/icon.svg,sha256=26OUw8DJz1vrL9uTugHFztGqM8RNPEL4zHAgJPYkpcw,1867
|
|
32
|
+
ddalab_app/backend/__init__.py,sha256=EjzlFTQrjHgu5TxV-AZOpLNQsmvXfTvezYIg_4WMN04,254
|
|
33
|
+
ddalab_app/backend/contracts.py,sha256=wV1zecq0M7wP_OL0URTCIB0tPIVCJcyjN6Pc-C5xw1o,4811
|
|
34
|
+
ddalab_app/backend/dda/__init__.py,sha256=B9pvY05NBZEIUMg9n_-o7Gf7tOIaeJEuG3Jh8gobcuQ,2151
|
|
35
|
+
ddalab_app/backend/dda/engine.py,sha256=PVhgb3dHBcVA8DjabBuUdFBFEF4-I-sxLjxnKuzYE0U,28612
|
|
36
|
+
ddalab_app/backend/dda/motifs.py,sha256=oPq94AGZoc_5JxkpjDO400ro185FtDDYiJJAjp8Omeg,4698
|
|
37
|
+
ddalab_app/backend/dda/scalar.py,sha256=Iau1Ljaf2xoz1a2NwtuMDSySlN85QQC42pF2_8l66RI,24132
|
|
38
|
+
ddalab_app/backend/dda/sidecar.py,sha256=87BmQoBdC11yW1sRAvhGwevSXcPzrby4QC2_74vct9o,7874
|
|
39
|
+
ddalab_app/backend/dda/sparse.py,sha256=TKgFDRW2pX9en0K2FxXB9Js84U4z-cbj-GBK4t_HZdI,36904
|
|
40
|
+
ddalab_app/backend/local/__init__.py,sha256=VgaxGkVdpaktLe4mJEB_ypf6rHuR_NlUY6W3Ee3TJe0,234
|
|
41
|
+
ddalab_app/backend/local/client.py,sha256=_WBNcQPWbDA_bHcWfKhXDQfUio3N_aX_EPOfjm2hZc4,9102
|
|
42
|
+
ddalab_app/backend/local/dda.py,sha256=0TWzrt-3d_8k4QAxOYbVl6fCrcyp8J2l_A--noFNjjI,40108
|
|
43
|
+
ddalab_app/backend/local/filesystem.py,sha256=yS3uwmf5Aojj3HvZJ3jw0LKHbILAN3PONAGmp29oYJU,4641
|
|
44
|
+
ddalab_app/backend/local/results.py,sha256=pt6BlDByQhkhXXNjxzQjm9wvIFf-SK7fEb9-STrbsmY,9143
|
|
45
|
+
ddalab_app/backend/local/runtime.py,sha256=fvqAlB-lLtMkIQeTa13riqWvi4ylwKGoOVabFLHoyMM,5647
|
|
46
|
+
ddalab_app/backend/readers/__init__.py,sha256=QlnWBE98mlNzXPzA_jOLEjlZKx1wRh_BeGFh884MRiY,299
|
|
47
|
+
ddalab_app/backend/readers/common.py,sha256=pn3BbzsK1MwPy_RGRb-_00kfgUb49VnUWeH6RF9xy1c,8929
|
|
48
|
+
ddalab_app/backend/readers/delimited.py,sha256=ktEHhytsi-Ch8a_j9x2cNvGZbcottDr7zeqqDpWzwes,10728
|
|
49
|
+
ddalab_app/backend/readers/local.py,sha256=tQN8aN9tPZNDhkjQIsZ-xE-FljQ6ZCWZIkg1rZNhrRs,2587
|
|
50
|
+
ddalab_app/backend/readers/mne.py,sha256=c_BuDmhKs7_a8XX3G1zl0d8Qml_5T86_6zK5QK62_oY,6295
|
|
51
|
+
ddalab_app/backend/readers/nifti.py,sha256=1YfiQoRz351qF-RsUpFLE-zhzAiqywBI3j_2OmPXbGA,8173
|
|
52
|
+
ddalab_app/backend/readers/nwb.py,sha256=zNamTE5jDjMDGiekT7CMBNsH1NyO_ssUx7J619ZGrrA,6727
|
|
53
|
+
ddalab_app/backend/readers/xdf.py,sha256=PkNnad2YN1ssFX7uSPbCGS9P8i7Bap9eZQaGUHObkps,7631
|
|
54
|
+
ddalab_app/backend/services/__init__.py,sha256=QJv9t4bmQbSI3k4a-xPr94FLeFpTythkN-zDAyrDx8U,253
|
|
55
|
+
ddalab_app/backend/services/ica.py,sha256=9N0v8GvT10ZIB9KIqBrqT97qO5e-60H5voJK34NUhLk,5805
|
|
56
|
+
ddalab_app/backend/services/nsg.py,sha256=Z0lsFm2GjALIWq2Fky4y9pIz6DchQUYgBsBI4S0-L-0,25572
|
|
57
|
+
ddalab_app/backend/services/openneuro.py,sha256=xOVnMp2mOu_rKxoP_I_1QZ8ip71nqB79Sj8FQtBLWMg,4658
|
|
58
|
+
ddalab_app/cli/__init__.py,sha256=bFFVE9jRKczNy4HhmegWoC6KOL_Nrej-ag37DAIDzaA,36
|
|
59
|
+
ddalab_app/cli/commands.py,sha256=UTSzEE18mrD3AKceruxK6E_p8bbC6pLR4cSoufX5Jms,11275
|
|
60
|
+
ddalab_app/cli/constants.py,sha256=lvczhH-2rL-w66USvvWWqxvVk3ox4GxAFnMgPPZ46Gk,1379
|
|
61
|
+
ddalab_app/cli/parser.py,sha256=8UMlqk4rYIstnzCaKVS32uSLk12PE5fDF47dOtnfjEE,10279
|
|
62
|
+
ddalab_app/cli/plot_style.py,sha256=34hKnC4bg34r1KlKOqesP28Kv0SjFAZwmklJje9LTpQ,1168
|
|
63
|
+
ddalab_app/cli/runtime.py,sha256=gO-A0bFQINCc-krBjr2wmTTK0b8LAgckYcK6qPxOj6o,18200
|
|
64
|
+
ddalab_app/domain/__init__.py,sha256=Hi8XD2DicapE64k3jc2oAxGs3-dInkmp-f03NDKEfmk,60
|
|
65
|
+
ddalab_app/domain/file_types.py,sha256=pLObJBAniDijJrceXQjzX1VQ4XgUgtpQX8cvXXMlZYk,7471
|
|
66
|
+
ddalab_app/domain/models.py,sha256=N9xYT7z02sZiPW2oYkBb1_-6_xghjWtq2ELxVT1yqKA,31852
|
|
67
|
+
ddalab_app/persistence/__init__.py,sha256=DKQN6zQTekOZh8SHTKGRZ9RfvAzgk2jg3afXGmGRLNw,68
|
|
68
|
+
ddalab_app/persistence/state_db.py,sha256=jDFhyBI8tdxnksHAvTwpUc4UxVUr8mjfYHLHc7Abkjc,35376
|
|
69
|
+
ddalab_app/runtime/bin/ddalab-backend.exe,sha256=VgUkXxRHuq9Uy5EilWJtz5tiVGOJOJ8ZZ5uV7qhFGrg,2724864
|
|
70
|
+
ddalab_app/ui/__init__.py,sha256=Z2CetSy5ceJDCuajBd3ESYcY3Ftn6kaj0yVtTHSU0J0,245
|
|
71
|
+
ddalab_app/ui/plot_benchmarks.py,sha256=2SA7a28uLkFQ4ImZnz-hLLvVnNO8dT2h73e4VM24U5U,5216
|
|
72
|
+
ddalab_app/ui/plot_data.py,sha256=wXjI0pJS9_SFPmTRfowjJ1gZz8IrPEzn0ODljGze25Q,2072
|
|
73
|
+
ddalab_app/ui/plot_data_common.py,sha256=wGU7emw61eP9-VkqHA89D7JPXsim2ejMjMQtaV2rg-o,4215
|
|
74
|
+
ddalab_app/ui/plot_heatmap_data.py,sha256=2Lw8rOYIs32lKFfYxjuQbvpunWK3VLdWiyfJTpgnE8s,2775
|
|
75
|
+
ddalab_app/ui/plot_layers.py,sha256=L43iS5TpktldOKzJf9hZlViwd3d8F9vuEfLjbwUzruk,257
|
|
76
|
+
ddalab_app/ui/plot_matrix_data.py,sha256=RQ5YhIVQM-S3kpRVyjl52K-gdvkh1TsIwIRURxopjU0,11573
|
|
77
|
+
ddalab_app/ui/plot_waveform_data.py,sha256=rFo93LLL6GCsTfVFln7oLtWmQQXpuKVVqhQgCE308Mc,20940
|
|
78
|
+
ddalab_app/ui/qt_plot_renderer.py,sha256=RhUzNq-Ku2fa3GJCP7fOZHqvuSiJUn7Vkfe71aFC-3Y,6493
|
|
79
|
+
ddalab_app/ui/quick_plot_surface.py,sha256=U29C3Zq74rMVIDrGHF6AWzT-f3YcsNRllX35ZfryMMI,26015
|
|
80
|
+
ddalab_app/ui/quick_waveform_surface.py,sha256=CM2ho5bYdjg9wSCYg0v76pi0c7j-DgupktpxNJxDmhg,21747
|
|
81
|
+
ddalab_app/ui/render_cache.py,sha256=E6OZw4v5rN-bwje5PK3P3YxO0zfePytW3XjZJIMQ_Xo,903
|
|
82
|
+
ddalab_app/ui/style.py,sha256=dTOQL_3yVjeW2-uvSX3qfPO8d_0pZplFava_Y9F9h_s,6689
|
|
83
|
+
ddalab_app/ui/qml/QuickPlotSurface.qml,sha256=Y_N0abEpOeJLAIV34gv1MFdhuFfPsuyzafyiGi2aPH0,9637
|
|
84
|
+
ddalab_app/ui/qml/QuickWaveformSurface.qml,sha256=yQYUdVHwQ2x6xjILkiCXEXJA6pPRVGDibivuaUvq7DM,11934
|
|
85
|
+
ddalab_app/ui/qml/__init__.py,sha256=7aBq1lfxIG5FLTuCLWhDQIRB6Q0QiZBmc6yaH2vH9XQ,72
|
|
86
|
+
ddalab_app/ui/qml/workbench/Main.qml,sha256=H_gCWdtwxJSaScJdCYRxqh4DaWlJdvn3MGd8rlC5i78,13340
|
|
87
|
+
ddalab_app/ui/qml/workbench/__init__.py,sha256=DPkO2mOqDlO2axsS3Of4wqENeO9q6XxR6U6zmibq8Gc,37
|
|
88
|
+
ddalab_app/ui/qml/workbench/components/ActivitySpinner.qml,sha256=EdapSc4n3XwVbkVrm2pUEKGkopennzjw5o3Is8caCR0,785
|
|
89
|
+
ddalab_app/ui/qml/workbench/components/AnalysisInputSummary.qml,sha256=uKHgGVvT83ThtWFFi0biwNuzAOaYQIfnktOnvRnvCBs,2554
|
|
90
|
+
ddalab_app/ui/qml/workbench/components/AnalysisInspector.qml,sha256=4bEVIVX2JszT4zl9NfALl501IItaCBKsVLXAY0065_s,7616
|
|
91
|
+
ddalab_app/ui/qml/workbench/components/CdrHeatmap.qml,sha256=ZZReFJI3UXURKxekyv_6np57aES_Oi-9n-rBIRO8rXQ,3013
|
|
92
|
+
ddalab_app/ui/qml/workbench/components/CdrLineChart.qml,sha256=a2BRmfRZOfykBpdh6WBd6Hy8t9lh4KvdN-k_Wvi7apU,8200
|
|
93
|
+
ddalab_app/ui/qml/workbench/components/CdrResultsView.qml,sha256=SmOFDKu3NdJwfL5Fp8QAmSmGR_XvafykTU1uVT__s44,2433
|
|
94
|
+
ddalab_app/ui/qml/workbench/components/InspectorPanel.qml,sha256=8XCeusls7G0mQM0wSf3ykU-gkmqG02Kv07D-jmqbMiY,3260
|
|
95
|
+
ddalab_app/ui/qml/workbench/components/LibraryRail.qml,sha256=RhZYuPonGMdfpX-imFPglB7-aXT-YR_2x6ImazGS5Hc,5016
|
|
96
|
+
ddalab_app/ui/qml/workbench/components/LoadingOverlay.qml,sha256=pwu39ayNT6EiLZK5d_NtvbGMFETPbFZ8NMG4yH322rU,704
|
|
97
|
+
ddalab_app/ui/qml/workbench/components/NavButton.qml,sha256=ziVw23WLROYBJpVpAwoIo_llAeJfvVnyqoVNej6X6ac,886
|
|
98
|
+
ddalab_app/ui/qml/workbench/components/ResultHistoryList.qml,sha256=axLmWhCBuj6fxL5nmKdNX6MEQYsDnGTI9xUZ1-Kg6yY,1932
|
|
99
|
+
ddalab_app/ui/qml/workbench/components/ResultsInspector.qml,sha256=toKlTL7Ibpzv4OIaX3TuyCCty0t_BQJ8nqeF95MAEj8,4860
|
|
100
|
+
ddalab_app/ui/qml/workbench/components/SectionLabel.qml,sha256=5D78qV3yoxqpxuV1teRgAvCfMpfRmj0KzrhaTVxaZog,174
|
|
101
|
+
ddalab_app/ui/qml/workbench/components/SettingsInspector.qml,sha256=HkVR4c1EdaoABWN5aL8mJYErDKOGQWJKqKdhYe7Fo5o,4050
|
|
102
|
+
ddalab_app/ui/qml/workbench/components/Toggle.qml,sha256=jJk1kKxUsEvhtg3KSDCLAMStyXNBBO4tCv5xTPRsaJA,1144
|
|
103
|
+
ddalab_app/ui/qml/workbench/components/WorkbenchButton.qml,sha256=kycnNl2WyoeBP7eUJd6poM-sQeWxZsThYCsbyFvfgtQ,1606
|
|
104
|
+
ddalab_app/ui/qml/workbench/components/WorkbenchField.qml,sha256=fGXJpjsuNauy10DFj1w4tEhaRp0oactLnqUgy5rQyfQ,561
|
|
105
|
+
ddalab_app/ui/qml/workbench/components/WorkspaceInspector.qml,sha256=4w4USbJOTslm1sX_x8osuFEDmwFfBcK4dpAMa27XJ94,4035
|
|
106
|
+
ddalab_app/ui/qml/workbench/components/__init__.py,sha256=NgYAbZxtCPTuzNbWu6NNrYhie1c8V6LiXHhXYBfQNpc,37
|
|
107
|
+
ddalab_app/ui/qml/workbench/pages/AnalysisPage.qml,sha256=C2P9vBMr3xtesYYQ5u69RJ09AHk621x-DC6EJU5L3z0,9274
|
|
108
|
+
ddalab_app/ui/qml/workbench/pages/ResultsPage.qml,sha256=1yo3gzn0DFlpLYbbmhB0m6hmcXsuMugRtTBqv6wsRDc,10416
|
|
109
|
+
ddalab_app/ui/qml/workbench/pages/SettingsPage.qml,sha256=i-1CvtfftqVedu8LWEzfEpLdavdn_c-AtiIp_lwy4FY,1293
|
|
110
|
+
ddalab_app/ui/qml/workbench/pages/WorkspacePage.qml,sha256=XL3kaVNNrhaJjpW0Aa1olfaoKhRdstgMdCP6Ro0G8jU,9563
|
|
111
|
+
ddalab_app/ui/qml/workbench/pages/__init__.py,sha256=GIQ00j6UixZn_KzluWKExcWBD8NkYTFIcPEFkZTGTzY,31
|
|
112
|
+
ddalab-1.2.11.dist-info/METADATA,sha256=kEm1HG88h_J28P7fuNXOPX7b_l0_wQ5Qd7RtFK-wGpk,10755
|
|
113
|
+
ddalab-1.2.11.dist-info/WHEEL,sha256=Zx98gwb_dQKckJK3HEOVKnxxD52EfU_PXDG_usMJ2ng,98
|
|
114
|
+
ddalab-1.2.11.dist-info/entry_points.txt,sha256=G1j5iVXh-26MI9fcJFTIa5QTWOZeR6s-8H_UQjSUEzc,128
|
|
115
|
+
ddalab-1.2.11.dist-info/top_level.txt,sha256=o4GDxaoK1ej70LrJjAx2U_XqlkZkqZB2MVmq75likkw,11
|
|
116
|
+
ddalab-1.2.11.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ddalab_app
|
ddalab_app/__init__.py
ADDED
ddalab_app/__main__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
__all__ = ["WorkbenchController", "build_workbench"]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def __getattr__(name: str):
|
|
7
|
+
if name in __all__:
|
|
8
|
+
from .workbench import WorkbenchController, build_workbench
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
"WorkbenchController": WorkbenchController,
|
|
12
|
+
"build_workbench": build_workbench,
|
|
13
|
+
}[name]
|
|
14
|
+
raise AttributeError(name)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Shared app-level helpers."""
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def parse_time_bounds(
|
|
7
|
+
start_text: str,
|
|
8
|
+
end_text: str,
|
|
9
|
+
*,
|
|
10
|
+
label: str,
|
|
11
|
+
default_start: Optional[float] = 0.0,
|
|
12
|
+
) -> tuple[Optional[float], Optional[float]]:
|
|
13
|
+
start = _parse_optional_seconds(
|
|
14
|
+
start_text,
|
|
15
|
+
field_name=f"{label} start",
|
|
16
|
+
default=default_start,
|
|
17
|
+
)
|
|
18
|
+
end = _parse_optional_seconds(
|
|
19
|
+
end_text,
|
|
20
|
+
field_name=f"{label} end",
|
|
21
|
+
default=None,
|
|
22
|
+
)
|
|
23
|
+
if start is not None and start < 0:
|
|
24
|
+
raise ValueError(f"{label} start must be greater than or equal to 0.")
|
|
25
|
+
if end is not None and end < 0:
|
|
26
|
+
raise ValueError(f"{label} end must be greater than or equal to 0.")
|
|
27
|
+
if start is not None and end is not None and end <= start:
|
|
28
|
+
raise ValueError(f"{label} end must be greater than the start time.")
|
|
29
|
+
return start, end
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _parse_optional_seconds(
|
|
33
|
+
raw_text: str,
|
|
34
|
+
*,
|
|
35
|
+
field_name: str,
|
|
36
|
+
default: Optional[float],
|
|
37
|
+
) -> Optional[float]:
|
|
38
|
+
text = str(raw_text or "").strip()
|
|
39
|
+
if not text:
|
|
40
|
+
return default
|
|
41
|
+
try:
|
|
42
|
+
return float(text)
|
|
43
|
+
except ValueError as exc:
|
|
44
|
+
raise ValueError(f"{field_name} must be a valid number of seconds.") from exc
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def relink_snapshot_payload(
|
|
9
|
+
payload: dict,
|
|
10
|
+
*,
|
|
11
|
+
old_path: str,
|
|
12
|
+
new_path: str,
|
|
13
|
+
) -> dict:
|
|
14
|
+
rewritten = deepcopy(payload)
|
|
15
|
+
rewritten["activeFilePath"] = _replace_exact_path(
|
|
16
|
+
rewritten.get("activeFilePath"),
|
|
17
|
+
old_path,
|
|
18
|
+
new_path,
|
|
19
|
+
)
|
|
20
|
+
rewritten["openFiles"] = _replace_path_list(
|
|
21
|
+
rewritten.get("openFiles"),
|
|
22
|
+
old_path,
|
|
23
|
+
new_path,
|
|
24
|
+
)
|
|
25
|
+
rewritten["pinnedFiles"] = _replace_path_list(
|
|
26
|
+
rewritten.get("pinnedFiles"),
|
|
27
|
+
old_path,
|
|
28
|
+
new_path,
|
|
29
|
+
)
|
|
30
|
+
annotations = rewritten.get("annotationsByFile")
|
|
31
|
+
if isinstance(annotations, dict):
|
|
32
|
+
remapped: dict[str, object] = {}
|
|
33
|
+
for key, value in annotations.items():
|
|
34
|
+
remapped[new_path if key == old_path else str(key)] = value
|
|
35
|
+
rewritten["annotationsByFile"] = remapped
|
|
36
|
+
_rewrite_result_path(rewritten.get("ddaResult"), old_path, new_path)
|
|
37
|
+
_rewrite_result_path(rewritten.get("icaResult"), old_path, new_path)
|
|
38
|
+
return rewritten
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def first_missing_snapshot_source(payload: dict) -> Optional[str]:
|
|
42
|
+
active_file = payload.get("activeFilePath")
|
|
43
|
+
return active_file if isinstance(active_file, str) and active_file else None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def missing_snapshot_source_name(payload: dict, missing_path: str) -> str:
|
|
47
|
+
active_file = payload.get("activeFilePath")
|
|
48
|
+
if isinstance(active_file, str) and active_file:
|
|
49
|
+
return Path(active_file).name
|
|
50
|
+
if missing_path:
|
|
51
|
+
return Path(missing_path).name
|
|
52
|
+
dda_result = payload.get("ddaResult")
|
|
53
|
+
if isinstance(dda_result, dict):
|
|
54
|
+
for key in ("fileName", "file_name"):
|
|
55
|
+
value = dda_result.get(key)
|
|
56
|
+
if isinstance(value, str) and value:
|
|
57
|
+
return value
|
|
58
|
+
return "snapshot dataset"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _replace_exact_path(value: object, old_path: str, new_path: str) -> object:
|
|
62
|
+
if isinstance(value, str) and value == old_path:
|
|
63
|
+
return new_path
|
|
64
|
+
return value
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _replace_path_list(value: object, old_path: str, new_path: str) -> list[str]:
|
|
68
|
+
if not isinstance(value, list):
|
|
69
|
+
return []
|
|
70
|
+
rewritten: list[str] = []
|
|
71
|
+
for item in value:
|
|
72
|
+
if not isinstance(item, str) or not item:
|
|
73
|
+
continue
|
|
74
|
+
rewritten.append(new_path if item == old_path else item)
|
|
75
|
+
return rewritten
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _rewrite_result_path(payload: object, old_path: str, new_path: str) -> None:
|
|
79
|
+
if not isinstance(payload, dict):
|
|
80
|
+
return
|
|
81
|
+
for key in ("filePath", "file_path"):
|
|
82
|
+
value = payload.get(key)
|
|
83
|
+
if isinstance(value, str) and value == old_path:
|
|
84
|
+
payload[key] = new_path
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""External integration and export actions."""
|