Files
AidsMonitoring/backend/routers/aids.py
T

163 lines
5.5 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from database import get_db
from models.aid import Aid
from models.lamp import Lamp
from models.vessel import RecordingEvent
from models.user import User
from routers.auth import get_current_user, require_admin
from routers.lamps import compute_thresholds
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
import uuid
router = APIRouter(prefix="/aids", tags=["aids"])
def _aid_dict(aid: Aid, lamp: Lamp | None) -> dict:
"""Aid → JSON, with embedded lamp + computed battery thresholds."""
base = {c.name: getattr(aid, c.name) for c in aid.__table__.columns}
base["lamp"] = None
base["battery_warn_v"] = None
base["battery_alarm_v"] = None
if lamp:
th = compute_thresholds(lamp.voltage_min, lamp.voltage_max)
base["lamp"] = {
"id": lamp.id, "manufacturer": lamp.manufacturer, "model": lamp.model,
"lamp_count": lamp.lamp_count,
"voltage_min": lamp.voltage_min, "voltage_max": lamp.voltage_max,
**th,
}
base["battery_warn_v"] = th["warn_v"]
base["battery_alarm_v"] = th["alarm_v"]
return base
def _lamp_for(aid: Aid, db: Session) -> Lamp | None:
if not aid.lamp_id: return None
return db.query(Lamp).filter(Lamp.id == aid.lamp_id).first()
class AidCreate(BaseModel):
nombre: str
numero_interno: Optional[str] = None
categoria: str
tipo: str
tipo_ais: str = "SIN_AIS"
lat_nominal: float
lon_nominal: float
fuente_posicion: str = "MANUAL"
radio_borneo_m: float = 10.0
mmsi: Optional[str] = None
caracteristica_luz: Optional[str] = None
alcance_nm: Optional[float] = None
class AidUpdate(BaseModel):
lat_nominal: Optional[float] = None
lon_nominal: Optional[float] = None
puerto_responsable: Optional[str] = None
empresa_responsable: Optional[str] = None
caracteristica_luz: Optional[str] = None
alcance_nm: Optional[float] = None
radio_borneo_m: Optional[float] = None
observaciones: Optional[str] = None
lamp_id: Optional[str] = None
motivo_cambio: str
modificado_por: str
class LampAssign(BaseModel):
lamp_id: Optional[str] = None # None → unassign
@router.get("/")
def list_aids(db: Session = Depends(get_db)):
aids = db.query(Aid).filter(Aid.activa == True).all()
return [_aid_dict(a, _lamp_for(a, db)) for a in aids]
@router.get("/{aid_id}")
def get_aid(aid_id: str, db: Session = Depends(get_db)):
aid = db.query(Aid).filter(Aid.id == aid_id).first()
if not aid:
raise HTTPException(status_code=404, detail="Ayuda no encontrada")
return _aid_dict(aid, _lamp_for(aid, db))
@router.patch("/{aid_id}/lamp")
def assign_lamp(aid_id: str, data: LampAssign, db: Session = Depends(get_db)):
aid = db.query(Aid).filter(Aid.id == aid_id).first()
if not aid:
raise HTTPException(404, "Ayuda no encontrada")
if data.lamp_id:
lamp = db.query(Lamp).filter(Lamp.id == data.lamp_id).first()
if not lamp:
raise HTTPException(404, "Lamp not found")
aid.lamp_id = data.lamp_id
db.commit(); db.refresh(aid)
return _aid_dict(aid, _lamp_for(aid, db))
@router.post("/")
def create_aid(data: AidCreate, db: Session = Depends(get_db)):
aid = Aid(id=str(uuid.uuid4()), **data.model_dump())
db.add(aid)
db.commit()
db.refresh(aid)
return aid
@router.put("/{aid_id}")
def update_aid(aid_id: str, data: AidUpdate, db: Session = Depends(get_db),
current_user: User = Depends(require_admin)):
aid = db.query(Aid).filter(Aid.id == aid_id).first()
if not aid:
raise HTTPException(status_code=404, detail="Ayuda no encontrada")
for field in ["lat_nominal", "lon_nominal", "puerto_responsable",
"empresa_responsable", "caracteristica_luz", "alcance_nm",
"radio_borneo_m", "observaciones", "lamp_id"]:
val = getattr(data, field)
if val is not None:
setattr(aid, field, val)
aid.motivo_cambio = data.motivo_cambio
aid.modificado_por = data.modificado_por
aid.modificado_en = datetime.utcnow()
db.commit()
db.refresh(aid)
return aid
@router.get("/recordings", tags=["recordings"])
def list_recordings(
db: Session = Depends(get_db),
from_: str = Query(None, alias="from"),
to: str = Query(None),
mmsi: str = Query(None),
):
q = db.query(RecordingEvent)
if mmsi:
q = q.filter(RecordingEvent.mmsi.ilike(f"%{mmsi}%"))
if from_:
try:
q = q.filter(RecordingEvent.inicio >= datetime.fromisoformat(from_))
except Exception:
pass
if to:
try:
q = q.filter(RecordingEvent.inicio <= datetime.fromisoformat(to + "T23:59:59"))
except Exception:
pass
recs = q.order_by(RecordingEvent.inicio.desc()).limit(200).all()
return [
{
"id": r.id,
"mmsi": r.mmsi,
"vessel_nombre": r.mmsi,
"aid_id": r.aid_id,
"aid_nombre": r.aid_id,
"inicio_utc": r.inicio.isoformat() if r.inicio else None,
"fin_utc": r.fin.isoformat() if r.fin else None,
"distancia_min_m": round(r.distancia_min_m, 1) if r.distancia_min_m else None,
"trigger": r.trigger,
"cerrado": r.cerrado,
}
for r in recs
]