feat: test version of unit swap tool.
This commit is contained in:
@@ -291,6 +291,14 @@ async def pending_deployments_page(request: Request):
|
||||
return templates.TemplateResponse("admin/pending_deployments.html", {"request": request})
|
||||
|
||||
|
||||
@app.get("/tools/unit-swap", response_class=HTMLResponse)
|
||||
async def unit_swap_page(request: Request):
|
||||
"""Mobile-first wizard for swapping a vibration unit (and optionally its
|
||||
modem) at a monitoring location. Pick project → location → incoming
|
||||
unit → modem decision → confirm → optional photo of the new install."""
|
||||
return templates.TemplateResponse("admin/unit_swap.html", {"request": request})
|
||||
|
||||
|
||||
@app.get("/modems", response_class=HTMLResponse)
|
||||
async def modems_page(request: Request):
|
||||
"""Field modems management dashboard"""
|
||||
|
||||
@@ -262,6 +262,83 @@ async def get_project_locations_json(
|
||||
]
|
||||
|
||||
|
||||
@router.get("/locations-with-assignments")
|
||||
async def get_locations_with_assignments(
|
||||
project_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
location_type: Optional[str] = Query(None),
|
||||
):
|
||||
"""
|
||||
Locations + their currently-active assignment + current unit + paired modem
|
||||
in one call. Used by the Unit Swap tool's location picker so a field tech
|
||||
can see what's deployed where without N+1 round-trips.
|
||||
|
||||
Empty locations come back with assignment/unit/modem all null.
|
||||
Removed locations are always excluded — you don't swap onto a dead slot.
|
||||
"""
|
||||
project = db.query(Project).filter_by(id=project_id).first()
|
||||
if not project:
|
||||
raise HTTPException(status_code=404, detail="Project not found")
|
||||
|
||||
query = db.query(MonitoringLocation).filter_by(project_id=project_id).filter(
|
||||
MonitoringLocation.removed_at == None # noqa: E711
|
||||
)
|
||||
if location_type:
|
||||
query = query.filter_by(location_type=location_type)
|
||||
locations = query.order_by(MonitoringLocation.sort_order, MonitoringLocation.name).all()
|
||||
|
||||
results = []
|
||||
for loc in locations:
|
||||
assignment = db.query(UnitAssignment).filter(
|
||||
and_(
|
||||
UnitAssignment.location_id == loc.id,
|
||||
UnitAssignment.assigned_until == None, # noqa: E711
|
||||
)
|
||||
).first()
|
||||
|
||||
unit_payload = None
|
||||
modem_payload = None
|
||||
if assignment:
|
||||
unit = db.query(RosterUnit).filter_by(id=assignment.unit_id).first()
|
||||
if unit:
|
||||
unit_payload = {
|
||||
"id": unit.id,
|
||||
"device_type": unit.device_type,
|
||||
"unit_type": unit.unit_type,
|
||||
"slm_model": unit.slm_model,
|
||||
"deployed_with_modem_id": unit.deployed_with_modem_id,
|
||||
}
|
||||
if unit.deployed_with_modem_id:
|
||||
modem = db.query(RosterUnit).filter_by(
|
||||
id=unit.deployed_with_modem_id, device_type="modem"
|
||||
).first()
|
||||
if modem:
|
||||
modem_payload = {
|
||||
"id": modem.id,
|
||||
"hardware_model": modem.hardware_model,
|
||||
"ip_address": modem.ip_address,
|
||||
"phone_number": modem.phone_number,
|
||||
}
|
||||
|
||||
results.append({
|
||||
"id": loc.id,
|
||||
"name": loc.name,
|
||||
"location_type": loc.location_type,
|
||||
"description": loc.description,
|
||||
"address": loc.address,
|
||||
"coordinates": loc.coordinates,
|
||||
"assignment": {
|
||||
"id": assignment.id,
|
||||
"assigned_at": assignment.assigned_at.isoformat() if assignment.assigned_at else None,
|
||||
"notes": assignment.notes,
|
||||
} if assignment else None,
|
||||
"unit": unit_payload,
|
||||
"modem": modem_payload,
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
|
||||
@router.post("/locations/create")
|
||||
async def create_location(
|
||||
project_id: str,
|
||||
@@ -1192,6 +1269,17 @@ async def swap_unit_on_location(
|
||||
new_value=f"swapped out → {unit_id}",
|
||||
notes=notes,
|
||||
)
|
||||
# Clear the outgoing unit's modem pairing so the bidirectional
|
||||
# deployed_with_modem_id / deployed_with_unit_id back-reference
|
||||
# doesn't orphan onto the unit that just left the field.
|
||||
old_unit = db.query(RosterUnit).filter_by(id=current.unit_id).first()
|
||||
if old_unit and old_unit.deployed_with_modem_id:
|
||||
old_modem = db.query(RosterUnit).filter_by(
|
||||
id=old_unit.deployed_with_modem_id, device_type="modem"
|
||||
).first()
|
||||
if old_modem and old_modem.deployed_with_unit_id == current.unit_id:
|
||||
old_modem.deployed_with_unit_id = None
|
||||
old_unit.deployed_with_modem_id = None
|
||||
|
||||
# Create new assignment
|
||||
new_assignment = UnitAssignment(
|
||||
|
||||
Reference in New Issue
Block a user