Make events summary endpoint DST-aware (#20786)
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions

This commit is contained in:
Josh Hawkins 2025-11-03 18:54:33 -06:00 committed by GitHub
parent 84409eab7e
commit 256817d5c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -2,6 +2,7 @@
import base64 import base64
import datetime import datetime
import json
import logging import logging
import os import os
import random import random
@ -58,7 +59,7 @@ from frigate.embeddings import EmbeddingsContext
from frigate.models import Event, ReviewSegment, Timeline, Trigger from frigate.models import Event, ReviewSegment, Timeline, Trigger
from frigate.track.object_processing import TrackedObject from frigate.track.object_processing import TrackedObject
from frigate.util.path import get_event_thumbnail_bytes from frigate.util.path import get_event_thumbnail_bytes
from frigate.util.time import get_tz_modifiers from frigate.util.time import get_dst_transitions, get_tz_modifiers
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -813,7 +814,6 @@ def events_summary(
allowed_cameras: List[str] = Depends(get_allowed_cameras_for_filter), allowed_cameras: List[str] = Depends(get_allowed_cameras_for_filter),
): ):
tz_name = params.timezone tz_name = params.timezone
hour_modifier, minute_modifier, seconds_offset = get_tz_modifiers(tz_name)
has_clip = params.has_clip has_clip = params.has_clip
has_snapshot = params.has_snapshot has_snapshot = params.has_snapshot
@ -828,7 +828,33 @@ def events_summary(
if len(clauses) == 0: if len(clauses) == 0:
clauses.append((True)) clauses.append((True))
groups = ( time_range_query = (
Event.select(
fn.MIN(Event.start_time).alias("min_time"),
fn.MAX(Event.start_time).alias("max_time"),
)
.where(reduce(operator.and_, clauses) & (Event.camera << allowed_cameras))
.dicts()
.get()
)
min_time = time_range_query.get("min_time")
max_time = time_range_query.get("max_time")
if min_time is None or max_time is None:
return JSONResponse(content=[])
dst_periods = get_dst_transitions(tz_name, min_time, max_time)
grouped: dict[tuple, dict] = {}
for period_start, period_end, period_offset in dst_periods:
hours_offset = int(period_offset / 60 / 60)
minutes_offset = int(period_offset / 60 - hours_offset * 60)
period_hour_modifier = f"{hours_offset} hour"
period_minute_modifier = f"{minutes_offset} minute"
period_groups = (
Event.select( Event.select(
Event.camera, Event.camera,
Event.label, Event.label,
@ -837,24 +863,56 @@ def events_summary(
fn.strftime( fn.strftime(
"%Y-%m-%d", "%Y-%m-%d",
fn.datetime( fn.datetime(
Event.start_time, "unixepoch", hour_modifier, minute_modifier Event.start_time,
"unixepoch",
period_hour_modifier,
period_minute_modifier,
), ),
).alias("day"), ).alias("day"),
Event.zones, Event.zones,
fn.COUNT(Event.id).alias("count"), fn.COUNT(Event.id).alias("count"),
) )
.where(reduce(operator.and_, clauses) & (Event.camera << allowed_cameras)) .where(
reduce(operator.and_, clauses)
& (Event.camera << allowed_cameras)
& (Event.start_time >= period_start)
& (Event.start_time <= period_end)
)
.group_by( .group_by(
Event.camera, Event.camera,
Event.label, Event.label,
Event.sub_label, Event.sub_label,
Event.data, Event.data,
(Event.start_time + seconds_offset).cast("int") / (3600 * 24), (Event.start_time + period_offset).cast("int") / (3600 * 24),
Event.zones, Event.zones,
) )
.namedtuples()
) )
return JSONResponse(content=[e for e in groups.dicts()]) for g in period_groups:
key = (
g.camera,
g.label,
g.sub_label,
json.dumps(g.data, sort_keys=True) if g.data is not None else None,
g.day,
json.dumps(g.zones, sort_keys=True) if g.zones is not None else None,
)
if key in grouped:
grouped[key]["count"] += int(g.count or 0)
else:
grouped[key] = {
"camera": g.camera,
"label": g.label,
"sub_label": g.sub_label,
"data": g.data,
"day": g.day,
"zones": g.zones,
"count": int(g.count or 0),
}
return JSONResponse(content=list(grouped.values()))
@router.get( @router.get(