"""Compliance domain entities with coverage calculation logic. Pure domain logic — no framework imports. """ from __future__ import annotations import enum import uuid from dataclasses import dataclass, field class ControlCoverageStatus(str, enum.Enum): covered = "covered" partially_covered = "partially_covered" not_covered = "not_covered" @dataclass class ComplianceControlEntity: control_id: str title: str id: uuid.UUID | None = None description: str | None = None category: str | None = None technique_statuses: list[str] = field(default_factory=list) @property def coverage_status(self) -> ControlCoverageStatus: if not self.technique_statuses: return ControlCoverageStatus.not_covered covered_statuses = {"validated", "partial"} covered = [s for s in self.technique_statuses if s in covered_statuses] if len(covered) == len(self.technique_statuses): return ControlCoverageStatus.covered elif len(covered) > 0: return ControlCoverageStatus.partially_covered return ControlCoverageStatus.not_covered @dataclass class ComplianceFrameworkEntity: name: str id: uuid.UUID | None = None version: str | None = None description: str | None = None is_active: bool = True controls: list[ComplianceControlEntity] = field(default_factory=list) @property def total_controls(self) -> int: return len(self.controls) @property def covered_controls(self) -> int: return sum( 1 for c in self.controls if c.coverage_status == ControlCoverageStatus.covered ) @property def coverage_pct(self) -> float: if self.total_controls == 0: return 0.0 return round(self.covered_controls / self.total_controls * 100, 1) def get_gap_controls(self) -> list[ComplianceControlEntity]: return [ c for c in self.controls if c.coverage_status != ControlCoverageStatus.covered ]