feat(phase-16): enhanced Tests view, Red/Blue dashboard metrics, and Template admin panel (T-122, T-123, T-124)
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
"""Pydantic schemas for coverage-metrics endpoints."""
|
||||
|
||||
from pydantic import BaseModel
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class CoverageSummary(BaseModel):
|
||||
@@ -25,3 +27,59 @@ class TacticCoverage(BaseModel):
|
||||
not_covered: int
|
||||
not_evaluated: int
|
||||
in_progress: int
|
||||
|
||||
|
||||
# ── V2 — Test Pipeline ────────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestPipelineCounts(BaseModel):
|
||||
"""Counters per state in the test pipeline."""
|
||||
|
||||
draft: int = 0
|
||||
red_executing: int = 0
|
||||
blue_evaluating: int = 0
|
||||
in_review: int = 0
|
||||
validated: int = 0
|
||||
rejected: int = 0
|
||||
total: int = 0
|
||||
|
||||
|
||||
# ── V2 — Team Activity ───────────────────────────────────────────────
|
||||
|
||||
|
||||
class TeamActivity(BaseModel):
|
||||
"""Activity summary for a team (Red or Blue)."""
|
||||
|
||||
team: str
|
||||
tests_completed: int = 0
|
||||
tests_pending: int = 0
|
||||
avg_completion_hours: float | None = None
|
||||
|
||||
|
||||
# ── V2 — Validation Rate ─────────────────────────────────────────────
|
||||
|
||||
|
||||
class ValidationRate(BaseModel):
|
||||
"""Approval / rejection rate for a manager role."""
|
||||
|
||||
role: str # "red_lead" or "blue_lead"
|
||||
total_reviewed: int = 0
|
||||
approved: int = 0
|
||||
rejected: int = 0
|
||||
approval_rate: float = 0.0 # percentage
|
||||
|
||||
|
||||
# ── V2 — Recent Test ─────────────────────────────────────────────────
|
||||
|
||||
|
||||
class RecentTestItem(BaseModel):
|
||||
"""Lightweight test entry for the recent-tests widget."""
|
||||
|
||||
id: str
|
||||
name: str
|
||||
state: str
|
||||
technique_mitre_id: str | None = None
|
||||
technique_name: str | None = None
|
||||
created_at: datetime | None = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@@ -126,4 +126,16 @@ class TestOut(BaseModel):
|
||||
blue_validation_status: str | None = None
|
||||
blue_validation_notes: str | None = None
|
||||
|
||||
# Technique info (populated when joined)
|
||||
technique_mitre_id: str | None = None
|
||||
technique_name: str | None = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@classmethod
|
||||
def model_validate(cls, obj, **kwargs):
|
||||
"""Override to populate technique fields from the relationship."""
|
||||
if hasattr(obj, "technique") and obj.technique is not None:
|
||||
obj.__dict__["technique_mitre_id"] = obj.technique.mitre_id
|
||||
obj.__dict__["technique_name"] = obj.technique.name
|
||||
return super().model_validate(obj, **kwargs)
|
||||
|
||||
Reference in New Issue
Block a user