Skip to content

feat(alerts): unmatch a sequence from an alert#582

Open
MateoLostanlen wants to merge 10 commits into
mainfrom
feat/alert-sequence-unmatch
Open

feat(alerts): unmatch a sequence from an alert#582
MateoLostanlen wants to merge 10 commits into
mainfrom
feat/alert-sequence-unmatch

Conversation

@MateoLostanlen
Copy link
Copy Markdown
Member

  • Add POST /alerts/{alert_id}/sequences/{sequence_id}/unmatch (admin + agent) that detaches a sequence from an alert and recomputes the alert state; if the sequence becomes orphaned a new alert is created for it. Reject when the alert has a single sequence.
  • Reject unmatch when the sequence's camera belongs to a different organization than the alert, to prevent cross-org alert creation.
  • Extract refresh_alert_state from endpoints/sequences.py into services/alerts.py so both endpoints share the helper.
  • Cover the new endpoint with tests for new-alert creation, no-op when the sequence is already linked elsewhere, single-sequence rejection, missing link, role and cross-org access denial.

Move the alert recomputation helper out of the sequences endpoint module so other endpoints can reuse it.
POST /alerts/{alert_id}/sequences/{sequence_id}/unmatch detaches a sequence from an alert and recomputes the alert state. If the sequence is no longer linked to any alert, a new alert is created for it. Restricted to admin and agent scopes; rejects unmatch when the alert has a single sequence.
Cover the new alert/sequence unmatch flow: new alert creation when the sequence becomes orphaned, no-op when the sequence is still linked elsewhere, single-sequence rejection, missing link, role and cross-org access denial.
Scope rejection from get_jwt returns 403, not 401. The label_sequence unit test now patches refresh_alert_state at its imported location.
Defensive guard so an authorized agent cannot trigger creation of an alert in another organization through a cross-org sequence link. Use the alert's organization_id when creating the replacement alert.
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 30, 2026

Codecov Report

❌ Patch coverage is 98.55072% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 91.89%. Comparing base (fe0b93c) to head (5250482).

Files with missing lines Patch % Lines
src/app/api/api_v1/endpoints/alerts.py 97.22% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #582      +/-   ##
==========================================
+ Coverage   90.09%   91.89%   +1.80%     
==========================================
  Files          55       53       -2     
  Lines        2503     2419      -84     
==========================================
- Hits         2255     2223      -32     
+ Misses        248      196      -52     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Wrap the new POST /alerts/{alert_id}/sequences/{sequence_id}/unmatch route.
Resolve conflicts in alerts.py and sequences.py imports + drop the
duplicated _refresh_alert_state from sequences.py (already extracted
to services/alerts.py). Update unmatch endpoint to pass detection_counts
into the new _serialize_alert signature and chain delete().where() to
satisfy mypy. Align unmatch tests with the 3-tuple return of
_create_alert_with_sequences.
Newer requests releases ship inline types that require headers to be a
MutableMapping[str, str | bytes], so Dict[str, str] no longer matches
through the invariant generic. Widen the property accordingly and
annotate the occlusion mask payload as Dict[str, Any] for the same
reason. Add a smoke test for unmatch_alert_sequence to keep the new
client wrapper covered.
SQLAlchemy/SQLModel async sessions schedule their actual I/O on a
separate greenlet via greenlet_spawn, so coverage.py stops recording
lines that execute after the first await on session.exec. This makes
fully-tested async endpoints look uncovered in codecov reports.

Adding concurrency = greenlet to both the root pyproject.toml and a
new src/.coveragerc (which docker-compose.dev.yml mounts into the
backend container) restores accurate line tracking. Locally the
unmatch_alert_sequence body coverage jumps from 0% to ~95% and
services/alerts.py from 62% to 93% without any test changes.
@github-actions github-actions Bot added the topic: build Related to build, installation & CI label May 14, 2026
@MateoLostanlen MateoLostanlen requested a review from fe51 May 14, 2026 06:51
…nmatch

# Conflicts:
#	client/pyroclient/client.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant