155 lines
5.6 KiB
Python
155 lines
5.6 KiB
Python
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.orm import Session
|
|
from datetime import datetime, date
|
|
from typing import Optional
|
|
import uuid
|
|
|
|
from backend.database import get_db
|
|
from backend.models import DeploymentRecord, RosterUnit
|
|
|
|
router = APIRouter(prefix="/api", tags=["deployments"])
|
|
|
|
|
|
def _serialize(record: DeploymentRecord) -> dict:
|
|
return {
|
|
"id": record.id,
|
|
"unit_id": record.unit_id,
|
|
"deployed_date": record.deployed_date.isoformat() if record.deployed_date else None,
|
|
"estimated_removal_date": record.estimated_removal_date.isoformat() if record.estimated_removal_date else None,
|
|
"actual_removal_date": record.actual_removal_date.isoformat() if record.actual_removal_date else None,
|
|
"project_ref": record.project_ref,
|
|
"project_id": record.project_id,
|
|
"location_name": record.location_name,
|
|
"notes": record.notes,
|
|
"created_at": record.created_at.isoformat() if record.created_at else None,
|
|
"updated_at": record.updated_at.isoformat() if record.updated_at else None,
|
|
"is_active": record.actual_removal_date is None,
|
|
}
|
|
|
|
|
|
@router.get("/deployments/{unit_id}")
|
|
def get_deployments(unit_id: str, db: Session = Depends(get_db)):
|
|
"""Get all deployment records for a unit, newest first."""
|
|
unit = db.query(RosterUnit).filter_by(id=unit_id).first()
|
|
if not unit:
|
|
raise HTTPException(status_code=404, detail=f"Unit {unit_id} not found")
|
|
|
|
records = (
|
|
db.query(DeploymentRecord)
|
|
.filter_by(unit_id=unit_id)
|
|
.order_by(DeploymentRecord.deployed_date.desc(), DeploymentRecord.created_at.desc())
|
|
.all()
|
|
)
|
|
return {"deployments": [_serialize(r) for r in records]}
|
|
|
|
|
|
@router.get("/deployments/{unit_id}/active")
|
|
def get_active_deployment(unit_id: str, db: Session = Depends(get_db)):
|
|
"""Get the current active deployment (actual_removal_date is NULL), or null."""
|
|
record = (
|
|
db.query(DeploymentRecord)
|
|
.filter(
|
|
DeploymentRecord.unit_id == unit_id,
|
|
DeploymentRecord.actual_removal_date == None
|
|
)
|
|
.order_by(DeploymentRecord.created_at.desc())
|
|
.first()
|
|
)
|
|
return {"deployment": _serialize(record) if record else None}
|
|
|
|
|
|
@router.post("/deployments/{unit_id}")
|
|
def create_deployment(unit_id: str, payload: dict, db: Session = Depends(get_db)):
|
|
"""
|
|
Create a new deployment record for a unit.
|
|
|
|
Body fields (all optional):
|
|
deployed_date (YYYY-MM-DD)
|
|
estimated_removal_date (YYYY-MM-DD)
|
|
project_ref (freeform string)
|
|
project_id (UUID if linked to Project)
|
|
location_name
|
|
notes
|
|
"""
|
|
unit = db.query(RosterUnit).filter_by(id=unit_id).first()
|
|
if not unit:
|
|
raise HTTPException(status_code=404, detail=f"Unit {unit_id} not found")
|
|
|
|
def parse_date(val) -> Optional[date]:
|
|
if not val:
|
|
return None
|
|
if isinstance(val, date):
|
|
return val
|
|
return date.fromisoformat(str(val))
|
|
|
|
record = DeploymentRecord(
|
|
id=str(uuid.uuid4()),
|
|
unit_id=unit_id,
|
|
deployed_date=parse_date(payload.get("deployed_date")),
|
|
estimated_removal_date=parse_date(payload.get("estimated_removal_date")),
|
|
actual_removal_date=None,
|
|
project_ref=payload.get("project_ref"),
|
|
project_id=payload.get("project_id"),
|
|
location_name=payload.get("location_name"),
|
|
notes=payload.get("notes"),
|
|
created_at=datetime.utcnow(),
|
|
updated_at=datetime.utcnow(),
|
|
)
|
|
db.add(record)
|
|
db.commit()
|
|
db.refresh(record)
|
|
return _serialize(record)
|
|
|
|
|
|
@router.put("/deployments/{unit_id}/{deployment_id}")
|
|
def update_deployment(unit_id: str, deployment_id: str, payload: dict, db: Session = Depends(get_db)):
|
|
"""
|
|
Update a deployment record. Used for:
|
|
- Setting/changing estimated_removal_date
|
|
- Closing a deployment (set actual_removal_date to mark unit returned)
|
|
- Editing project_ref, location_name, notes
|
|
"""
|
|
record = db.query(DeploymentRecord).filter_by(id=deployment_id, unit_id=unit_id).first()
|
|
if not record:
|
|
raise HTTPException(status_code=404, detail="Deployment record not found")
|
|
|
|
def parse_date(val) -> Optional[date]:
|
|
if val is None:
|
|
return None
|
|
if val == "":
|
|
return None
|
|
if isinstance(val, date):
|
|
return val
|
|
return date.fromisoformat(str(val))
|
|
|
|
if "deployed_date" in payload:
|
|
record.deployed_date = parse_date(payload["deployed_date"])
|
|
if "estimated_removal_date" in payload:
|
|
record.estimated_removal_date = parse_date(payload["estimated_removal_date"])
|
|
if "actual_removal_date" in payload:
|
|
record.actual_removal_date = parse_date(payload["actual_removal_date"])
|
|
if "project_ref" in payload:
|
|
record.project_ref = payload["project_ref"]
|
|
if "project_id" in payload:
|
|
record.project_id = payload["project_id"]
|
|
if "location_name" in payload:
|
|
record.location_name = payload["location_name"]
|
|
if "notes" in payload:
|
|
record.notes = payload["notes"]
|
|
|
|
record.updated_at = datetime.utcnow()
|
|
db.commit()
|
|
db.refresh(record)
|
|
return _serialize(record)
|
|
|
|
|
|
@router.delete("/deployments/{unit_id}/{deployment_id}")
|
|
def delete_deployment(unit_id: str, deployment_id: str, db: Session = Depends(get_db)):
|
|
"""Delete a deployment record."""
|
|
record = db.query(DeploymentRecord).filter_by(id=deployment_id, unit_id=unit_id).first()
|
|
if not record:
|
|
raise HTTPException(status_code=404, detail="Deployment record not found")
|
|
db.delete(record)
|
|
db.commit()
|
|
return {"ok": True}
|