"""Professional Pydantic models for request/response validation.""" from typing import List, Optional from pydantic import BaseModel, Field, field_validator from datetime import datetime class Location(BaseModel): """Location model with latitude and longitude.""" lat: float = Field(..., description="Latitude") lng: float = Field(..., description="Longitude") class PickupLocation(BaseModel): """Pickup location model with latitude and longitude.""" pickuplat: float = Field( ..., description="Pickup latitude", ge=-90, le=90, examples=[11.0050534] ) pickuplon: float = Field( ..., description="Pickup longitude", ge=-180, le=180, examples=[76.9508991] ) @field_validator("pickuplat", "pickuplon") @classmethod def validate_coordinates(cls, v): """Validate coordinate values.""" if v is None: raise ValueError("Coordinate cannot be None") return float(v) class DeliveryLocation(BaseModel): """Delivery location model with latitude and longitude.""" deliverylat: float = Field( ..., description="Delivery latitude", ge=-90, le=90, examples=[11.0309723] ) deliverylong: float = Field( ..., description="Delivery longitude", ge=-180, le=180, examples=[77.0004574] ) @field_validator("deliverylat", "deliverylong") @classmethod def validate_coordinates(cls, v): """Validate coordinate values.""" if v is None: raise ValueError("Coordinate cannot be None") return float(v) class Delivery(BaseModel): """Delivery order model.""" deliveryid: str = Field(..., description="Unique delivery identifier") deliverycustomerid: int = Field(..., description="Customer ID for this delivery") location: DeliveryLocation = Field(..., description="Delivery location coordinates") class RouteOptimizationRequest(BaseModel): """ Request model for route optimization. Optimizes delivery routes starting from a pickup location (warehouse/store) to multiple delivery locations. Uses greedy nearest-neighbor algorithm for fast, efficient route calculation. """ pickup_location: PickupLocation = Field( ..., description="Pickup location (warehouse/store) coordinates - starting point for optimization" ) pickup_location_id: Optional[int] = Field( None, description="Optional pickup location ID for tracking purposes" ) deliveries: List[Delivery] = Field( ..., min_items=1, max_items=50, description="List of delivery locations to optimize (1-50 deliveries supported)" ) class Config: json_schema_extra = { "example": { "pickup_location": { "pickuplat": 11.0050534, "pickuplon": 76.9508991 }, "pickup_location_id": 1, "deliveries": [ { "deliveryid": "90465", "deliverycustomerid": 1, "location": { "deliverylat": 11.0309723, "deliverylong": 77.0004574 } } ] } } class RouteStep(BaseModel): """Single step in the optimized route.""" step_number: int = Field(..., description="Step number in the route") delivery_id: str = Field(..., description="Delivery ID for this step") delivery_customer_id: int = Field(..., description="Customer ID for this delivery") location: DeliveryLocation = Field(..., description="Delivery location coordinates") distance_from_previous_km: float = Field(..., description="Distance from previous step in kilometers") cumulative_distance_km: float = Field(..., description="Total distance traveled so far in kilometers") class OptimizedRoute(BaseModel): """ Optimized route response with step-by-step delivery sequence. Contains the optimized route starting from pickup location, with each step showing: - Delivery order (Step 1, Step 2, etc.) - Distance from previous step - Cumulative distance traveled """ route_id: str = Field(..., description="Unique route identifier (UUID)") pickup_location_id: Optional[int] = Field(None, description="Pickup location ID") pickup_location: PickupLocation = Field(..., description="Pickup location (warehouse/store) coordinates") total_distance_km: float = Field( ..., ge=0, description="Total route distance in kilometers", examples=[12.45] ) total_deliveries: int = Field( ..., ge=1, description="Total number of deliveries in the route", examples=[5] ) optimization_algorithm: str = Field( "greedy", description="Algorithm used for optimization", examples=["greedy"] ) steps: List[RouteStep] = Field( ..., description="Ordered list of route steps (Step 1 = nearest from pickup, Step 2 = nearest from Step 1, etc.)" ) created_at: str = Field( default_factory=lambda: datetime.utcnow().isoformat(), description="Route creation timestamp (ISO 8601)" ) # Batch optimization removed - no rider support needed # Use single-route optimization for each pickup location