Present one movement across multiple cameras as a single journey #146
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Today motion events are grouped per camera. A single real-world movement — a vehicle or person coming through the gate and travelling up one of the driveways to the carpark — shows up in the day view as two or three unrelated event tiles.
On this low-traffic, single-access site those per-camera events are almost always the same object moving through the property, and the day view fails to tell that story.
What I want:
Scope agreed during brainstorming: browsing only — no notifications or alerts for the foreseeable future. Timestamps only — no object classification and no camera topology config in v1 (the design keeps the door open for direction labels later).
Findings from exploration
events.pyalready derives per-camera events at query time (group_events_per_camera, 60 s gap hardcoded as a function default); nothing is persisted.aiosqlite.Rowat runtime (db.row_factory) but the grouping functions are duck-typed; tests useMagicMock/dict fakes./snapshots/{snapshot_id}/imgalready serves images by primary key — the journey viewer can reuse it directly.event_countis derived from per-camera events.config.pyhas[server]/[[camera]]/[web]sections; no[events]section yet.Spec (approved):
.superpowers/specs/2026-09-05-cross-camera-journeys-design.mdPlan of attack
events.py: add aSnapshotstructural protocol (typing only; also re-annotate the two existing functions) andgroup_events_into_journeys— greedy chronological chaining of per-camera events with an inclusive per-hop join window, attaching to the most-recently-ended eligible journey. Plus small journey helpers (journey_start,journey_end,journey_anchor_id). TDD.config.py: new[events]section withjourney_gap_seconds(default 180) —EventsConfig+_parse_events+AppConfig.events.web/routes/days.py+day.html: derive journeys in the day view; single-segment journeys render as today's tiles, multi-segment ones as journey cards with ordered segment chips linking to the viewer; calendar counts journeys.web/routes/journeys.py+journey.html: stitched viewer at/days/{day}/journeys/{anchor_id}(anchor = first snapshot id of the journey's first segment), 404 on unknown/purged anchors, images via the existing by-id route.docs/config.md,docs/web-ui.md, README bullet, Release-Notes entry.Everything derives at request time in cangjian; cangcang and the DB schema stay untouched.
Follow-up from prod-data testing (branch
feat/issue-146-cross-camera-journeys, PR #147): the separate journey viewer diverged from the event viewer — different image sizing, plain-text prev/next links instead of the scrubber controls, no play button.Decision: there is now ONE viewer. Journeys become a filter mode of the snapshot scrubber (
GET /days/{day}/snapshots?journey=<anchor_id>); the separate/days/{day}/journeys/{anchor_id}route andjourney.htmlare deleted. Parity with the event viewer is guaranteed by construction — same template, same JS (play/pause, speed, filmstrip, slider, keyboard), camera selector hidden in journey mode, per-frame camera label in the header (back-drive · 10:04:47 · 3 / 12), blue camera-change ticks on the scrub track alongside the orange motion markers.Spec updated:
.superpowers/specs/2026-09-05-cross-camera-journeys-design.md(Unified journey mode in the scrubber).Closed via PR #147 (merged into
main).