94 lines
3.6 KiB
Python
94 lines
3.6 KiB
Python
"""Architecture change governance dashboard.
|
|
|
|
Displays architecture change classification, owner approval requirements,
|
|
implementation readiness, version recommendation and audit evidence for Web UI.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import asdict, dataclass, field
|
|
from datetime import datetime, timezone
|
|
from typing import Any
|
|
|
|
from .architecture_change_classifier import ArchitectureChangeClassifier
|
|
|
|
|
|
def utc_now() -> str:
|
|
return datetime.now(timezone.utc).isoformat()
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ArchitectureChangeView:
|
|
change_id: str
|
|
title: str
|
|
level: str
|
|
owner_required: bool
|
|
auto_implement_allowed: bool
|
|
version_bump: str
|
|
status: str
|
|
reasons: list[str] = field(default_factory=list)
|
|
required_gates: list[str] = field(default_factory=list)
|
|
owner_decision: str | None = None
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
class ArchitectureChangeDashboard:
|
|
def __init__(self, classifier: ArchitectureChangeClassifier | None = None):
|
|
self.classifier = classifier or ArchitectureChangeClassifier()
|
|
|
|
def overview(self, changes: list[dict[str, Any]] | None = None) -> dict[str, Any]:
|
|
changes = changes or []
|
|
views = [self._view(change) for change in changes]
|
|
return {
|
|
"module": "architecture-change-governance",
|
|
"route": "/admin/ai-evolution/architecture-changes",
|
|
"title": "架构变更治理中心",
|
|
"status": self._status(views),
|
|
"generated_at": utc_now(),
|
|
"summary": self._summary(views),
|
|
"changes": [view.to_dict() for view in views],
|
|
}
|
|
|
|
def _view(self, change: dict[str, Any]) -> ArchitectureChangeView:
|
|
classification = self.classifier.classify(change)
|
|
owner_decision = change.get("owner_decision")
|
|
if classification.owner_required and owner_decision != "approved":
|
|
status = "waiting_owner_approval"
|
|
elif classification.auto_implement_allowed:
|
|
status = "auto_implementation_allowed"
|
|
else:
|
|
status = "ready_after_required_gates"
|
|
return ArchitectureChangeView(
|
|
change_id=change.get("change_id", change.get("id", "unknown")),
|
|
title=change.get("title", "architecture change"),
|
|
level=classification.level,
|
|
owner_required=classification.owner_required,
|
|
auto_implement_allowed=classification.auto_implement_allowed,
|
|
version_bump=classification.version_bump,
|
|
status=status,
|
|
reasons=classification.reasons,
|
|
required_gates=classification.required_gates,
|
|
owner_decision=owner_decision,
|
|
)
|
|
|
|
def _status(self, views: list[ArchitectureChangeView]) -> str:
|
|
if any(view.status == "waiting_owner_approval" for view in views):
|
|
return "attention_required"
|
|
return "healthy"
|
|
|
|
def _summary(self, views: list[ArchitectureChangeView]) -> dict[str, Any]:
|
|
return {
|
|
"total": len(views),
|
|
"small": len([v for v in views if v.level == "small"]),
|
|
"medium": len([v for v in views if v.level == "medium"]),
|
|
"large": len([v for v in views if v.level == "large"]),
|
|
"waiting_owner_approval": len([v for v in views if v.status == "waiting_owner_approval"]),
|
|
"auto_allowed": len([v for v in views if v.auto_implement_allowed]),
|
|
"version_bumps": sorted({v.version_bump for v in views if v.version_bump and v.version_bump != "NONE"}),
|
|
}
|
|
|
|
|
|
__all__ = ["ArchitectureChangeView", "ArchitectureChangeDashboard"]
|