Files
Aegis/backend/app/models/executive_dashboard.py
T
kitos 9472fe91fa
Aegis CI / lint-and-test (push) Has been cancelled
fix(lint): resolve 2132 ruff errors to pass CI lint-and-test job
- Remove ANN (type annotations) and D (docstrings) from ruff select; not
  feasible to add thousands of missing annotations/docstrings across the codebase
- Add I001 and E501 to ignore: comment-interleaved import style and SQLAlchemy
  FK definitions naturally exceed line limits
- Fix F811 duplicate import blocks in main.py, models/__init__.py, routers
  (campaigns, system, tests, evidence) and services (test_workflow, test_crud,
  campaign_service, schemas/test)
- Add missing Evidence/IntelItem/Technique/Test/TestTemplate/User imports to
  models/__init__.py (were only in duplicate block)
- Fix F821: add missing JWTError import in auth.py
- Fix F401 unused imports across 15+ files (jira_service, sso_service,
  notification_service, playbook_service, tempo_service, models, schemas,
  routers: admin_config, attack_paths, executive_dashboard, knowledge,
  ownership, risk_intelligence, sso, api_keys, email_service)
- Fix F841 unused variables: owned_technique_ids (executive_dashboard_service),
  severity (jira_service), priority_order (revalidation_queue_service)
- Fix F541 f-strings without placeholders in system.py and attck_evaluations_service
- Fix F601 duplicate dict key G0067 in threat_actor_import_service
- Fix E701 multiple-statements-on-one-line in risk_intelligence_service
- Fix E741 ambiguous variable name l -> lvl in risk_intelligence_service
- Fix N806 uppercase vars in functions: technique.py, heatmap_service.py;
  add noqa for compliance_import_service.py large unused constant dicts
- Fix W293 whitespace on blank lines in tests/conftest.py
2026-06-12 10:47:48 +02:00

69 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Phase 13: Executive Dashboard — PostureSnapshot model."""
import uuid
from datetime import datetime
from sqlalchemy import (
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) # 0100
# ── 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"),
)