183 lines
6.1 KiB
Python
183 lines
6.1 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Form, UploadFile, File
|
|
from sqlalchemy.orm import Session
|
|
from datetime import datetime
|
|
import csv
|
|
import io
|
|
|
|
from backend.database import get_db
|
|
from backend.models import RosterUnit
|
|
|
|
router = APIRouter(prefix="/api/roster", tags=["roster-edit"])
|
|
|
|
|
|
def get_or_create_roster_unit(db: Session, unit_id: str):
|
|
unit = db.query(RosterUnit).filter(RosterUnit.id == unit_id).first()
|
|
if not unit:
|
|
unit = RosterUnit(id=unit_id)
|
|
db.add(unit)
|
|
db.commit()
|
|
db.refresh(unit)
|
|
return unit
|
|
|
|
|
|
@router.post("/add")
|
|
def add_roster_unit(
|
|
id: str = Form(...),
|
|
unit_type: str = Form("series3"),
|
|
deployed: bool = Form(False),
|
|
note: str = Form(""),
|
|
project_id: str = Form(None),
|
|
location: str = Form(None),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
if db.query(RosterUnit).filter(RosterUnit.id == id).first():
|
|
raise HTTPException(status_code=400, detail="Unit already exists")
|
|
|
|
unit = RosterUnit(
|
|
id=id,
|
|
unit_type=unit_type,
|
|
deployed=deployed,
|
|
note=note,
|
|
project_id=project_id,
|
|
location=location,
|
|
last_updated=datetime.utcnow(),
|
|
)
|
|
db.add(unit)
|
|
db.commit()
|
|
return {"message": "Unit added", "id": id}
|
|
|
|
|
|
@router.post("/set-deployed/{unit_id}")
|
|
def set_deployed(unit_id: str, deployed: bool = Form(...), db: Session = Depends(get_db)):
|
|
unit = get_or_create_roster_unit(db, unit_id)
|
|
unit.deployed = deployed
|
|
unit.last_updated = datetime.utcnow()
|
|
db.commit()
|
|
return {"message": "Updated", "id": unit_id, "deployed": deployed}
|
|
|
|
|
|
@router.post("/set-retired/{unit_id}")
|
|
def set_retired(unit_id: str, retired: bool = Form(...), db: Session = Depends(get_db)):
|
|
unit = get_or_create_roster_unit(db, unit_id)
|
|
unit.retired = retired
|
|
unit.last_updated = datetime.utcnow()
|
|
db.commit()
|
|
return {"message": "Updated", "id": unit_id, "retired": retired}
|
|
|
|
|
|
@router.post("/set-note/{unit_id}")
|
|
def set_note(unit_id: str, note: str = Form(""), db: Session = Depends(get_db)):
|
|
unit = get_or_create_roster_unit(db, unit_id)
|
|
unit.note = note
|
|
unit.last_updated = datetime.utcnow()
|
|
db.commit()
|
|
return {"message": "Updated", "id": unit_id, "note": note}
|
|
|
|
|
|
@router.post("/import-csv")
|
|
async def import_csv(
|
|
file: UploadFile = File(...),
|
|
update_existing: bool = Form(True),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""
|
|
Import roster units from CSV file.
|
|
|
|
Expected CSV columns (unit_id is required, others are optional):
|
|
- unit_id: Unique identifier for the unit
|
|
- unit_type: Type of unit (default: "series3")
|
|
- deployed: Boolean for deployment status (default: False)
|
|
- retired: Boolean for retirement status (default: False)
|
|
- note: Notes about the unit
|
|
- project_id: Project identifier
|
|
- location: Location description
|
|
|
|
Args:
|
|
file: CSV file upload
|
|
update_existing: If True, update existing units; if False, skip them
|
|
"""
|
|
|
|
if not file.filename.endswith('.csv'):
|
|
raise HTTPException(status_code=400, detail="File must be a CSV")
|
|
|
|
# Read file content
|
|
contents = await file.read()
|
|
csv_text = contents.decode('utf-8')
|
|
csv_reader = csv.DictReader(io.StringIO(csv_text))
|
|
|
|
results = {
|
|
"added": [],
|
|
"updated": [],
|
|
"skipped": [],
|
|
"errors": []
|
|
}
|
|
|
|
for row_num, row in enumerate(csv_reader, start=2): # Start at 2 to account for header
|
|
try:
|
|
# Validate required field
|
|
unit_id = row.get('unit_id', '').strip()
|
|
if not unit_id:
|
|
results["errors"].append({
|
|
"row": row_num,
|
|
"error": "Missing required field: unit_id"
|
|
})
|
|
continue
|
|
|
|
# Check if unit exists
|
|
existing_unit = db.query(RosterUnit).filter(RosterUnit.id == unit_id).first()
|
|
|
|
if existing_unit:
|
|
if not update_existing:
|
|
results["skipped"].append(unit_id)
|
|
continue
|
|
|
|
# Update existing unit
|
|
existing_unit.unit_type = row.get('unit_type', existing_unit.unit_type or 'series3')
|
|
existing_unit.deployed = row.get('deployed', '').lower() in ('true', '1', 'yes') if row.get('deployed') else existing_unit.deployed
|
|
existing_unit.retired = row.get('retired', '').lower() in ('true', '1', 'yes') if row.get('retired') else existing_unit.retired
|
|
existing_unit.note = row.get('note', existing_unit.note or '')
|
|
existing_unit.project_id = row.get('project_id', existing_unit.project_id)
|
|
existing_unit.location = row.get('location', existing_unit.location)
|
|
existing_unit.last_updated = datetime.utcnow()
|
|
|
|
results["updated"].append(unit_id)
|
|
else:
|
|
# Create new unit
|
|
new_unit = RosterUnit(
|
|
id=unit_id,
|
|
unit_type=row.get('unit_type', 'series3'),
|
|
deployed=row.get('deployed', '').lower() in ('true', '1', 'yes'),
|
|
retired=row.get('retired', '').lower() in ('true', '1', 'yes'),
|
|
note=row.get('note', ''),
|
|
project_id=row.get('project_id'),
|
|
location=row.get('location'),
|
|
last_updated=datetime.utcnow()
|
|
)
|
|
db.add(new_unit)
|
|
results["added"].append(unit_id)
|
|
|
|
except Exception as e:
|
|
results["errors"].append({
|
|
"row": row_num,
|
|
"unit_id": row.get('unit_id', 'unknown'),
|
|
"error": str(e)
|
|
})
|
|
|
|
# Commit all changes
|
|
try:
|
|
db.commit()
|
|
except Exception as e:
|
|
db.rollback()
|
|
raise HTTPException(status_code=500, detail=f"Database error: {str(e)}")
|
|
|
|
return {
|
|
"message": "CSV import completed",
|
|
"summary": {
|
|
"added": len(results["added"]),
|
|
"updated": len(results["updated"]),
|
|
"skipped": len(results["skipped"]),
|
|
"errors": len(results["errors"])
|
|
},
|
|
"details": results
|
|
}
|