from __future__ import annotations from dataclasses import dataclass, field from typing import Any @dataclass(frozen=True) class AssertionDef: id: str target: str op: str value: Any = None description: str = "" @classmethod def from_dict(cls, data: dict[str, Any]) -> "AssertionDef": return cls( id=str(data["id"]), target=str(data.get("target", "")), op=str(data["op"]), value=data.get("value"), description=str(data.get("description", "")), ) @dataclass(frozen=True) class WorkstationDef: name: str entry_assertions: list[AssertionDef] = field(default_factory=list) exit_assertions: list[AssertionDef] = field(default_factory=list) description: str = "" @classmethod def from_dict(cls, data: dict[str, Any]) -> "WorkstationDef": return cls( name=str(data["name"]), description=str(data.get("description", "")), entry_assertions=[ AssertionDef.from_dict(item) for item in data.get("entry_assertions", []) or [] ], exit_assertions=[ AssertionDef.from_dict(item) for item in data.get("exit_assertions", []) or [] ], ) @dataclass(frozen=True) class FlowDef: id: str entity_type: str workstations: list[WorkstationDef] @classmethod def from_dict(cls, data: dict[str, Any]) -> "FlowDef": return cls( id=str(data["id"]), entity_type=str(data["entity_type"]), workstations=[ WorkstationDef.from_dict(item) for item in data.get("workstations", []) or [] ], ) def workstation(self, name: str) -> WorkstationDef | None: return next((item for item in self.workstations if item.name == name), None) @dataclass(frozen=True) class AssertionResult: id: str passed: bool target: str op: str expected: Any = None actual: list[Any] = field(default_factory=list) description: str = "" reason: str = "" @dataclass(frozen=True) class UnreachableWorkstation: workstation: str blocking: AssertionResult @dataclass(frozen=True) class FlowResult: current_workstation: str exit_blocked: bool blocking_assertions: list[AssertionResult] reachable: list[str] unreachable: list[UnreachableWorkstation]