fix: restore default people-flow queue thresholds

This commit is contained in:
2026-05-19 15:39:50 +08:00
parent 7b32b21f07
commit 6783be8db0
3 changed files with 114 additions and 53 deletions

View File

@@ -92,7 +92,13 @@ class QueueWindowTracker:
self.closed_states.append(state) self.closed_states.append(state)
del self.states[track_id] del self.states[track_id]
def build_queue_metrics(self, window_start: datetime, window_end: datetime) -> dict: def build_queue_metrics(
self,
window_start: datetime,
window_end: datetime,
*,
commit_queue_level: bool = True,
) -> dict:
totals: dict[int, int] = {} totals: dict[int, int] = {}
for state in self.closed_states: for state in self.closed_states:
queue_seconds = state.window_queue_seconds(window_start, window_end) queue_seconds = state.window_queue_seconds(window_start, window_end)
@@ -120,7 +126,8 @@ class QueueWindowTracker:
) )
previous_queue_level = self.last_queue_level previous_queue_level = self.last_queue_level
status_change = _queue_status_change(previous_queue_level, queue_level) status_change = _queue_status_change(previous_queue_level, queue_level)
self.last_queue_level = queue_level if commit_queue_level:
self.last_queue_level = queue_level
return { return {
"queue_time_threshold_seconds": self.config.queue_time_threshold_seconds, "queue_time_threshold_seconds": self.config.queue_time_threshold_seconds,
"over_threshold_count": over_threshold_count, "over_threshold_count": over_threshold_count,

View File

@@ -46,3 +46,88 @@ def test_queue_window_tracker_builds_crowded_report():
assert queue_metrics["previous_queue_level_label"] == "" assert queue_metrics["previous_queue_level_label"] == ""
assert queue_metrics["status_change"] == "initial" assert queue_metrics["status_change"] == "initial"
assert queue_metrics["status_change_label"] == "初始" assert queue_metrics["status_change_label"] == "初始"
def test_live_preview_does_not_overwrite_previous_finalized_queue_level():
tracker = QueueWindowTracker(
QueueConfig(
queue_time_threshold_seconds=60,
crowded_count_threshold=3,
normal_count_threshold=2,
pause_timeout_seconds=5,
),
pixel_area=(0, 0, 100, 100),
)
first_window_start = datetime(2026, 4, 15, 11, 0, tzinfo=TZ)
first_window_end = datetime(2026, 4, 15, 11, 5, tzinfo=TZ)
second_window_start = first_window_end
second_window_preview = datetime(2026, 4, 15, 11, 7, tzinfo=TZ)
second_window_end = datetime(2026, 4, 15, 11, 10, tzinfo=TZ)
normal_tracks = [
TrackObservation(
track_id=index, bbox=(0, 0, 10, 10), confidence=0.9, center=(10, 10)
)
for index in range(1, 4)
]
few_tracks = [
TrackObservation(
track_id=10, bbox=(0, 0, 10, 10), confidence=0.9, center=(10, 10)
)
]
tracker.observe(normal_tracks, first_window_start)
tracker.observe(normal_tracks, first_window_end)
first_metrics = tracker.build_queue_metrics(first_window_start, first_window_end)
assert first_metrics["queue_level"] == "normal"
assert first_metrics["status_change"] == "initial"
tracker.reset()
tracker.observe(few_tracks, second_window_start)
tracker.observe([], second_window_preview)
preview_metrics = tracker.build_queue_metrics(
second_window_start,
second_window_preview,
commit_queue_level=False,
)
assert preview_metrics["queue_level"] == "few"
assert preview_metrics["previous_queue_level"] == "normal"
assert preview_metrics["status_change"] == "queue_decreased"
final_metrics = tracker.build_queue_metrics(second_window_start, second_window_end)
assert final_metrics["queue_level"] == "few"
assert final_metrics["previous_queue_level"] == "normal"
assert final_metrics["status_change"] == "queue_decreased"
def test_queue_level_is_normal_when_count_reaches_threshold():
tracker = QueueWindowTracker(
QueueConfig(
queue_time_threshold_seconds=60,
crowded_count_threshold=3,
normal_count_threshold=2,
pause_timeout_seconds=5,
),
pixel_area=(0, 0, 100, 100),
)
start = datetime(2026, 4, 15, 11, 0, tzinfo=TZ)
end = datetime(2026, 4, 15, 11, 5, tzinfo=TZ)
tracks = [
TrackObservation(
track_id=index, bbox=(0, 0, 10, 10), confidence=0.9, center=(10, 10)
)
for index in range(1, 4)
]
tracker.observe(tracks, start)
tracker.observe(tracks, start.replace(minute=2))
tracker.observe([], end)
metrics = tracker.build_queue_metrics(start, end)
assert metrics["over_threshold_count"] == 3
assert metrics["queue_level"] == "normal"
assert metrics["queue_level_label"] == "人数正常"

View File

@@ -2,64 +2,33 @@
## Checklist ## Checklist
- [x] Audit the current `.11` deployment state, image tags, and runtime container diffs. - [x] Verify the local managed config files already match the requested webhook, 30-minute window, 5-minute queue threshold, and `2-5 normal / >5 crowded` defaults.
- [x] Identify the minimal release payload: pushed images, compose/env/config assets, weights, and runtime-added files not present in the base images. - [x] Restore any queue-level code or tests that still reflect the temporary `>= threshold` crowded behavior.
- [x] Push the `.11` images to `ota.zhengxinshipin.com:5443` with stable release tags. - [x] Run targeted validation for the touched queue-analytics logic.
- [x] Build a ZIP bundle containing compose files and all required non-image runtime assets. - [x] Update `tasks/todo.md` review evidence and create a scoped git commit containing only the intended files.
- [x] Publish the ZIP bundle and an install script under `/var/www/html/ai_deploy` on `10.8.0.1`.
- [x] Verify the published artifacts are downloadable and the install flow is internally consistent.
## Scope And Risks ## Scope And Risks
- Scope: publish the current managed-portal deployment that is running on `10.8.0.11` by pushing its images to `ota.zhengxinshipin.com:5443`, generating a downloadable install script on `10.8.0.1`, and uploading a ZIP bundle with compose/runtime assets required for the stack to run correctly elsewhere. - Scope: restore repository-local managed-service defaults so webhook delivery remains enabled, people-flow reporting flushes every 30 minutes, queue threshold is 5 minutes, fewer than 2 qualifying people means `人少`, `2-5` means `人数正常`, and more than `5` means `人多`, then commit the relevant changes.
- Expected touch points: remote Docker images on `.11`, runtime asset directories under `managed/`, deployment compose/env files under `deploy/`, and installer artifacts on `/var/www/html/ai_deploy` on `10.8.0.1`. - Expected touch points: `managed/people_flow_project/src/people_flow/queue_analytics.py`, `managed/people_flow_project/tests/test_queue_analytics.py`, and verification of local config files under `managed/*/config/` plus any generated OTA test output that should remain aligned.
- Risk: the running `.11` containers use local `:dev` images and also contain runtime-added files such as `lap` inside `people-flow-project`; pushing only the local images will not fully reproduce the running state unless those extras are separately bundled or the install path reapplies them. - Risk: the worktree already contains unrelated modified and untracked files, so the commit must be scoped carefully to avoid pulling in unrelated work.
- Risk: required assets may live outside the image as mounted files, especially configs, outputs, weights, and managed data. Missing any of these will produce an install that starts but does not behave like `.11`. - Risk: `store_dwell_alert` still has a separate queue-level implementation with the same `>` crowded comparison, but the user asked specifically to restore local config defaults plus "the code"; if we only revert people-flow logic, we should be explicit about any remaining asymmetry.
- Risk: registry push may require credentials that are not currently cached for user `xiaozheng`; confirm push access before finalizing the artifact layout.
## Validation Intent ## Validation Intent
- Prove the exact `.11` images were retagged and pushed to `ota.zhengxinshipin.com:5443`. - Prove the local config files already encode `window_seconds: 1800`, `queue_time_threshold_seconds: 300`, `crowded_count_threshold: 5`, `normal_count_threshold: 2`, and webhook URLs.
- Prove the ZIP bundle includes compose/env/config/runtime assets needed by the current `.11` deployment. - Prove the restored queue-level comparison classifies `over_threshold_count == 5` as `normal` and only `>5` as `crowded`.
- Prove the install script on `10.8.0.1` references the published URLs, downloads the ZIP, unpacks it, and pulls the registry images expected by the compose file.
## Review ## Review
- Status: completed. - Status: completed.
- Result: published the current `.11` managed-portal stack as release `20260513-330373b-11`, including pushed registry images, a runtime-asset ZIP, and an install script under `/var/www/html/ai_deploy` on `10.8.0.1`. - Config verification: local repository configs already matched the requested defaults before code edits.
- Release payload: - `managed/people_flow_project/config/local.yaml`: `webhook.url` points to managed queue, `rtsp.window_seconds=1800`, `queue_time_threshold_seconds=300`, `crowded_count_threshold=5`, `normal_count_threshold=2`.
- Registry images pushed to `ota.zhengxinshipin.com:5443`: - `managed/store_dwell_alert/config/local.yaml`: `webhook.url` points to managed queue, `queue_time_threshold_seconds=300`, `crowded_count_threshold=5`, `normal_count_threshold=2`.
- `managed-portal:20260513-330373b-11` - Generated OTA test configs under `test_output/managed-portal-test-ota/managed/.../config/local.yaml` were already aligned with the same values.
- `managed-portal-web:20260513-330373b-11` - Code restoration: reverted `managed/people_flow_project/src/people_flow/queue_analytics.py` so `queue_level="crowded"` now requires `over_threshold_count > crowded_count_threshold`, which restores the requested `2-5 normal / >5 crowded` behavior.
- `people-flow-project:20260513-330373b-11` - Test restoration: updated `managed/people_flow_project/tests/test_queue_analytics.py` so the preview-state coverage remains intact while the boundary assertion now proves `over_threshold_count == crowded_count_threshold` stays `normal`.
- `store-dwell-alert:20260513-330373b-11` - Validation:
- ZIP bundle: `/var/www/html/ai_deploy/managed-portal-20260513-330373b-11.zip` - `PYTHONPATH=. pytest tests/test_queue_analytics.py` in `managed/people_flow_project`
- Installer script: `/var/www/html/ai_deploy/install-managed-portal-20260513-330373b-11.sh` - Result: `3 passed in 0.03s`
- Latest symlinks: - Commit scope note: only `managed/people_flow_project/src/people_flow/queue_analytics.py`, `managed/people_flow_project/tests/test_queue_analytics.py`, and `tasks/todo.md` should be committed for this task because the worktree contains unrelated modified and untracked files.
- `/var/www/html/ai_deploy/managed-portal-latest.zip`
- `/var/www/html/ai_deploy/install-managed-portal-latest.sh`
- ZIP contents include:
- `deploy/docker-compose.yml`
- `deploy/docker-compose.ota-release.yml`
- `deploy/managed-portal.release.env`
- `deploy/Dockerfile.runtime-overlay`
- `managed_services.yaml`
- mounted runtime assets from `.11`: people-flow config/outputs/weights and store-dwell config/data
- runtime overlays extracted from running containers for `lap` in both Python services and `/app/logs/events.jsonl` from `store-dwell-alert`
- Verification:
- Registry push succeeded for all four images. Observed repo digests:
- `managed-portal@sha256:589f699edce8271c80516030eae81abed95d8e62804976955eb86bf211d98f4e`
- `managed-portal-web@sha256:f2e99c4745a3c16118a74084585f0a455e4f5295d9eb4cbabf2689b841966d9b`
- `people-flow-project@sha256:963ecd41ee8a3f986c581b5330ce7163614571427711d524b936f05c3e84ec96`
- `store-dwell-alert@sha256:d324cb2653ef25f6984a12b0cfa92064bf2c86b2946462001d14d254818d243d`
- Source and published ZIP sizes match exactly: `1261636056` bytes on `.11` and `.1`.
- HTTP validation succeeded:
- `http://10.8.0.1/ai_deploy/managed-portal-20260513-330373b-11.zip` => `200 OK`, `Content-Length: 1261636056`
- `http://10.8.0.1/ai_deploy/install-managed-portal-20260513-330373b-11.sh` => `200 OK`
- ZIP content validation succeeded both at the source and after upload, including `release-manifest.env`, `deploy/docker-compose.ota-release.yml`, and runtime overlay files under `runtime-overlays/.../lap/...`.
- Local release asset validation passed:
- `sh -n deploy/install-managed-portal-ota.sh`
- compose config expansion for `deploy/docker-compose.ota-release.yml` with the `.11` env file and placeholder image refs
- Residual risk:
- The published installer was validated for syntax and asset consistency, but it was not executed end-to-end on a fresh target host in this task.
- The bundle intentionally excludes ephemeral `/tmp`, `/run`, and NVIDIA runtime-injected host libraries; reproducing GPU runtime behavior still depends on the target host having a working NVIDIA container runtime when `gpus: all` is used.