Sistema completo para monitoreo y gestion de flotas de vehiculos con: - Backend FastAPI con PostgreSQL/TimescaleDB - Frontend React con TypeScript y TailwindCSS - App movil React Native con Expo - Soporte para dispositivos GPS, Meshtastic y celulares - Video streaming en vivo con MediaMTX - Geocercas, alertas, viajes y reportes - Autenticacion JWT y WebSockets en tiempo real Documentacion completa y guias de usuario incluidas.
265 lines
8.1 KiB
Python
265 lines
8.1 KiB
Python
"""
|
|
Schemas Pydantic para Cámara, Grabación y Evento de Video.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from typing import List, Optional
|
|
|
|
from pydantic import Field
|
|
|
|
from app.schemas.base import BaseSchema, TimestampSchema
|
|
|
|
|
|
# ============================================================================
|
|
# Schemas de Cámara
|
|
# ============================================================================
|
|
|
|
|
|
class CamaraBase(BaseSchema):
|
|
"""Schema base de cámara."""
|
|
|
|
nombre: str = Field(..., min_length=2, max_length=100)
|
|
posicion: str = Field(default="frontal", max_length=50)
|
|
tipo: str = Field(default="ip", max_length=50)
|
|
|
|
|
|
class CamaraCreate(CamaraBase):
|
|
"""Schema para crear cámara."""
|
|
|
|
vehiculo_id: int
|
|
marca: Optional[str] = Field(None, max_length=50)
|
|
modelo: Optional[str] = Field(None, max_length=50)
|
|
numero_serie: Optional[str] = Field(None, max_length=100)
|
|
resolucion: Optional[str] = Field(None, max_length=20)
|
|
url_stream: Optional[str] = Field(None, max_length=500)
|
|
puerto: Optional[int] = Field(None, ge=1, le=65535)
|
|
protocolo: str = Field(default="rtsp", max_length=20)
|
|
usuario: Optional[str] = Field(None, max_length=100)
|
|
password: Optional[str] = Field(None, max_length=100) # Se encriptará
|
|
mediamtx_path: Optional[str] = Field(None, max_length=100)
|
|
grabacion_continua: bool = False
|
|
grabacion_evento: bool = True
|
|
duracion_pre_evento: int = Field(default=10, ge=0, le=60)
|
|
duracion_post_evento: int = Field(default=20, ge=0, le=120)
|
|
deteccion_colision: bool = False
|
|
deteccion_distraccion: bool = False
|
|
deteccion_fatiga: bool = False
|
|
deteccion_cambio_carril: bool = False
|
|
notas: Optional[str] = None
|
|
|
|
|
|
class CamaraUpdate(BaseSchema):
|
|
"""Schema para actualizar cámara."""
|
|
|
|
nombre: Optional[str] = Field(None, min_length=2, max_length=100)
|
|
posicion: Optional[str] = Field(None, max_length=50)
|
|
tipo: Optional[str] = Field(None, max_length=50)
|
|
marca: Optional[str] = Field(None, max_length=50)
|
|
modelo: Optional[str] = Field(None, max_length=50)
|
|
numero_serie: Optional[str] = Field(None, max_length=100)
|
|
resolucion: Optional[str] = Field(None, max_length=20)
|
|
url_stream: Optional[str] = Field(None, max_length=500)
|
|
puerto: Optional[int] = Field(None, ge=1, le=65535)
|
|
protocolo: Optional[str] = Field(None, max_length=20)
|
|
usuario: Optional[str] = Field(None, max_length=100)
|
|
password: Optional[str] = Field(None, max_length=100)
|
|
mediamtx_path: Optional[str] = Field(None, max_length=100)
|
|
grabacion_continua: Optional[bool] = None
|
|
grabacion_evento: Optional[bool] = None
|
|
duracion_pre_evento: Optional[int] = Field(None, ge=0, le=60)
|
|
duracion_post_evento: Optional[int] = Field(None, ge=0, le=120)
|
|
deteccion_colision: Optional[bool] = None
|
|
deteccion_distraccion: Optional[bool] = None
|
|
deteccion_fatiga: Optional[bool] = None
|
|
deteccion_cambio_carril: Optional[bool] = None
|
|
activa: Optional[bool] = None
|
|
notas: Optional[str] = None
|
|
|
|
|
|
class CamaraResponse(CamaraBase, TimestampSchema):
|
|
"""Schema de respuesta de cámara."""
|
|
|
|
id: int
|
|
vehiculo_id: int
|
|
marca: Optional[str] = None
|
|
modelo: Optional[str] = None
|
|
numero_serie: Optional[str] = None
|
|
resolucion: Optional[str] = None
|
|
url_stream: Optional[str] = None
|
|
puerto: Optional[int] = None
|
|
protocolo: str
|
|
usuario: Optional[str] = None
|
|
# password no se expone
|
|
mediamtx_path: Optional[str] = None
|
|
estado: str
|
|
activa: bool
|
|
ultima_conexion: Optional[datetime] = None
|
|
grabacion_continua: bool
|
|
grabacion_evento: bool
|
|
duracion_pre_evento: int
|
|
duracion_post_evento: int
|
|
deteccion_colision: bool
|
|
deteccion_distraccion: bool
|
|
deteccion_fatiga: bool
|
|
deteccion_cambio_carril: bool
|
|
notas: Optional[str] = None
|
|
|
|
|
|
class CamaraConVehiculo(CamaraResponse):
|
|
"""Schema de cámara con información del vehículo."""
|
|
|
|
vehiculo_nombre: Optional[str] = None
|
|
vehiculo_placa: Optional[str] = None
|
|
|
|
|
|
class CamaraStreamURL(BaseSchema):
|
|
"""Schema con URLs de streaming de una cámara."""
|
|
|
|
camara_id: int
|
|
camara_nombre: str
|
|
rtsp_url: Optional[str] = None
|
|
hls_url: Optional[str] = None
|
|
webrtc_url: Optional[str] = None
|
|
estado: str
|
|
|
|
|
|
# ============================================================================
|
|
# Schemas de Grabación
|
|
# ============================================================================
|
|
|
|
|
|
class GrabacionBase(BaseSchema):
|
|
"""Schema base de grabación."""
|
|
|
|
camara_id: int
|
|
vehiculo_id: int
|
|
inicio_tiempo: datetime
|
|
tipo: str = Field(default="continua", max_length=50)
|
|
|
|
|
|
class GrabacionCreate(GrabacionBase):
|
|
"""Schema para crear registro de grabación."""
|
|
|
|
archivo_url: str = Field(..., max_length=500)
|
|
archivo_nombre: str = Field(..., max_length=255)
|
|
formato: str = Field(default="mp4", max_length=10)
|
|
|
|
|
|
class GrabacionResponse(GrabacionBase, TimestampSchema):
|
|
"""Schema de respuesta de grabación."""
|
|
|
|
id: int
|
|
fin_tiempo: Optional[datetime] = None
|
|
duracion_segundos: Optional[int] = None
|
|
archivo_url: str
|
|
archivo_nombre: str
|
|
tamaño_mb: Optional[float] = None
|
|
formato: str
|
|
resolucion: Optional[str] = None
|
|
evento_video_id: Optional[int] = None
|
|
lat: Optional[float] = None
|
|
lng: Optional[float] = None
|
|
estado: str
|
|
thumbnail_url: Optional[str] = None
|
|
notas: Optional[str] = None
|
|
|
|
# Calculado
|
|
duracion_formateada: str
|
|
|
|
|
|
class GrabacionResumen(BaseSchema):
|
|
"""Schema resumido de grabación."""
|
|
|
|
id: int
|
|
camara_id: int
|
|
vehiculo_id: int
|
|
inicio_tiempo: datetime
|
|
duracion_formateada: str
|
|
tipo: str
|
|
thumbnail_url: Optional[str] = None
|
|
|
|
|
|
# ============================================================================
|
|
# Schemas de Evento de Video
|
|
# ============================================================================
|
|
|
|
|
|
class EventoVideoBase(BaseSchema):
|
|
"""Schema base de evento de video."""
|
|
|
|
camara_id: int
|
|
vehiculo_id: int
|
|
tipo: str = Field(..., max_length=50)
|
|
severidad: str = Field(default="media", pattern="^(baja|media|alta|critica)$")
|
|
tiempo: datetime
|
|
|
|
|
|
class EventoVideoCreate(EventoVideoBase):
|
|
"""Schema para crear evento de video."""
|
|
|
|
lat: Optional[float] = Field(None, ge=-90, le=90)
|
|
lng: Optional[float] = Field(None, ge=-180, le=180)
|
|
velocidad: Optional[float] = Field(None, ge=0)
|
|
descripcion: Optional[str] = None
|
|
confianza: Optional[float] = Field(None, ge=0, le=100)
|
|
datos_extra: Optional[str] = None
|
|
snapshot_url: Optional[str] = Field(None, max_length=500)
|
|
clip_url: Optional[str] = Field(None, max_length=500)
|
|
clip_duracion: Optional[int] = Field(None, ge=0)
|
|
|
|
|
|
class EventoVideoUpdate(BaseSchema):
|
|
"""Schema para actualizar evento de video."""
|
|
|
|
revisado: Optional[bool] = None
|
|
notas_revision: Optional[str] = None
|
|
falso_positivo: Optional[bool] = None
|
|
|
|
|
|
class EventoVideoResponse(EventoVideoBase, TimestampSchema):
|
|
"""Schema de respuesta de evento de video."""
|
|
|
|
id: int
|
|
lat: Optional[float] = None
|
|
lng: Optional[float] = None
|
|
velocidad: Optional[float] = None
|
|
descripcion: Optional[str] = None
|
|
confianza: Optional[float] = None
|
|
datos_extra: Optional[str] = None
|
|
revisado: bool
|
|
revisado_por_id: Optional[int] = None
|
|
revisado_en: Optional[datetime] = None
|
|
notas_revision: Optional[str] = None
|
|
falso_positivo: bool
|
|
snapshot_url: Optional[str] = None
|
|
clip_url: Optional[str] = None
|
|
clip_duracion: Optional[int] = None
|
|
|
|
|
|
class EventoVideoConRelaciones(EventoVideoResponse):
|
|
"""Schema con información de cámara y vehículo."""
|
|
|
|
camara_nombre: Optional[str] = None
|
|
vehiculo_nombre: Optional[str] = None
|
|
vehiculo_placa: Optional[str] = None
|
|
|
|
|
|
class EventoVideoResumen(BaseSchema):
|
|
"""Schema resumido de evento de video."""
|
|
|
|
id: int
|
|
tipo: str
|
|
severidad: str
|
|
tiempo: datetime
|
|
vehiculo_nombre: str
|
|
camara_nombre: str
|
|
revisado: bool
|
|
falso_positivo: bool
|
|
snapshot_url: Optional[str] = None
|
|
|
|
|
|
class TiposEventoVideoResponse(BaseSchema):
|
|
"""Schema con tipos de eventos de video disponibles."""
|
|
|
|
tipos: List[dict] # [{codigo, nombre, severidad}]
|