feat(dashboard): Phase 13 — Executive Dashboard
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
PostureSnapshot model, Alembic migration (b039exec), schemas, service aggregating all phases (coverage/risk/operations/knowledge/MTTD), and router at /api/v1/dashboard with executive view, KPIs, coverage-by-tactic, posture-history, posture-snapshot, and activity-feed endpoints. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
68
backend/app/models/executive_dashboard.py
Normal file
68
backend/app/models/executive_dashboard.py
Normal file
@@ -0,0 +1,68 @@
|
||||
"""Phase 13: Executive Dashboard — PostureSnapshot model."""
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import (
|
||||
Boolean, Column, Date, DateTime, Float, ForeignKey,
|
||||
Index, Integer, UniqueConstraint,
|
||||
)
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class PostureSnapshot(Base):
|
||||
"""
|
||||
Daily point-in-time capture of the organisation's security posture.
|
||||
|
||||
Aggregates data from all phases (coverage, risk, ownership, knowledge,
|
||||
attack-paths) into a single row that can be trended over time.
|
||||
"""
|
||||
|
||||
__tablename__ = "posture_snapshots"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
snapshot_date = Column(Date, nullable=False) # one per calendar day
|
||||
|
||||
# ── Coverage ──────────────────────────────────────────────────────────────
|
||||
total_techniques = Column(Integer, nullable=False, default=0)
|
||||
validated_count = Column(Integer, nullable=False, default=0)
|
||||
partial_count = Column(Integer, nullable=False, default=0)
|
||||
not_covered_count = Column(Integer, nullable=False, default=0)
|
||||
coverage_pct = Column(Float, nullable=False, default=0.0) # 0–100
|
||||
|
||||
# ── Risk ─────────────────────────────────────────────────────────────────
|
||||
avg_risk_score = Column(Float, nullable=False, default=0.0)
|
||||
critical_count = Column(Integer, nullable=False, default=0)
|
||||
high_count = Column(Integer, nullable=False, default=0)
|
||||
medium_count = Column(Integer, nullable=False, default=0)
|
||||
low_count = Column(Integer, nullable=False, default=0)
|
||||
|
||||
# ── Operations ────────────────────────────────────────────────────────────
|
||||
open_queue_items = Column(Integer, nullable=False, default=0)
|
||||
orphan_techniques = Column(Integer, nullable=False, default=0)
|
||||
|
||||
# ── Knowledge ─────────────────────────────────────────────────────────────
|
||||
playbook_count = Column(Integer, nullable=False, default=0)
|
||||
lesson_count = Column(Integer, nullable=False, default=0)
|
||||
|
||||
# ── MTTD (from attack-path executions completed in last 30 d) ────────────
|
||||
mttd_avg_seconds = Column(Float, nullable=True) # None if no data
|
||||
executions_30d = Column(Integer, nullable=False, default=0)
|
||||
detection_rate_30d = Column(Float, nullable=True) # avg across executions
|
||||
|
||||
# ── Meta ─────────────────────────────────────────────────────────────────
|
||||
created_by = Column(
|
||||
UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
extra = Column(JSONB, nullable=True) # full breakdown / by-tactic
|
||||
|
||||
creator = relationship("User", foreign_keys=[created_by])
|
||||
|
||||
__table_args__ = (
|
||||
UniqueConstraint("snapshot_date", name="uq_posture_snapshot_date"),
|
||||
Index("ix_posture_snapshots_date", "snapshot_date"),
|
||||
)
|
||||
Reference in New Issue
Block a user