diff --git a/backend/app/models.py b/backend/app/models.py index 5887c13..c2a2c4e 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -87,6 +87,7 @@ class Dewar(Base): dewar_serial_number = relationship("DewarSerialNumber") slot = relationship("Slot", back_populates="dewar") events = relationship("LogisticsEvent", back_populates="dewar") + beamline_location = None @property def number_of_pucks(self) -> int: diff --git a/backend/app/routers/logistics.py b/backend/app/routers/logistics.py index ee499e1..5295e22 100644 --- a/backend/app/routers/logistics.py +++ b/backend/app/routers/logistics.py @@ -1,4 +1,5 @@ from fastapi import APIRouter, HTTPException, Depends +from pydantic import ValidationError from sqlalchemy.orm import Session, joinedload from typing import List, Optional from ..models import Dewar as DewarModel, Slot as SlotModel, LogisticsEvent as LogisticsEventModel @@ -24,55 +25,46 @@ def calculate_time_until_refill(last_refill: Optional[datetime], refill_interval return max(0, int(time_until_next_refill.total_seconds())) -@router.post("/dewars/retrieve", response_model=DewarSchema) -async def retrieve_or_return_dewar(data: LogisticsEventCreate, db: Session = Depends(get_db)): - logger.info(f"Received data for retrieve_or_return_dewar: {data}") - dewar = db.query(DewarModel).filter(DewarModel.unique_id == data.dewar_qr_code).first() - if not dewar: - raise HTTPException(status_code=404, detail="Dewar not found") +@router.post("/dewars/return", response_model=DewarSchema) +async def return_to_storage(data: LogisticsEventCreate, db: Session = Depends(get_db)): + logger.info(f"Returning dewar to storage: {data.dewar_qr_code} at location {data.location_qr_code}") - # Fetch the associated slot if it exists - slot = db.query(SlotModel).filter(SlotModel.qr_code == data.location_qr_code).first() - if not slot: - raise HTTPException(status_code=404, detail="Slot not found") + try: + # Log the incoming payload + logger.info("Received payload: %s", data.json()) - # Check the last event for this dewar to determine the next action - last_event = db.query(LogisticsEventModel).filter( - LogisticsEventModel.dewar_id == dewar.id - ).order_by(LogisticsEventModel.timestamp.desc()).first() + dewar = db.query(DewarModel).filter(DewarModel.unique_id == data.dewar_qr_code).first() + if not dewar: + logger.error(f"Dewar not found for unique ID: {data.dewar_qr_code}") + raise HTTPException(status_code=404, detail="Dewar not found") - if last_event and last_event.event_type == "retrieved": - # Create a 'returned' event if the last event was 'retrieved' - new_event = LogisticsEventModel( - dewar_id=dewar.id, - slot_id=slot.id, - event_type="returned", - timestamp=datetime.now(), - ) - dewar.last_retrieved_timestamp = None + slot = db.query(SlotModel).filter(SlotModel.qr_code == data.location_qr_code).first() + if not slot: + logger.error(f"Slot not found for QR code: {data.location_qr_code}") + raise HTTPException(status_code=404, detail="Slot not found") - # Update the location to the slot + if slot.occupied and slot.dewar_unique_id != data.dewar_qr_code: + logger.error(f"Slot {data.location_qr_code} is already occupied by another dewar") + raise HTTPException(status_code=400, detail="Selected slot is already occupied by another dewar") + + # Update slot with dewar information slot.dewar_unique_id = dewar.unique_id slot.occupied = True + dewar.last_retrieved_timestamp = None + + # Log the event + log_event(db, dewar.id, slot.id, "returned") db.commit() - logger.info(f"Dewar {dewar.unique_id} returned to slot {slot.qr_code} successfully.") - else: - # Create a 'retrieved' event - new_event = LogisticsEventModel( - dewar_id=dewar.id, - slot_id=None, - event_type="retrieved", - timestamp=datetime.now(), - ) - dewar.last_retrieved_timestamp = new_event.timestamp.isoformat() - logger.info(f"Dewar {dewar.unique_id} retrieved successfully.") - - db.add(new_event) - db.commit() - db.refresh(dewar) - - return dewar + logger.info(f"Dewar {data.dewar_qr_code} successfully returned to storage slot {slot.qr_code}.") + db.refresh(dewar) + return dewar + except ValidationError as e: + logger.error(f"Validation error: {e.json()}") + raise HTTPException(status_code=400, detail="Invalid payload") + except Exception as e: + logger.error(f"Unexpected error: {str(e)}") + raise HTTPException(status_code=500, detail="Internal server error") @router.post("/dewar/scan", response_model=dict) async def scan_dewar(event_data: LogisticsEventCreate, db: Session = Depends(get_db)): @@ -109,13 +101,12 @@ async def scan_dewar(event_data: LogisticsEventCreate, db: Session = Depends(get logger.error(f"Beamline location not found: {location_qr_code}") raise HTTPException(status_code=400, detail="Beamline location not found") dewar.beamline_location = location_qr_code + logger.info(f"Dewar {dewar_qr_code} assigned to beamline {location_qr_code}") log_event(db, dewar.id, slot.id if slot else None, transaction_type) - db.commit() return {"message": "Status updated successfully"} - @router.get("/slots", response_model=List[SlotSchema]) async def get_all_slots(db: Session = Depends(get_db)): slots = db.query(SlotModel).options(joinedload(SlotModel.dewar)).all()