mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-08 15:52:48 +03:00
Handle sub labeled objects to still show up in review filter (#24391)
This commit is contained in:
+20
-11
@@ -43,6 +43,22 @@ logger = logging.getLogger(__name__)
|
|||||||
router = APIRouter(tags=[Tags.review])
|
router = APIRouter(tags=[Tags.review])
|
||||||
|
|
||||||
|
|
||||||
|
def get_label_clause(label: str, include_audio: bool = True):
|
||||||
|
"""Build a clause matching a label within a review segment's data.
|
||||||
|
|
||||||
|
Verified objects are stored with a `-verified` suffix (eg. `person-verified`)
|
||||||
|
so that variant is matched as well.
|
||||||
|
"""
|
||||||
|
clause = (ReviewSegment.data["objects"].cast("text") % f'*"{label}"*') | (
|
||||||
|
ReviewSegment.data["objects"].cast("text") % f'*"{label}-verified"*'
|
||||||
|
)
|
||||||
|
|
||||||
|
if include_audio:
|
||||||
|
clause |= ReviewSegment.data["audio"].cast("text") % f'*"{label}"*'
|
||||||
|
|
||||||
|
return clause
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/review",
|
"/review",
|
||||||
response_model=list[ReviewSegmentResponse],
|
response_model=list[ReviewSegmentResponse],
|
||||||
@@ -92,10 +108,7 @@ async def review(
|
|||||||
filtered_labels = labels.split(",")
|
filtered_labels = labels.split(",")
|
||||||
|
|
||||||
for label in filtered_labels:
|
for label in filtered_labels:
|
||||||
label_clauses.append(
|
label_clauses.append(get_label_clause(label))
|
||||||
(ReviewSegment.data["objects"].cast("text") % f'*"{label}"*')
|
|
||||||
| (ReviewSegment.data["audio"].cast("text") % f'*"{label}"*')
|
|
||||||
)
|
|
||||||
clauses.append(reduce(operator.or_, label_clauses))
|
clauses.append(reduce(operator.or_, label_clauses))
|
||||||
|
|
||||||
if zones != "all":
|
if zones != "all":
|
||||||
@@ -236,10 +249,7 @@ async def review_summary(
|
|||||||
filtered_labels = labels.split(",")
|
filtered_labels = labels.split(",")
|
||||||
|
|
||||||
for label in filtered_labels:
|
for label in filtered_labels:
|
||||||
label_clauses.append(
|
label_clauses.append(get_label_clause(label))
|
||||||
(ReviewSegment.data["objects"].cast("text") % f'*"{label}"*')
|
|
||||||
| (ReviewSegment.data["audio"].cast("text") % f'*"{label}"*')
|
|
||||||
)
|
|
||||||
clauses.append(reduce(operator.or_, label_clauses))
|
clauses.append(reduce(operator.or_, label_clauses))
|
||||||
if zones != "all":
|
if zones != "all":
|
||||||
# use matching so segments with multiple zones
|
# use matching so segments with multiple zones
|
||||||
@@ -337,9 +347,8 @@ async def review_summary(
|
|||||||
filtered_labels = labels.split(",")
|
filtered_labels = labels.split(",")
|
||||||
|
|
||||||
for label in filtered_labels:
|
for label in filtered_labels:
|
||||||
label_clauses.append(
|
label_clauses.append(get_label_clause(label, include_audio=False))
|
||||||
ReviewSegment.data["objects"].cast("text") % f'*"{label}"*'
|
|
||||||
)
|
|
||||||
clauses.append(reduce(operator.or_, label_clauses))
|
clauses.append(reduce(operator.or_, label_clauses))
|
||||||
|
|
||||||
# Find the time range of available data
|
# Find the time range of available data
|
||||||
|
|||||||
@@ -240,9 +240,101 @@ class TestHttpReview(BaseTestHttp):
|
|||||||
assert len(response_json) == 1
|
assert len(response_json) == 1
|
||||||
assert response_json[0]["id"] == id_reviewed
|
assert response_json[0]["id"] == id_reviewed
|
||||||
|
|
||||||
|
def test_get_review_with_label_filter_matches_verified(self):
|
||||||
|
"""Test that a label filter also matches the `-verified` variant."""
|
||||||
|
now = datetime.now().timestamp()
|
||||||
|
|
||||||
|
with AuthTestClient(self.app) as client:
|
||||||
|
super().insert_mock_review_segment(
|
||||||
|
"123456.person", now, now + 2, data={"objects": ["person"]}
|
||||||
|
)
|
||||||
|
super().insert_mock_review_segment(
|
||||||
|
"123456.verified", now, now + 2, data={"objects": ["person-verified"]}
|
||||||
|
)
|
||||||
|
super().insert_mock_review_segment(
|
||||||
|
"123456.car", now, now + 2, data={"objects": ["car"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"labels": "person",
|
||||||
|
"after": now - 1,
|
||||||
|
"before": now + 3,
|
||||||
|
}
|
||||||
|
response = client.get("/review", params=params)
|
||||||
|
assert response.status_code == 200
|
||||||
|
response_json = response.json()
|
||||||
|
assert {r["id"] for r in response_json} == {
|
||||||
|
"123456.person",
|
||||||
|
"123456.verified",
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_get_review_with_label_filter_does_not_match_prefix(self):
|
||||||
|
"""Test that a label filter does not match labels that only share a prefix."""
|
||||||
|
now = datetime.now().timestamp()
|
||||||
|
|
||||||
|
with AuthTestClient(self.app) as client:
|
||||||
|
super().insert_mock_review_segment(
|
||||||
|
"123456.carrot", now, now + 2, data={"objects": ["carrot"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"labels": "car",
|
||||||
|
"after": now - 1,
|
||||||
|
"before": now + 3,
|
||||||
|
}
|
||||||
|
response = client.get("/review", params=params)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert len(response.json()) == 0
|
||||||
|
|
||||||
|
def test_get_review_with_audio_label_filter(self):
|
||||||
|
"""Test that a label filter still matches audio labels."""
|
||||||
|
now = datetime.now().timestamp()
|
||||||
|
|
||||||
|
with AuthTestClient(self.app) as client:
|
||||||
|
super().insert_mock_review_segment(
|
||||||
|
"123456.audio", now, now + 2, data={"audio": ["speech"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"labels": "speech",
|
||||||
|
"after": now - 1,
|
||||||
|
"before": now + 3,
|
||||||
|
}
|
||||||
|
response = client.get("/review", params=params)
|
||||||
|
assert response.status_code == 200
|
||||||
|
response_json = response.json()
|
||||||
|
assert len(response_json) == 1
|
||||||
|
assert response_json[0]["id"] == "123456.audio"
|
||||||
|
|
||||||
####################################################################################################################
|
####################################################################################################################
|
||||||
################################### GET /review/summary Endpoint #################################################
|
################################### GET /review/summary Endpoint #################################################
|
||||||
####################################################################################################################
|
####################################################################################################################
|
||||||
|
def test_get_review_summary_label_filter_matches_verified(self):
|
||||||
|
"""Test that the summary label filter also matches the `-verified` variant."""
|
||||||
|
with AuthTestClient(self.app) as client:
|
||||||
|
super().insert_mock_review_segment(
|
||||||
|
"123456.verified", data={"objects": ["person-verified"]}
|
||||||
|
)
|
||||||
|
super().insert_mock_review_segment(
|
||||||
|
"123456.car", data={"objects": ["car"]}, severity=SeverityEnum.detection
|
||||||
|
)
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"cameras": "front_door",
|
||||||
|
"labels": "person",
|
||||||
|
"zones": "all",
|
||||||
|
"timezone": "utc",
|
||||||
|
}
|
||||||
|
response = client.get("/review/summary", params=params)
|
||||||
|
assert response.status_code == 200
|
||||||
|
response_json = response.json()
|
||||||
|
assert response_json["last24Hours"]["total_alert"] == 1
|
||||||
|
assert response_json["last24Hours"]["total_detection"] == 0
|
||||||
|
|
||||||
|
today_formatted = datetime.today().strftime("%Y-%m-%d")
|
||||||
|
assert response_json[today_formatted]["total_alert"] == 1
|
||||||
|
assert response_json[today_formatted]["total_detection"] == 0
|
||||||
|
|
||||||
def test_get_review_summary_all_filters(self):
|
def test_get_review_summary_all_filters(self):
|
||||||
with AuthTestClient(self.app) as client:
|
with AuthTestClient(self.app) as client:
|
||||||
super().insert_mock_review_segment("123456.random")
|
super().insert_mock_review_segment("123456.random")
|
||||||
|
|||||||
Reference in New Issue
Block a user