Repository navigation
Conversation
Add GET /alerts/all/fromdate/count-per-day so that a month calendar gets
its daily alert counts in one request instead of one per day.
- from_date / to_date (inclusive), at most 62 days: a month calendar shows
at most 6 weeks, and this bounds the per-day risk lookups.
- tz: IANA timezone used to split the days, UTC by default to match
/alerts/all/fromdate.
- Same alerts as /alerts/all/fromdate/count: organization scoping, and the
risk filter of each day (fetched concurrently; days sharing the same
scores are counted by a single query), or the risk_score override.
- Returns [{"date": ..., "count": ...}] for the days with alerts only.
Closes pyronear#719
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #724 +/- ##
==========================================
+ Coverage 94.87% 94.96% +0.08%
==========================================
Files 60 60
Lines 3278 3334 +56
==========================================
+ Hits 3110 3166 +56
Misses 168 168
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
MateoLostanlen
left a comment
There was a problem hiding this comment.
Thanks, the approach matches what we agreed in #719. A few things before merging:
Blocking
-
The 62-day cap is checked after the day list is built.
daysis built for the whole range first, sofrom_date=0001-01-01&to_date=9999-12-31creates ~3.65M dates before returning 422. Could you check(to_date - from_date).days + 1 > MAX_COUNT_PER_DAY_PERIODbefore building the list? -
No index-friendly filter on
started_at. The queries only filter ondate(timezone(tz, timezone('UTC', started_at))) IN (...), so each one scans all of the organization's alerts (all alerts for admins). FWI classes change most days, so grouping by identical scores rarely helps: a month can mean ~30 sequential queries, each a full scan. AddingAlert.started_at >= start AND < end(the period bounds converted fromtzto UTC in Python) would fix it. -
Up to 62 concurrent risk API calls. Each call opens a new
httpx.AsyncClient, and a failure or timeout returns{}, so that day is counted with no risk filter and no error. The calendar can then disagree with/all/fromdate/countfor that day. A semaphore and one shared client (as in #708) would make this safer.
Non-blocking
tzis checked with Python'szoneinfo, but the conversion runs in Postgres, which uses its own timezone database. A name Python accepts but Postgres doesn't would give a 500 instead of a 422.- Risk scores are also fetched for days after today. For the current month's calendar that can be ~30 wasted calls; we could clip the period to today in
tz. - The sequence filter (
AlertSequencejoin +max_conf_filter_clause) is copied from_build_alerts_from_date_stmt. Since the goal is the same alerts as/all/fromdate/count, a small shared helper would stop the two from drifting apart. - Telemetry is sent before validation, so rejected requests are counted too.
raise HTTPException(...)in theexceptshould befrom None(ruff B904,"B"is enabled).- Nit:
_user_authin the tests does what the existing tests do inline withpytest.get_token(...). Could we use one style or the other in this file?
…er-day # Conflicts: # src/tests/endpoints/test_alerts.py
- Check the period length on the dates, before listing the days. - Filter started_at on UTC ranges: day boundaries are computed in Python with zoneinfo and days are bucketed with a CASE, so Postgres no longer converts timezones (index-friendly, consistent tz validation). - Clip the period to today in tz: no risk lookup for future days. - RiskService: shared httpx client closed in the app lifespan, and a semaphore bounding concurrent calls. - Share the sequence filter of /all/fromdate in _filter_alerts_by_sequences. - Send telemetry after validation, chain the tz exception with from None, inline get_token in tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What
Adds
GET /alerts/all/fromdate/count-per-dayso that the History calendar (pyronear/new-pyro-platform#308) gets its daily alert counts in one request instead of one per day.How (as agreed in #719)
from_date/to_date(inclusive), at most 62 days: a month calendar shows at most 6 weeks, and this bounds the per-day risk lookups. Inverted or longer periods → 422.tz: IANA timezone used to split the days,UTCby default to match/alerts/all/fromdate. Unknown timezone → 422. (tzon/all/fromdateis left out of scope, as discussed.)/alerts/all/fromdate/count: organization scoping (admins see all), and the risk filter of each day, or therisk_scoreoverride. The per-day risk scores are fetched concurrently, and days sharing the same scores are counted by a single grouped query (a single query when the risk API is not configured).[{"date": "2026-10-23", "count": 2}, …], only days with alerts.Tests
/all/fromdate/count, and atzcase where 23:30 UTC falls on the next day in Paris.risk_scoreoverride skipping the risk API.Full suite, ruff and ty pass locally. Also tried in pyro-envdev against the demo alerts.
Note
The response field is
date, consistent withfrom_date/to_date; in the schema it is typeddt.datesince a field nameddatewould otherwise shadow the type for Pydantic.Closes #719
Generated with Claude Code