Pieces API + records/dashboard/bins overhaul#186
Draft
spencerhhubert wants to merge 10 commits into
Draft
Conversation
Wiping bin contents now flushes each bin's load (aggregates + category assignment at wipe time) into an auto-managed snapshot: single-bin/layer wipes accumulate layers in the open snapshot, empty-all closes it. Pieces stay in piece_events keyed by (session, bin, epoch) - nothing is copied. New endpoints: list/detail snapshots, CSV export per snapshot and for current bin contents (bl_part_id, bl_color_id, category_id_in_profile, profile from the sorting session, created/classified/distributed unix timestamps). piece_events grows created_at/classified_at columns. /api/bins/contents drops inline base64 images by default (8.1MB -> 480KB per poll; a backgrounded tab was saturating the GBL uplink), the bins page stops polling while hidden, and piece_events gets a bin-lookup index (contents build 5.5s -> 1.1s). Snapshots modal + export buttons on /bins. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…files)
Every recognition crop (C4 burst + upstream C2/C3) is persisted as a plain
JPEG under software/sorter/backend/piece_images/<uuid>/ and indexed in
local_state.sqlite, so piece images survive restarts and LRU eviction.
Broadcaster enqueues (bounded, drop-on-full); a single daemon worker does
decode + file/DB writes and a 500MB retention sweep (oldest first, synced-
to-hive files preferentially). Rows outlive evicted files and carry
synced_at/hive_image_id for the upcoming hive uploader.
New endpoints: GET /api/pieces/{uuid}/images (index),
GET /api/pieces/{uuid}/images/{id} (file), GET /api/piece-images/stats.
C4 crop encode quality 80 -> 90 since these now feed training data.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…disk-fallback) The /records page previously fetched crops only from the in-memory known-objects lookup, so every restart blanked images for all history. Now it falls back to the piece-image store: file URLs served with Cache-Control: immutable (the browser is the cache — repeat visits render without re-fetching), hydrated 6-at-a-time instead of 100 concurrent fetches, with Skeleton placeholders while loading. The store also persists used/excluded_from_result/score per image now — flushed once per piece when the applied Brickognize result settles — so the used/dropped badges survive reboots too. Reclassify stays memory-only (needs the base64 payloads). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add /api/bins/contents/version, a cheap change-token endpoint (session_id + row count + max updated_at + summed epoch/piece_count) so the bins page can poll frequently for auto-update without pulling the full contents payload (with images) every tick. Also pause polling while the tab is hidden and force-refresh on visibility regain. Split the ~2000-line bins +page.svelte into components: BinCard, LayerPanel, ColumnsPanel, SnapshotsModal, BinDetailsModal, shared types/pieces helpers, a bricklinkParts rune-based store, and new Skeleton/ToggleSwitch primitives (replacing 5x copy-pasted switch markup). No behavior change — same endpoints, confirm dialogs, and busy/disabled semantics as before. Co-Authored-By: Claude <noreply@anthropic.com>
…ed status
- New server/routers/pieces.py: GET /api/pieces (keyset cursor, filters,
PieceSummary rows w/ has_images, preview_url, est_value), tiered
/api/pieces/{uuid} (memory-first zero-sqlite hot path, disk fallback),
streamed /api/pieces/export.csv, /api/pieces/aggregates chart data, and
overview/value/lifetime(+csv) relocated from /api/records/*.
- Deleted /api/records/* and /api/known-objects/{uuid}; one read API.
- Per-second metric snapshots move to their own local_metrics.sqlite
(buffered 15s flush, 24h/2.5M-row retention); legacy 6.8M-row tables in
local_state.sqlite drain+drop in the idle-priority pruner thread.
- getValueStats: price cache + 60s memo (was ~1.2s/request); lifetime
best-hour PPM de-correlated to one grouped query.
- recent_known_objects blob + WS-connect replay removed (dropdown refills
from GET /api/pieces).
- ClassificationStatus gains 'failed' for transport-error identifications
(persisted, distributable so failed pieces discard instead of jamming C4);
piece_records persists brickognize_preview_url.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dges - New lib/pieces store: one client Piece shape that REST summaries and live known_object WS events reduce into; RecentObjects, the records list, and tracked detail all read it. Dropdown refills via GET /api/pieces?limit=32. - Records page componentized (~370 lines from ~1040): merged lifetime stats header, cursor pagination, live rows on page 1, per-day table in two-week blocks + CSV export buttons, lazy hand-rolled SVG charts from /api/pieces/aggregates. - Tracked page gains disk fallback (no more 404 after backend restart). - New shared PieceStatusBadge: green Classified strictly gated on classification_status === 'classified' — failed/unidentified pieces no longer wear the green chip (RecentObjects lifecyclePhase bug), 'failed' renders as ID failed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… bins card, logs tab - CameraFeed gains an optional headerActions snippet; the three split-feeder views get right-justified -1 deg / +1 deg / 180 deg CW buttons (motor degrees via 130/12 gear ratio, stored per-stepper speed, one in-flight action, Popover tooltips). - Incident handling extracted to IncidentHandlingSection under the Models settings page; dashboard block removed. - Dashboard bins card removed (SortingStatusCard deleted); Logs tab removed from the top bar. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… search - Grid cells preview server-grouped items with a xN QuantityBadge instead of duplicate recent-piece images; same badge in the modal (theme token, raw hex removed). - Layers render per-section groups with enable/point controls inline; cards show types/total counts and last-updated recency; client-side search highlights bins by part id/name, color, category; modal items get PieceStatusBadge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-safe Concurrent price lookups (value-stats cold fill racing aggregates and the classification pipeline) hit "bad parameter or other API misuse" on the shared check_same_thread=False connection and permanently cached those parts as unpriced. Python-level execute() on one connection needs a lock. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
First value/aggregates computation walks every historical (part,color) pair through parts.db — ~60s serialized on the Pi eMMC (part_bricklink_ids has no index). Warm it in a background daemon thread at startup so no records-page request ever pays it; steady-state requests stay ~50ms. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Draft — stacked on
spencer/hive-sync-from-sorthive-01(its commits are excluded from this diff).Overview
A clean, unified Pieces API that the records page, the recent-pieces dropdown, exports, and the websocket all draw from — replacing a read surface that had grown into four divergent paths (
piece_records, the in-memory known-object LRU,piece_image_store, and a 10-item recent-objects blob). One piece shape, tiered payloads, lazy subresources. Plus the records/dashboard/bins UI work that rides on it. Net effect is smaller: old endpoints are deleted, not left in parallel.Backend — unified read API
server/routers/pieces.py(new):GET /api/pieces— keyset cursor pagination (on the autoincrement id, sinceseen_atis nullable), filters (status, part_id, color_id, run_id, dead, date range),recent/oldestsort. Returns lightPieceSummaryrows (uuid, timestamps, classification, bin, price,has_images,preview_url) — no images, no metadata blob.GET /api/pieces/{uuid}— tiered{origin, summary, detail, detail_available}: memory-first (full known-object detail) with disk fallback (durable summary), the same pattern the image store already uses. Memory hits do zero SQLite work — this endpoint is polled per active piece during sorting.GET /api/pieces/{uuid}/images— unchanged, remains the lazy image subresource.GET /api/pieces/export.csv— streamed CSV of the whole filtered set (keyset-chunked), not capped to a page.GET /api/pieces/aggregates— cached chart series (pieces/day, status breakdown, unique parts over time, PPM/day, per-color, top parts, value/day).overview/value/lifetime(+lifetime/export.csv) relocated under/api/pieces./api/records/*and/api/known-objects/{uuid}, plus therecent_known_objectsblob and its websocket-connect replay. Clients refill fromGET /api/piecesinstead.piece_recordsgains a nullablebrickognize_preview_urlcolumn (guarded ALTER) so summary rows carry a thumbnail without an image fetch.Backend — performance & correctness
local_metrics.sqlite(buffered flush, bounded retention). These per-second, write-only diagnostic rows had grown into millions and bloated the shared DB, slowing every query against it; the legacy tables drain and drop in the background at idle priority.execute(); parallel lookups could error and cache bad results. Serialized with a lock; the price cache is also pre-warmed off the request path at startup.Backend — classification status (Area 8)
failedClassificationStatusfor transport-error identifications (persisted, distributable so a failed piece discards rather than jamming the channel). Previously "failed to identify" was indistinguishable from a genuineclassifiedoutcome in some UI paths.Frontend
lib/pieces/) — REST summaries and liveknown_objectwebsocket events reduce into a single per-machinePieceshape with one upsert reducer. The recent-pieces dropdown, the records list's live rows, and the tracked detail page all read it; the dropdown refills fromGET /api/piecesinstead of the removed websocket replay.PieceStatusBadge— one shared badge replacing hand-rolled chips that had diverged across pages. The green "Classified" is now gated strictly onclassification_status === 'classified'; a failed/unidentified piece can no longer read as classified anywhere (records, dropdown, tracked, bin modal). Fixes a dropdown lifecycle chip that showed green for any piece whose classification simply finished, success or not.Notes
events.tscarries a one-linefailedunion addition; a full codegen regen is deferred to its own change (the generator rewrites unrelated types).VACUUMduring downtime would shrink the file.🤖 Generated with Claude Code