152 lines
3.6 KiB
Python
152 lines
3.6 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 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 365 days."""
|
|
|
|
days: list[HeatmapDay]
|
|
max_count: int
|
|
|
|
|
|
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] = {}
|