mnemo-ocr-core / src /visual_spec.py
MABobrov's picture
Deploy updated core backend pipeline
7fb79e4
from __future__ import annotations
import re
from dataclasses import dataclass
from typing import Iterable
_SPLIT_RE = re.compile(r"[,;\n\r]+")
_VALUE_PREFIXES = (
"dynamic_analog",
"dynamic_string",
"dynamic_level",
"dynamic_dict",
"dynamic_equipment_dict",
)
_INDICATOR_PREFIXES = (
"dynamic_status",
"dynamic_asutp_status_indicator",
)
def split_visual_names(value: object) -> list[str]:
parts = [
part.strip()
for part in _SPLIT_RE.split(str(value or ""))
if part and part.strip()
]
seen: set[str] = set()
ordered: list[str] = []
for part in parts:
key = part.lower()
if key in seen:
continue
seen.add(key)
ordered.append(part)
return ordered
def _normalized_static_names(known_static_names: Iterable[str] | None = None) -> set[str]:
return {
str(name or "").strip().lower()
for name in (known_static_names or ())
if str(name or "").strip()
}
def visual_name_role(name: str, *, known_static_names: Iterable[str] | None = None) -> str:
normalized = str(name or "").strip().lower()
if not normalized:
return "other"
known_statics = _normalized_static_names(known_static_names)
if normalized in known_statics:
return "static"
if any(normalized.startswith(prefix) for prefix in _INDICATOR_PREFIXES):
return "indicator"
if any(normalized.startswith(prefix) for prefix in _VALUE_PREFIXES):
return "value"
if normalized.startswith("dynamic_"):
return "equipment_widget"
return "static"
@dataclass(frozen=True)
class VisualSpec:
ordered: tuple[str, ...]
statics: tuple[str, ...]
value_widgets: tuple[str, ...]
indicator_widgets: tuple[str, ...]
equipment_widgets: tuple[str, ...]
@property
def widgets(self) -> tuple[str, ...]:
return self.value_widgets + self.indicator_widgets + self.equipment_widgets
@property
def is_composite(self) -> bool:
return bool(self.statics) and bool(self.widgets)
@property
def primary_category(self) -> str:
if self.value_widgets:
return "value"
if self.indicator_widgets:
return "indicator"
if self.statics or self.equipment_widgets:
return "equipment"
return "other"
def parse_visual_spec(value: object, *, known_static_names: Iterable[str] | None = None) -> VisualSpec:
ordered = split_visual_names(value)
statics: list[str] = []
value_widgets: list[str] = []
indicator_widgets: list[str] = []
equipment_widgets: list[str] = []
for part in ordered:
role = visual_name_role(part, known_static_names=known_static_names)
if role == "static":
statics.append(part)
elif role == "value":
value_widgets.append(part)
elif role == "indicator":
indicator_widgets.append(part)
elif role == "equipment_widget":
equipment_widgets.append(part)
return VisualSpec(
ordered=tuple(ordered),
statics=tuple(statics),
value_widgets=tuple(value_widgets),
indicator_widgets=tuple(indicator_widgets),
equipment_widgets=tuple(equipment_widgets),
)