p3 improvements
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
# Impakt -- Project Status
|
# Impakt -- Project Status
|
||||||
|
|
||||||
**Date:** 2026-04-10
|
**Date:** 2026-04-11
|
||||||
**Version:** 0.1.0
|
**Version:** 0.1.0
|
||||||
**Tests:** 181 passing
|
**Tests:** 240 passing (69.7% coverage)
|
||||||
**Source:** ~11,000 lines (py+js+css+html) | ~2,150 lines tests
|
**Source:** ~10,300 lines (py) | ~2,700 lines tests
|
||||||
**Tooling:** uv (Python 3.12.12), hatchling build backend
|
**Quality:** 78.3/100 (Grade B) -- see `docs/QA-*.md`
|
||||||
|
**Tooling:** uv (Python 3.12.12), hatchling build backend, ruff, mypy strict
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -13,7 +14,7 @@
|
|||||||
```bash
|
```bash
|
||||||
cd /Users/noise/Code/impakt
|
cd /Users/noise/Code/impakt
|
||||||
uv sync --dev # install all dependencies
|
uv sync --dev # install all dependencies
|
||||||
uv run pytest tests/ # run all 181 tests
|
uv run pytest tests/ # run all 240 tests (with coverage)
|
||||||
uv run impakt info tests/mme_data/3239 # show test metadata
|
uv run impakt info tests/mme_data/3239 # show test metadata
|
||||||
uv run impakt serve tests/mme_data/3239 # launch web UI on :8050
|
uv run impakt serve tests/mme_data/3239 # launch web UI on :8050
|
||||||
```
|
```
|
||||||
@@ -211,13 +212,59 @@ Every selected channel has a stable color index (position in selection order). T
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Next Steps (Priority 3)
|
## Roadmap
|
||||||
|
|
||||||
1. **Annotations** -- text on plots, measurement lines, highlight regions
|
Informed by competitive landscape survey (`research/landscape.md`). No open-source web-based tool covers this domain end-to-end. See `BRAINSTORM.md` for full feature ideas with priority tiers.
|
||||||
2. **Comparison mode** -- side-by-side tests, delta plots, synced cursors
|
|
||||||
3. **Report builder** -- drag-and-drop report composer, PDF preview
|
### Priority 3 — Performance & Rendering
|
||||||
4. **Keyboard shortcuts** -- Ctrl+O, Ctrl+S, 1-9 pane switch, F fullscreen, R reset zoom
|
|
||||||
5. **Consider Python/WASM frontend** -- NiceGUI or Solara for pure-Python UI (no JS)
|
Goal: handle large datasets (500+ channels, 100kHz sample rates) without lag.
|
||||||
|
|
||||||
|
1. **plotly-resampler integration** -- Drop-in `FigureResampler` wrapper for Plotly figures. Handles 110M+ points via LTTB downsampling. Works natively with Dash. Repo cloned at `research/repos/plotly-resampler/`. This is the single highest-impact performance improvement.
|
||||||
|
2. **Synchronized zoom/pan** -- When plotting multiple subplots, zoom/pan syncs across all panes sharing an X axis. Most-requested feature in crash test visualization. Implement via shared `xaxis` config or callback-based range sync.
|
||||||
|
3. **Lazy channel loading** -- Load `.dat` files on first access, not at `Session.open()`. Load headers eagerly, data lazily. Keeps startup fast for tests with 500+ channels.
|
||||||
|
4. **Channel sparklines** -- Tiny inline sparklines in the channel grid sidebar. Engineers visually scan 100+ channels before selecting. A 60px-wide sparkline column is transformative for signal browsing.
|
||||||
|
|
||||||
|
### Priority 4 — Data Format Expansion
|
||||||
|
|
||||||
|
Goal: read the world's crash test data, not just ISO MME.
|
||||||
|
|
||||||
|
5. **UDS reader plugin** -- NHTSA's proprietary binary format. Required to access the largest public crash test database. The NHTSA-Tools Fortran source (`research/repos/NHTSA-Tools/`) documents the UDS spec.
|
||||||
|
6. **ASAM MDF reader plugin** -- Standard for ECU/CAN bus measurement data. Many labs record vehicle bus data alongside crash instrumentation. asammdf (`research/repos/asammdf/`) is a mature library — add as optional dependency.
|
||||||
|
7. **Flexible CSV reader** -- Column mapping, delimiter detection, header conventions. Engineers frequently receive data as CSV exports from other tools.
|
||||||
|
|
||||||
|
### Priority 5 — Comparison & Reporting
|
||||||
|
|
||||||
|
Goal: make multi-test comparison and deliverable generation effortless.
|
||||||
|
|
||||||
|
8. **Quick comparison mode** -- Two tests side-by-side with synchronized cursors. One-click "compare" button. Color-by-test with channel differentiation via line dash.
|
||||||
|
9. **Multi-page PDF reports** -- Combine plots + injury summary + protocol rating into a single PDF with table of contents. Currently each report type is standalone.
|
||||||
|
10. **Excel export** -- Criteria results and cursor values to .xlsx. Engineers live in spreadsheets.
|
||||||
|
11. **Static HTML export** -- Bundle data + Plotly.js into a self-contained HTML file. Opens in any browser without a Python server. Learned from FalCon's CustomerView distribution model.
|
||||||
|
|
||||||
|
### Priority 6 — Video & Advanced Analysis
|
||||||
|
|
||||||
|
Goal: close the biggest remaining gap vs. commercial tools.
|
||||||
|
|
||||||
|
12. **Video synchronization** -- Link high-speed camera footage with channel data. Scrubbing video moves the time cursor; moving the cursor seeks the video. Every major commercial competitor (measX, DIAdem, Kistler, FalCon) has this. Foxglove and Rerun demonstrate web-native approaches.
|
||||||
|
13. **Frequency spectrum viewer** -- FFT / PSD alongside time-domain plots. Diagnose noise, verify CFC filter behavior.
|
||||||
|
14. **Integration / differentiation transforms** -- Acceleration -> velocity -> displacement with cumulative unit tracking.
|
||||||
|
15. **Data quality dashboard** -- Automated polarity check, sensor sanity, missing channel detection, completeness scoring. No commercial competitor is strong here — opportunity to differentiate.
|
||||||
|
|
||||||
|
### Priority 7 — Simulation Correlation & Ecosystem
|
||||||
|
|
||||||
|
Goal: bridge the gap between physical test and CAE simulation.
|
||||||
|
|
||||||
|
16. **LS-DYNA data import** via lasso-python (`research/repos/lasso-python/`). Enables test-vs-simulation overlay — a premium feature in Altair HyperGraph and Siemens Simcenter.
|
||||||
|
17. **ISO/TS 18571 CORA correlation** -- Quantitative rating of test-vs-simulation agreement. Standard metric for model validation.
|
||||||
|
18. **Additional injury criteria** -- BrIC, DAMAGE, TTI, pedestrian criteria, OLC. Required for broader Euro NCAP coverage.
|
||||||
|
19. **Additional NCAP programs** -- J-NCAP, C-NCAP, K-NCAP, ANCAP, Latin NCAP as protocol plugins.
|
||||||
|
20. **Jupyter integration** -- `_repr_html_` on Session, Channel, ProtocolResult for rich notebook output.
|
||||||
|
|
||||||
|
### Validation (ongoing)
|
||||||
|
|
||||||
|
- **Cross-validate CFC filter** against PyAvia's J211_2pole (`research/repos/pyavia/`) and NHTSA-Tools' BwFilt. PyAvia's author notes that scipy's generic `sosfiltfilt` may differ from the SAE J211 Appendix C digital Butterworth algorithm for CFC 60 and 180.
|
||||||
|
- **Cross-validate injury criteria** against NHTSA-Tools Fortran reference implementations, pyisomme, and EPFL crash-tests-service-robots. Four independent codebases available in `research/repos/`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -238,7 +285,8 @@ Every selected channel has a stable color index (position in selection order). T
|
|||||||
|
|
||||||
Core: numpy, scipy, plotly, dash, dash-bootstrap-components, pandas, pyyaml, jinja2, weasyprint, pydantic
|
Core: numpy, scipy, plotly, dash, dash-bootstrap-components, pandas, pyyaml, jinja2, weasyprint, pydantic
|
||||||
Dev: pytest, pytest-cov, ruff, mypy
|
Dev: pytest, pytest-cov, ruff, mypy
|
||||||
Optional: nptdms (for future TDMS reader plugin)
|
Optional: nptdms (TDMS reader plugin)
|
||||||
|
Planned: plotly-resampler (P3), asammdf (P4), openpyxl (P5), lasso-python (P7)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -249,4 +297,29 @@ Optional: nptdms (for future TDMS reader plugin)
|
|||||||
3. **DataTable deprecation warning** -- Dash recommends migrating to dash-ag-grid.
|
3. **DataTable deprecation warning** -- Dash recommends migrating to dash-ag-grid.
|
||||||
4. **Cursor poll interval (80ms)** -- slight latency in cursor grid updates.
|
4. **Cursor poll interval (80ms)** -- slight latency in cursor grid updates.
|
||||||
5. **Chest deflection auto-detect** skips DS channels with peak > 150mm to avoid steering column displacement.
|
5. **Chest deflection auto-detect** skips DS channels with peak > 150mm to avoid steering column displacement.
|
||||||
6. **Dead files** -- `cursors.py` and `inspector.py` were replaced by `channel_values.py` and deleted; `inspector_callbacks.py` deleted. If old `.pyc` files cause issues, run `find src -name __pycache__ -type d | xargs rm -rf`.
|
6. **CFC filter implementation** uses scipy `sosfiltfilt` which may diverge from SAE J211 Appendix C for CFC 60/180. Needs cross-validation against PyAvia and NHTSA-Tools reference implementations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quality Assurance
|
||||||
|
|
||||||
|
Automated QA scoring is configured:
|
||||||
|
- **Scoring agent:** `.claude/agents/quality-scorer.md` -- collects metrics, applies rubrics, writes report
|
||||||
|
- **Improvement agent:** `.claude/agents/qa-improver.md` -- reads QA report, auto-fixes mechanical issues
|
||||||
|
- **Methodology:** `docs/QA-INSTRUCTIONS.md` -- reproducible 8-dimension rubric
|
||||||
|
- **Reports:** `docs/QA-*.md` -- timestamped scorecards with deltas
|
||||||
|
- **Scripts:** `scripts/qa-score.sh` (score only), `scripts/qa-improve.sh` (score -> fix -> re-score)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Competitive Landscape
|
||||||
|
|
||||||
|
Full survey at `research/landscape.md` with 11 cloned open-source repos in `research/repos/`.
|
||||||
|
|
||||||
|
**Key finding:** No existing tool combines open-source + web-based + ISO-MME + CFC + injury criteria + protocol scoring + templates + reports. Commercial tools (measX X-Crash, NI DIAdem, Kistler) do this but are expensive and Windows-only. Impakt occupies a genuinely unserved niche.
|
||||||
|
|
||||||
|
**Most actionable libraries:**
|
||||||
|
- plotly-resampler (performance) -- `research/repos/plotly-resampler/`
|
||||||
|
- asammdf (MDF format) -- `research/repos/asammdf/`
|
||||||
|
- lasso-python (LS-DYNA) -- `research/repos/lasso-python/`
|
||||||
|
- PyAvia, NHTSA-Tools (validation) -- `research/repos/pyavia/`, `research/repos/NHTSA-Tools/`
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ dependencies = [
|
|||||||
"jinja2>=3.1",
|
"jinja2>=3.1",
|
||||||
"weasyprint>=60.0",
|
"weasyprint>=60.0",
|
||||||
"pydantic>=2.0",
|
"pydantic>=2.0",
|
||||||
|
"plotly-resampler>=0.11.0",
|
||||||
|
"pytz>=2026.1.post1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
|||||||
Binary file not shown.
@@ -5,17 +5,24 @@ support for corridors, dual X-cursors, and export.
|
|||||||
|
|
||||||
The PlotEngine is the **single rendering path** — both the scripting
|
The PlotEngine is the **single rendering path** — both the scripting
|
||||||
API and the web UI construct PlotSpec objects and delegate here.
|
API and the web UI construct PlotSpec objects and delegate here.
|
||||||
|
|
||||||
|
When ``resample=True``, wraps the figure in a plotly-resampler
|
||||||
|
``FigureResampler`` for dynamic LTTB downsampling on zoom/pan.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import plotly.graph_objects as go
|
import plotly.graph_objects as go
|
||||||
|
from plotly_resampler import FigureResampler
|
||||||
|
|
||||||
from impakt.channel.model import Channel
|
from impakt.channel.model import Channel
|
||||||
from impakt.plot.spec import Corridor, CursorValues, PlotSpec
|
from impakt.plot.spec import Corridor, CursorValues, PlotSpec
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Default color palette (colorblind-friendly)
|
# Default color palette (colorblind-friendly)
|
||||||
DEFAULT_COLORS = [
|
DEFAULT_COLORS = [
|
||||||
"#1f77b4", # blue
|
"#1f77b4", # blue
|
||||||
@@ -39,11 +46,32 @@ class PlotEngine:
|
|||||||
- **Compact** (``spec.compact=True``): Web UI mode with no legend, no
|
- **Compact** (``spec.compact=True``): Web UI mode with no legend, no
|
||||||
hover tooltips, tight margins, smaller axis labels. Cursor tracking
|
hover tooltips, tight margins, smaller axis labels. Cursor tracking
|
||||||
is handled by external JS.
|
is handled by external JS.
|
||||||
|
|
||||||
|
When ``resample=True``, the figure is wrapped in a
|
||||||
|
``FigureResampler`` that dynamically downsamples traces via LTTB
|
||||||
|
aggregation on zoom/pan events.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def render(self, spec: PlotSpec) -> go.Figure:
|
def render(
|
||||||
"""Render a PlotSpec into an interactive Plotly figure."""
|
self, spec: PlotSpec, *, resample: bool = False
|
||||||
fig = go.Figure()
|
) -> go.Figure | FigureResampler:
|
||||||
|
"""Render a PlotSpec into an interactive Plotly figure.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
spec: The plot specification.
|
||||||
|
resample: If True, wrap in FigureResampler for dynamic
|
||||||
|
downsampling. The caller must handle ``relayoutData``
|
||||||
|
callbacks to trigger resampling on zoom/pan.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A ``go.Figure`` (default) or ``FigureResampler`` (when
|
||||||
|
``resample=True``).
|
||||||
|
"""
|
||||||
|
fig: go.Figure | FigureResampler
|
||||||
|
if resample:
|
||||||
|
fig = FigureResampler(go.Figure(), default_n_shown_samples=1500)
|
||||||
|
else:
|
||||||
|
fig = go.Figure()
|
||||||
|
|
||||||
if not spec.channels:
|
if not spec.channels:
|
||||||
fig.update_layout(
|
fig.update_layout(
|
||||||
@@ -67,7 +95,7 @@ class PlotEngine:
|
|||||||
|
|
||||||
# Add corridor fills first (behind data traces)
|
# Add corridor fills first (behind data traces)
|
||||||
for corridor in spec.corridors:
|
for corridor in spec.corridors:
|
||||||
self._add_corridor(fig, corridor)
|
self._add_corridor(fig, corridor, resample=resample)
|
||||||
|
|
||||||
# Add channel traces
|
# Add channel traces
|
||||||
for i, ch_ref in enumerate(spec.channels):
|
for i, ch_ref in enumerate(spec.channels):
|
||||||
@@ -84,8 +112,6 @@ class PlotEngine:
|
|||||||
label = style.label or ch_ref.label
|
label = style.label or ch_ref.label
|
||||||
|
|
||||||
trace_kwargs: dict[str, Any] = {
|
trace_kwargs: dict[str, Any] = {
|
||||||
"x": ch.time,
|
|
||||||
"y": ch.data,
|
|
||||||
"mode": "lines",
|
"mode": "lines",
|
||||||
"name": label,
|
"name": label,
|
||||||
"line": dict(
|
"line": dict(
|
||||||
@@ -101,7 +127,18 @@ class PlotEngine:
|
|||||||
f"{label}<br>t=%{{x:.6f}}s<br>%{{y:.4f}} {ch.unit}<extra></extra>"
|
f"{label}<br>t=%{{x:.6f}}s<br>%{{y:.4f}} {ch.unit}<extra></extra>"
|
||||||
)
|
)
|
||||||
|
|
||||||
fig.add_trace(go.Scatter(**trace_kwargs))
|
if resample:
|
||||||
|
# FigureResampler: pass full-resolution data via hf_x/hf_y
|
||||||
|
# and use Scattergl for WebGL-accelerated rendering
|
||||||
|
fig.add_trace(
|
||||||
|
go.Scattergl(**trace_kwargs),
|
||||||
|
hf_x=ch.time,
|
||||||
|
hf_y=ch.data,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
trace_kwargs["x"] = ch.time
|
||||||
|
trace_kwargs["y"] = ch.data
|
||||||
|
fig.add_trace(go.Scatter(**trace_kwargs))
|
||||||
|
|
||||||
# Add cursor lines
|
# Add cursor lines
|
||||||
if spec.x_cursors:
|
if spec.x_cursors:
|
||||||
@@ -181,33 +218,43 @@ class PlotEngine:
|
|||||||
|
|
||||||
return fig
|
return fig
|
||||||
|
|
||||||
def _add_corridor(self, fig: go.Figure, corridor: Corridor) -> None:
|
def _add_corridor(
|
||||||
|
self,
|
||||||
|
fig: go.Figure | FigureResampler,
|
||||||
|
corridor: Corridor,
|
||||||
|
*,
|
||||||
|
resample: bool = False,
|
||||||
|
) -> None:
|
||||||
"""Add a corridor (tolerance band) to the figure."""
|
"""Add a corridor (tolerance band) to the figure."""
|
||||||
style = corridor.style
|
style = corridor.style
|
||||||
|
|
||||||
fig.add_trace(
|
upper_trace = go.Scatter(
|
||||||
go.Scatter(
|
x=corridor.time,
|
||||||
x=corridor.time,
|
y=corridor.upper,
|
||||||
y=corridor.upper,
|
mode="lines",
|
||||||
mode="lines",
|
name=f"{corridor.name} (upper)",
|
||||||
name=f"{corridor.name} (upper)",
|
line=dict(color=style.line_color, width=style.line_width, dash=style.line_dash),
|
||||||
line=dict(color=style.line_color, width=style.line_width, dash=style.line_dash),
|
showlegend=False,
|
||||||
showlegend=False,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
fig.add_trace(
|
lower_trace = go.Scatter(
|
||||||
go.Scatter(
|
x=corridor.time,
|
||||||
x=corridor.time,
|
y=corridor.lower,
|
||||||
y=corridor.lower,
|
mode="lines",
|
||||||
mode="lines",
|
name=f"{corridor.name} (lower)",
|
||||||
name=f"{corridor.name} (lower)",
|
line=dict(color=style.line_color, width=style.line_width, dash=style.line_dash),
|
||||||
line=dict(color=style.line_color, width=style.line_width, dash=style.line_dash),
|
fill="tonexty",
|
||||||
fill="tonexty",
|
fillcolor=style.fill_color,
|
||||||
fillcolor=style.fill_color,
|
showlegend=True,
|
||||||
showlegend=True,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if resample:
|
||||||
|
# Corridors are small — pass through without downsampling
|
||||||
|
fig.add_trace(upper_trace, limit_to_view=False)
|
||||||
|
fig.add_trace(lower_trace, limit_to_view=False)
|
||||||
|
else:
|
||||||
|
fig.add_trace(upper_trace)
|
||||||
|
fig.add_trace(lower_trace)
|
||||||
|
|
||||||
def to_image(self, spec: PlotSpec, format: str = "png", scale: float = 2.0) -> bytes:
|
def to_image(self, spec: PlotSpec, format: str = "png", scale: float = 2.0) -> bytes:
|
||||||
"""Render to a static image."""
|
"""Render to a static image."""
|
||||||
fig = self.render(spec)
|
fig = self.render(spec)
|
||||||
|
|||||||
@@ -6,17 +6,23 @@ scripting API renders the web UI plots.
|
|||||||
|
|
||||||
Transform application uses TransformChain, making the pipeline
|
Transform application uses TransformChain, making the pipeline
|
||||||
serializable and reproducible.
|
serializable and reproducible.
|
||||||
|
|
||||||
|
The plot figure is wrapped in a ``FigureResampler`` for dynamic LTTB
|
||||||
|
downsampling on zoom/pan events. A separate ``relayoutData`` callback
|
||||||
|
triggers resampling when the user interacts with the axes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import dash
|
import dash
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import plotly.graph_objects as go
|
import plotly.graph_objects as go
|
||||||
from dash import Input, Output, State
|
from dash import Input, Output, State, no_update
|
||||||
|
from plotly_resampler import FigureResampler
|
||||||
|
|
||||||
from impakt.channel.model import Channel
|
from impakt.channel.model import Channel
|
||||||
from impakt.plot.engine import DEFAULT_COLORS, PlotEngine
|
from impakt.plot.engine import DEFAULT_COLORS, PlotEngine
|
||||||
@@ -27,9 +33,15 @@ from impakt.transform.cfc import CFCFilter
|
|||||||
from impakt.transform.resultant import resultant_from_channels
|
from impakt.transform.resultant import resultant_from_channels
|
||||||
from impakt.web.state import AppState
|
from impakt.web.state import AppState
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Module-level engine instance (stateless, safe to reuse)
|
# Module-level engine instance (stateless, safe to reuse)
|
||||||
_engine = PlotEngine()
|
_engine = PlotEngine()
|
||||||
|
|
||||||
|
# Server-side storage for the current FigureResampler instance.
|
||||||
|
# Single-user desktop app — no multi-tenant isolation needed.
|
||||||
|
_current_resampler: FigureResampler | None = None
|
||||||
|
|
||||||
|
|
||||||
def _build_transform_chain(
|
def _build_transform_chain(
|
||||||
cfc_value: str,
|
cfc_value: str,
|
||||||
@@ -201,6 +213,7 @@ def _build_plot_spec(
|
|||||||
|
|
||||||
def register_plot_callbacks(app: dash.Dash, app_state: AppState) -> None:
|
def register_plot_callbacks(app: dash.Dash, app_state: AppState) -> None:
|
||||||
"""Register all plot-related callbacks."""
|
"""Register all plot-related callbacks."""
|
||||||
|
global _current_resampler
|
||||||
|
|
||||||
@app.callback(
|
@app.callback(
|
||||||
Output({"type": "plot-graph", "index": 0}, "figure"),
|
Output({"type": "plot-graph", "index": 0}, "figure"),
|
||||||
@@ -231,6 +244,8 @@ def register_plot_callbacks(app: dash.Dash, app_state: AppState) -> None:
|
|||||||
corridors_data: list[dict[str, Any]] | None,
|
corridors_data: list[dict[str, Any]] | None,
|
||||||
x_align_value: float | None,
|
x_align_value: float | None,
|
||||||
) -> go.Figure:
|
) -> go.Figure:
|
||||||
|
global _current_resampler
|
||||||
|
|
||||||
if not selected_keys:
|
if not selected_keys:
|
||||||
selected_keys = []
|
selected_keys = []
|
||||||
|
|
||||||
@@ -245,4 +260,25 @@ def register_plot_callbacks(app: dash.Dash, app_state: AppState) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
spec = _build_plot_spec(channels, cursor_x1, cursor_x2, corridors_data)
|
spec = _build_plot_spec(channels, cursor_x1, cursor_x2, corridors_data)
|
||||||
return _engine.render(spec)
|
fig = _engine.render(spec, resample=True)
|
||||||
|
|
||||||
|
# Store the resampler for relayoutData callbacks
|
||||||
|
if isinstance(fig, FigureResampler):
|
||||||
|
_current_resampler = fig
|
||||||
|
|
||||||
|
return fig
|
||||||
|
|
||||||
|
@app.callback(
|
||||||
|
Output({"type": "plot-graph", "index": 0}, "figure", allow_duplicate=True),
|
||||||
|
Input({"type": "plot-graph", "index": 0}, "relayoutData"),
|
||||||
|
prevent_initial_call=True,
|
||||||
|
)
|
||||||
|
def resample_on_zoom(relayout_data: dict[str, Any] | None) -> Any:
|
||||||
|
"""Resample traces when the user zooms or pans.
|
||||||
|
|
||||||
|
FigureResampler intercepts relayoutData events and returns a
|
||||||
|
Patch that updates only the visible trace data — no full re-render.
|
||||||
|
"""
|
||||||
|
if _current_resampler is None or relayout_data is None:
|
||||||
|
return no_update
|
||||||
|
return _current_resampler.construct_update_data_patch(relayout_data)
|
||||||
|
|||||||
@@ -13,17 +13,59 @@ without reordering.
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import base64
|
||||||
import fnmatch
|
import fnmatch
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import dash_bootstrap_components as dbc
|
import dash_bootstrap_components as dbc
|
||||||
|
import numpy as np
|
||||||
from dash import dash_table, dcc, html
|
from dash import dash_table, dcc, html
|
||||||
|
|
||||||
from impakt.channel.lookup import MAIN_LOCATIONS, MEASUREMENTS
|
from impakt.channel.lookup import MAIN_LOCATIONS, MEASUREMENTS
|
||||||
|
from impakt.channel.model import Channel
|
||||||
from impakt.plot.engine import DEFAULT_COLORS
|
from impakt.plot.engine import DEFAULT_COLORS
|
||||||
from impakt.web.state import AppState
|
from impakt.web.state import AppState
|
||||||
|
|
||||||
|
|
||||||
|
def _sparkline_svg(ch: Channel, width: int = 60, height: int = 16) -> str:
|
||||||
|
"""Generate a tiny inline SVG sparkline for a channel.
|
||||||
|
|
||||||
|
Returns a base64-encoded data URI suitable for use in a Dash
|
||||||
|
DataTable markdown cell: ````.
|
||||||
|
"""
|
||||||
|
data = ch.data
|
||||||
|
n = len(data)
|
||||||
|
if n < 2:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# Downsample to ~60 points for the sparkline
|
||||||
|
if n > 60:
|
||||||
|
indices = np.linspace(0, n - 1, 60, dtype=int)
|
||||||
|
data = data[indices]
|
||||||
|
n = 60
|
||||||
|
|
||||||
|
# Normalize to SVG coordinate space
|
||||||
|
dmin = float(np.min(data))
|
||||||
|
dmax = float(np.max(data))
|
||||||
|
drange = dmax - dmin
|
||||||
|
if drange == 0:
|
||||||
|
drange = 1.0
|
||||||
|
|
||||||
|
xs = np.linspace(1, width - 1, n)
|
||||||
|
ys = height - 1 - ((data - dmin) / drange) * (height - 2)
|
||||||
|
|
||||||
|
# Build SVG polyline path
|
||||||
|
points = " ".join(f"{x:.1f},{y:.1f}" for x, y in zip(xs, ys))
|
||||||
|
svg = (
|
||||||
|
f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}">'
|
||||||
|
f'<polyline points="{points}" fill="none" stroke="#6c757d" stroke-width="1"/>'
|
||||||
|
f"</svg>"
|
||||||
|
)
|
||||||
|
|
||||||
|
encoded = base64.b64encode(svg.encode()).decode()
|
||||||
|
return f'<img src="data:image/svg+xml;base64,{encoded}" />'
|
||||||
|
|
||||||
|
|
||||||
def _build_channel_rows(app_state: AppState) -> list[dict[str, Any]]:
|
def _build_channel_rows(app_state: AppState) -> list[dict[str, Any]]:
|
||||||
"""Build flat row data for the channel grid.
|
"""Build flat row data for the channel grid.
|
||||||
|
|
||||||
@@ -63,6 +105,7 @@ def _build_channel_rows(app_state: AppState) -> list[dict[str, Any]]:
|
|||||||
"test_id": test_id if multi_test else "",
|
"test_id": test_id if multi_test else "",
|
||||||
"iso_code": ch.name,
|
"iso_code": ch.name,
|
||||||
"description": desc,
|
"description": desc,
|
||||||
|
"spark": _sparkline_svg(ch),
|
||||||
"unit": ch.unit,
|
"unit": ch.unit,
|
||||||
"min": f"{float(ch.data.min()):.2f}",
|
"min": f"{float(ch.data.min()):.2f}",
|
||||||
"max": f"{float(ch.data.max()):.2f}",
|
"max": f"{float(ch.data.max()):.2f}",
|
||||||
@@ -115,6 +158,7 @@ def build_channel_grid(app_state: AppState) -> dbc.Card:
|
|||||||
{"name": "#", "id": "ch_num", "type": "numeric"},
|
{"name": "#", "id": "ch_num", "type": "numeric"},
|
||||||
{"name": "ISO Code", "id": "iso_code"},
|
{"name": "ISO Code", "id": "iso_code"},
|
||||||
{"name": "Description", "id": "description"},
|
{"name": "Description", "id": "description"},
|
||||||
|
{"name": "", "id": "spark", "presentation": "markdown"},
|
||||||
{"name": "Unit", "id": "unit"},
|
{"name": "Unit", "id": "unit"},
|
||||||
{"name": "Min", "id": "min", "type": "numeric"},
|
{"name": "Min", "id": "min", "type": "numeric"},
|
||||||
{"name": "Max", "id": "max", "type": "numeric"},
|
{"name": "Max", "id": "max", "type": "numeric"},
|
||||||
@@ -207,6 +251,7 @@ def build_channel_grid(app_state: AppState) -> dbc.Card:
|
|||||||
id="channel-grid",
|
id="channel-grid",
|
||||||
columns=columns,
|
columns=columns,
|
||||||
data=rows,
|
data=rows,
|
||||||
|
markdown_options={"html": True},
|
||||||
row_selectable="multi",
|
row_selectable="multi",
|
||||||
selected_rows=[],
|
selected_rows=[],
|
||||||
sort_action="native",
|
sort_action="native",
|
||||||
@@ -255,6 +300,12 @@ def build_channel_grid(app_state: AppState) -> dbc.Card:
|
|||||||
"width": "50px",
|
"width": "50px",
|
||||||
"textAlign": "center",
|
"textAlign": "center",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"if": {"column_id": "spark"},
|
||||||
|
"width": "68px",
|
||||||
|
"textAlign": "center",
|
||||||
|
"padding": "2px 4px",
|
||||||
|
},
|
||||||
{"if": {"column_id": "min"}, "width": "65px", "textAlign": "right"},
|
{"if": {"column_id": "min"}, "width": "65px", "textAlign": "right"},
|
||||||
{"if": {"column_id": "max"}, "width": "65px", "textAlign": "right"},
|
{"if": {"column_id": "max"}, "width": "65px", "textAlign": "right"},
|
||||||
{"if": {"column_id": "test_id"}, "width": "60px"},
|
{"if": {"column_id": "test_id"}, "width": "60px"},
|
||||||
|
|||||||
145
uv.lock
generated
145
uv.lock
generated
@@ -535,7 +535,9 @@ dependencies = [
|
|||||||
{ name = "numpy" },
|
{ name = "numpy" },
|
||||||
{ name = "pandas" },
|
{ name = "pandas" },
|
||||||
{ name = "plotly" },
|
{ name = "plotly" },
|
||||||
|
{ name = "plotly-resampler" },
|
||||||
{ name = "pydantic" },
|
{ name = "pydantic" },
|
||||||
|
{ name = "pytz" },
|
||||||
{ name = "pyyaml" },
|
{ name = "pyyaml" },
|
||||||
{ name = "scipy" },
|
{ name = "scipy" },
|
||||||
{ name = "weasyprint" },
|
{ name = "weasyprint" },
|
||||||
@@ -563,7 +565,9 @@ requires-dist = [
|
|||||||
{ name = "numpy", specifier = ">=1.24" },
|
{ name = "numpy", specifier = ">=1.24" },
|
||||||
{ name = "pandas", specifier = ">=2.0" },
|
{ name = "pandas", specifier = ">=2.0" },
|
||||||
{ name = "plotly", specifier = ">=5.18" },
|
{ name = "plotly", specifier = ">=5.18" },
|
||||||
|
{ name = "plotly-resampler", specifier = ">=0.11.0" },
|
||||||
{ name = "pydantic", specifier = ">=2.0" },
|
{ name = "pydantic", specifier = ">=2.0" },
|
||||||
|
{ name = "pytz", specifier = ">=2026.1.post1" },
|
||||||
{ name = "pyyaml", specifier = ">=6.0" },
|
{ name = "pyyaml", specifier = ">=6.0" },
|
||||||
{ name = "scipy", specifier = ">=1.10" },
|
{ name = "scipy", specifier = ">=1.10" },
|
||||||
{ name = "weasyprint", specifier = ">=60.0" },
|
{ name = "weasyprint", specifier = ">=60.0" },
|
||||||
@@ -932,6 +936,74 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" },
|
{ url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "orjson"
|
||||||
|
version = "3.11.8"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/9d/1b/2024d06792d0779f9dbc51531b61c24f76c75b9f4ce05e6f3377a1814cea/orjson-3.11.8.tar.gz", hash = "sha256:96163d9cdc5a202703e9ad1b9ae757d5f0ca62f4fa0cc93d1f27b0e180cc404e", size = 5603832, upload-time = "2026-03-31T16:16:27.878Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/41/5aa7fa3b0f4dc6b47dcafc3cea909299c37e40e9972feabc8b6a74e2730d/orjson-3.11.8-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:003646067cc48b7fcab2ae0c562491c9b5d2cbd43f1e5f16d98fd118c5522d34", size = 229229, upload-time = "2026-03-31T16:14:50.424Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0a/d7/57e7f2458e0a2c41694f39fc830030a13053a84f837a5b73423dca1f0938/orjson-3.11.8-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:ed193ce51d77a3830cad399a529cd4ef029968761f43ddc549e1bc62b40d88f8", size = 128871, upload-time = "2026-03-31T16:14:51.888Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/53/4a/e0fdb9430983e6c46e0299559275025075568aad5d21dd606faee3703924/orjson-3.11.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30491bc4f862aa15744b9738517454f1e46e56c972a2be87d70d727d5b2a8f8", size = 132104, upload-time = "2026-03-31T16:14:53.142Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/08/4a/2025a60ff3f5c8522060cda46612d9b1efa653de66ed2908591d8d82f22d/orjson-3.11.8-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eda5b8b6be91d3f26efb7dc6e5e68ee805bc5617f65a328587b35255f138bf4", size = 130483, upload-time = "2026-03-31T16:14:54.605Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2d/3c/b9cde05bdc7b2385c66014e0620627da638d3d04e4954416ab48c31196c5/orjson-3.11.8-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee8db7bfb6fe03581bbab54d7c4124a6dd6a7f4273a38f7267197890f094675f", size = 135481, upload-time = "2026-03-31T16:14:55.901Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ff/f2/a8238e7734de7cb589fed319857a8025d509c89dc52fdcc88f39c6d03d5a/orjson-3.11.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d8b5231de76c528a46b57010bbd83fb51e056aa0220a372fd5065e978406f1c", size = 146819, upload-time = "2026-03-31T16:14:57.548Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/db/10/dbf1e2a3cafea673b1b4350e371877b759060d6018a998643b7040e5de48/orjson-3.11.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58a4a208a6fbfdb7a7327b8f201c6014f189f721fd55d047cafc4157af1bc62a", size = 132846, upload-time = "2026-03-31T16:14:58.91Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f8/fc/55e667ec9c85694038fcff00573d221b085d50777368ee3d77f38668bf3c/orjson-3.11.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f8952d6d2505c003e8f0224ff7858d341fa4e33fef82b91c4ff0ef070f2393c", size = 133580, upload-time = "2026-03-31T16:15:00.519Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7e/a6/c08c589a9aad0cb46c4831d17de212a2b6901f9d976814321ff8e69e8785/orjson-3.11.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0022bb50f90da04b009ce32c512dc1885910daa7cb10b7b0cba4505b16db82a8", size = 142042, upload-time = "2026-03-31T16:15:01.906Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/cc/2f78ea241d52b717d2efc38878615fe80425bf2beb6e68c984dde257a766/orjson-3.11.8-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ff51f9d657d1afb6f410cb435792ce4e1fe427aab23d2fcd727a2876e21d4cb6", size = 423845, upload-time = "2026-03-31T16:15:03.703Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/70/07/c17dcf05dd8045457538428a983bf1f1127928df5bf328cb24d2b7cddacb/orjson-3.11.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6dbe9a97bdb4d8d9d5367b52a7c32549bba70b2739c58ef74a6964a6d05ae054", size = 147729, upload-time = "2026-03-31T16:15:05.203Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/90/6c/0fb6e8a24e682e0958d71711ae6f39110e4b9cd8cab1357e2a89cb8e1951/orjson-3.11.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5c370674ebabe16c6ccac33ff80c62bf8a6e59439f5e9d40c1f5ab8fd2215b7", size = 136425, upload-time = "2026-03-31T16:15:07.052Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b2/35/4d3cc3a3d616035beb51b24a09bb872942dc452cf2df0c1d11ab35046d9f/orjson-3.11.8-cp311-cp311-win32.whl", hash = "sha256:0e32f7154299f42ae66f13488963269e5eccb8d588a65bc839ed986919fc9fac", size = 131870, upload-time = "2026-03-31T16:15:08.678Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/13/26/9fe70f81d16b702f8c3a775e8731b50ad91d22dacd14c7599b60a0941cd1/orjson-3.11.8-cp311-cp311-win_amd64.whl", hash = "sha256:25e0c672a2e32348d2eb33057b41e754091f2835f87222e4675b796b92264f06", size = 127440, upload-time = "2026-03-31T16:15:09.994Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e8/c6/b038339f4145efd2859c1ca53097a52c0bb9cbdd24f947ebe146da1ad067/orjson-3.11.8-cp311-cp311-win_arm64.whl", hash = "sha256:9185589c1f2a944c17e26c9925dcdbc2df061cc4a145395c57f0c51f9b5dbfcd", size = 127399, upload-time = "2026-03-31T16:15:11.412Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/01/f6/8d58b32ab32d9215973a1688aebd098252ee8af1766c0e4e36e7831f0295/orjson-3.11.8-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1cd0b77e77c95758f8e1100139844e99f3ccc87e71e6fc8e1c027e55807c549f", size = 229233, upload-time = "2026-03-31T16:15:12.762Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a9/8b/2ffe35e71f6b92622e8ea4607bf33ecf7dfb51b3619dcfabfd36cbe2d0a5/orjson-3.11.8-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:6a3d159d5ffa0e3961f353c4b036540996bf8b9697ccc38261c0eac1fd3347a6", size = 128772, upload-time = "2026-03-31T16:15:14.237Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/27/d2/1f8682ae50d5c6897a563cb96bc106da8c9cb5b7b6e81a52e4cc086679b9/orjson-3.11.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76070a76e9c5ae661e2d9848f216980d8d533e0f8143e6ed462807b242e3c5e8", size = 131946, upload-time = "2026-03-31T16:15:15.607Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/52/4b/5500f76f0eece84226e0689cb48dcde081104c2fa6e2483d17ca13685ffb/orjson-3.11.8-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:54153d21520a71a4c82a0dbb4523e468941d549d221dc173de0f019678cf3813", size = 130368, upload-time = "2026-03-31T16:15:17.066Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/4e/58b927e08fbe9840e6c920d9e299b051ea667463b1f39a56e668669f8508/orjson-3.11.8-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:469ac2125611b7c5741a0b3798cd9e5786cbad6345f9f400c77212be89563bec", size = 135540, upload-time = "2026-03-31T16:15:18.404Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/56/7c/ba7cb871cba1bcd5cd02ee34f98d894c6cea96353ad87466e5aef2429c60/orjson-3.11.8-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14778ffd0f6896aa613951a7fbf4690229aa7a543cb2bfbe9f358e08aafa9546", size = 146877, upload-time = "2026-03-31T16:15:19.833Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0b/5d/eb9c25fc1386696c6a342cd361c306452c75e0b55e86ad602dd4827a7fd7/orjson-3.11.8-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea56a955056a6d6c550cf18b3348656a9d9a4f02e2d0c02cabf3c73f1055d506", size = 132837, upload-time = "2026-03-31T16:15:21.282Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/37/87/5ddeb7fc1fbd9004aeccab08426f34c81a5b4c25c7061281862b015fce2b/orjson-3.11.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53a0f57e59a530d18a142f4d4ba6dfc708dc5fdedce45e98ff06b44930a2a48f", size = 133624, upload-time = "2026-03-31T16:15:22.641Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/22/09/90048793db94ee4b2fcec4ac8e5ddb077367637d6650be896b3494b79bb7/orjson-3.11.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b48e274f8824567d74e2158199e269597edf00823a1b12b63d48462bbf5123e", size = 141904, upload-time = "2026-03-31T16:15:24.435Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c0/cf/eb284847487821a5d415e54149a6449ba9bfc5872ce63ab7be41b8ec401c/orjson-3.11.8-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3f262401086a3960586af06c054609365e98407151f5ea24a62893a40d80dbbb", size = 423742, upload-time = "2026-03-31T16:15:26.155Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/44/09/e12423d327071c851c13e76936f144a96adacfc037394dec35ac3fc8d1e8/orjson-3.11.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8e8c6218b614badf8e229b697865df4301afa74b791b6c9ade01d19a9953a942", size = 147806, upload-time = "2026-03-31T16:15:27.909Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b3/6d/37c2589ba864e582ffe7611643314785c6afb1f83c701654ef05daa8fcc7/orjson-3.11.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:093d489fa039ddade2db541097dbb484999fcc65fc2b0ff9819141e2ab364f25", size = 136485, upload-time = "2026-03-31T16:15:29.749Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/be/c9/135194a02ab76b04ed9a10f68624b7ebd238bbe55548878b11ff15a0f352/orjson-3.11.8-cp312-cp312-win32.whl", hash = "sha256:e0950ed1bcb9893f4293fd5c5a7ee10934fbf82c4101c70be360db23ce24b7d2", size = 131966, upload-time = "2026-03-31T16:15:31.687Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ed/9a/9796f8fbe3cf30ce9cb696748dbb535e5c87be4bf4fe2e9ca498ef1fa8cf/orjson-3.11.8-cp312-cp312-win_amd64.whl", hash = "sha256:3cf17c141617b88ced4536b2135c552490f07799f6ad565948ea07bef0dcb9a6", size = 127441, upload-time = "2026-03-31T16:15:33.333Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cc/47/5aaf54524a7a4a0dd09dd778f3fa65dd2108290615b652e23d944152bc8e/orjson-3.11.8-cp312-cp312-win_arm64.whl", hash = "sha256:48854463b0572cc87dac7d981aa72ed8bf6deedc0511853dc76b8bbd5482d36d", size = 127364, upload-time = "2026-03-31T16:15:34.748Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/66/7f/95fba509bb2305fab0073558f1e8c3a2ec4b2afe58ed9fcb7d3b8beafe94/orjson-3.11.8-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3f23426851d98478c8970da5991f84784a76682213cd50eb73a1da56b95239dc", size = 229180, upload-time = "2026-03-31T16:15:36.426Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f6/9d/b237215c743ca073697d759b5503abd2cb8a0d7b9c9e21f524bcf176ab66/orjson-3.11.8-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:ebaed4cef74a045b83e23537b52ef19a367c7e3f536751e355a2a394f8648559", size = 128754, upload-time = "2026-03-31T16:15:38.049Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/42/3d/27d65b6d11e63f133781425f132807aef793ed25075fec686fc8e46dd528/orjson-3.11.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97c8f5d3b62380b70c36ffacb2a356b7c6becec86099b177f73851ba095ef623", size = 131877, upload-time = "2026-03-31T16:15:39.484Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dd/cc/faee30cd8f00421999e40ef0eba7332e3a625ce91a58200a2f52c7fef235/orjson-3.11.8-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:436c4922968a619fb7fef1ccd4b8b3a76c13b67d607073914d675026e911a65c", size = 130361, upload-time = "2026-03-31T16:15:41.274Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/bb/a6c55896197f97b6d4b4e7c7fd77e7235517c34f5d6ad5aadd43c54c6d7c/orjson-3.11.8-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ab359aff0436d80bfe8a23b46b5fea69f1e18aaf1760a709b4787f1318b317f", size = 135521, upload-time = "2026-03-31T16:15:42.758Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9c/7c/ca3a3525aa32ff636ebb1778e77e3587b016ab2edb1b618b36ba96f8f2c0/orjson-3.11.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f89b6d0b3a8d81e1929d3ab3d92bbc225688bd80a770c49432543928fe09ac55", size = 146862, upload-time = "2026-03-31T16:15:44.341Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3c/0c/18a9d7f18b5edd37344d1fd5be17e94dc652c67826ab749c6e5948a78112/orjson-3.11.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c009e7a2ca9ad0ed1376ce20dd692146a5d9fe4310848904b6b4fee5c5c137", size = 132847, upload-time = "2026-03-31T16:15:46.368Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/23/91/7e722f352ad67ca573cee44de2a58fb810d0f4eb4e33276c6a557979fd8a/orjson-3.11.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b895b781b3e395c067129d8551655642dfe9437273211d5404e87ac752b53", size = 133637, upload-time = "2026-03-31T16:15:48.123Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/af/04/32845ce13ac5bd1046ddb02ac9432ba856cc35f6d74dde95864fe0ad5523/orjson-3.11.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:88006eda83858a9fdf73985ce3804e885c2befb2f506c9a3723cdeb5a2880e3e", size = 141906, upload-time = "2026-03-31T16:15:49.626Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/02/5e/c551387ddf2d7106d9039369862245c85738b828844d13b99ccb8d61fd06/orjson-3.11.8-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:55120759e61309af7fcf9e961c6f6af3dde5921cdb3ee863ef63fd9db126cae6", size = 423722, upload-time = "2026-03-31T16:15:51.176Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/a3/ecfe62434096f8a794d4976728cb59bcfc4a643977f21c2040545d37eb4c/orjson-3.11.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:98bdc6cb889d19bed01de46e67574a2eab61f5cc6b768ed50e8ac68e9d6ffab6", size = 147801, upload-time = "2026-03-31T16:15:52.939Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/18/6d/0dce10b9f6643fdc59d99333871a38fa5a769d8e2fc34a18e5d2bfdee900/orjson-3.11.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:708c95f925a43ab9f34625e45dcdadf09ec8a6e7b664a938f2f8d5650f6c090b", size = 136460, upload-time = "2026-03-31T16:15:54.431Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/01/d6/6dde4f31842d87099238f1f07b459d24edc1a774d20687187443ab044191/orjson-3.11.8-cp313-cp313-win32.whl", hash = "sha256:01c4e5a6695dc09098f2e6468a251bc4671c50922d4d745aff1a0a33a0cf5b8d", size = 131956, upload-time = "2026-03-31T16:15:56.081Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c1/f9/4e494a56e013db957fb77186b818b916d4695b8fa2aa612364974160e91b/orjson-3.11.8-cp313-cp313-win_amd64.whl", hash = "sha256:c154a35dd1330707450bb4d4e7dd1f17fa6f42267a40c1e8a1daa5e13719b4b8", size = 127410, upload-time = "2026-03-31T16:15:57.54Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/57/7f/803203d00d6edb6e9e7eef421d4e1adbb5ea973e40b3533f3cfd9aeb374e/orjson-3.11.8-cp313-cp313-win_arm64.whl", hash = "sha256:4861bde57f4d253ab041e374f44023460e60e71efaa121f3c5f0ed457c3a701e", size = 127338, upload-time = "2026-03-31T16:15:59.106Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6d/35/b01910c3d6b85dc882442afe5060cbf719c7d1fc85749294beda23d17873/orjson-3.11.8-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ec795530a73c269a55130498842aaa762e4a939f6ce481a7e986eeaa790e9da4", size = 229171, upload-time = "2026-03-31T16:16:00.651Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c2/56/c9ec97bd11240abef39b9e5d99a15462809c45f677420fd148a6c5e6295e/orjson-3.11.8-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c492a0e011c0f9066e9ceaa896fbc5b068c54d365fea5f3444b697ee01bc8625", size = 128746, upload-time = "2026-03-31T16:16:02.673Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3b/e4/66d4f30a90de45e2f0cbd9623588e8ae71eef7679dbe2ae954ed6d66a41f/orjson-3.11.8-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:883206d55b1bd5f5679ad5e6ddd3d1a5e3cac5190482927fdb8c78fb699193b5", size = 131867, upload-time = "2026-03-31T16:16:04.342Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/19/30/2a645fc9286b928675e43fa2a3a16fb7b6764aa78cc719dc82141e00f30b/orjson-3.11.8-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5774c1fdcc98b2259800b683b19599c133baeb11d60033e2095fd9d4667b82db", size = 124664, upload-time = "2026-03-31T16:16:05.837Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/db/44/77b9a86d84a28d52ba3316d77737f6514e17118119ade3f91b639e859029/orjson-3.11.8-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7381c83dd3d4a6347e6635950aa448f54e7b8406a27c7ecb4a37e9f1ae08b", size = 129701, upload-time = "2026-03-31T16:16:07.407Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b3/ea/eff3d9bfe47e9bc6969c9181c58d9f71237f923f9c86a2d2f490cd898c82/orjson-3.11.8-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14439063aebcb92401c11afc68ee4e407258d2752e62d748b6942dad20d2a70d", size = 141202, upload-time = "2026-03-31T16:16:09.48Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/52/c8/90d4b4c60c84d62068d0cf9e4d8f0a4e05e76971d133ac0c60d818d4db20/orjson-3.11.8-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa72e71977bff96567b0f500fc5bfd2fdf915f34052c782a4c6ebbdaa97aa858", size = 127194, upload-time = "2026-03-31T16:16:11.02Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8d/c7/ea9e08d1f0ba981adffb629811148b44774d935171e7b3d780ae43c4c254/orjson-3.11.8-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7679bc2f01bb0d219758f1a5f87bb7c8a81c0a186824a393b366876b4948e14f", size = 133639, upload-time = "2026-03-31T16:16:13.434Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6c/8c/ddbbfd6ba59453c8fc7fe1d0e5983895864e264c37481b2a791db635f046/orjson-3.11.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:14f7b8fcb35ef403b42fa5ecfa4ed032332a91f3dc7368fbce4184d59e1eae0d", size = 141914, upload-time = "2026-03-31T16:16:14.955Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4e/31/dbfbefec9df060d34ef4962cd0afcb6fa7a9ec65884cb78f04a7859526c3/orjson-3.11.8-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c2bdf7b2facc80b5e34f48a2d557727d5c5c57a8a450de122ae81fa26a81c1bc", size = 423800, upload-time = "2026-03-31T16:16:16.594Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/87/cf/f74e9ae9803d4ab46b163494adba636c6d7ea955af5cc23b8aaa94cfd528/orjson-3.11.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ccd7ba1b0605813a0715171d39ec4c314cb97a9c85893c2c5c0c3a3729df38bf", size = 147837, upload-time = "2026-03-31T16:16:18.585Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/64/e6/9214f017b5db85e84e68602792f742e5dc5249e963503d1b356bee611e01/orjson-3.11.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbc8c9c02463fef4d3c53a9ba3336d05496ec8e1f1c53326a1e4acc11f5c600", size = 136441, upload-time = "2026-03-31T16:16:20.151Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/24/dd/3590348818f58f837a75fb969b04cdf187ae197e14d60b5e5a794a38b79d/orjson-3.11.8-cp314-cp314-win32.whl", hash = "sha256:0b57f67710a8cd459e4e54eb96d5f77f3624eba0c661ba19a525807e42eccade", size = 131983, upload-time = "2026-03-31T16:16:21.823Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3f/0f/b6cb692116e05d058f31ceee819c70f097fa9167c82f67fabe7516289abc/orjson-3.11.8-cp314-cp314-win_amd64.whl", hash = "sha256:735e2262363dcbe05c35e3a8869898022af78f89dde9e256924dc02e99fe69ca", size = 127396, upload-time = "2026-03-31T16:16:23.685Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c0/d1/facb5b5051fabb0ef9d26c6544d87ef19a939a9a001198655d0d891062dd/orjson-3.11.8-cp314-cp314-win_arm64.whl", hash = "sha256:6ccdea2c213cf9f3d9490cbd5d427693c870753df41e6cb375bd79bcbafc8817", size = 127330, upload-time = "2026-03-31T16:16:25.496Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "26.0"
|
version = "26.0"
|
||||||
@@ -1110,6 +1182,23 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl", hash = "sha256:ac8aca1c25c663a59b5b9140a549264a5badde2e057d79b8c772ae2920e32ff0", size = 9898444, upload-time = "2026-04-09T20:36:39.812Z" },
|
{ url = "https://files.pythonhosted.org/packages/90/ad/cba91b3bcf04073e4d1655a5c1710ef3f457f56f7d1b79dcc3d72f4dd912/plotly-6.7.0-py3-none-any.whl", hash = "sha256:ac8aca1c25c663a59b5b9140a549264a5badde2e057d79b8c772ae2920e32ff0", size = 9898444, upload-time = "2026-04-09T20:36:39.812Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotly-resampler"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "dash" },
|
||||||
|
{ name = "numpy" },
|
||||||
|
{ name = "orjson" },
|
||||||
|
{ name = "pandas" },
|
||||||
|
{ name = "plotly" },
|
||||||
|
{ name = "tsdownsample" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/0f/5c/e3990691542d789b8c07a5c021ace489b5b046d7ff66357c67536c535fe4/plotly_resampler-0.11.0.tar.gz", hash = "sha256:e3f9ceeb5749de9a85d39755f50711a7d6a28eb1b284da250251c8896ccc1cfd", size = 57400, upload-time = "2025-08-29T12:15:49.574Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/77/df/921eff232a2a8da7bdf84688b8cacfd9cef1bdc8fe695c547f7994804bee/plotly_resampler-0.11.0-py3-none-any.whl", hash = "sha256:5ce05a2b7eac5ec00379b219249ea583715077db9268fd4f4423715ce90cf653", size = 82684, upload-time = "2025-08-29T12:15:48.027Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pluggy"
|
name = "pluggy"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
@@ -1309,6 +1398,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
|
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytz"
|
||||||
|
version = "2026.1.post1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/56/db/b8721d71d945e6a8ac63c0fc900b2067181dbb50805958d4d4661cf7d277/pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1", size = 321088, upload-time = "2026-03-03T07:47:50.683Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/99/781fe0c827be2742bcc775efefccb3b048a3a9c6ce9aec0cbf4a101677e5/pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a", size = 510489, upload-time = "2026-03-03T07:47:49.167Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyyaml"
|
name = "pyyaml"
|
||||||
version = "6.0.3"
|
version = "6.0.3"
|
||||||
@@ -1580,6 +1678,53 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
|
{ url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tsdownsample"
|
||||||
|
version = "0.1.4.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "numpy" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/4b/bf/7590ae93ee6970876a79ab08602e4098a0c00db4b19f813d87935bb16607/tsdownsample-0.1.4.1.tar.gz", hash = "sha256:cc2e2fc0031a5fb75c0f5204b498e86eae0333bcc19da481c0859f1943690f3b", size = 555937, upload-time = "2025-01-29T13:13:25.185Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/35/57/1b0fb3bfd1ddd4bb97490e8fe5494b592028ec9fed98079d872ecb135226/tsdownsample-0.1.4.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d93a7de6e684e2f314444f75d2100e43dc76deed5e6cfd94bf6ecff0a2270383", size = 1266928, upload-time = "2025-01-29T13:10:56.332Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6c/9a/1b1327238914ffb5e8ce4001b744df0e36a903fdfe0796e0309e86cfe1cd/tsdownsample-0.1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c2569bffb94a24c781623ded653a1e50ef9f2ab102cc0be091add3dfd534338e", size = 1166982, upload-time = "2025-01-29T13:10:59.257Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ed/9e/ebdcc469bbe9e7d2decbc23cb835043c927acd7b8e8103cafced54e722cb/tsdownsample-0.1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab0d54355b292be0426ec97a36cf8ebdd22d69b0f9e08154d7a81eea30074d52", size = 1237143, upload-time = "2025-01-29T13:11:01.218Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/31/a5/497f68a50e8e1fd7828af74e9534c591a36e8ab24a20814f47a1708115b2/tsdownsample-0.1.4.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:814eca271f2a8178ad5f614c13b6093e477fa905c61f00f292bcb77e152f2139", size = 1332403, upload-time = "2025-01-29T13:11:03.469Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b0/6e/b0239c632d110f0fde3289b1730543286397fb18c2a04572e74aef7fa51a/tsdownsample-0.1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97538b126a733ca88b00466580ac6f123e9a639c297058628791e05d9512369d", size = 1322541, upload-time = "2025-01-29T13:11:05.21Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6d/b7/83ec7fac8396fad3918bb71baf9286107b4d6335e06417fae244b1250a8a/tsdownsample-0.1.4.1-cp311-cp311-manylinux_2_24_armv7l.whl", hash = "sha256:1c6e1c2422b93ec2c3962e04fb10595709b894483918ef7977d2f84f37d6acf7", size = 1258659, upload-time = "2025-01-29T13:11:08.778Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0e/a3/8e9ff9f0191aece44581dc0b94a03ee3fe48cfb8650a32a1cec4948043e2/tsdownsample-0.1.4.1-cp311-cp311-manylinux_2_24_ppc64le.whl", hash = "sha256:8af543398045920bc3e7078eab627934069ba74d99a49d3dd62dda5c5c5c8499", size = 1269297, upload-time = "2025-01-29T13:11:11.044Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/12/1b/e077b7c4b4813ac40c9b9e429138afcaf35037303207c3cf065746630d24/tsdownsample-0.1.4.1-cp311-cp311-manylinux_2_24_s390x.whl", hash = "sha256:036e783d81e773ddba377a45ac19489b365b6aac16e915ed31fe6f5260b588c8", size = 1448877, upload-time = "2025-01-29T13:11:15.36Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/07/df/fef81e7b9f86d68f704824527bbc5d064059a94b03d46635cdfc8d42e534/tsdownsample-0.1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0afc76e88ad51a0887323823dd96cbe1a49300c543c9baa7cb93456c4bf69e11", size = 1398147, upload-time = "2025-01-29T13:11:17.866Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/69/ce/779747b031404739977f5996260e7386298c983c3820c5f79fd4b28257b6/tsdownsample-0.1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:46a109da21a3be8ef81cc3b988824309bbf1ea89e6385468911f46ee6f308d85", size = 1523919, upload-time = "2025-01-29T13:11:21.484Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cb/af/e847569c61bc58bd9ba41f24493d5159d8d42fdbd36a2e0039255196107a/tsdownsample-0.1.4.1-cp311-cp311-win32.whl", hash = "sha256:d6232c1c0e0e5cf49e57de2a860bb0c6e1279da4e1d360ab2bd456b25fef6a7d", size = 773506, upload-time = "2025-01-29T13:11:23.735Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/19/aa/a3d611c83b2c647451ffbbbce3d589c377f9de990a616eda17c459a74d83/tsdownsample-0.1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:3378ef3ac19557191400697c8fb2586917fd42cc73099eab32a8a7874a48c417", size = 1034102, upload-time = "2025-01-29T13:11:27.735Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/01/5c/1bc005378e4a7f0738c5ca093f7b2893eac932d44a59be6331b373119c88/tsdownsample-0.1.4.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:13aa99d40f0e25e49a28fd540fe13759cc19ade788013d46922a1cb60f475ff1", size = 1252185, upload-time = "2025-01-29T13:11:29.508Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3f/0e/4b12eeb6119389c83a7618e7bbfb9b3f6fc2ae6fa271d978982ebabede7f/tsdownsample-0.1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d177219c46d9499e28a9b637a6f2d87ccbeb82e1cf57d9a5e68c5fe85df33fe", size = 1166653, upload-time = "2025-01-29T13:11:32.104Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c9/3d/52e84bc71b1f7f497953563055c82f7a8bf975291f13f248f219f94cedc9/tsdownsample-0.1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fbed20c6dbbe029ae380cc01cc5ddc0db74aa65d57b26302ec4c3f92744efdd8", size = 1231341, upload-time = "2025-01-29T13:11:34.044Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a6/63/150984d8abef039ca48562cc8ef8dbf6a955aeba88a48dc9bb4f5e15f86a/tsdownsample-0.1.4.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7b780d155e2ef47b36db09b9c166512c153c533fe22ff24d5b351cdab9a76b1", size = 1312409, upload-time = "2025-01-29T13:11:36.904Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5b/25/52552bfe812c872660875d82e7a142558d917e0c25196c8c7a6d54d6eb67/tsdownsample-0.1.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f941f9da83d436516546251ca4145e5748b7fa20f1f5f76d49bb924f2af87b35", size = 1325040, upload-time = "2025-01-29T13:11:41.191Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/71/39181f51b37c864decbfb8f778b7ebf78f54048fb701fabb800fd62b0930/tsdownsample-0.1.4.1-cp312-cp312-manylinux_2_24_armv7l.whl", hash = "sha256:49ce60fa007e25b9a4889110a24a10ea96a0f568647d74e263cf7dd325c746d1", size = 1260784, upload-time = "2025-01-29T13:11:43.025Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/76/fe/dc9efb88c8913149439b7ede45b6e4ca5cc3d585edded50c850f9a8a7abf/tsdownsample-0.1.4.1-cp312-cp312-manylinux_2_24_ppc64le.whl", hash = "sha256:656f7b1c27ba457fb2af2b87451925887bce4c7990a1cc43e8b58bcbe56ab729", size = 1286794, upload-time = "2025-01-29T13:11:45.768Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6c/fb/3546fbf09a6dddbf6362b252ee01cd9ee2a2695828991dae0611f51f19d0/tsdownsample-0.1.4.1-cp312-cp312-manylinux_2_24_s390x.whl", hash = "sha256:498c99c512bccc8cb77c823340b73ba2f0b07c392f0091f17ce172a426eeb007", size = 1444825, upload-time = "2025-01-29T13:11:49.202Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/13/fb/39cd6067bb833a1d478560070002fa2737785a9dd8fc3fe231cafae5b0b7/tsdownsample-0.1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:24d6e29cc89fa1ab836df9c9590fd4f26088c661e3ed9f5add966c5b076df6fd", size = 1392808, upload-time = "2025-01-29T13:11:51.152Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4a/31/0db2e5a3ff868f6b09a1ba4e87d565c4552596e6f2c16e15c3fe29f6000b/tsdownsample-0.1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d9afa84ec630081ce87060d2f0c294df084da2627ac2b623d36dfcfaef02005a", size = 1527173, upload-time = "2025-01-29T13:11:53.485Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/70/cf/3a407a40ac6b9e08f6e59846b5a4b5a68797f0c604219deace02a5c9fe16/tsdownsample-0.1.4.1-cp312-cp312-win32.whl", hash = "sha256:128abd5c326f7a41436ec3f450c9ab128558498bf0abc67a760a94d2ac3a68b4", size = 767553, upload-time = "2025-01-29T13:11:55.966Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3e/6b/d80ef005f1ccbb5a325d4daf9e48d09f39ffcb20c65108f4eafb89b88510/tsdownsample-0.1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:9138b330c0172211bdd34fca24947fb35648784046f4d08e32f7fb97d775ad96", size = 1029860, upload-time = "2025-01-29T13:11:58.474Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c5/d8/b59fde996a4371e719a2cdb8bc16990beaed737b97de71e716a186c8546a/tsdownsample-0.1.4.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0a3dcac6f49f9543c5e12caab5b3c5ed78dafd76afeb70ea71453b5821f0fede", size = 1252057, upload-time = "2025-01-29T13:12:01.235Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8d/6f/3f8f18fa5f4b1b899c4a81b0eadb4ba22014adc70d22f2c9eff1ba3e0c80/tsdownsample-0.1.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2a3b690075a240478de70d512b50db165e7ef98b9e6f7d55e101146dbf3a09b1", size = 1166465, upload-time = "2025-01-29T13:12:02.866Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/53/2c/253e00fa376ca3cc8b6eb42fee144be70824e80150742d9fae5323533421/tsdownsample-0.1.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263fd9259e8a6541d5c1e2744e90b38c8d6f98f343b03a19951fbf94fb10cb43", size = 1231028, upload-time = "2025-01-29T13:12:04.724Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/88/be/0ff315a337d176737f6ffc05af4190f8cf9d344d71b64e2eb81bb2ca7347/tsdownsample-0.1.4.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fec09afa83d162a560fb902a60ec4f5b4cbcbbe3eb595c5c75bc28571dcf03c", size = 1311979, upload-time = "2025-01-29T13:12:06.664Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a4/31/7a572efe29e678c2bb172bf93f14a7fa14f4ecc421e36700a48447f99b96/tsdownsample-0.1.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92629086cf799f0364e8e70996de9e3724f738ab2ab323d9228fc1275c8c0adb", size = 1325055, upload-time = "2025-01-29T13:12:08.579Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/05/7c3e32662f6059b3c739c6528d3c1eece57ff3955168dc483a37a6a1b576/tsdownsample-0.1.4.1-cp313-cp313-manylinux_2_24_armv7l.whl", hash = "sha256:a917c885998c851eb1e6ca58e8f19a2f2f4e09dc6b6719bd1755bcc6247e98eb", size = 1259867, upload-time = "2025-01-29T13:12:11.362Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b7/e6/1e97e37cc4cec3ee1e5ad1934ea75d83e41a8258686adc7b2309bf32ca84/tsdownsample-0.1.4.1-cp313-cp313-manylinux_2_24_ppc64le.whl", hash = "sha256:e93179108e9721d637151f0645e3b4f50819446e4458156103472b35ffaa026d", size = 1286896, upload-time = "2025-01-29T13:12:13.502Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2e/bd/f93db320eb21660a443e849174423aa9324dc5cba016c096255bdc3c17a7/tsdownsample-0.1.4.1-cp313-cp313-manylinux_2_24_s390x.whl", hash = "sha256:26e74a6a9591bc52310ce16c316efe59a2443f7718188d459c5a2e5fb58883a9", size = 1445464, upload-time = "2025-01-29T13:12:15.415Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7f/0f/2711e5e24071b55fa653b13bbbbafad08f96832d92094c3c6f3368d986a3/tsdownsample-0.1.4.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c3953f484205b70e7a4e8473e2cc96a3ef6a24eb6600b89980f47447cbff4492", size = 1392698, upload-time = "2025-01-29T13:12:17.836Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/07/5e/a7cce9d82017b5bbf3629acab08e58dafedd24f602b603c3c7f528bd8dd6/tsdownsample-0.1.4.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fd5b786931d6500938942398e0aadb44046366ac28460ea008181ea75fb6dd52", size = 1527159, upload-time = "2025-01-29T13:12:19.687Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/08/0c/49aac73ea3702bab30c6c4e29344ccfe3fcfbc20df739e3bbda4e571c258/tsdownsample-0.1.4.1-cp313-cp313-win32.whl", hash = "sha256:e7720aad79b6b76a1cdd20c2931e8e60e0465b437dc07c567d090493f39c36a5", size = 767855, upload-time = "2025-01-29T13:12:21.465Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ab/a3/8e4b211b81559fa6ae3bb98dc08bfa68e7b5e60e5ad394da12064951e8d0/tsdownsample-0.1.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:7985be2da94507bc507982c3f868b34b8bb70c84ab80ca53a9e8756ca91758c8", size = 1029209, upload-time = "2025-01-29T13:12:24.842Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typing-extensions"
|
name = "typing-extensions"
|
||||||
version = "4.15.0"
|
version = "4.15.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user