test(occupancy): characterization tests and trace for single silent camera outage (#199) #248
No reviewers
Labels
No labels
blocked
bug
enhancement
high-priority
low-priority
needs-info
needs-triage
ready-for-agent
ready-for-human
referenced
research
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gabogg/hikcentral!248
Loading…
Reference in a new issue
No description provided.
Delete branch "test/silent-camera-trace-199"
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?
Summary
Implements Issue #199 (Phase A): documents and characterizes current system behaviour when exactly one counting camera goes silent during business hours while others report traffic, and contrasts it with a legitimately quiet camera. Changes no application runtime behaviour, preserving baseline compatibility for Phase B policy decisions under #236.
Findings per Surface (Current Behaviour & File:Line)
Passenger-flow Polling & Ingestion (
app/services/occupancy_service.py:2235-2505):enterNum,exitNum). The delta calculation yieldsdelta <= 0, so no counting events are generated for the silent camera. Because other cameras report flow, the poll succeeds with{"success": True}.Ingestion-Gap Detection (
app/services/occupancy_service.py:2132-2148, 2415-2420):_passenger_flow_poll_failed_asyncis only invoked when the entire Artemis HTTP request fails or returns zero groups. The per-group gap checknow - gap_start >= GAP_THRESHOLD_SECONDSevaluates againstprevious.observed_at, which updates every 1–2 seconds.ingestion_anomalies).Cycle Completeness & Data Trust (
app/services/occupancy_service.py:1040-1113):evaluate_cycle_integrity_asyncassesses facility-wide totals. Active hours (\ge 10) and footfall (\ge 5000) checks pass from reporting cameras. No data quality flags fire.is_trusted=True,data_trust_score=100,cycle_completeness_score=100).Cycle Verdict (
app/db/occupancy_repository.py:95-128,app/services/occupancy_service.py:1500-1530):is_trusted=1andtrust_status='TRUSTED'. The ranked verdict priority treats the day as trusted.Data-Quality Marker (
app/services/analytics_service.py:218-230, 871-908):day_quality_stateevaluateshas_data=True,excluded=False,trusted=True,gap_estimated=False, returning"OK". Period quality reportsestimate_days=0,unreliable_days=0.Statistics Comparison & Weekday Baseline (
app/services/analytics_service.py:292-330, 348-395):basis="TOTAL"with depressed visitor counts.get_usual_weekday_baseline_dates_asyncselects the candidate day as a trusted baseline sample because it has data, no gaps, and trusted verdict.Calibration Learning (
app/db/occupancy_repository.py:2253-2276,app/services/occupancy_service.py:1880-1920):get_trusted_calibration_history_asyncselects the cycle becauseis_trusted=1andFLAG_INGESTION_GAPis absent. The distorted I/O ratio enters the EWMA for exit multiplierk.Multi-Camera Group Attributon (
app/services/occupancy_service.py:2438-2442):divmod(span_count, len(cams))). If Camera 1 is silent and Camera 2 records 20 passages, each receives 10 passages.Camera Weights & Outage Thresholds:
app/config.py, database schemas, or service constants.Legitimately Quiet Camera Contrast:
Deliverables
docs/research/199-single-camera-outage-behaviour.mdcovering Q1 (offline API analysis and open questions for #236), Q2 (state and stage-by-stage trace), Q3 (weights/thresholds), quiet-camera contrast, and multi-camera groups.docs/research/README.mdlinked fromdocs/README.md.tests/test_silent_camera_trace.pypinning pre-#236 behaviour across all pipeline surfaces.Verification Evidence
node --test tests/frontend/*.test.js).rtk ruff check .andrtk ruff format --check .clean (0 errors, 151 files formatted).python3 scripts/check_docs.pyclean (44 Markdown files and 80 HTTP operations checked).Closes #199
WIP: test(occupancy): characterization tests and trace for single silent camera outage (#199)to test(occupancy): characterization tests and trace for single silent camera outage (#199)Requesting code review pass 1 for 016a012...1dd0db7, spec #199. Phase A investigation complete: findings documented in docs/research/199-single-camera-outage-behaviour.md, research index added, open questions for live testing posted to #236, and characterization tests pinning current behaviour across all surfaces added in tests/test_silent_camera_trace.py. Full pytest (537 passed), frontend node tests (242 passed), ruff, and check_docs pass cleanly.
Code review, pass 1 (
origin/master...1dd0db7, spec #199 phase A)Result: 6 P2s and 13 P3s. Not mergeable yet. This is pass 1, so fix every finding, then rebase or merge onto
origin/master(9a72ff4) first, since most file:line references and one traced signature changed with #178 and #177. Request a second pass after that.check_docs.pyare clean.docs/research/<issue>-<slug>.mdand index convention.Spec
P2
The "group missing from the response" mode is untested, and the write-up describes it wrongly. The spec asks for "one camera returns nothing (or zero)", and the doc says an omitted group "emits no events, and logs no errors".
gbwas left out ofcount_listfor 6 minutes, then returned.IngestionAnomaly(group_code='gb', kind='gap', delta_in=30)and spread the backlog over the interval (is_gap,occupancy_service.py~3478 on master).gap_estimated, so the day gets the ESTIMATE marker (analytics_service.py~241, ~896).Surfaces not traced at all:
apply_retroactive_audit_async, and the drift reconcile);"Published totals" are checked only as raw
people_counting_eventsrows. Assert a published aggregate.The quiet-camera test stops at
evaluate_cycle_integrity_async(test:362-368). The spec asks the quiet scenario to assert the marker, comparison eligibility and calibration inclusion too.Stages 3–6 of the silent test hold by construction.
cam-out-2is silent too) and passes hand-pickedtotal_in/out(test:192-215).TRUSTEDlog by hand (test:224-242) instead of running the nocturnal job, then callsday_quality_statewith literals (test:244).-25.0is arithmetic on the literals 6000 and 8000.Nothing links the silent interval to the verdict. Run the real nocturnal and verdict path over the day the poll ingested. A stage that can't be driven end to end should say so, rather than claim "pins end-to-end".
Every file:line reference is stale against master, in both the doc and the test docstrings.
evaluate_cycle_integrity_async1040 → ~2055,is_gap2415 → ~3478,get_trusted_calibration_history_async2253 → ~2991.get_trusted_calibration_history_async's signature also changed: it now takesreset_time/now_epochand filters on pendingCALIBRATION_REEVAL.P3
data: {"type": "object"}with no schema. The shape in the doc (total,pageNo,pageSize) is invented. "No fields indicate sensor health" really means "the parser reads onlyenterNum/exitNum; the docs specify nothing more". Say that.previous.observed_atupdates every 1–2 seconds" is true only while the group stays in the response.Standards
P2
tests/test_silent_camera_trace.py:44-56). The autouseisolated_camera_trace_dbrepeats conftest's opt-inisolated_repository_dbline for line. Usepytestmark = pytest.mark.usefixtures("isolated_repository_db"), astest_business_cycle_labels.py:10does, plus a small fixture for the reset time and manager setup.P3
_mock_artemis(:59-75) is a verbatim copy oftests/test_counting_integrity.py:59-75. Share it._register_camera(code, name, direction)helper.ScheduleCalendar,facility_now,PeriodQuality(:23, 26, 33). Ruff misses them only because F401 is ignored undertests/*.people_counting_events(:110-115, 277-280, 366-369). Use a repository read where one exists.#L2235-L2302, etc.). Same fix as P2-5.Co-Authored-Bytrailer.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.Merge
Merge the changes and update on Forgejo.Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.