From fa81d87dc0f63d36aca9f42031066271fa7fad60 Mon Sep 17 00:00:00 2001 From: Rui Alves Date: Wed, 23 Oct 2024 14:35:49 +0100 Subject: [PATCH] Updated Documentation for the Review endpoints (#14401) * Updated documentation for the review endpoint * Updated documentation for the review/summary endpoint * Updated documentation for the review/summary endpoint * Documentation for the review activity audio and motion endpoints * Added responses for more review.py endpoints * Added responses for more review.py endpoints * Fixed review.py responses and proper path parameter names * Added body model for /reviews/viewed and /reviews/delete * Updated OpenAPI specification for the review controller endpoints * Run ruff format frigate * Drop significant_motion * Updated frigate-api.yaml * Deleted total_motion * Combine 2 models into generic --- docs/static/frigate-api.yaml | 428 +++++++++++++------- frigate/api/defs/generic_response.py | 6 + frigate/api/defs/review_body.py | 6 + frigate/api/defs/review_query_parameters.py | 37 +- frigate/api/defs/review_responses.py | 43 ++ frigate/api/fastapi_app.py | 4 + frigate/api/review.py | 196 ++------- frigate/models.py | 2 +- web/src/pages/Events.tsx | 4 - web/src/types/review.ts | 2 - web/src/views/events/EventView.tsx | 2 - 11 files changed, 404 insertions(+), 326 deletions(-) create mode 100644 frigate/api/defs/generic_response.py create mode 100644 frigate/api/defs/review_body.py create mode 100644 frigate/api/defs/review_responses.py diff --git a/docs/static/frigate-api.yaml b/docs/static/frigate-api.yaml index 1833aab99..9a6364e27 100644 --- a/docs/static/frigate-api.yaml +++ b/docs/static/frigate-api.yaml @@ -172,76 +172,65 @@ paths: in: query required: false schema: - anyOf: - - type: string - - type: 'null' + type: string default: all title: Cameras - name: labels in: query required: false schema: - anyOf: - - type: string - - type: 'null' + type: string default: all title: Labels - name: zones in: query required: false schema: - anyOf: - - type: string - - type: 'null' + type: string default: all title: Zones - name: reviewed in: query required: false schema: - anyOf: - - type: integer - - type: 'null' + type: integer default: 0 title: Reviewed - name: limit in: query required: false schema: - anyOf: - - type: integer - - type: 'null' + type: integer title: Limit - name: severity in: query required: false schema: - anyOf: - - type: string - - type: 'null' + allOf: + - $ref: '#/components/schemas/SeverityEnum' title: Severity - name: before in: query required: false schema: - anyOf: - - type: number - - type: 'null' + type: number title: Before - name: after in: query required: false schema: - anyOf: - - type: number - - type: 'null' + type: number title: After responses: '200': description: Successful Response content: application/json: - schema: { } + schema: + type: array + items: + $ref: '#/components/schemas/ReviewSegmentResponse' + title: Response Review Review Get '422': description: Validation Error content: @@ -259,36 +248,28 @@ paths: in: query required: false schema: - anyOf: - - type: string - - type: 'null' + type: string default: all title: Cameras - name: labels in: query required: false schema: - anyOf: - - type: string - - type: 'null' + type: string default: all title: Labels - name: zones in: query required: false schema: - anyOf: - - type: string - - type: 'null' + type: string default: all title: Zones - name: timezone in: query required: false schema: - anyOf: - - type: string - - type: 'null' + type: string default: utc title: Timezone responses: @@ -296,7 +277,8 @@ paths: description: Successful Response content: application/json: - schema: { } + schema: + $ref: '#/components/schemas/ReviewSummaryResponse' '422': description: Validation Error content: @@ -310,17 +292,18 @@ paths: summary: Set Multiple Reviewed operationId: set_multiple_reviewed_reviews_viewed_post requestBody: + required: true content: application/json: schema: - type: object - title: Body + $ref: '#/components/schemas/ReviewSetMultipleReviewedBody' responses: '200': description: Successful Response content: application/json: - schema: { } + schema: + $ref: '#/components/schemas/GenericResponse' '422': description: Validation Error content: @@ -334,17 +317,18 @@ paths: summary: Delete Reviews operationId: delete_reviews_reviews_delete_post requestBody: + required: true content: application/json: schema: - type: object - title: Body + $ref: '#/components/schemas/ReviewDeleteMultipleReviewsBody' responses: '200': description: Successful Response content: application/json: - schema: { } + schema: + $ref: '#/components/schemas/GenericResponse' '422': description: Validation Error content: @@ -363,96 +347,38 @@ paths: in: query required: false schema: - anyOf: - - type: string - - type: 'null' + type: string default: all title: Cameras - name: before in: query required: false schema: - anyOf: - - type: number - - type: 'null' + type: number title: Before - name: after in: query required: false schema: - anyOf: - - type: number - - type: 'null' + type: number title: After - name: scale in: query required: false schema: - anyOf: - - type: integer - - type: 'null' + type: integer default: 30 title: Scale responses: '200': description: Successful Response - content: - application/json: - schema: { } - '422': - description: Validation Error content: application/json: schema: - $ref: '#/components/schemas/HTTPValidationError' - /review/activity/audio: - get: - tags: - - Review - summary: Audio Activity - description: Get motion and audio activity. - operationId: audio_activity_review_activity_audio_get - parameters: - - name: cameras - in: query - required: false - schema: - anyOf: - - type: string - - type: 'null' - default: all - title: Cameras - - name: before - in: query - required: false - schema: - anyOf: - - type: number - - type: 'null' - title: Before - - name: after - in: query - required: false - schema: - anyOf: - - type: number - - type: 'null' - title: After - - name: scale - in: query - required: false - schema: - anyOf: - - type: integer - - type: 'null' - default: 30 - title: Scale - responses: - '200': - description: Successful Response - content: - application/json: - schema: { } + type: array + items: + $ref: '#/components/schemas/ReviewActivityMotionResponse' + title: Response Motion Activity Review Activity Motion Get '422': description: Validation Error content: @@ -477,57 +403,60 @@ paths: description: Successful Response content: application/json: - schema: { } + schema: + $ref: '#/components/schemas/ReviewSegmentResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /review/{event_id}: + /review/{review_id}: get: tags: - Review summary: Get Review - operationId: get_review_review__event_id__get + operationId: get_review_review__review_id__get parameters: - - name: event_id + - name: review_id in: path required: true schema: type: string - title: Event Id + title: Review Id responses: '200': description: Successful Response content: application/json: - schema: { } + schema: + $ref: '#/components/schemas/ReviewSegmentResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - /review/{event_id}/viewed: + /review/{review_id}/viewed: delete: tags: - Review summary: Set Not Reviewed - operationId: set_not_reviewed_review__event_id__viewed_delete + operationId: set_not_reviewed_review__review_id__viewed_delete parameters: - - name: event_id + - name: review_id in: path required: true schema: type: string - title: Event Id + title: Review Id responses: '200': description: Successful Response content: application/json: - schema: { } + schema: + $ref: '#/components/schemas/GenericResponse' '422': description: Validation Error content: @@ -763,13 +692,25 @@ paths: content: application/json: schema: { } + /nvinfo: + get: + tags: + - App + summary: Nvinfo + operationId: nvinfo_nvinfo_get + responses: + '200': + description: Successful Response + content: + application/json: + schema: { } /logs/{service}: get: tags: - App - Logs summary: Logs - description: Get logs for the requested service (frigate/nginx/go2rtc/chroma) + description: Get logs for the requested service (frigate/nginx/go2rtc) operationId: logs_logs__service__get parameters: - name: service @@ -781,7 +722,6 @@ paths: - frigate - nginx - go2rtc - - chroma title: Service - name: download in: query @@ -1042,7 +982,8 @@ paths: - Preview summary: Preview Hour description: Get all mp4 previews relevant for time period given the timezone - operationId: preview_hour_preview__year_month___day___hour___camera_name___tz_name__get + operationId: >- + preview_hour_preview__year_month___day___hour___camera_name___tz_name__get parameters: - name: year_month in: path @@ -1092,7 +1033,8 @@ paths: - Preview summary: Get Preview Frames From Cache description: Get list of cached preview frames - operationId: get_preview_frames_from_cache_preview__camera_name__start__start_ts__end__end_ts__frames_get + operationId: >- + get_preview_frames_from_cache_preview__camera_name__start__start_ts__end__end_ts__frames_get parameters: - name: camera_name in: path @@ -1177,7 +1119,8 @@ paths: tags: - Export summary: Export Recording - operationId: export_recording_export__camera_name__start__start_time__end__end_time__post + operationId: >- + export_recording_export__camera_name__start__start_time__end__end_time__post parameters: - name: camera_name in: path @@ -1656,6 +1599,30 @@ paths: - type: 'null' default: utc title: Timezone + - name: min_score + in: query + required: false + schema: + anyOf: + - type: number + - type: 'null' + title: Min Score + - name: max_score + in: query + required: false + schema: + anyOf: + - type: number + - type: 'null' + title: Max Score + - name: sort + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Sort responses: '200': description: Successful Response @@ -1942,6 +1909,15 @@ paths: schema: type: string title: Event Id + - name: source + in: query + required: false + schema: + anyOf: + - $ref: '#/components/schemas/RegenerateDescriptionEnum' + - type: 'null' + default: thumbnails + title: Source responses: '200': description: Successful Response @@ -2029,12 +2005,12 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' - '{camera_name}': + /{camera_name}: get: tags: - Media summary: Mjpeg Feed - operationId: mjpeg_feed_camera_name__get + operationId: mjpeg_feed__camera_name__get parameters: - name: camera_name in: path @@ -2241,7 +2217,8 @@ paths: tags: - Media summary: Get Snapshot From Recording - operationId: get_snapshot_from_recording__camera_name__recordings__frame_time__snapshot__format__get + operationId: >- + get_snapshot_from_recording__camera_name__recordings__frame_time__snapshot__format__get parameters: - name: camera_name in: path @@ -2363,7 +2340,9 @@ paths: tags: - Media summary: Recordings - description: Return specific camera recordings between the given 'after'/'end' times. If not provided the last hour will be used + description: >- + Return specific camera recordings between the given 'after'/'end' times. + If not provided the last hour will be used operationId: recordings__camera_name__recordings_get parameters: - name: camera_name @@ -2377,14 +2356,14 @@ paths: required: false schema: type: number - default: 1727542549.303557 + default: 1729274204.653048 title: After - name: before in: query required: false schema: type: number - default: 1727546149.303926 + default: 1729277804.653095 title: Before responses: '200': @@ -2423,13 +2402,6 @@ paths: schema: type: number title: End Ts - - name: download - in: query - required: false - schema: - type: boolean - default: false - title: Download responses: '200': description: Successful Response @@ -2800,13 +2772,6 @@ paths: schema: type: string title: Event Id - - name: download - in: query - required: false - schema: - type: boolean - default: false - title: Download responses: '200': description: Successful Response @@ -3121,7 +3086,9 @@ paths: tags: - Media summary: Label Snapshot - description: Returns the snapshot image from the latest event for the given camera and label combo + description: >- + Returns the snapshot image from the latest event for the given camera + and label combo operationId: label_snapshot__camera_name___label__snapshot_jpg_get parameters: - name: camera_name @@ -3193,6 +3160,32 @@ components: required: - password title: AppPutPasswordBody + DayReview: + properties: + day: + type: string + format: date-time + title: Day + reviewed_alert: + type: integer + title: Reviewed Alert + reviewed_detection: + type: integer + title: Reviewed Detection + total_alert: + type: integer + title: Total Alert + total_detection: + type: integer + title: Total Detection + type: object + required: + - day + - reviewed_alert + - reviewed_detection + - total_alert + - total_detection + title: DayReview EventsCreateBody: properties: source_type: @@ -3237,7 +3230,6 @@ components: description: anyOf: - type: string - minLength: 1 - type: 'null' title: The description of the event type: object @@ -3278,6 +3270,19 @@ components: - jpg - jpeg title: Extension + GenericResponse: + properties: + success: + type: boolean + title: Success + message: + type: string + title: Message + type: object + required: + - success + - message + title: GenericResponse HTTPValidationError: properties: detail: @@ -3287,6 +3292,133 @@ components: title: Detail type: object title: HTTPValidationError + Last24HoursReview: + properties: + reviewed_alert: + type: integer + title: Reviewed Alert + reviewed_detection: + type: integer + title: Reviewed Detection + total_alert: + type: integer + title: Total Alert + total_detection: + type: integer + title: Total Detection + type: object + required: + - reviewed_alert + - reviewed_detection + - total_alert + - total_detection + title: Last24HoursReview + RegenerateDescriptionEnum: + type: string + enum: + - thumbnails + - snapshot + title: RegenerateDescriptionEnum + ReviewActivityMotionResponse: + properties: + start_time: + type: integer + title: Start Time + motion: + type: number + title: Motion + camera: + type: string + title: Camera + type: object + required: + - start_time + - motion + - camera + title: ReviewActivityMotionResponse + ReviewDeleteMultipleReviewsBody: + properties: + ids: + items: + type: string + minLength: 1 + type: array + minItems: 1 + title: Ids + type: object + required: + - ids + title: ReviewDeleteMultipleReviewsBody + ReviewSegmentResponse: + properties: + id: + type: string + title: Id + camera: + type: string + title: Camera + start_time: + type: string + format: date-time + title: Start Time + end_time: + type: string + format: date-time + title: End Time + has_been_reviewed: + type: boolean + title: Has Been Reviewed + severity: + $ref: '#/components/schemas/SeverityEnum' + thumb_path: + type: string + title: Thumb Path + data: + title: Data + type: object + required: + - id + - camera + - start_time + - end_time + - has_been_reviewed + - severity + - thumb_path + - data + title: ReviewSegmentResponse + ReviewSetMultipleReviewedBody: + properties: + ids: + items: + type: string + minLength: 1 + type: array + minItems: 1 + title: Ids + type: object + required: + - ids + title: ReviewSetMultipleReviewedBody + ReviewSummaryResponse: + properties: + last24Hours: + $ref: '#/components/schemas/Last24HoursReview' + root: + additionalProperties: + $ref: '#/components/schemas/DayReview' + type: object + title: Root + type: object + required: + - last24Hours + - root + title: ReviewSummaryResponse + SeverityEnum: + type: string + enum: + - alert + - detection + title: SeverityEnum SubmitPlusBody: properties: include_annotation: diff --git a/frigate/api/defs/generic_response.py b/frigate/api/defs/generic_response.py new file mode 100644 index 000000000..dbf9434f9 --- /dev/null +++ b/frigate/api/defs/generic_response.py @@ -0,0 +1,6 @@ +from pydantic import BaseModel + + +class GenericResponse(BaseModel): + success: bool + message: str diff --git a/frigate/api/defs/review_body.py b/frigate/api/defs/review_body.py new file mode 100644 index 000000000..991f190f8 --- /dev/null +++ b/frigate/api/defs/review_body.py @@ -0,0 +1,6 @@ +from pydantic import BaseModel, conlist, constr + + +class ReviewModifyMultipleBody(BaseModel): + # List of string with at least one element and each element with at least one char + ids: conlist(constr(min_length=1), min_length=1) diff --git a/frigate/api/defs/review_query_parameters.py b/frigate/api/defs/review_query_parameters.py index a3f63d292..4361d313c 100644 --- a/frigate/api/defs/review_query_parameters.py +++ b/frigate/api/defs/review_query_parameters.py @@ -1,28 +1,31 @@ -from typing import Optional +from typing import Union from pydantic import BaseModel +from pydantic.json_schema import SkipJsonSchema + +from frigate.review.maintainer import SeverityEnum class ReviewQueryParams(BaseModel): - cameras: Optional[str] = "all" - labels: Optional[str] = "all" - zones: Optional[str] = "all" - reviewed: Optional[int] = 0 - limit: Optional[int] = None - severity: Optional[str] = None - before: Optional[float] = None - after: Optional[float] = None + cameras: str = "all" + labels: str = "all" + zones: str = "all" + reviewed: int = 0 + limit: Union[int, SkipJsonSchema[None]] = None + severity: Union[SeverityEnum, SkipJsonSchema[None]] = None + before: Union[float, SkipJsonSchema[None]] = None + after: Union[float, SkipJsonSchema[None]] = None class ReviewSummaryQueryParams(BaseModel): - cameras: Optional[str] = "all" - labels: Optional[str] = "all" - zones: Optional[str] = "all" - timezone: Optional[str] = "utc" + cameras: str = "all" + labels: str = "all" + zones: str = "all" + timezone: str = "utc" class ReviewActivityMotionQueryParams(BaseModel): - cameras: Optional[str] = "all" - before: Optional[float] = None - after: Optional[float] = None - scale: Optional[int] = 30 + cameras: str = "all" + before: Union[float, SkipJsonSchema[None]] = None + after: Union[float, SkipJsonSchema[None]] = None + scale: int = 30 diff --git a/frigate/api/defs/review_responses.py b/frigate/api/defs/review_responses.py new file mode 100644 index 000000000..39e078b21 --- /dev/null +++ b/frigate/api/defs/review_responses.py @@ -0,0 +1,43 @@ +from datetime import datetime +from typing import Dict + +from pydantic import BaseModel, Json + +from frigate.review.maintainer import SeverityEnum + + +class ReviewSegmentResponse(BaseModel): + id: str + camera: str + start_time: datetime + end_time: datetime + has_been_reviewed: bool + severity: SeverityEnum + thumb_path: str + data: Json + + +class Last24HoursReview(BaseModel): + reviewed_alert: int + reviewed_detection: int + total_alert: int + total_detection: int + + +class DayReview(BaseModel): + day: datetime + reviewed_alert: int + reviewed_detection: int + total_alert: int + total_detection: int + + +class ReviewSummaryResponse(BaseModel): + last24Hours: Last24HoursReview + root: Dict[str, DayReview] + + +class ReviewActivityMotionResponse(BaseModel): + start_time: int + motion: float + camera: str diff --git a/frigate/api/fastapi_app.py b/frigate/api/fastapi_app.py index 3980e0b40..e3542458e 100644 --- a/frigate/api/fastapi_app.py +++ b/frigate/api/fastapi_app.py @@ -82,6 +82,10 @@ def create_fastapi_app( database.close() return response + @app.on_event("startup") + async def startup(): + logger.info("FastAPI started") + # Rate limiter (used for login endpoint) auth.rateLimiter.set_limit(frigate_config.auth.failed_login_rate_limit or "") app.state.limiter = limiter diff --git a/frigate/api/review.py b/frigate/api/review.py index 7c05386ef..21b468640 100644 --- a/frigate/api/review.py +++ b/frigate/api/review.py @@ -12,11 +12,18 @@ from fastapi.responses import JSONResponse from peewee import Case, DoesNotExist, fn, operator from playhouse.shortcuts import model_to_dict +from frigate.api.defs.generic_response import GenericResponse +from frigate.api.defs.review_body import ReviewModifyMultipleBody from frigate.api.defs.review_query_parameters import ( ReviewActivityMotionQueryParams, ReviewQueryParams, ReviewSummaryQueryParams, ) +from frigate.api.defs.review_responses import ( + ReviewActivityMotionResponse, + ReviewSegmentResponse, + ReviewSummaryResponse, +) from frigate.api.defs.tags import Tags from frigate.models import Recordings, ReviewSegment from frigate.util.builtin import get_tz_modifiers @@ -26,7 +33,7 @@ logger = logging.getLogger(__name__) router = APIRouter(tags=[Tags.review]) -@router.get("/review") +@router.get("/review", response_model=list[ReviewSegmentResponse]) def review(params: ReviewQueryParams = Depends()): cameras = params.cameras labels = params.labels @@ -102,7 +109,7 @@ def review(params: ReviewQueryParams = Depends()): return JSONResponse(content=[r for r in review]) -@router.get("/review/summary") +@router.get("/review/summary", response_model=ReviewSummaryResponse) def review_summary(params: ReviewSummaryQueryParams = Depends()): hour_modifier, minute_modifier, seconds_offset = get_tz_modifiers(params.timezone) day_ago = (datetime.datetime.now() - datetime.timedelta(hours=24)).timestamp() @@ -173,18 +180,6 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): 0, ) ).alias("reviewed_detection"), - fn.SUM( - Case( - None, - [ - ( - (ReviewSegment.severity == "significant_motion"), - ReviewSegment.has_been_reviewed, - ) - ], - 0, - ) - ).alias("reviewed_motion"), fn.SUM( Case( None, @@ -209,18 +204,6 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): 0, ) ).alias("total_detection"), - fn.SUM( - Case( - None, - [ - ( - (ReviewSegment.severity == "significant_motion"), - 1, - ) - ], - 0, - ) - ).alias("total_motion"), ) .where(reduce(operator.and_, clauses)) .dicts() @@ -282,18 +265,6 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): 0, ) ).alias("reviewed_detection"), - fn.SUM( - Case( - None, - [ - ( - (ReviewSegment.severity == "significant_motion"), - ReviewSegment.has_been_reviewed, - ) - ], - 0, - ) - ).alias("reviewed_motion"), fn.SUM( Case( None, @@ -318,18 +289,6 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): 0, ) ).alias("total_detection"), - fn.SUM( - Case( - None, - [ - ( - (ReviewSegment.severity == "significant_motion"), - 1, - ) - ], - 0, - ) - ).alias("total_motion"), ) .where(reduce(operator.and_, clauses)) .group_by( @@ -348,19 +307,10 @@ def review_summary(params: ReviewSummaryQueryParams = Depends()): return JSONResponse(content=data) -@router.post("/reviews/viewed") -def set_multiple_reviewed(body: dict = None): - json: dict[str, any] = body or {} - list_of_ids = json.get("ids", "") - - if not list_of_ids or len(list_of_ids) == 0: - return JSONResponse( - context=({"success": False, "message": "Not a valid list of ids"}), - status_code=404, - ) - +@router.post("/reviews/viewed", response_model=GenericResponse) +def set_multiple_reviewed(body: ReviewModifyMultipleBody): ReviewSegment.update(has_been_reviewed=True).where( - ReviewSegment.id << list_of_ids + ReviewSegment.id << body.ids ).execute() return JSONResponse( @@ -369,17 +319,9 @@ def set_multiple_reviewed(body: dict = None): ) -@router.post("/reviews/delete") -def delete_reviews(body: dict = None): - json: dict[str, any] = body or {} - list_of_ids = json.get("ids", "") - - if not list_of_ids or len(list_of_ids) == 0: - return JSONResponse( - content=({"success": False, "message": "Not a valid list of ids"}), - status_code=404, - ) - +@router.post("/reviews/delete", response_model=GenericResponse) +def delete_reviews(body: ReviewModifyMultipleBody): + list_of_ids = body.ids reviews = ( ReviewSegment.select( ReviewSegment.camera, @@ -424,7 +366,9 @@ def delete_reviews(body: dict = None): ) -@router.get("/review/activity/motion") +@router.get( + "/review/activity/motion", response_model=list[ReviewActivityMotionResponse] +) def motion_activity(params: ReviewActivityMotionQueryParams = Depends()): """Get motion and audio activity.""" cameras = params.cameras @@ -498,98 +442,44 @@ def motion_activity(params: ReviewActivityMotionQueryParams = Depends()): return JSONResponse(content=normalized) -@router.get("/review/activity/audio") -def audio_activity(params: ReviewActivityMotionQueryParams = Depends()): - """Get motion and audio activity.""" - cameras = params.cameras - before = params.before or datetime.datetime.now().timestamp() - after = ( - params.after - or (datetime.datetime.now() - datetime.timedelta(hours=1)).timestamp() - ) - # get scale in seconds - scale = params.scale - - clauses = [(Recordings.start_time > after) & (Recordings.end_time < before)] - - if cameras != "all": - camera_list = cameras.split(",") - clauses.append((Recordings.camera << camera_list)) - - all_recordings: list[Recordings] = ( - Recordings.select( - Recordings.start_time, - Recordings.duration, - Recordings.objects, - Recordings.dBFS, - ) - .where(reduce(operator.and_, clauses)) - .order_by(Recordings.start_time.asc()) - .iterator() - ) - - # format is: { timestamp: segment_start_ts, motion: [0-100], audio: [0 - -100] } - # periods where active objects / audio was detected will cause audio to be scaled down - data: list[dict[str, float]] = [] - - for rec in all_recordings: - data.append( - { - "start_time": rec.start_time, - "audio": rec.dBFS if rec.objects == 0 else 0, - } - ) - - # resample data using pandas to get activity on scaled basis - df = pd.DataFrame(data, columns=["start_time", "audio"]) - df = df.astype(dtype={"audio": "float16"}) - - # set date as datetime index - df["start_time"] = pd.to_datetime(df["start_time"], unit="s") - df.set_index(["start_time"], inplace=True) - - # normalize data - df = df.resample(f"{scale}S").mean().fillna(0.0) - df["audio"] = ( - (df["audio"] - df["audio"].max()) - / (df["audio"].min() - df["audio"].max()) - * -100 - ) - - # change types for output - df.index = df.index.astype(int) // (10**9) - normalized = df.reset_index().to_dict("records") - return JSONResponse(content=normalized) - - -@router.get("/review/event/{event_id}") +@router.get("/review/event/{event_id}", response_model=ReviewSegmentResponse) def get_review_from_event(event_id: str): try: - return model_to_dict( - ReviewSegment.get( - ReviewSegment.data["detections"].cast("text") % f'*"{event_id}"*' + return JSONResponse( + model_to_dict( + ReviewSegment.get( + ReviewSegment.data["detections"].cast("text") % f'*"{event_id}"*' + ) ) ) except DoesNotExist: - return "Review item not found", 404 + return JSONResponse( + content={"success": False, "message": "Review item not found"}, + status_code=404, + ) -@router.get("/review/{event_id}") -def get_review(event_id: str): +@router.get("/review/{review_id}", response_model=ReviewSegmentResponse) +def get_review(review_id: str): try: - return model_to_dict(ReviewSegment.get(ReviewSegment.id == event_id)) + return JSONResponse( + content=model_to_dict(ReviewSegment.get(ReviewSegment.id == review_id)) + ) except DoesNotExist: - return "Review item not found", 404 + return JSONResponse( + content={"success": False, "message": "Review item not found"}, + status_code=404, + ) -@router.delete("/review/{event_id}/viewed") -def set_not_reviewed(event_id: str): +@router.delete("/review/{review_id}/viewed", response_model=GenericResponse) +def set_not_reviewed(review_id: str): try: - review: ReviewSegment = ReviewSegment.get(ReviewSegment.id == event_id) + review: ReviewSegment = ReviewSegment.get(ReviewSegment.id == review_id) except DoesNotExist: return JSONResponse( content=( - {"success": False, "message": "Review " + event_id + " not found"} + {"success": False, "message": "Review " + review_id + " not found"} ), status_code=404, ) @@ -598,6 +488,8 @@ def set_not_reviewed(event_id: str): review.save() return JSONResponse( - content=({"success": True, "message": "Reviewed " + event_id + " not viewed"}), + content=( + {"success": True, "message": "Set Review " + review_id + " as not viewed"} + ), status_code=200, ) diff --git a/frigate/models.py b/frigate/models.py index c73033b3e..62bbf0bd3 100644 --- a/frigate/models.py +++ b/frigate/models.py @@ -93,7 +93,7 @@ class ReviewSegment(Model): # type: ignore[misc] start_time = DateTimeField() end_time = DateTimeField() has_been_reviewed = BooleanField(default=False) - severity = CharField(max_length=30) # alert, detection, significant_motion + severity = CharField(max_length=30) # alert, detection thumb_path = CharField(unique=True) data = JSONField() # additional data about detection like list of labels, zone, areas of significant motion diff --git a/web/src/pages/Events.tsx b/web/src/pages/Events.tsx index fa582ca3b..8c6f3cd38 100644 --- a/web/src/pages/Events.tsx +++ b/web/src/pages/Events.tsx @@ -407,10 +407,6 @@ export default function Events() { review.severity == "detection" ? item.reviewed_detection + 1 : item.reviewed_detection, - reviewed_motion: - review.severity == "significant_motion" - ? item.reviewed_motion + 1 - : item.reviewed_motion, }, }; }, diff --git a/web/src/types/review.ts b/web/src/types/review.ts index d1d03e637..73b8fed14 100644 --- a/web/src/types/review.ts +++ b/web/src/types/review.ts @@ -42,10 +42,8 @@ type ReviewSummaryDay = { day: string; reviewed_alert: number; reviewed_detection: number; - reviewed_motion: number; total_alert: number; total_detection: number; - total_motion: number; }; export type ReviewSummary = { diff --git a/web/src/views/events/EventView.tsx b/web/src/views/events/EventView.tsx index 615bfc535..d7997bf62 100644 --- a/web/src/views/events/EventView.tsx +++ b/web/src/views/events/EventView.tsx @@ -117,13 +117,11 @@ export default function EventView({ return { alert: summary.total_alert ?? 0, detection: summary.total_detection ?? 0, - significant_motion: summary.total_motion ?? 0, }; } else { return { alert: summary.total_alert - summary.reviewed_alert, detection: summary.total_detection - summary.reviewed_detection, - significant_motion: summary.total_motion - summary.reviewed_motion, }; } }, [filter, showReviewed, reviewSummary]);