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
This commit is contained in:
Rui Alves 2024-10-23 14:35:49 +01:00 committed by GitHub
parent 8bc145472a
commit fa81d87dc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 404 additions and 326 deletions

View File

@ -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:

View File

@ -0,0 +1,6 @@
from pydantic import BaseModel
class GenericResponse(BaseModel):
success: bool
message: str

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,
)

View File

@ -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

View File

@ -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,
},
};
},

View File

@ -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 = {

View File

@ -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]);