Lapache web-based lap analysis#158
Open
jacobjurek wants to merge 16 commits into
Open
Conversation
Add a JSON analysis blob to the Session model so Lapache lap-analysis results (lat/lon fields, normalization, crop, geometric segments, laps, summary) persist via the existing session upsert. Add a custom JSON type whose driver value is text so it casts cleanly into a Postgres jsonb column. Add two query-service endpoints for browsing raw signal data when sessioning: GET /query/signals/names (distinct signals in a window) and GET /query/clusters (contiguous data blocks via minute-bucketed gap merging). Includes Go and pytest coverage for the new model field, JSON type, cluster merge logic, and route wiring. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Port the PyQt Lapache desktop tool to a web page at /lapache. Pure logic (segment intersection, coordinate normalization, lap/sector detection, the segment manager) ports to TypeScript under lib/lapache; an API client swaps the desktop app's direct DB access for the Mapache gateway endpoints. The page browses sessions and raw-data clusters, fetches lat/lon telemetry, renders the track on an HTML5 canvas where the user places S/F and sector lines (click to add, shift/right-click to remove, keys 1-9 to switch, P to process, Ctrl+Z to undo), then processes laps and saves the analysis blob back to the session. Adds a sidebar nav entry and the route. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
main's mapache-go (v3.3.0) defaults Session.Analysis to {} rather than
null, so un-analyzed sessions now arrive as a truthy empty object. Add a
hasAnalysis() type guard keyed on a required field (lat_field) and use it
for the sidebar "analyzed ✓" badge and the LapachePage load-existing
branch, so the {} default no longer reads as "already analyzed."
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
query_signals bound a tuple to a single :signals placeholder, which only worked by accident on psycopg2 (tuple adaptation) and errored on other backends like SQLite. Declare it with bindparam(expanding=True) so the IN list renders as individual placeholders on any backend. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
get_database_url interpolated DATABASE_USER/PASSWORD straight into the DSN string. A password containing '@' (or '%'/non-ASCII bytes) bled into the host portion, so psycopg2 tried to resolve "<pwtail>@<host>" and failed with "could not translate host name". Build the URL via sqlalchemy.URL.create so credentials are escaped. Go services were unaffected because GORM doesn't parse a URL. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Keeps docker-compose.override.yml (used to point query at a remote DB for local testing) out of git so it doesn't affect others' local stacks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…uency signal - signal picker falls back to distinct signal names present for the selected vehicle when the curated signal_definition catalog is empty - pass vehicle_id from the dashboard so the fallback can scope to the vehicle - bucket clustering on the highest-frequency signal instead of the alphabetically-first name, which could be sparse and collapse every cluster Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The signal_definition catalog is now populated, so the read-time fallback to live signal names is no longer needed. Drops the fallback block, the unused vehicle_id query param, and the dashboard query param. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The bind-mounted /app/query holds .venv, scripts/ and tests/ alongside the app package. uvicorn --reload watched the whole dir, so `uv run` touching the venv triggered an endless reload loop that took the service down and dropped it from Rincon's registry (gateway then 404'd /query/* routes, surfacing as network errors in the dashboard). Limit the watcher to the query package with --reload-dir. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Root .gitignore had no Python rules, so local .venv/ and __pycache__/ dirs (e.g. under gr26/tools, query) surfaced as untracked noise on feature branches. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a reusable SignalTree that groups signals by node prefix (ecu_, bcu_, …) into collapsible sections, showing each signal's formatted name with its full id as subtext. Wire it into the query signal multi-select and the lapache lat/lon pickers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a calendar date selector that limits cluster/session browsing to a single local day. The backend gains a /clusters/dates endpoint (get_data_dates) plus optional date/tz scoping on /clusters, so clusters and the anchor bucketing are computed for just that day. On the frontend, fetchClusters takes an optional date (sent with the browser timezone) and a new fetchDataDates backs the default day and the "days with data" hints. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two fixes for raw-data timeouts and latency: - Cache the per-vehicle anchor signal (5 min TTL). Deriving it runs a GROUP BY name ORDER BY COUNT(*) that scans the whole vehicle partition, and it was recomputed on every /clusters and /clusters/dates request. - Add optional server-side decimation to query_signals via a max_points param: when the window is known, bucket each signal by time and keep one row per bucket (DISTINCT ON), capping rows transferred, pivoted, and serialized. The track map (fetchSignalData) requests max_points=5000, which the canvas can't exceed visually; exports stay full-resolution. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts: # .gitignore # query/query/service/query.py # query/uv.lock
Add an in-page Lap/Calibration toggle. Calibration mode plots one or more selected signals against time instead of a GPS track, for runs without a usable GPS fix. Reuses the shared timeline crop to trim a window and create a plain session over the crop (no lap/segment analysis). Chart supports a raw <-> normalized Y-axis toggle so signals with different scales compare. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Coerce lat/lon to numbers and skip non-finite readings and the 0,0 "null island" fix the receiver emits before lock. The track autoscales to its bounding box, so a single bad outlier collapsed the real path and drew stray lines shooting to it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
✅ Deploy Preview for gr-mapache ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Branch bundling the web-based Lapache lap-analysis work for review. Highlights:
0,0"null island") from track points so a single bad outlier no longer collapses the real path.Review notes
Opening this primarily to review the code on the branch. Several commits build on each other; the most recent is the GPS-track hardening.
🤖 Generated with Claude Code