feat: initialize managed portal
This commit is contained in:
103
managed/store_dwell_alert/app/config.py
Normal file
103
managed/store_dwell_alert/app/config.py
Normal file
@@ -0,0 +1,103 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class Thresholds:
|
||||
min_people: int = 5
|
||||
min_dwell_seconds: int = 600
|
||||
pause_timeout_seconds: int = 300
|
||||
alert_cooldown_seconds: int = 600
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class StreamConfig:
|
||||
rtsp_url: str = ""
|
||||
sample_fps: float = 2.0
|
||||
reconnect_backoff_seconds: float = 5.0
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class StaffConfig:
|
||||
gallery_dir: str = "data/staff_gallery"
|
||||
min_hits: int = 3
|
||||
similarity_threshold: float = 0.9
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class WebhookConfig:
|
||||
alert_url: str = ""
|
||||
report_url: str = ""
|
||||
timeout_seconds: float = 5.0
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class EventSinkConfig:
|
||||
path: str = "logs/events.jsonl"
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class AppConfig:
|
||||
camera_id: str
|
||||
timezone: str = "Asia/Shanghai"
|
||||
thresholds: Thresholds = field(default_factory=Thresholds)
|
||||
stream: StreamConfig = field(default_factory=StreamConfig)
|
||||
staff: StaffConfig = field(default_factory=StaffConfig)
|
||||
event_sink: EventSinkConfig = field(default_factory=EventSinkConfig)
|
||||
webhook: WebhookConfig = field(default_factory=WebhookConfig)
|
||||
|
||||
|
||||
def _load_section(raw: dict, key: str, cls):
|
||||
return cls(**raw.get(key, {}))
|
||||
|
||||
|
||||
def resolve_config_path(config_path: str | Path | None = None) -> Path:
|
||||
if config_path is not None:
|
||||
return Path(config_path).expanduser().resolve()
|
||||
project_root = Path(__file__).resolve().parent.parent
|
||||
local_path = project_root / "config" / "108.local.yaml"
|
||||
if local_path.exists():
|
||||
return local_path
|
||||
return project_root / "config" / "config.example.yaml"
|
||||
|
||||
|
||||
def resolve_project_root(config_path: str | Path) -> Path:
|
||||
return Path(config_path).expanduser().resolve().parent.parent
|
||||
|
||||
|
||||
def resolve_project_path(project_root: str | Path, raw_path: str | Path) -> Path:
|
||||
path = Path(raw_path)
|
||||
if path.is_absolute():
|
||||
return path.resolve()
|
||||
return (Path(project_root).resolve() / path).resolve()
|
||||
|
||||
|
||||
def load_config_document(path: Path) -> dict:
|
||||
return yaml.safe_load(path.read_text(encoding="utf-8")) or {}
|
||||
|
||||
|
||||
def load_config(path: Path) -> AppConfig:
|
||||
raw = load_config_document(path)
|
||||
return AppConfig(
|
||||
camera_id=raw["camera_id"],
|
||||
timezone=raw.get("timezone", "Asia/Shanghai"),
|
||||
thresholds=_load_section(raw, "thresholds", Thresholds),
|
||||
stream=_load_section(raw, "stream", StreamConfig),
|
||||
staff=_load_section(raw, "staff", StaffConfig),
|
||||
event_sink=_load_section(raw, "event_sink", EventSinkConfig),
|
||||
webhook=_load_section(raw, "webhook", WebhookConfig),
|
||||
)
|
||||
|
||||
|
||||
def save_config_document(path: Path, raw: dict) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
temp_path = path.with_suffix(path.suffix + ".tmp")
|
||||
temp_path.write_text(
|
||||
yaml.safe_dump(raw, allow_unicode=True, sort_keys=False),
|
||||
encoding="utf-8",
|
||||
)
|
||||
temp_path.replace(path)
|
||||
Reference in New Issue
Block a user