Add dashboard summary tiles

Shows rider count, activities imported in the last 7 days, 7-day
sync success rate, and how many riders currently need attention,
right above the rider list where an admin looks first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Bastian Wagner
2026-08-16 12:48:44 +02:00
parent 9df6a619d2
commit a0890126bc
6 changed files with 232 additions and 5 deletions

View File

@@ -1,13 +1,14 @@
from dataclasses import dataclass
from datetime import datetime
from sqlalchemy import and_, or_, select
from sqlalchemy import and_, func, or_, select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
from app.db.models import (
Activity,
ActivityStatus,
HealthState,
SchedulerSettings,
SyncRun,
SyncRunStatus,
@@ -19,6 +20,15 @@ from app.db.models import (
_SCHEDULER_SETTINGS_ID = 1
@dataclass(frozen=True)
class DashboardSummary:
rider_total: int
rider_enabled: int
imported_recent: int
success_rate_recent: float | None
action_required_count: int
@dataclass(frozen=True)
class UserDashboardRow:
id: int
@@ -78,6 +88,39 @@ class UserRepository:
))
return rows
def dashboard_summary(self, *, since: datetime) -> DashboardSummary:
rider_total = self.session.scalar(select(func.count()).select_from(SyncUser)) or 0
rider_enabled = self.session.scalar(
select(func.count()).select_from(SyncUser).where(SyncUser.enabled.is_(True))
) or 0
action_required_count = self.session.scalar(
select(func.count()).select_from(SyncUser).where(SyncUser.health_state == HealthState.ACTION_REQUIRED)
) or 0
imported_recent = self.session.scalar(
select(func.coalesce(func.sum(SyncRun.imported_count), 0)).where(SyncRun.started_at >= since)
) or 0
finished_runs = list(
self.session.scalars(
select(SyncRun).where(SyncRun.started_at >= since, SyncRun.finished_at.is_not(None))
)
)
if finished_runs:
successful = sum(
1 for run in finished_runs if run.status in (SyncRunStatus.SUCCESS, SyncRunStatus.PARTIAL)
)
success_rate_recent = (successful / len(finished_runs)) * 100
else:
success_rate_recent = None
return DashboardSummary(
rider_total=rider_total,
rider_enabled=rider_enabled,
imported_recent=imported_recent,
success_rate_recent=success_rate_recent,
action_required_count=action_required_count,
)
class ActivityRepository:
def __init__(self, session: Session) -> None: