Convert first review API endpoints to FastAPI

This commit is contained in:
Rui Alves 2024-09-15 17:40:23 +01:00
parent 023f1431f2
commit 640dce6bc2
3 changed files with 40 additions and 32 deletions

View File

@ -7,3 +7,4 @@ class Tags(Enum):
logs = "Logs" logs = "Logs"
media = "Media" media = "Media"
notifications = "Notifications" notifications = "Notifications"
review = "Review"

View File

@ -3,7 +3,7 @@ import logging
from fastapi import FastAPI from fastapi import FastAPI
from frigate.api import app as main_app from frigate.api import app as main_app
from frigate.api import media, notification, preview from frigate.api import media, notification, preview, review
from frigate.plus import PlusApi from frigate.plus import PlusApi
from frigate.ptz.onvif import OnvifController from frigate.ptz.onvif import OnvifController
from frigate.stats.emitter import StatsEmitter from frigate.stats.emitter import StatsEmitter
@ -30,6 +30,7 @@ def create_fastapi_app(
app.include_router(media.router) app.include_router(media.router)
app.include_router(preview.router) app.include_router(preview.router)
app.include_router(notification.router) app.include_router(notification.router)
app.include_router(review.router)
# App Properties # App Properties
app.frigate_config = frigate_config app.frigate_config = frigate_config
app.detected_frames_processor = detected_frames_processor app.detected_frames_processor = detected_frames_processor

View File

@ -4,53 +4,59 @@ import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
from functools import reduce from functools import reduce
from pathlib import Path from pathlib import Path
from typing import Optional
import pandas as pd import pandas as pd
from fastapi import APIRouter, Request
from fastapi.params import Depends
from fastapi.responses import JSONResponse
from flask import Blueprint, jsonify, make_response, request from flask import Blueprint, jsonify, make_response, request
from peewee import Case, DoesNotExist, fn, operator from peewee import Case, DoesNotExist, fn, operator
from playhouse.shortcuts import model_to_dict from playhouse.shortcuts import model_to_dict
from pydantic import BaseModel
from frigate.api.defs.tags import Tags
from frigate.models import Recordings, ReviewSegment from frigate.models import Recordings, ReviewSegment
from frigate.util.builtin import get_tz_modifiers from frigate.util.builtin import get_tz_modifiers
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
ReviewBp = Blueprint("reviews", __name__) ReviewBp = Blueprint("reviews", __name__)
router = APIRouter(tags=[Tags.review])
@ReviewBp.route("/review") class ItemQueryParams(BaseModel):
def review(): cameras: Optional[str] = "all"
cameras = request.args.get("cameras", "all") labels: Optional[str] = "all"
labels = request.args.get("labels", "all") zones: Optional[str] = "all"
zones = request.args.get("zones", "all") reviewed: Optional[int] = 0
reviewed = request.args.get("reviewed", type=int, default=0) limit: Optional[int] = None
limit = request.args.get("limit", type=int, default=None) severity: Optional[int] = None
severity = request.args.get("severity", None) before: Optional[float] = datetime.now().timestamp()
after: Optional[float] = (datetime.now() - timedelta(hours=24)).timestamp()
before = request.args.get("before", type=float, default=datetime.now().timestamp())
after = request.args.get(
"after", type=float, default=(datetime.now() - timedelta(hours=24)).timestamp()
)
@router.get("/review")
def review(params: ItemQueryParams = Depends()):
clauses = [ clauses = [
( (
(ReviewSegment.start_time > after) (ReviewSegment.start_time > params.after)
& ( & (
(ReviewSegment.end_time.is_null(True)) (ReviewSegment.end_time.is_null(True))
| (ReviewSegment.end_time < before) | (ReviewSegment.end_time < params.before)
) )
) )
] ]
if cameras != "all": if params.cameras != "all":
camera_list = cameras.split(",") camera_list = params.cameras.split(",")
clauses.append((ReviewSegment.camera << camera_list)) clauses.append((ReviewSegment.camera << camera_list))
if labels != "all": if params.labels != "all":
# use matching so segments with multiple labels # use matching so segments with multiple labels
# still match on a search where any label matches # still match on a search where any label matches
label_clauses = [] label_clauses = []
filtered_labels = labels.split(",") filtered_labels = params.labels.split(",")
for label in filtered_labels: for label in filtered_labels:
label_clauses.append( label_clauses.append(
@ -61,11 +67,11 @@ def review():
label_clause = reduce(operator.or_, label_clauses) label_clause = reduce(operator.or_, label_clauses)
clauses.append((label_clause)) clauses.append((label_clause))
if zones != "all": if params.zones != "all":
# use matching so segments with multiple zones # use matching so segments with multiple zones
# still match on a search where any zone matches # still match on a search where any zone matches
zone_clauses = [] zone_clauses = []
filtered_zones = zones.split(",") filtered_zones = params.zones.split(",")
for zone in filtered_zones: for zone in filtered_zones:
zone_clauses.append( zone_clauses.append(
@ -75,41 +81,41 @@ def review():
zone_clause = reduce(operator.or_, zone_clauses) zone_clause = reduce(operator.or_, zone_clauses)
clauses.append((zone_clause)) clauses.append((zone_clause))
if reviewed == 0: if params.reviewed == 0:
clauses.append((ReviewSegment.has_been_reviewed == False)) clauses.append((ReviewSegment.has_been_reviewed == False))
if severity: if params.severity:
clauses.append((ReviewSegment.severity == severity)) clauses.append((ReviewSegment.severity == params.severity))
review = ( review = (
ReviewSegment.select() ReviewSegment.select()
.where(reduce(operator.and_, clauses)) .where(reduce(operator.and_, clauses))
.order_by(ReviewSegment.severity.asc()) .order_by(ReviewSegment.severity.asc())
.order_by(ReviewSegment.start_time.desc()) .order_by(ReviewSegment.start_time.desc())
.limit(limit) .limit(params.limit)
.dicts() .dicts()
.iterator() .iterator()
) )
return jsonify([r for r in review]) return JSONResponse(content=[r for r in review])
@ReviewBp.route("/review/event/<id>") @router.get("/review/event/{event_id}")
def get_review_from_event(id: str): def get_review_from_event(event_id: str):
try: try:
return model_to_dict( return model_to_dict(
ReviewSegment.get( ReviewSegment.get(
ReviewSegment.data["detections"].cast("text") % f'*"{id}"*' ReviewSegment.data["detections"].cast("text") % f'*"{event_id}"*'
) )
) )
except DoesNotExist: except DoesNotExist:
return "Review item not found", 404 return "Review item not found", 404
@ReviewBp.route("/review/<id>") @router.get("/review/{event_id}")
def get_review(id: str): def get_review(event_id: str):
try: try:
return model_to_dict(ReviewSegment.get(ReviewSegment.id == id)) return model_to_dict(ReviewSegment.get(ReviewSegment.id == event_id))
except DoesNotExist: except DoesNotExist:
return "Review item not found", 404 return "Review item not found", 404