Files
MobilityOps/backend/app/schemas.py
T
NuklearRabbit 4bf9afbeff Final acceptance audit: fix all mypy defects, verify full journey matrix and degraded modes
Ran a dedicated post-M7 release-readiness audit. Found and fixed the one real gap: mypy
was a declared dev dependency but had never been run in any milestone's validation loop.
Fixed all 43 pre-existing type errors it surfaced, including two genuine defensive-
programming gaps (unguarded Optional vehicle/customer lookups that could have crashed
with unhandled 500s instead of clean 404/401 responses) rather than suppressing them.
make lint now runs ruff + mypy; mypy reports zero errors across 44 source files.

Re-verified end to end against a genuinely wiped-volumes clean checkout: automatic
migrations, deterministic seed, 66/66 backend tests, and the full user-journey matrix
(login, dashboard, vehicle/booking detail, return workflow, invalid-mileage rejection,
data-quality review, duplicate-customer merge, audit trail, Knowledge Assistant, n8n,
MCP Hub) via curl and Playwright.

Live-verified both external-dependency degraded modes, not just unit tests: stopped n8n
mid-flow and confirmed a return still commits with the outbox event staying pending and
retrying with backoff, then self-healing to succeeded with zero manual intervention once
n8n came back; verified RAGcore's unavailable-degradation path against an unreachable
host. Added frontend/e2e/interactive-elements.spec.ts (11 tests covering every nav item,
filter, tab, and role boundary) alongside the existing demo script test — 12/12 e2e tests
passing.

Verified no secrets are committed (.env never tracked, clean git history scan) and
.env.example covers every operator-configurable setting. Confirmed no placeholders,
TODOs, fake responses, hardcoded metrics, or dead routes anywhere in the codebase.

Updated README.md with an honest integration-status section and PROJECT_STATE.md with
the full audit findings. Added artifacts/final-acceptance/summary.md as the authoritative
final evidence document (commands, results, URLs, demo access, integration status per
external dependency, known limitations, deployment instructions, five-minute demo flow).
2026-08-02 01:27:01 +02:00

222 lines
4.8 KiB
Python

from __future__ import annotations
from datetime import datetime
from typing import Annotated, Any, Literal
from pydantic import BaseModel, Field
Role = Literal["operations_manager", "rental_employee"]
class DemoLoginRequest(BaseModel):
role: Role
class CurrentUser(BaseModel):
public_ref: str
display_name: str
role: Role
class VehicleOut(BaseModel):
public_ref: str
make: str
model: str
model_year: int
registration_number: str
location: str
operational_status: str
odometer_km: int
next_service_km: int
active: bool
attention: bool = False
class BookingSummaryOut(BaseModel):
public_ref: str
customer_ref: str
vehicle_ref: str
starts_at: datetime
ends_at: datetime
status: str
class BookingOut(BookingSummaryOut):
start_odometer_km: int | None
end_odometer_km: int | None
requirements_complete: bool
customer_name: str
class RegisterReturnRequest(BaseModel):
end_odometer_km: Annotated[int, Field(ge=0)]
fuel_level_percent: Annotated[int, Field(ge=0, le=100)]
cleanliness_ok: bool
damage_reported: bool
technical_warning: bool
notes: str | None = Field(default=None, max_length=2000)
class NextBookingRisk(BaseModel):
booking_ref: str
starts_at: datetime
at_risk: bool
class RegisterReturnResult(BaseModel):
booking_ref: str
vehicle_ref: str
inspection_ref: str
resulting_vehicle_status: str
odometer_regression: bool
quality_issue_ref: str | None
workflow_event_id: str
next_booking_risk: NextBookingRisk | None
class InspectionOut(BaseModel):
public_ref: str
booking_ref: str
type: str
fuel_level_percent: int
cleanliness_ok: bool
damage_reported: bool
technical_warning: bool
odometer_km: int
completed_at: datetime
class MaintenanceOut(BaseModel):
public_ref: str
occurred_at: datetime
odometer_km: int
category: str
summary: str
class DataQualityIssueOut(BaseModel):
public_ref: str
rule_type: str
entity_type: str
entity_ref: str
severity: str
status: str
evidence: dict[str, Any]
detected_at: datetime
resolved_at: datetime | None = None
class DataQualityIssueDetailOut(DataQualityIssueOut):
entity_snapshot: dict[str, Any] | None = None
related_snapshots: list[dict[str, Any]] = Field(default_factory=list)
class MergeCustomersRequest(BaseModel):
survivor_ref: str
field_overrides: dict[str, str] | None = None
class MergeCustomersResult(BaseModel):
issue_ref: str
survivor_ref: str
loser_ref: str
rewired_bookings: int
class ScanResultOut(BaseModel):
created: dict[str, int]
class VehicleDetailOut(VehicleOut):
bookings: list[BookingSummaryOut] = Field(default_factory=list)
inspections: list[InspectionOut] = Field(default_factory=list)
maintenance: list[MaintenanceOut] = Field(default_factory=list)
quality_issues: list[DataQualityIssueOut] = Field(default_factory=list)
class DashboardMetrics(BaseModel):
available: int
rented: int
cleaning: int
maintenance: int
blocked: int
open_quality_issues: int
pending_or_failed_workflows: int
class AttentionItem(BaseModel):
kind: Literal["quality_issue", "vehicle"]
severity: str
title: str
detail: str
link_type: Literal["vehicle", "booking", "customer"]
link_ref: str
issue_ref: str | None = None
class TodayItem(BaseModel):
kind: Literal["departure", "return"]
booking_ref: str
vehicle_ref: str
scheduled_at: datetime
class AutomationRunOut(BaseModel):
event_id: str
event_type: str
aggregate_ref: str
status: str
attempts: int
last_error: str | None
occurred_at: datetime
class DashboardOut(BaseModel):
metrics: DashboardMetrics
attention_items: list[AttentionItem]
today: list[TodayItem]
recent_automation: list[AutomationRunOut]
class OperationsSummaryOut(BaseModel):
tenant: str
metrics: DashboardMetrics
class AttentionVehicleOut(BaseModel):
vehicle_ref: str
severity: Literal["low", "medium", "high"]
rule_type: str
summary: str
detected_at: datetime
class McpVehicleDetailOut(BaseModel):
public_ref: str
make: str
model: str
model_year: int
location: str
operational_status: str
odometer_km: int
next_service_km: int
open_quality_issue_count: int
current_booking_ref: str | None
class McpKnowledgeSearchRequest(BaseModel):
question: str = Field(min_length=3, max_length=1000)
max_sources: int = Field(default=4, ge=1, le=8)
class AuditEventOut(BaseModel):
id: str
actor_type: str
actor_label: str
action: str
entity_type: str
entity_id: str | None
correlation_id: str
occurred_at: datetime
metadata: dict[str, Any] | None = None