Pipeline/backend/app/schemas/metrics.py

166 lines
4.1 KiB
Python

"""Dashboard metrics schemas for KPI and time-series API responses."""
from __future__ import annotations
from datetime import datetime
from typing import Literal
from uuid import UUID
from sqlmodel import SQLModel
RUNTIME_ANNOTATION_TYPES = (datetime, UUID)
DashboardRangeKey = Literal["24h", "3d", "7d", "14d", "1m", "3m", "6m", "1y"]
DashboardBucketKey = Literal["hour", "day", "week", "month"]
class DashboardSeriesPoint(SQLModel):
"""Single numeric time-series point."""
period: datetime
value: float
class DashboardWipPoint(SQLModel):
"""Work-in-progress point split by task status buckets."""
period: datetime
inbox: int
in_progress: int
review: int
done: int
class DashboardRangeSeries(SQLModel):
"""Series payload for a single range/bucket combination."""
range: DashboardRangeKey
bucket: DashboardBucketKey
points: list[DashboardSeriesPoint]
class DashboardWipRangeSeries(SQLModel):
"""WIP series payload for a single range/bucket combination."""
range: DashboardRangeKey
bucket: DashboardBucketKey
points: list[DashboardWipPoint]
class DashboardSeriesSet(SQLModel):
"""Primary vs comparison pair for generic series metrics."""
primary: DashboardRangeSeries
comparison: DashboardRangeSeries
class DashboardWipSeriesSet(SQLModel):
"""Primary vs comparison pair for WIP status series metrics."""
primary: DashboardWipRangeSeries
comparison: DashboardWipRangeSeries
class DashboardKpis(SQLModel):
"""Topline dashboard KPI summary values."""
active_agents: int
tasks_in_progress: int
inbox_tasks: int
in_progress_tasks: int
review_tasks: int
done_tasks: int
error_rate_pct: float
median_cycle_time_hours_7d: float | None
class DashboardPendingApproval(SQLModel):
"""Single pending approval item for cross-board dashboard listing."""
approval_id: UUID
board_id: UUID
board_name: str
action_type: str
confidence: float
created_at: datetime
task_title: str | None = None
class DashboardPendingApprovals(SQLModel):
"""Pending approval snapshot used on the dashboard."""
total: int
items: list[DashboardPendingApproval]
class DashboardMetrics(SQLModel):
"""Complete dashboard metrics response payload."""
range: DashboardRangeKey
generated_at: datetime
kpis: DashboardKpis
throughput: DashboardSeriesSet
cycle_time: DashboardSeriesSet
error_rate: DashboardSeriesSet
wip: DashboardWipSeriesSet
pending_approvals: DashboardPendingApprovals
class ForgejoIssueMetrics(SQLModel):
"""Forgejo issue tracking metrics."""
open_issues: int
closed_issues: int
closed_in_selected_range: int
selected_range_days: int
closed_last_7_days: int
closed_last_30_days: int
stale_open_issues: int
repositories_synced: int
repository_sync_error_count: int
last_sync_timestamps: dict[str, str]
sync_error_counts: dict[str, int]
class LastPushRead(SQLModel):
"""Most-recent commit pushed across all tracked repositories."""
sha: str # short (7-char) hash
message: str # first line of commit message
author: str # display name or login
repo: str # "owner/repo"
branch: str # branch name
pushed_at: str # ISO-8601 timestamp
class HeatmapDay(SQLModel):
"""Single day in the issue activity heatmap."""
date: str # "YYYY-MM-DD"
count: int
class HeatmapResponse(SQLModel):
"""Issue activity heatmap for the last year."""
days: list[HeatmapDay]
max_count: int
total_additions: int = 0
total_deletions: int = 0
has_line_stats: bool = False # False when Forgejo is still computing stats (HTTP 202)
class MetricsResponse(SQLModel):
"""Generic metrics response wrapper."""
open_issues: int = 0
closed_issues: int = 0
closed_in_selected_range: int = 0
selected_range_days: int = 7
closed_last_7_days: int = 0
closed_last_30_days: int = 0
stale_open_issues: int = 0
repositories_synced: int = 0
repository_sync_error_count: int = 0
last_sync_timestamps: dict[str, str] = {}
sync_error_counts: dict[str, int] = {}