54 lines
1.9 KiB
Python
54 lines
1.9 KiB
Python
"""Stable mapping from an internal rule hit to an internal event candidate."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
|
|
from yovision_brain.rules import RuleDecision
|
|
from yovision_brain.vision import DetectorMetadata, TrackedObject
|
|
|
|
from .models import INTERNAL_EVENT_SCHEMA, InternalEventCandidate
|
|
|
|
|
|
def candidate_from_decision(
|
|
decision: RuleDecision,
|
|
track: TrackedObject,
|
|
*,
|
|
logical_input_id: str,
|
|
detector: DetectorMetadata,
|
|
) -> InternalEventCandidate:
|
|
if not decision.triggered or decision.track_id != track.track_id:
|
|
raise ValueError("only a triggered decision for the same anonymous track can become an event")
|
|
event_type = "danger_area_entered" if decision.rule_type == "danger_area" else "directional_line_crossed"
|
|
observation: dict[str, object] = {
|
|
"category": track.category,
|
|
"confidence": track.confidence,
|
|
"box": {
|
|
"left": track.box.left,
|
|
"top": track.box.top,
|
|
"right": track.box.right,
|
|
"bottom": track.box.bottom,
|
|
},
|
|
"anchor": {"x": decision.anchor.x, "y": decision.anchor.y},
|
|
}
|
|
fact = {
|
|
"schema": INTERNAL_EVENT_SCHEMA,
|
|
"logical_input_id": logical_input_id,
|
|
"event_type": event_type,
|
|
"occurred_at_ns": decision.timestamp_ns,
|
|
"rule_id": decision.rule_id,
|
|
"rule_version": decision.config_version,
|
|
"model_name": detector.name,
|
|
"model_version": detector.version,
|
|
"profile_id": decision.profile_id,
|
|
"frame_width": decision.width,
|
|
"frame_height": decision.height,
|
|
"track_id": track.track_id,
|
|
"observation": observation,
|
|
"reason": decision.reason,
|
|
}
|
|
canonical = json.dumps(fact, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
|
event_id = "brain-local-" + hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
|
return InternalEventCandidate(event_id=event_id, **fact)
|