diff --git a/backend/__pycache__/main.cpython-312.pyc b/backend/__pycache__/main.cpython-312.pyc index fdbc28f..77fcb04 100644 Binary files a/backend/__pycache__/main.cpython-312.pyc and b/backend/__pycache__/main.cpython-312.pyc differ diff --git a/backend/main.py b/backend/main.py index 024c22d..bff5e75 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,8 +1,11 @@ -from fastapi import FastAPI, Depends, HTTPException +from fastapi import FastAPI, Depends, HTTPException, WebSocket, WebSocketDisconnect from fastapi.middleware.cors import CORSMiddleware from sqlalchemy.orm import Session from apscheduler.schedulers.background import BackgroundScheduler from database import engine, get_db +from typing import Dict, Set +import asyncio +import json import models, schemas, auth, simulator models.Base.metadata.create_all(bind=engine) @@ -12,6 +15,32 @@ app = FastAPI(title="HackOnLinces 2026 - Recolección de Residuos") app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) +class ConnectionManager: + def __init__(self): + self.active_connections: Dict[str, Set[WebSocket]] = {} + + async def connect(self, websocket: WebSocket, route_id: str): + await websocket.accept() + if route_id not in self.active_connections: + self.active_connections[route_id] = set() + self.active_connections[route_id].add(websocket) + + def disconnect(self, websocket: WebSocket, route_id: str): + if route_id in self.active_connections: + self.active_connections[route_id].discard(websocket) + + async def broadcast_to_route(self, route_id: str, message: dict): + if route_id in self.active_connections: + dead = set() + for ws in self.active_connections[route_id]: + try: + await ws.send_json(message) + except: + dead.add(ws) + self.active_connections[route_id] -= dead + +manager = ConnectionManager() + scheduler = BackgroundScheduler() scheduler.add_job(simulator.avanzar_rutas, "interval", minutes=2) scheduler.start() @@ -62,6 +91,14 @@ def crear_domicilio(data: schemas.DomicilioCreate, db.refresh(dom) return dom +@app.get("/domicilios") +def listar_domicilios( + current_user=Depends(auth.get_current_user), + db: Session = Depends(get_db) +): + domicilios = db.query(models.Domicilio).filter_by(usuario_id=current_user.id).all() + return [{"id": d.id, "direccion": d.direccion, "colonia": d.colonia, "route_id": d.route_id} for d in domicilios] + @app.get("/eta/{domicilio_id}", response_model=schemas.ETAResponse) def get_eta(domicilio_id: int, current_user=Depends(auth.get_current_user), @@ -95,10 +132,51 @@ def crear_reporte( "estado": "PENDIENTE" } -@app.get("/domicilios") -def listar_domicilios( - current_user=Depends(auth.get_current_user), +@app.post("/alertas/operativa") +def crear_alerta_operativa( + route_id: str, + tipo: str, + mensaje: str, db: Session = Depends(get_db) ): - domicilios = db.query(models.Domicilio).filter_by(usuario_id=current_user.id).all() - return [{"id": d.id, "direccion": d.direccion, "colonia": d.colonia, "route_id": d.route_id} for d in domicilios] \ No newline at end of file + estado = db.query(models.EstadoRuta).filter_by(route_id=route_id).first() + if not estado: + raise HTTPException(status_code=404, detail="Ruta no encontrada") + return { + "route_id": route_id, + "tipo": tipo, + "mensaje": mensaje, + "evento": "ALERTA_OPERATIVA", + "estado": "ENVIADA" + } + +@app.websocket("/ws/eta/{domicilio_id}") +async def websocket_eta(websocket: WebSocket, domicilio_id: int, + token: str, db: Session = Depends(get_db)): + try: + payload = auth.jwt.decode(token, auth.SECRET_KEY, algorithms=[auth.ALGORITHM]) + email = payload.get("sub") + user = db.query(models.Usuario).filter_by(email=email).first() + if not user: + await websocket.close(code=1008) + return + dom = db.query(models.Domicilio).filter_by(id=domicilio_id).first() + if not dom or dom.usuario_id != user.id: + await websocket.close(code=1008) + return + except: + await websocket.close(code=1008) + return + + await manager.connect(websocket, dom.route_id) + try: + eta = simulator.get_eta(dom.route_id, db) + if eta: + await websocket.send_json({**eta, "route_id": dom.route_id, "colonia": dom.colonia}) + while True: + await asyncio.sleep(30) + eta = simulator.get_eta(dom.route_id, db) + if eta: + await websocket.send_json({**eta, "route_id": dom.route_id, "colonia": dom.colonia}) + except WebSocketDisconnect: + manager.disconnect(websocket, dom.route_id) \ No newline at end of file