Files
AidsMonitoring/backend/routers/org.py
T

241 lines
8.7 KiB
Python

"""
Organization CRUD: Ports, Companies, BuoyOwnership.
GET /org/ports → list ports
POST /org/ports → create port (admin)
PUT /org/ports/{id} → update port (admin)
GET /org/companies → list companies
POST /org/companies → create company (admin)
PUT /org/companies/{id} → update company (admin)
GET /org/companies/{company_id}/buoys → list owned buoys
POST /org/companies/{company_id}/buoys → assign buoy to company (admin)
DELETE /org/companies/{company_id}/buoys/{id} → remove ownership (admin)
GET /org/me/company → current user's company + port (for homepage)
"""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import Optional
import uuid
import os
import json
from database import get_db
from models.org import Port, Company, BuoyOwnership
from models.user import User
from models.aid import Aid
from routers.auth import get_current_user, require_admin
# Charts directory — one level above the backend package
_CHARTS_DIR = os.path.normpath(
os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', 'charts')
)
def _read_chart_bbox(chart_name: str) -> list | None:
"""Return [west, south, east, north] from the chart's meta.json, or None."""
if not chart_name:
return None
meta = os.path.join(_CHARTS_DIR, chart_name, 'meta.json')
try:
with open(meta, 'r', encoding='utf-8') as f:
data = json.load(f)
bbox = data.get('bbox')
if bbox and len(bbox) == 4:
return bbox # [minLon, minLat, maxLon, maxLat]
except Exception:
pass
return None
router = APIRouter(prefix="/org", tags=["org"])
# ── Ports ─────────────────────────────────────────────────────────────────────
@router.get("/ports")
def list_ports(db: Session = Depends(get_db)):
return [_port_dict(p) for p in db.query(Port).filter(Port.activo == True).all()]
@router.post("/ports", dependencies=[Depends(require_admin)])
def create_port(data: dict, db: Session = Depends(get_db)):
if not data.get("name"):
raise HTTPException(400, "name is required")
port = Port(
id=str(uuid.uuid4()),
name=data["name"],
country=data.get("country", "Colombia"),
center_lat=data.get("center_lat"),
center_lon=data.get("center_lon"),
default_zoom=data.get("default_zoom", 12.0),
chart_name=data.get("chart_name"),
)
db.add(port); db.commit()
return _port_dict(port)
@router.put("/ports/{port_id}", dependencies=[Depends(require_admin)])
def update_port(port_id: str, data: dict, db: Session = Depends(get_db)):
port = db.query(Port).filter(Port.id == port_id).first()
if not port:
raise HTTPException(404, "Port not found")
for field in ("name", "country", "center_lat", "center_lon",
"default_zoom", "chart_name", "activo"):
if field in data:
setattr(port, field, data[field])
db.commit()
return _port_dict(port)
def _port_dict(p: Port) -> dict:
return {
"id": p.id, "name": p.name, "country": p.country,
"center_lat": p.center_lat, "center_lon": p.center_lon,
"default_zoom": p.default_zoom, "chart_name": p.chart_name,
"chart_bbox": _read_chart_bbox(p.chart_name), # [W,S,E,N] or null
"activo": p.activo,
}
# ── Companies ─────────────────────────────────────────────────────────────────
@router.get("/companies")
def list_companies(db: Session = Depends(get_db)):
return [_company_dict(c) for c in db.query(Company).all()]
@router.post("/companies", dependencies=[Depends(require_admin)])
def create_company(data: dict, db: Session = Depends(get_db)):
if not data.get("name"):
raise HTTPException(400, "name is required")
company = Company(
id=str(uuid.uuid4()),
name=data["name"],
port_id=data.get("port_id"),
contact_email=data.get("contact_email"),
contact_phone=data.get("contact_phone"),
notas=data.get("notas"),
)
db.add(company); db.commit()
return _company_dict(company)
@router.put("/companies/{company_id}", dependencies=[Depends(require_admin)])
def update_company(company_id: str, data: dict, db: Session = Depends(get_db)):
company = db.query(Company).filter(Company.id == company_id).first()
if not company:
raise HTTPException(404, "Company not found")
for field in ("name", "port_id", "contact_email", "contact_phone", "notas", "activa"):
if field in data:
setattr(company, field, data[field])
db.commit()
return _company_dict(company)
def _company_dict(c: Company) -> dict:
return {
"id": c.id, "name": c.name, "port_id": c.port_id,
"contact_email": c.contact_email, "contact_phone": c.contact_phone,
"activa": c.activa, "notas": c.notas,
}
# ── Buoy Ownership ────────────────────────────────────────────────────────────
@router.get("/companies/{company_id}/buoys")
def list_company_buoys(company_id: str, db: Session = Depends(get_db)):
rows = db.query(BuoyOwnership).filter(BuoyOwnership.company_id == company_id).all()
result = []
for r in rows:
entry = {"id": r.id, "company_id": r.company_id,
"aid_id": r.aid_id, "mmsi": r.mmsi, "notas": r.notas}
# Enrich with aid name if available
if r.aid_id:
aid = db.query(Aid).filter(Aid.id == r.aid_id).first()
if aid:
entry["aid_nombre"] = aid.nombre
entry["mmsi"] = entry["mmsi"] or aid.mmsi
result.append(entry)
return result
@router.post("/companies/{company_id}/buoys", dependencies=[Depends(require_admin)])
def assign_buoy(company_id: str, data: dict, db: Session = Depends(get_db)):
"""
Assign a buoy to a company. Provide either aid_id or mmsi (or both).
If aid_id is given, mmsi is auto-filled from the Aid row.
"""
company = db.query(Company).filter(Company.id == company_id).first()
if not company:
raise HTTPException(404, "Company not found")
aid_id = data.get("aid_id")
mmsi = data.get("mmsi")
if not aid_id and not mmsi:
raise HTTPException(400, "Provide aid_id or mmsi")
if aid_id:
aid = db.query(Aid).filter(Aid.id == aid_id).first()
if not aid:
raise HTTPException(404, "Aid not found")
mmsi = mmsi or aid.mmsi # fill from aid if not explicitly given
# Prevent duplicate
existing = db.query(BuoyOwnership).filter(
BuoyOwnership.company_id == company_id,
BuoyOwnership.aid_id == aid_id,
).first()
if existing:
raise HTTPException(409, "Already assigned")
row = BuoyOwnership(
id=str(uuid.uuid4()),
company_id=company_id,
aid_id=aid_id,
mmsi=mmsi,
notas=data.get("notas"),
)
db.add(row); db.commit()
return {"ok": True, "id": row.id, "mmsi": mmsi}
@router.delete("/companies/{company_id}/buoys/{ownership_id}",
dependencies=[Depends(require_admin)])
def remove_ownership(company_id: str, ownership_id: str, db: Session = Depends(get_db)):
row = db.query(BuoyOwnership).filter(
BuoyOwnership.id == ownership_id,
BuoyOwnership.company_id == company_id,
).first()
if not row:
raise HTTPException(404, "Ownership record not found")
db.delete(row); db.commit()
return {"ok": True}
# ── Current user's company / home port ───────────────────────────────────────
@router.get("/me/company")
def my_company(
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
):
"""
Returns the company and port associated with the logged-in user.
Used by the frontend to set the default map view on login.
"""
company_id = getattr(current_user, "company_id", None)
if not company_id:
return {"company": None, "port": None}
company = db.query(Company).filter(Company.id == company_id).first()
port = db.query(Port).filter(Port.id == company.port_id).first() \
if company and company.port_id else None
return {
"company": _company_dict(company) if company else None,
"port": _port_dict(port) if port else None,
}