"""Phase 13: Executive Dashboard — Pydantic schemas.""" from __future__ import annotations from datetime import date, datetime from typing import Any, Dict, List, Optional from uuid import UUID from pydantic import BaseModel, Field class PostureSnapshotOut(BaseModel): id: UUID snapshot_date: date # Coverage total_techniques: int validated_count: int partial_count: int not_covered_count: int coverage_pct: float # Risk avg_risk_score: float critical_count: int high_count: int medium_count: int low_count: int # Operations open_queue_items: int orphan_techniques: int # Knowledge playbook_count: int lesson_count: int # MTTD mttd_avg_seconds: Optional[float] = None executions_30d: int detection_rate_30d: Optional[float] = None # Meta created_by: Optional[UUID] = None created_at: Optional[datetime] = None extra: Optional[Dict[str, Any]] = None class Config: from_attributes = True class ExecutiveSummary(BaseModel): """Full executive view — current posture + trends.""" snapshot: PostureSnapshotOut coverage_trend: List[Dict[str, Any]] = Field( default_factory=list, description="Last 30-day coverage_pct series [{date, value}]", ) risk_trend: List[Dict[str, Any]] = Field( default_factory=list, description="Last 30-day avg_risk_score series [{date, value}]", ) top_risks: List[Dict[str, Any]] = Field( default_factory=list, description="Top 5 highest-risk techniques", ) coverage_by_tactic: List[Dict[str, Any]] = Field( default_factory=list, description="Per-tactic validated/partial/not_covered counts", ) recent_activity: List[Dict[str, Any]] = Field( default_factory=list, description="Most-recent events (tests, paths, queue changes)", ) class KpiBlock(BaseModel): """Compact KPI block for a dashboard header.""" coverage_pct: float avg_risk_score: float critical_count: int open_queue_items: int orphan_techniques: int mttd_avg_seconds: Optional[float] = None detection_rate_30d: Optional[float] = None playbook_count: int lesson_count: int snapshot_date: date snapshot_id: UUID class CoverageByTactic(BaseModel): tactic: str total: int validated: int partial: int not_covered: int coverage_pct: float class PostureHistoryEntry(BaseModel): snapshot_date: date coverage_pct: float avg_risk_score: float critical_count: int open_queue_items: int class ActivityEntry(BaseModel): ts: datetime category: str # "test" | "attack_path" | "queue" | "osint" title: str detail: Optional[str] = None