Files
ATLAS/backend/app/api/v1/ubicaciones.py
FlotillasGPS Developer 51d78bacf4 FlotillasGPS - Sistema completo de monitoreo de flotillas GPS
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.
2026-01-21 08:18:00 +00:00

238 lines
6.5 KiB
Python

"""
Endpoints para recepción y consulta de ubicaciones GPS.
"""
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Depends, Query, Request
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from app.core.security import get_current_user
from app.models.usuario import Usuario
from app.schemas.ubicacion import (
UbicacionCreate,
UbicacionBulkCreate,
UbicacionResponse,
HistorialUbicacionesResponse,
OsmAndLocationCreate,
)
from app.services.ubicacion_service import UbicacionService
from app.services.alerta_service import AlertaService
from app.services.viaje_service import ViajeService
router = APIRouter(prefix="/ubicaciones", tags=["Ubicaciones"])
@router.post("", response_model=Optional[UbicacionResponse])
async def recibir_ubicacion(
ubicacion_data: UbicacionCreate,
db: AsyncSession = Depends(get_db),
):
"""
Recibe una ubicación GPS desde la app móvil o dispositivo.
Este endpoint no requiere autenticación para facilitar
la integración con dispositivos GPS simples.
Args:
ubicacion_data: Datos de la ubicación.
Returns:
Ubicación procesada o None si se descartó.
"""
ubicacion_service = UbicacionService(db)
resultado = await ubicacion_service.procesar_ubicacion(ubicacion_data)
if resultado:
# Procesar alertas y viajes en background
alerta_service = AlertaService(db)
viaje_service = ViajeService(db)
# Verificar velocidad
if ubicacion_data.velocidad:
await alerta_service.verificar_velocidad(
resultado.vehiculo_id,
ubicacion_data.velocidad,
ubicacion_data.lat,
ubicacion_data.lng,
)
# Verificar batería
if ubicacion_data.bateria_dispositivo:
await alerta_service.verificar_bateria_baja(
resultado.vehiculo_id,
ubicacion_data.bateria_dispositivo,
ubicacion_data.lat,
ubicacion_data.lng,
)
# Procesar viaje
await viaje_service.procesar_ubicacion_viaje(
resultado.vehiculo_id,
ubicacion_data.lat,
ubicacion_data.lng,
ubicacion_data.velocidad or 0,
resultado.tiempo,
)
return resultado
@router.post("/bulk", response_model=dict)
async def recibir_ubicaciones_bulk(
data: UbicacionBulkCreate,
db: AsyncSession = Depends(get_db),
):
"""
Recibe múltiples ubicaciones en una sola petición.
Útil para sincronización de datos acumulados cuando
el dispositivo estuvo offline.
Args:
data: Lista de ubicaciones.
Returns:
Conteo de ubicaciones procesadas.
"""
ubicacion_service = UbicacionService(db)
procesadas = 0
errores = 0
for ubicacion_data in data.ubicaciones:
try:
resultado = await ubicacion_service.procesar_ubicacion(ubicacion_data)
if resultado:
procesadas += 1
except Exception:
errores += 1
return {
"total": len(data.ubicaciones),
"procesadas": procesadas,
"errores": errores,
}
@router.get("/osmand")
async def recibir_ubicacion_osmand(
request: Request,
id: str,
lat: float,
lon: float,
timestamp: Optional[int] = None,
speed: Optional[float] = None,
bearing: Optional[float] = None,
altitude: Optional[float] = None,
accuracy: Optional[float] = None,
batt: Optional[float] = None,
db: AsyncSession = Depends(get_db),
):
"""
Endpoint compatible con OsmAnd Live Tracking.
OsmAnd envía ubicaciones mediante GET con parámetros en URL.
Args:
id: Identificador del dispositivo.
lat: Latitud.
lon: Longitud.
timestamp: Unix timestamp (opcional).
speed: Velocidad en km/h (opcional).
bearing: Rumbo en grados (opcional).
altitude: Altitud en metros (opcional).
accuracy: Precisión en metros (opcional).
batt: Porcentaje de batería (opcional).
Returns:
Confirmación de recepción.
"""
ubicacion_data = UbicacionCreate(
dispositivo_id=id,
lat=lat,
lng=lon,
velocidad=speed,
rumbo=bearing,
altitud=altitude,
precision=accuracy,
bateria_dispositivo=batt,
tiempo=datetime.fromtimestamp(timestamp) if timestamp else None,
fuente="osmand",
)
ubicacion_service = UbicacionService(db)
resultado = await ubicacion_service.procesar_ubicacion(ubicacion_data)
if resultado:
return {"status": "ok"}
return {"status": "device_not_found"}
@router.get("/historial/{vehiculo_id}", response_model=HistorialUbicacionesResponse)
async def obtener_historial(
vehiculo_id: int,
desde: datetime,
hasta: datetime,
simplificar: bool = True,
intervalo_segundos: Optional[int] = None,
db: AsyncSession = Depends(get_db),
current_user: Usuario = Depends(get_current_user),
):
"""
Obtiene el historial de ubicaciones de un vehículo.
Args:
vehiculo_id: ID del vehículo.
desde: Fecha/hora de inicio.
hasta: Fecha/hora de fin.
simplificar: Simplificar ruta (Douglas-Peucker).
intervalo_segundos: Muestreo por intervalo.
Returns:
Historial con estadísticas.
"""
ubicacion_service = UbicacionService(db)
return await ubicacion_service.obtener_historial(
vehiculo_id,
desde,
hasta,
simplificar,
intervalo_segundos,
)
@router.get("/ultima/{vehiculo_id}", response_model=Optional[UbicacionResponse])
async def obtener_ultima_ubicacion(
vehiculo_id: int,
db: AsyncSession = Depends(get_db),
current_user: Usuario = Depends(get_current_user),
):
"""
Obtiene la última ubicación de un vehículo.
Args:
vehiculo_id: ID del vehículo.
Returns:
Última ubicación conocida.
"""
ubicacion_service = UbicacionService(db)
return await ubicacion_service.obtener_ultima_ubicacion(vehiculo_id)
@router.get("/flota")
async def obtener_ubicaciones_flota(
db: AsyncSession = Depends(get_db),
current_user: Usuario = Depends(get_current_user),
):
"""
Obtiene las ubicaciones actuales de toda la flota.
Returns:
Lista de ubicaciones de todos los vehículos activos.
"""
ubicacion_service = UbicacionService(db)
return await ubicacion_service.obtener_ubicaciones_flota()