56 lines
2.0 KiB
Python
56 lines
2.0 KiB
Python
"""Binary SAFE / UNSAFE classification — every item in REQUIRED_PPE must be worn.
|
|
|
|
The required list comes from ``config/core_config.json::compliance.required_ppe``
|
|
so it can be retuned without a code change.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from typing import Dict, List, Tuple
|
|
|
|
from core.detection import POSITIVE_TO_NEGATIVE, PPE_DISPLAY_ORDER
|
|
from utils.config import load_config
|
|
|
|
# PPE items required for SAFE status. Other items (gloves/goggles/boots) are
|
|
# still detected and logged but don't affect compliance.
|
|
REQUIRED_PPE: Tuple[str, ...] = tuple(
|
|
load_config("core").get("compliance", {}).get("required_ppe", ["helmet", "vest"])
|
|
)
|
|
|
|
|
|
def _is_wearing(items: Dict[str, float], ppe: str) -> bool:
|
|
pos = items.get(ppe, 0.0)
|
|
neg = items.get(POSITIVE_TO_NEGATIVE[ppe], 0.0)
|
|
return pos > neg and pos > 0
|
|
|
|
|
|
def status_from_items(items: Dict[str, float]) -> str:
|
|
"""SAFE only when every required item is confidently worn."""
|
|
if all(_is_wearing(items, p) for p in REQUIRED_PPE):
|
|
return "SAFE"
|
|
return "UNSAFE"
|
|
|
|
|
|
def split_wearing_missing(items: Dict[str, float]) -> Tuple[List[str], List[str], List[str]]:
|
|
"""Bucket each PPE item into wearing / missing / unknown.
|
|
|
|
``missing`` only contains REQUIRED items that aren't confidently worn —
|
|
this is what the TTS announcement keys off of, so it maps cleanly to a
|
|
recorded clip. Non-required items (gloves/goggles/boots) are never in
|
|
``missing`` regardless of whether their ``no-X`` class was detected; they
|
|
go to ``unknown`` so they're still visible in the event log.
|
|
"""
|
|
wearing, missing, unknown = [], [], []
|
|
for pos in PPE_DISPLAY_ORDER:
|
|
neg = POSITIVE_TO_NEGATIVE[pos]
|
|
pos_conf = items.get(pos, 0.0)
|
|
neg_conf = items.get(neg, 0.0)
|
|
|
|
worn = pos_conf > neg_conf and pos_conf > 0
|
|
if worn:
|
|
wearing.append(pos)
|
|
elif pos in REQUIRED_PPE:
|
|
missing.append(pos)
|
|
else:
|
|
unknown.append(pos)
|
|
return wearing, missing, unknown
|