Add support for filtering history page and add support for creating timeline entries for audio / custom events (#9034)

* Add filter popover

* Add api filter hook and use UI with filtering

* Get history filtering working for cameras and labels

* Allow filtering on detail level

* Save timeline entries for api events

* reset

* fix width
This commit is contained in:
Nicolas Mowen
2023-12-21 05:52:54 -07:00
committed by Blake Blackshear
parent feb3ee0703
commit a1e5c658d5
10 changed files with 519 additions and 49 deletions

View File

@@ -52,7 +52,7 @@ class ExternalEventProcessor:
(
EventTypeEnum.api,
"new",
camera_config,
camera,
{
"id": event_id,
"label": label,

View File

@@ -109,6 +109,16 @@ class EventProcessor(threading.Thread):
self.handle_object_detection(event_type, camera, event_data)
elif source_type == EventTypeEnum.api:
self.timeline_queue.put(
(
camera,
source_type,
event_type,
{},
event_data,
)
)
self.handle_external_detection(event_type, event_data)
# set an end_time on events without an end_time before exiting

View File

@@ -614,20 +614,30 @@ def timeline():
@bp.route("/timeline/hourly")
def hourly_timeline():
"""Get hourly summary for timeline."""
camera = request.args.get("camera", "all")
cameras = request.args.get("cameras", "all")
labels = request.args.get("labels", "all")
before = request.args.get("before", type=float)
after = request.args.get("after", type=float)
limit = request.args.get("limit", 200)
tz_name = request.args.get("timezone", default="utc", type=str)
_, minute_modifier, _ = get_tz_modifiers(tz_name)
clauses = []
if camera != "all":
clauses.append((Timeline.camera == camera))
if cameras != "all":
camera_list = cameras.split(",")
clauses.append((Timeline.camera << camera_list))
if labels != "all":
label_list = labels.split(",")
clauses.append((Timeline.data["label"] << label_list))
if before:
clauses.append((Timeline.timestamp < before))
if after:
clauses.append((Timeline.timestamp > after))
if len(clauses) == 0:
clauses.append((True))

View File

@@ -48,6 +48,8 @@ class TimelineProcessor(threading.Thread):
self.handle_object_detection(
camera, event_type, prev_event_data, event_data
)
elif input_type == EventTypeEnum.api:
self.handle_api_entry(camera, event_type, event_data)
def insert_or_save(
self,
@@ -140,3 +142,40 @@ class TimelineProcessor(threading.Thread):
if save:
self.insert_or_save(timeline_entry, prev_event_data, event_data)
def handle_api_entry(
self,
camera: str,
event_type: str,
event_data: dict[any, any],
) -> bool:
if event_type != "new":
return False
if event_data.get("type", "api") == "audio":
timeline_entry = {
Timeline.class_type: "heard",
Timeline.timestamp: event_data["start_time"],
Timeline.camera: camera,
Timeline.source: "audio",
Timeline.source_id: event_data["id"],
Timeline.data: {
"label": event_data["label"],
"sub_label": event_data.get("sub_label"),
},
}
else:
timeline_entry = {
Timeline.class_type: "external",
Timeline.timestamp: event_data["start_time"],
Timeline.camera: camera,
Timeline.source: "api",
Timeline.source_id: event_data["id"],
Timeline.data: {
"label": event_data["label"],
"sub_label": event_data.get("sub_label"),
},
}
Timeline.insert(timeline_entry).execute()
return True