243 lines
7.0 KiB
Python
243 lines
7.0 KiB
Python
from datetime import datetime
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.orm import Session, selectinload
|
|
|
|
from app.database import get_db
|
|
from app.models import Alert, Route, User
|
|
from app.schemas import AlertOut, OperatorActionOut, RouteDetailOut, RouteOut
|
|
from app.security import require_role
|
|
|
|
router = APIRouter(prefix="/operator", tags=["Operator"])
|
|
|
|
|
|
def get_assigned_route(db: Session, route_id: str, operator: User) -> Route:
|
|
route = (
|
|
db.query(Route)
|
|
.options(selectinload(Route.positions))
|
|
.filter(Route.route_id == route_id)
|
|
.first()
|
|
)
|
|
if not route:
|
|
raise HTTPException(status_code=404, detail="Ruta no encontrada")
|
|
if route.assigned_operator_id != operator.id:
|
|
raise HTTPException(status_code=403, detail="Esta ruta no está asignada a este operador")
|
|
return route
|
|
|
|
|
|
def create_alert(
|
|
db: Session,
|
|
route: Route,
|
|
operator: User,
|
|
type_: str,
|
|
title: str,
|
|
message: str,
|
|
priority: int,
|
|
) -> Alert:
|
|
alert = Alert(
|
|
type=type_,
|
|
title=title,
|
|
message=message,
|
|
route_id=route.route_id,
|
|
truck_id=route.truck_id,
|
|
operator_id=operator.id,
|
|
priority=priority,
|
|
status="NUEVA",
|
|
)
|
|
db.add(alert)
|
|
db.flush()
|
|
return alert
|
|
|
|
|
|
def action_response(db: Session, route: Route, alert: Alert | None, message: str) -> OperatorActionOut:
|
|
db.commit()
|
|
db.refresh(route)
|
|
if alert:
|
|
db.refresh(alert)
|
|
return OperatorActionOut(ok=True, route=route, alert=alert, message=message)
|
|
|
|
|
|
@router.get("/routes", response_model=list[RouteOut])
|
|
def my_routes(
|
|
db: Session = Depends(get_db),
|
|
operator: User = Depends(require_role("operador")),
|
|
):
|
|
return (
|
|
db.query(Route)
|
|
.filter(Route.assigned_operator_id == operator.id)
|
|
.order_by(Route.route_id.asc())
|
|
.all()
|
|
)
|
|
|
|
|
|
@router.get("/routes/{route_id}", response_model=RouteDetailOut)
|
|
def route_detail(
|
|
route_id: str,
|
|
db: Session = Depends(get_db),
|
|
operator: User = Depends(require_role("operador")),
|
|
):
|
|
route = get_assigned_route(db, route_id, operator)
|
|
route.positions = sorted(route.positions, key=lambda p: p.position_id)
|
|
return route
|
|
|
|
|
|
@router.get("/alerts", response_model=list[AlertOut])
|
|
def my_sent_alerts(
|
|
db: Session = Depends(get_db),
|
|
operator: User = Depends(require_role("operador")),
|
|
):
|
|
return (
|
|
db.query(Alert)
|
|
.filter(Alert.operator_id == operator.id)
|
|
.order_by(Alert.created_at.desc())
|
|
.limit(30)
|
|
.all()
|
|
)
|
|
|
|
|
|
@router.post("/routes/{route_id}/start", response_model=OperatorActionOut)
|
|
def start_route(
|
|
route_id: str,
|
|
db: Session = Depends(get_db),
|
|
operator: User = Depends(require_role("operador")),
|
|
):
|
|
route = get_assigned_route(db, route_id, operator)
|
|
route.status = "EN_RUTA"
|
|
route.current_position_id = 2
|
|
route.updated_at = datetime.utcnow()
|
|
alert = create_alert(
|
|
db,
|
|
route,
|
|
operator,
|
|
"ROUTE_START",
|
|
"Ruta iniciada",
|
|
f"El camión {route.truck_id} inició la ruta {route.route_id}.",
|
|
1,
|
|
)
|
|
return action_response(db, route, alert, "Jornada iniciada correctamente")
|
|
|
|
|
|
@router.post("/routes/{route_id}/advance/{position_id}", response_model=OperatorActionOut)
|
|
def advance_route(
|
|
route_id: str,
|
|
position_id: int,
|
|
db: Session = Depends(get_db),
|
|
operator: User = Depends(require_role("operador")),
|
|
):
|
|
route = get_assigned_route(db, route_id, operator)
|
|
valid_positions = {p.position_id for p in route.positions}
|
|
if position_id not in valid_positions:
|
|
raise HTTPException(status_code=422, detail="positionId inválido para esta ruta")
|
|
|
|
route.current_position_id = position_id
|
|
route.status = "EN_RUTA" if position_id < 8 else "FINALIZADA"
|
|
route.updated_at = datetime.utcnow()
|
|
|
|
alert = None
|
|
if position_id == 4:
|
|
alert = create_alert(
|
|
db,
|
|
route,
|
|
operator,
|
|
"TRUCK_PROXIMITY",
|
|
"Camión cercano",
|
|
f"El camión {route.truck_id} está a menos de 15 minutos de la zona asignada.",
|
|
2,
|
|
)
|
|
elif position_id == 8:
|
|
alert = create_alert(
|
|
db,
|
|
route,
|
|
operator,
|
|
"ROUTE_COMPLETED",
|
|
"Servicio finalizado",
|
|
f"El camión {route.truck_id} finalizó la ruta {route.route_id}.",
|
|
1,
|
|
)
|
|
|
|
return action_response(db, route, alert, "Avance de ruta actualizado")
|
|
|
|
|
|
@router.post("/routes/{route_id}/delay", response_model=OperatorActionOut)
|
|
def delay_route(
|
|
route_id: str,
|
|
db: Session = Depends(get_db),
|
|
operator: User = Depends(require_role("operador")),
|
|
):
|
|
route = get_assigned_route(db, route_id, operator)
|
|
route.status = "RETRASO"
|
|
route.updated_at = datetime.utcnow()
|
|
alert = create_alert(
|
|
db,
|
|
route,
|
|
operator,
|
|
"DELAY",
|
|
"Retraso operativo",
|
|
f"La ruta {route.route_id} presenta un retraso aproximado de 25 minutos.",
|
|
2,
|
|
)
|
|
return action_response(db, route, alert, "Retraso reportado")
|
|
|
|
|
|
@router.post("/routes/{route_id}/breakdown", response_model=OperatorActionOut)
|
|
def breakdown_route(
|
|
route_id: str,
|
|
db: Session = Depends(get_db),
|
|
operator: User = Depends(require_role("operador")),
|
|
):
|
|
route = get_assigned_route(db, route_id, operator)
|
|
route.status = "AVERIA"
|
|
route.updated_at = datetime.utcnow()
|
|
alert = create_alert(
|
|
db,
|
|
route,
|
|
operator,
|
|
"MECHANICAL_FAILURE",
|
|
"Avería mecánica",
|
|
f"El camión {route.truck_id} presenta falla mecánica. Se requiere apoyo logístico.",
|
|
3,
|
|
)
|
|
return action_response(db, route, alert, "Avería reportada")
|
|
|
|
|
|
@router.post("/routes/{route_id}/incident", response_model=OperatorActionOut)
|
|
def incident_route(
|
|
route_id: str,
|
|
db: Session = Depends(get_db),
|
|
operator: User = Depends(require_role("operador")),
|
|
):
|
|
route = get_assigned_route(db, route_id, operator)
|
|
route.status = "INCIDENCIA"
|
|
route.updated_at = datetime.utcnow()
|
|
alert = create_alert(
|
|
db,
|
|
route,
|
|
operator,
|
|
"INCIDENT",
|
|
"Incidencia en ruta",
|
|
f"Se registró una incidencia menor en {route.route_id}: obstrucción vial o exceso de residuos.",
|
|
2,
|
|
)
|
|
return action_response(db, route, alert, "Incidencia reportada")
|
|
|
|
|
|
@router.post("/routes/{route_id}/complete", response_model=OperatorActionOut)
|
|
def complete_route(
|
|
route_id: str,
|
|
db: Session = Depends(get_db),
|
|
operator: User = Depends(require_role("operador")),
|
|
):
|
|
route = get_assigned_route(db, route_id, operator)
|
|
route.status = "FINALIZADA"
|
|
route.current_position_id = 8
|
|
route.updated_at = datetime.utcnow()
|
|
alert = create_alert(
|
|
db,
|
|
route,
|
|
operator,
|
|
"ROUTE_COMPLETED",
|
|
"Servicio finalizado",
|
|
f"El operador finalizó la ruta {route.route_id}.",
|
|
1,
|
|
)
|
|
return action_response(db, route, alert, "Ruta finalizada")
|