""" Roster Unit Rename Router Provides endpoint for safely renaming unit IDs across all database tables. """ from fastapi import APIRouter, Depends, HTTPException, Form from sqlalchemy.orm import Session from datetime import datetime import logging from backend.database import get_db from backend.models import RosterUnit, Emitter, UnitHistory from backend.routers.roster_edit import record_history, sync_slm_to_slmm_cache router = APIRouter(prefix="/api/roster", tags=["roster-rename"]) logger = logging.getLogger(__name__) @router.post("/rename") async def rename_unit( old_id: str = Form(...), new_id: str = Form(...), db: Session = Depends(get_db) ): """ Rename a unit ID across all tables. Updates the unit ID in roster, emitters, unit_history, and all foreign key references. IMPORTANT: This operation updates the primary key, which affects all relationships. """ # Validate input if not old_id or not new_id: raise HTTPException(status_code=400, detail="Both old_id and new_id are required") if old_id == new_id: raise HTTPException(status_code=400, detail="New ID must be different from old ID") # Check if old unit exists old_unit = db.query(RosterUnit).filter(RosterUnit.id == old_id).first() if not old_unit: raise HTTPException(status_code=404, detail=f"Unit '{old_id}' not found") # Check if new ID already exists existing_unit = db.query(RosterUnit).filter(RosterUnit.id == new_id).first() if existing_unit: raise HTTPException(status_code=409, detail=f"Unit ID '{new_id}' already exists") device_type = old_unit.device_type try: # Record history for the rename operation (using old_id since that's still valid) record_history( db=db, unit_id=old_id, change_type="id_change", field_name="id", old_value=old_id, new_value=new_id, source="manual", notes=f"Unit renamed from '{old_id}' to '{new_id}'" ) # Update roster table (primary) old_unit.id = new_id old_unit.last_updated = datetime.utcnow() # Update emitters table emitter = db.query(Emitter).filter(Emitter.id == old_id).first() if emitter: emitter.id = new_id # Update unit_history table (all entries for this unit) db.query(UnitHistory).filter(UnitHistory.unit_id == old_id).update( {"unit_id": new_id}, synchronize_session=False ) # Update deployed_with_modem_id references (units that reference this as modem) db.query(RosterUnit).filter(RosterUnit.deployed_with_modem_id == old_id).update( {"deployed_with_modem_id": new_id}, synchronize_session=False ) # Update unit_assignments table (if exists) try: from backend.models import UnitAssignment db.query(UnitAssignment).filter(UnitAssignment.unit_id == old_id).update( {"unit_id": new_id}, synchronize_session=False ) except Exception as e: logger.warning(f"Could not update unit_assignments: {e}") # Update recording_sessions table (if exists) try: from backend.models import RecordingSession db.query(RecordingSession).filter(RecordingSession.unit_id == old_id).update( {"unit_id": new_id}, synchronize_session=False ) except Exception as e: logger.warning(f"Could not update recording_sessions: {e}") # Commit all changes db.commit() # If sound level meter, sync updated config to SLMM cache if device_type == "sound_level_meter": logger.info(f"Syncing renamed SLM {new_id} (was {old_id}) config to SLMM cache...") result = await sync_slm_to_slmm_cache( unit_id=new_id, host=old_unit.slm_host, tcp_port=old_unit.slm_tcp_port, ftp_port=old_unit.slm_ftp_port, deployed_with_modem_id=old_unit.deployed_with_modem_id, db=db ) if not result["success"]: logger.warning(f"SLMM cache sync warning for renamed unit {new_id}: {result['message']}") logger.info(f"Successfully renamed unit '{old_id}' to '{new_id}'") return { "success": True, "message": f"Successfully renamed unit from '{old_id}' to '{new_id}'", "old_id": old_id, "new_id": new_id, "device_type": device_type } except Exception as e: db.rollback() logger.error(f"Error renaming unit '{old_id}' to '{new_id}': {e}") raise HTTPException( status_code=500, detail=f"Failed to rename unit: {str(e)}" )