from __future__ import annotations from datetime import date from sqlalchemy import func, select from sqlalchemy.orm import Session from app.models.data_quality import DataQualityIssue from app.models.outbox import OutboxEvent from app.models.vehicle import Vehicle from app.schemas import DashboardMetrics SEVERITY_ORDER = {"high": 0, "medium": 1, "low": 2} def compute_metrics(db: Session) -> DashboardMetrics: """Shared operations-summary computation used by both the dashboard and the MCP provider API, so the two never drift out of sync with two copies of the same query.""" status_counts: dict[str, int] = dict( db.execute( select(Vehicle.operational_status, func.count()).group_by(Vehicle.operational_status) ).all() # type: ignore[arg-type] ) open_issues = db.scalar( select(func.count()).select_from(DataQualityIssue).where(DataQualityIssue.status == "open") ) pending_or_failed = db.scalar( select(func.count()) .select_from(OutboxEvent) .where(OutboxEvent.delivery_status.in_(["pending", "failed"])) ) return DashboardMetrics( available=status_counts.get("available", 0), rented=status_counts.get("rented", 0), cleaning=status_counts.get("cleaning", 0), maintenance=status_counts.get("maintenance", 0), blocked=status_counts.get("blocked", 0), open_quality_issues=open_issues or 0, pending_or_failed_workflows=pending_or_failed or 0, ) def list_attention_vehicles( db: Session, minimum_severity: str = "medium", on_or_before: date | None = None, limit: int = 20 ) -> list[dict]: max_rank = SEVERITY_ORDER.get(minimum_severity, 1) stmt = ( select(DataQualityIssue) .where(DataQualityIssue.entity_type == "vehicle", DataQualityIssue.status == "open") .order_by(DataQualityIssue.detected_at.asc()) ) if on_or_before is not None: stmt = stmt.where(func.date(DataQualityIssue.detected_at) <= on_or_before) issues = db.scalars(stmt).all() filtered = [i for i in issues if SEVERITY_ORDER.get(i.severity, 3) <= max_rank] filtered.sort(key=lambda i: SEVERITY_ORDER.get(i.severity, 3)) vehicle_ids = {i.entity_id for i in filtered} vehicles_by_id = { v.id: v for v in db.scalars(select(Vehicle).where(Vehicle.id.in_(vehicle_ids))).all() } results = [] for issue in filtered[:limit]: vehicle = vehicles_by_id.get(issue.entity_id) if vehicle is None: continue results.append( { "vehicle_ref": vehicle.public_ref, "severity": issue.severity, "rule_type": issue.rule_type, "summary": issue.evidence_json.get("summary", ""), "detected_at": issue.detected_at, } ) return results