""" Schemas Pydantic para Ubicación GPS. """ from datetime import datetime from typing import List, Optional from pydantic import Field from app.schemas.base import BaseSchema, GeoJSONFeature class UbicacionBase(BaseSchema): """Schema base de ubicación.""" lat: float = Field(..., ge=-90, le=90) lng: float = Field(..., ge=-180, le=180) velocidad: Optional[float] = Field(None, ge=0) rumbo: Optional[float] = Field(None, ge=0, le=360) altitud: Optional[float] = None precision: Optional[float] = Field(None, ge=0) satelites: Optional[int] = Field(None, ge=0) class UbicacionCreate(UbicacionBase): """Schema para crear/recibir ubicación.""" vehiculo_id: Optional[int] = None # Puede venir por identificador de dispositivo dispositivo_id: Optional[str] = None # Identificador del dispositivo tiempo: Optional[datetime] = None # Si no se envía, usa timestamp del servidor fuente: str = Field(default="gps", max_length=20) bateria_dispositivo: Optional[float] = Field(None, ge=0, le=100) bateria_vehiculo: Optional[float] = None motor_encendido: Optional[bool] = None odometro: Optional[float] = Field(None, ge=0) hdop: Optional[float] = None # Datos OBD opcionales rpm: Optional[int] = Field(None, ge=0) temperatura_motor: Optional[float] = None nivel_combustible: Optional[float] = Field(None, ge=0, le=100) class UbicacionBulkCreate(BaseSchema): """Schema para recibir múltiples ubicaciones.""" ubicaciones: List[UbicacionCreate] class UbicacionResponse(UbicacionBase): """Schema de respuesta de ubicación.""" tiempo: datetime vehiculo_id: int fuente: str bateria_dispositivo: Optional[float] = None motor_encendido: Optional[bool] = None odometro: Optional[float] = None class UbicacionConVehiculo(UbicacionResponse): """Schema de ubicación con información del vehículo.""" vehiculo_nombre: str vehiculo_placa: str vehiculo_color: str class HistorialUbicacionesRequest(BaseSchema): """Schema para solicitar historial de ubicaciones.""" vehiculo_id: int desde: datetime hasta: datetime simplificar: bool = True # Simplificar ruta con Douglas-Peucker intervalo_segundos: Optional[int] = None # Muestreo por intervalo class HistorialUbicacionesResponse(BaseSchema): """Schema de respuesta de historial de ubicaciones.""" vehiculo_id: int desde: datetime hasta: datetime total_puntos: int distancia_km: float tiempo_movimiento_segundos: int velocidad_promedio: Optional[float] = None velocidad_maxima: Optional[float] = None ubicaciones: List[UbicacionResponse] class UbicacionGeoJSON(GeoJSONFeature): """Schema de ubicación en formato GeoJSON.""" pass class RutaGeoJSON(BaseSchema): """Schema de ruta completa en formato GeoJSON LineString.""" type: str = "Feature" geometry: dict # LineString properties: dict # Schema para recibir ubicaciones de OsmAnd/Traccar class OsmAndLocationCreate(BaseSchema): """Schema para ubicaciones recibidas de OsmAnd.""" id: str # Device identifier lat: float lon: float timestamp: Optional[int] = None # Unix timestamp speed: Optional[float] = None # km/h bearing: Optional[float] = None # degrees altitude: Optional[float] = None # meters accuracy: Optional[float] = None # meters batt: Optional[float] = None # battery percentage class TraccarLocationCreate(BaseSchema): """Schema para ubicaciones recibidas de Traccar.""" id: int # Device ID in Traccar deviceId: int protocol: str serverTime: datetime deviceTime: datetime fixTime: datetime valid: bool latitude: float longitude: float altitude: Optional[float] = None speed: Optional[float] = None # knots course: Optional[float] = None address: Optional[str] = None accuracy: Optional[float] = None attributes: Optional[dict] = None