From c2215860bfb9260acb3eee43f87db1c9d846aa74 Mon Sep 17 00:00:00 2001 From: GotthardG <51994228+GotthardG@users.noreply.github.com> Date: Thu, 30 Jan 2025 13:39:49 +0100 Subject: [PATCH] Refactor Dewar service methods and improve field handling Updated Dewar API methods to use protected endpoints for enhanced security and consistency. Added `pgroups` handling in various frontend components and modified the LogisticsView contact field for clarity. Simplified backend router imports for better readability. --- backend/app/data/data.py | 136 +++++++++--- backend/app/models.py | 6 +- backend/app/routers/__init__.py | 2 +- backend/app/routers/dewar.py | 73 +++---- backend/app/routers/logistics.py | 8 +- backend/app/routers/protected_router.py | 2 + backend/app/routers/puck.py | 17 +- backend/app/routers/sample.py | 4 +- backend/app/routers/shipment.py | 18 +- backend/app/schemas.py | 18 +- backend/main.py | 8 +- frontend/src/components/DewarDetails.tsx | 21 +- .../src/components/SampleSpreadsheetGrid.tsx | 6 +- frontend/src/components/ShipmentDetails.tsx | 11 +- frontend/src/components/ShipmentPanel.tsx | 1 + frontend/src/components/SpreadsheetTable.tsx | 21 +- frontend/src/components/UploadDialog.tsx | 4 +- logistics/src/pages/LogisticsView.tsx | 8 +- pyproject.toml | 2 +- testfunctions.ipynb | 200 +++++++----------- 20 files changed, 304 insertions(+), 262 deletions(-) diff --git a/backend/app/data/data.py b/backend/app/data/data.py index d141b75..c846038 100644 --- a/backend/app/data/data.py +++ b/backend/app/data/data.py @@ -9,6 +9,7 @@ from app.models import ( DewarType, DewarSerialNumber, SampleEvent, + LogisticsEvent, ) from datetime import datetime, timedelta import random @@ -196,11 +197,11 @@ dewars = [ tracking_number="TRACK123", return_address_id=1, contact_id=1, - status="Ready for Shipping", - ready_date=datetime.strptime("2023-09-30", "%Y-%m-%d"), - shipping_date=None, - arrival_date=None, - returning_date=None, + status="active", + # ready_date=datetime.strptime("2023-09-30", "%Y-%m-%d"), + # shipping_date=None, + # arrival_date=None, + # returning_date=None, unique_id=generate_unique_id(), ), Dewar( @@ -212,11 +213,11 @@ dewars = [ tracking_number="TRACK124", return_address_id=2, contact_id=2, - status="In Preparation", - ready_date=None, - shipping_date=None, - arrival_date=None, - returning_date=None, + status="active", + # ready_date=None, + # shipping_date=None, + # arrival_date=None, + # returning_date=None, unique_id=generate_unique_id(), ), Dewar( @@ -228,11 +229,11 @@ dewars = [ tracking_number="TRACK125", return_address_id=1, contact_id=3, - status="Not Shipped", - ready_date=datetime.strptime("2024-01-01", "%Y-%m-%d"), - shipping_date=None, - arrival_date=None, - returning_date=None, + status="active", + # ready_date=datetime.strptime("2024-01-01", "%Y-%m-%d"), + # shipping_date=None, + # arrival_date=None, + # returning_date=None, unique_id=None, ), Dewar( @@ -244,11 +245,11 @@ dewars = [ tracking_number="", return_address_id=1, contact_id=3, - status="Delayed", - ready_date=datetime.strptime("2024-01-01", "%Y-%m-%d"), - shipping_date=datetime.strptime("2024-01-02", "%Y-%m-%d"), - arrival_date=None, - returning_date=None, + status="active", + # ready_date=datetime.strptime("2024-01-01", "%Y-%m-%d"), + # shipping_date=datetime.strptime("2024-01-02", "%Y-%m-%d"), + # arrival_date=None, + # returning_date=None, unique_id=None, ), Dewar( @@ -257,16 +258,103 @@ dewars = [ dewar_name="Dewar Five", dewar_type_id=1, dewar_serial_number_id=1, - tracking_number="", + tracking_number="TRACK126", return_address_id=1, contact_id=3, - status="Returned", - arrival_date=datetime.strptime("2024-01-03", "%Y-%m-%d"), - returning_date=datetime.strptime("2024-01-07", "%Y-%m-%d"), + status="active", + # arrival_date=datetime.strptime("2024-01-03", "%Y-%m-%d"), + # returning_date=datetime.strptime("2024-01-07", "%Y-%m-%d"), unique_id=None, ), ] +logistics_events = [ + LogisticsEvent( + id=1, + event_type="in preparation", + dewar_id=1, + timestamp=datetime.strptime("2024-12-03", "%Y-%m-%d"), + ), + LogisticsEvent( + id=2, + event_type="ready for shipping", + dewar_id=1, + timestamp=datetime.strptime("2024-12-03", "%Y-%m-%d"), + ), + LogisticsEvent( + id=3, + event_type="shipped", + dewar_id=1, + timestamp=datetime.strptime("2024-12-04", "%Y-%m-%d"), + ), + LogisticsEvent( + id=4, + event_type="arrived", + dewar_id=1, + timestamp=datetime.strptime("2024-12-06", "%Y-%m-%d"), + ), + LogisticsEvent( + id=5, + event_type="returned", + dewar_id=1, + timestamp=datetime.strptime("2024-12-12", "%Y-%m-%d"), + ), + LogisticsEvent( + id=6, + event_type="in preparation", + dewar_id=2, + timestamp=datetime.strptime("2025-01-06", "%Y-%m-%d"), + ), + LogisticsEvent( + id=7, + event_type="ready", + dewar_id=2, + timestamp=datetime.strptime("2025-01-06", "%Y-%m-%d"), + ), + LogisticsEvent( + id=8, + event_type="shipped", + dewar_id=2, + timestamp=datetime.strptime("2025-01-06", "%Y-%m-%d"), + ), + LogisticsEvent( + id=9, + event_type="in preparation", + dewar_id=3, + timestamp=datetime.strptime("2025-01-06", "%Y-%m-%d"), + ), + LogisticsEvent( + id=10, + event_type="ready", + dewar_id=3, + timestamp=datetime.strptime("2025-01-07", "%Y-%m-%d"), + ), + LogisticsEvent( + id=11, + event_type="in preparation", + dewar_id=4, + timestamp=datetime.strptime("2025-01-12", "%Y-%m-%d"), + ), + LogisticsEvent( + id=12, + event_type="ready", + dewar_id=5, + timestamp=datetime.strptime("2025-01-12", "%Y-%m-%d"), + ), + LogisticsEvent( + id=12, + event_type="shipped", + dewar_id=5, + timestamp=datetime.strptime("2025-01-13", "%Y-%m-%d"), + ), + LogisticsEvent( + id=12, + event_type="delayed", + dewar_id=5, + timestamp=datetime.strptime("2025-01-15", "%Y-%m-%d"), + ), +] + # Define proposals proposals = [ Proposal(id=1, number="202400125"), diff --git a/backend/app/models.py b/backend/app/models.py index 4cc988d..c6a33c5 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -86,13 +86,9 @@ class Dewar(Base): dewar_serial_number_id = Column( Integer, ForeignKey("dewar_serial_numbers.id"), nullable=True ) - tracking_number = Column(String(255), nullable=True) status = Column(String(255), nullable=True) - ready_date = Column(Date, nullable=True) - shipping_date = Column(Date, nullable=True) - arrival_date = Column(Date, nullable=True) - returning_date = Column(Date, nullable=True) unique_id = Column(String(255), unique=True, index=True, nullable=True) + tracking_number = Column(String(255), nullable=True) shipment_id = Column(Integer, ForeignKey("shipments.id")) return_address_id = Column(Integer, ForeignKey("addresses.id")) contact_id = Column(Integer, ForeignKey("contacts.id")) diff --git a/backend/app/routers/__init__.py b/backend/app/routers/__init__.py index d4fc23d..d68dba9 100644 --- a/backend/app/routers/__init__.py +++ b/backend/app/routers/__init__.py @@ -1,7 +1,7 @@ from .address import address_router from .contact import contact_router from .proposal import router as proposal_router -from .dewar import router as dewar_router +from .dewar import dewar_router from .shipment import shipment_router from .auth import router as auth_router from .protected_router import protected_router as protected_router diff --git a/backend/app/routers/dewar.py b/backend/app/routers/dewar.py index 45bc3f4..6aa197d 100644 --- a/backend/app/routers/dewar.py +++ b/backend/app/routers/dewar.py @@ -9,7 +9,6 @@ from typing import List import logging from sqlalchemy.exc import SQLAlchemyError from app.schemas import ( - Dewar as DewarSchema, DewarCreate, DewarUpdate, DewarType as DewarTypeSchema, @@ -21,7 +20,8 @@ from app.schemas import ( SampleUpdate, Sample, Puck, - SampleEventResponse, # Clearer name for schema + SampleEventResponse, + DewarSchema, # Clearer name for schema ) from app.models import ( Dewar as DewarModel, @@ -42,11 +42,11 @@ from reportlab.lib.pagesizes import A5, landscape from reportlab.lib.units import cm from reportlab.pdfgen import canvas from app.crud import ( - get_shipments, get_shipment_by_id, -) # Import CRUD functions for shipment +) -router = APIRouter() + +dewar_router = APIRouter() def generate_unique_id(db: Session, length: int = 16) -> str: @@ -63,12 +63,12 @@ def generate_unique_id(db: Session, length: int = 16) -> str: return unique_id -@router.post("/", response_model=DewarSchema, status_code=status.HTTP_201_CREATED) +@dewar_router.post("/", response_model=Dewar, status_code=status.HTTP_201_CREATED) async def create_or_update_dewar( shipment_id: int, dewar: DewarCreate, db: Session = Depends(get_db), -) -> DewarSchema: +): try: # Query existing dewar by name within the shipment existing_dewar = ( @@ -170,14 +170,11 @@ async def create_or_update_dewar( # Create a completely new dewar if none exists dewar_obj = DewarModel( + pgroups=dewar.pgroups, dewar_name=dewar.dewar_name, tracking_number=dewar.tracking_number, status=dewar.status, - ready_date=dewar.ready_date, - shipping_date=dewar.shipping_date, - arrival_date=dewar.arrival_date, - returning_date=dewar.returning_date, - contact_person_id=dewar.contact_person_id, + contact_id=dewar.contact_id, return_address_id=dewar.return_address_id, shipment_id=shipment_id, # Associate with the shipment ) @@ -220,7 +217,7 @@ async def create_or_update_dewar( raise HTTPException(status_code=500, detail="Internal server error") -@router.post("/{dewar_id}/generate-qrcode") +@dewar_router.post("/{dewar_id}/generate-qrcode") async def generate_dewar_qrcode(dewar_id: int, db: Session = Depends(get_db)): dewar = db.query(DewarModel).filter(DewarModel.id == dewar_id).first() if not dewar: @@ -365,7 +362,7 @@ def generate_label(dewar): return buffer -@router.get("/{dewar_id}/download-label", response_class=Response) +@dewar_router.get("/{dewar_id}/download-label", response_class=Response) async def download_dewar_label(dewar_id: int, db: Session = Depends(get_db)): dewar = ( db.query(DewarModel) @@ -397,7 +394,7 @@ async def download_dewar_label(dewar_id: int, db: Session = Depends(get_db)): ) -@router.put("/samples/{sample_id}", response_model=Sample) +@dewar_router.put("/samples/{sample_id}", response_model=Sample) async def update_sample( sample_id: int, sample_update: SampleUpdate, @@ -431,7 +428,7 @@ async def update_sample( return Sample.from_orm(sample) -@router.get("/dewars/{dewar_id}/samples", response_model=Dewar) +@dewar_router.get("/dewars/{dewar_id}/samples", response_model=Dewar) async def get_dewar_samples(dewar_id: int, db: Session = Depends(get_db)): # Fetch the Dewar with nested relationships dewar = db.query(DewarModel).filter(DewarModel.id == dewar_id).first() @@ -441,6 +438,7 @@ async def get_dewar_samples(dewar_id: int, db: Session = Depends(get_db)): # Explicitly map nested relationships dewar_data = { "id": dewar.id, + "pgroups": dewar.pgroups, "dewar_name": dewar.dewar_name, "tracking_number": dewar.tracking_number, "status": dewar.status, @@ -448,14 +446,10 @@ async def get_dewar_samples(dewar_id: int, db: Session = Depends(get_db)): "number_of_samples": sum( len(puck.samples) for puck in dewar.pucks ), # Calculate total samples - "ready_date": dewar.ready_date, - "shipping_date": dewar.shipping_date, - "arrival_date": dewar.arrival_date, - "returning_date": dewar.returning_date, - "contact_person_id": dewar.contact_person.id if dewar.contact_person else None, + "contact_id": dewar.contact.id if dewar.contact else None, "return_address_id": dewar.return_address.id if dewar.return_address else None, "shipment_id": dewar.shipment_id, - "contact_person": dewar.contact_person, + "contact": dewar.contact, "return_address": dewar.return_address, "pucks": [ Puck( @@ -492,7 +486,7 @@ async def get_dewar_samples(dewar_id: int, db: Session = Depends(get_db)): return Dewar(**dewar_data) -@router.get("/", response_model=List[DewarSchema]) +@dewar_router.get("/", response_model=List[Dewar]) async def get_dewars(db: Session = Depends(get_db)): try: dewars = db.query(DewarModel).options(joinedload(DewarModel.pucks)).all() @@ -502,12 +496,12 @@ async def get_dewars(db: Session = Depends(get_db)): raise HTTPException(status_code=500, detail="Internal server error") -@router.get("/dewar-types", response_model=List[DewarTypeSchema]) +@dewar_router.get("/dewar-types", response_model=List[DewarTypeSchema]) def get_dewar_types(db: Session = Depends(get_db)): return db.query(DewarTypeModel).all() -@router.get( +@dewar_router.get( "/dewar-types/{type_id}/serial-numbers", response_model=List[DewarSerialNumberSchema], ) @@ -519,7 +513,7 @@ def get_serial_numbers(type_id: int, db: Session = Depends(get_db)): ) -@router.post("/dewar-types", response_model=DewarTypeSchema) +@dewar_router.post("/dewar-types", response_model=DewarTypeSchema) def create_dewar_type(dewar_type: DewarTypeCreate, db: Session = Depends(get_db)): db_type = DewarTypeModel(**dewar_type.dict()) db.add(db_type) @@ -528,7 +522,7 @@ def create_dewar_type(dewar_type: DewarTypeCreate, db: Session = Depends(get_db) return db_type -@router.post("/dewar-serial-numbers", response_model=DewarSerialNumberSchema) +@dewar_router.post("/dewar-serial-numbers", response_model=DewarSerialNumberSchema) def create_dewar_serial_number( serial_number: DewarSerialNumberCreate, db: Session = Depends(get_db) ): @@ -539,7 +533,7 @@ def create_dewar_serial_number( return db_serial -@router.get("/dewar-serial-numbers", response_model=List[DewarSerialNumberSchema]) +@dewar_router.get("/dewar-serial-numbers", response_model=List[DewarSerialNumberSchema]) def get_all_serial_numbers(db: Session = Depends(get_db)): try: serial_numbers = db.query(DewarSerialNumberModel).all() @@ -549,13 +543,13 @@ def get_all_serial_numbers(db: Session = Depends(get_db)): raise HTTPException(status_code=500, detail="Internal server error") -@router.get("/{dewar_id}", response_model=DewarSchema) +@dewar_router.get("/{dewar_id}", response_model=Dewar) async def get_dewar(dewar_id: int, db: Session = Depends(get_db)): dewar = ( db.query(DewarModel) .options( joinedload(DewarModel.pucks).joinedload(PuckModel.samples), - joinedload(DewarModel.contact_person), + joinedload(DewarModel.contact), joinedload(DewarModel.return_address), joinedload(DewarModel.shipment), ) @@ -569,10 +563,10 @@ async def get_dewar(dewar_id: int, db: Session = Depends(get_db)): return DewarSchema.from_orm(dewar) -@router.put("/{dewar_id}", response_model=DewarSchema) +@dewar_router.put("/{dewar_id}", response_model=Dewar) async def update_dewar( dewar_id: int, dewar_update: DewarUpdate, db: Session = Depends(get_db) -) -> DewarSchema: +) -> Dewar: dewar = db.query(DewarModel).filter(DewarModel.id == dewar_id).first() if not dewar: @@ -587,7 +581,7 @@ async def update_dewar( return dewar -@router.delete("/{dewar_id}", status_code=status.HTTP_204_NO_CONTENT) +@dewar_router.delete("/{dewar_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_dewar(dewar_id: int, db: Session = Depends(get_db)): # Fetch the Dewar from the database dewar = db.query(DewarModel).filter(DewarModel.id == dewar_id).first() @@ -642,18 +636,7 @@ async def delete_dewar(dewar_id: int, db: Session = Depends(get_db)): return -# New routes for shipments -@router.get("/shipments", response_model=List[ShipmentSchema]) -async def get_all_shipments(db: Session = Depends(get_db)): - try: - shipments = get_shipments(db) - return shipments - except SQLAlchemyError as e: - logging.error(f"Database error occurred: {e}") - raise HTTPException(status_code=500, detail="Internal server error") - - -@router.get("/shipments/{id}", response_model=ShipmentSchema) +@dewar_router.get("/shipments/{id}", response_model=ShipmentSchema) async def get_single_shipment(id: int, db: Session = Depends(get_db)): try: shipment = get_shipment_by_id(db, id) diff --git a/backend/app/routers/logistics.py b/backend/app/routers/logistics.py index faefb8a..cdbebdd 100644 --- a/backend/app/routers/logistics.py +++ b/backend/app/routers/logistics.py @@ -263,9 +263,9 @@ async def get_all_slots(db: Session = Depends(get_db)): # Correct the contact_person assignment contact_person = None - if slot.dewar and slot.dewar.contact_person: - first_name = slot.dewar.contact_person.firstname - last_name = slot.dewar.contact_person.lastname + if slot.dewar and slot.dewar.contact: + first_name = slot.dewar.contact.firstname + last_name = slot.dewar.contact.lastname contact_person = f"{first_name} {last_name}" # Prepare the slot data for the response @@ -287,7 +287,7 @@ async def get_all_slots(db: Session = Depends(get_db)): if slot.dewar and slot.dewar.shipment else None ), - contact_person=contact_person, + contact=contact_person, local_contact="local contact placeholder", ) # Add updated slot data to the response list diff --git a/backend/app/routers/protected_router.py b/backend/app/routers/protected_router.py index 216a44a..f1236f3 100644 --- a/backend/app/routers/protected_router.py +++ b/backend/app/routers/protected_router.py @@ -4,6 +4,7 @@ from app.routers.auth import get_current_user from app.routers.address import address_router from app.routers.contact import contact_router from app.routers.shipment import shipment_router +from app.routers.dewar import dewar_router protected_router = APIRouter( dependencies=[Depends(get_current_user)] # Applies to all routes @@ -14,3 +15,4 @@ protected_router.include_router(contact_router, prefix="/contacts", tags=["conta protected_router.include_router( shipment_router, prefix="/shipments", tags=["shipments"] ) +protected_router.include_router(dewar_router, prefix="/dewars", tags=["dewars"]) diff --git a/backend/app/routers/puck.py b/backend/app/routers/puck.py index 4577572..9418704 100644 --- a/backend/app/routers/puck.py +++ b/backend/app/routers/puck.py @@ -251,6 +251,9 @@ async def get_pucks_with_tell_position(db: Session = Depends(get_db)): dewar_name=str(dewar.dewar_name) if dewar and dewar.dewar_name else None, + pgroup=str(dewar.pgroups) + if dewar.pgroups + else None, # will be replaced later by puck pgroup samples=[ Sample( id=sample.id, @@ -413,6 +416,7 @@ async def get_pucks_by_slot(slot_identifier: str, db: Session = Depends(get_db)) dewar_ids = [dewar.id for dewar in dewars] dewar_map = {dewar.id: dewar.dewar_name for dewar in dewars} + dewar_pgroups = {dewar.id: dewar.pgroups for dewar in dewars} # Subquery to fetch the latest event for each puck (any type of event) latest_event_subquery = ( @@ -430,9 +434,9 @@ async def get_pucks_by_slot(slot_identifier: str, db: Session = Depends(get_db)) PuckModel, PuckEventModel.event_type, PuckEventModel.tell_position, + DewarModel, # Include DewarModel ) .join( # Join pucks with the latest event - # (outer join to include pucks without events) latest_event_subquery, PuckModel.id == latest_event_subquery.c.puck_id, isouter=True, @@ -443,6 +447,11 @@ async def get_pucks_by_slot(slot_identifier: str, db: Session = Depends(get_db)) & (PuckEventModel.timestamp == latest_event_subquery.c.latest_event_time), isouter=True, ) + .join( # Join with DewarModel to get dewar details + DewarModel, + PuckModel.dewar_id == DewarModel.id, + isouter=True, + ) .filter(PuckModel.dewar_id.in_(dewar_ids)) # Restrict pucks to relevant dewars .all() ) @@ -458,13 +467,16 @@ async def get_pucks_by_slot(slot_identifier: str, db: Session = Depends(get_db)) # Prepare the final response results = [] - for puck, event_type, tell_position in pucks_with_latest_events: + for puck, event_type, dewar, tell_position in pucks_with_latest_events: logger.debug( f"Puck ID: {puck.id}, Name: {puck.puck_name}, Event Type: {event_type}, " f"Tell Position: {tell_position}" ) dewar_name = dewar_map.get(puck.dewar_id, "Unknown") + pgroup = dewar_pgroups.get( + puck.dewar_id + ) # will be replaced later by puck pgroup # For pucks with no events or whose latest event is `puck_removed`, set # `tell_position` to None @@ -475,6 +487,7 @@ async def get_pucks_by_slot(slot_identifier: str, db: Session = Depends(get_db)) results.append( PuckWithTellPosition( id=puck.id, + pgroup=pgroup, puck_name=puck.puck_name, puck_type=puck.puck_type, puck_location_in_dewar=int(puck.puck_location_in_dewar) diff --git a/backend/app/routers/sample.py b/backend/app/routers/sample.py index 811d428..81f7285 100644 --- a/backend/app/routers/sample.py +++ b/backend/app/routers/sample.py @@ -111,7 +111,7 @@ async def upload_sample_images( raise HTTPException(status_code=404, detail="Sample not found") # 2. Define Directory Structure - username = "e16371" # Hardcoded username; replace with dynamic logic if applicable + pgroup = sample.puck.dewar.pgroups today = datetime.now().strftime("%Y-%m-%d") dewar_name = ( sample.puck.dewar.dewar_name @@ -120,7 +120,7 @@ async def upload_sample_images( ) puck_name = sample.puck.puck_name if sample.puck else "default_puck" position = sample.position if sample.position else "default_position" - base_dir = Path(f"images/{username}/{today}/{dewar_name}/{puck_name}/{position}") + base_dir = Path(f"images/{pgroup}/{today}/{dewar_name}/{puck_name}/{position}") base_dir.mkdir(parents=True, exist_ok=True) # 3. Process and Save Each File diff --git a/backend/app/routers/shipment.py b/backend/app/routers/shipment.py index adf1c48..cfd4082 100644 --- a/backend/app/routers/shipment.py +++ b/backend/app/routers/shipment.py @@ -3,6 +3,8 @@ from sqlalchemy.orm import Session from typing import List from datetime import date import json +import logging +from sqlalchemy.exc import SQLAlchemyError from app.models import ( Shipment as ShipmentModel, @@ -20,11 +22,11 @@ from app.schemas import ( Shipment as ShipmentSchema, Contact as ContactSchema, Sample as SampleSchema, - DewarSchema, loginData, + Dewar, ) from app.database import get_db -from app.crud import get_shipment_by_id +from app.crud import get_shipment_by_id, get_shipments from app.routers.auth import get_current_user shipment_router = APIRouter() @@ -59,7 +61,17 @@ async def fetch_shipments( return shipments -@shipment_router.get("/{shipment_id}/dewars", response_model=List[DewarSchema]) +@shipment_router.get("/shipments", response_model=List[ShipmentSchema]) +async def get_all_shipments(db: Session = Depends(get_db)): + try: + shipments = get_shipments(db) + return shipments + except SQLAlchemyError as e: + logging.error(f"Database error occurred: {e}") + raise HTTPException(status_code=500, detail="Internal server error") + + +@shipment_router.get("/{shipment_id}/dewars", response_model=List[Dewar]) async def get_dewars_by_shipment_id(shipment_id: int, db: Session = Depends(get_db)): shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first() if not shipment: diff --git a/backend/app/schemas.py b/backend/app/schemas.py index d07248d..a5e3cd0 100644 --- a/backend/app/schemas.py +++ b/backend/app/schemas.py @@ -429,7 +429,7 @@ class Sample(BaseModel): priority: Optional[int] = None comments: Optional[str] = None data_collection_parameters: Optional[DataCollectionParameters] - events: List[SampleEventResponse] + events: List[SampleEventResponse] = [] mount_count: Optional[int] = None unmount_count: Optional[int] = None # results: Optional[Results] = None @@ -497,6 +497,7 @@ class Puck(BaseModel): class DewarBase(BaseModel): + pgroups: str dewar_name: str dewar_type_id: Optional[int] = None dewar_serial_number_id: Optional[int] = None @@ -505,10 +506,6 @@ class DewarBase(BaseModel): number_of_pucks: Optional[int] = None number_of_samples: Optional[int] = None status: str - ready_date: Optional[date] - shipping_date: Optional[date] - arrival_date: Optional[date] - returning_date: Optional[date] contact_id: Optional[int] return_address_id: Optional[int] pucks: List[PuckCreate] = [] @@ -534,22 +531,20 @@ class Dewar(DewarBase): class DewarUpdate(BaseModel): + pgroups: str dewar_name: Optional[str] = None dewar_type_id: Optional[int] = None dewar_serial_number_id: Optional[int] = None unique_id: Optional[str] = None tracking_number: Optional[str] = None status: Optional[str] = None - ready_date: Optional[date] = None - shipping_date: Optional[date] = None - arrival_date: Optional[date] = None - returning_date: Optional[date] = None contact_id: Optional[int] = None address_id: Optional[int] = None class DewarSchema(BaseModel): id: int + pgroups: str dewar_name: str tracking_number: str status: str @@ -559,6 +554,9 @@ class DewarSchema(BaseModel): class Config: from_attributes = True + # shipping status etc will become a logistics event. + # Tracking will also become an event + class Proposal(BaseModel): id: int @@ -681,7 +679,7 @@ class PuckWithTellPosition(BaseModel): dewar_name: Optional[ str ] # was changed to optional but probably needs to be not optional - user: str = "e16371" + pgroup: str samples: Optional[List[Sample]] = None tell_position: Optional[str] diff --git a/backend/main.py b/backend/main.py index c3272cc..050d328 100644 --- a/backend/main.py +++ b/backend/main.py @@ -7,7 +7,6 @@ from fastapi.middleware.cors import CORSMiddleware from app import ssl_heidi from app.routers import ( proposal, - dewar, puck, spreadsheet, logistics, @@ -137,8 +136,8 @@ def on_startup(): load_slots_data(db) else: # dev or test environments print(f"{environment.capitalize()} environment: Regenerating database.") - Base.metadata.drop_all(bind=engine) - Base.metadata.create_all(bind=engine) + # Base.metadata.drop_all(bind=engine) + # Base.metadata.create_all(bind=engine) if environment == "dev": from app.database import load_sample_data @@ -152,10 +151,9 @@ def on_startup(): # Include routers with correct configuration -app.include_router(protected_router, prefix="/protected", tags=["protected"]) +app.include_router(protected_router, prefix="/protected") app.include_router(auth.router, prefix="/auth", tags=["auth"]) app.include_router(proposal.router, prefix="/proposals", tags=["proposals"]) -app.include_router(dewar.router, prefix="/dewars", tags=["dewars"]) app.include_router(puck.router, prefix="/pucks", tags=["pucks"]) app.include_router(spreadsheet.router, tags=["spreadsheet"]) app.include_router(logistics.router, prefix="/logistics", tags=["logistics"]) diff --git a/frontend/src/components/DewarDetails.tsx b/frontend/src/components/DewarDetails.tsx index 49eb10d..ef3ca0f 100644 --- a/frontend/src/components/DewarDetails.tsx +++ b/frontend/src/components/DewarDetails.tsx @@ -117,7 +117,7 @@ const DewarDetails: React.FC = ({ useEffect(() => { const fetchDewarTypes = async () => { try { - const response = await DewarsService.getDewarTypesDewarsDewarTypesGet(); + const response = await DewarsService.getDewarTypesProtectedDewarsDewarTypesGet(); setKnownDewarTypes(response ?? []); } catch (error) { setFeedbackMessage('Failed to fetch dewar types.'); @@ -132,7 +132,7 @@ const DewarDetails: React.FC = ({ useEffect(() => { const fetchSerialNumbers = async () => { try { - const response = await DewarsService.getAllSerialNumbersDewarsDewarSerialNumbersGet(); + const response = await DewarsService.getAllSerialNumbersProtectedDewarsDewarSerialNumbersGet(); setKnownSerialNumbers(response ?? []); } catch (error) { setFeedbackMessage('Failed to fetch serial numbers.'); @@ -248,8 +248,8 @@ const DewarDetails: React.FC = ({ const handleSaveNewDewarTypeAndSerialNumber = async () => { if (newDewarType) { try { - const typeResponse = await DewarsService.createDewarTypeDewarsDewarTypesPost({ dewar_type: newDewarType }); - const serialResponse = await DewarsService.createDewarSerialNumberDewarsDewarSerialNumbersPost({ + const typeResponse = await DewarsService.createDewarTypeProtectedDewarsDewarTypesPost({ dewar_type: newDewarType }); + const serialResponse = await DewarsService.createDewarSerialNumberProtectedDewarsDewarSerialNumbersPost({ serial_number: newDewarSerialNumber, dewar_type_id: typeResponse.id, }); @@ -266,7 +266,7 @@ const DewarDetails: React.FC = ({ } } else if (newDewarSerialNumber && selectedDewarType) { try { - const response = await DewarsService.createDewarSerialNumberDewarsDewarSerialNumbersPost({ + const response = await DewarsService.createDewarSerialNumberProtectedDewarsDewarSerialNumbersPost({ serial_number: newDewarSerialNumber, dewar_type_id: parseInt(selectedDewarType, 10), }); @@ -371,6 +371,7 @@ const DewarDetails: React.FC = ({ try { const payload = { + pgroups: activePgroup, dewar_name: dewar.dewar_name, dewar_type_id: parseInt(selectedDewarType, 10), dewar_serial_number_id: parseInt(selectedSerialNumber, 10), @@ -378,15 +379,11 @@ const DewarDetails: React.FC = ({ number_of_pucks: dewar.number_of_pucks, number_of_samples: dewar.number_of_samples, status: dewar.status, - ready_date: formatDate(dewar.ready_date), - shipping_date: formatDate(dewar.shipping_date), - arrival_date: dewar.arrival_date, - returning_date: dewar.returning_date, return_address_id: parseInt(selectedReturnAddress ?? '', 10), contact_id: parseInt(selectedContact ?? '', 10), }; - await DewarsService.updateDewarDewarsDewarIdPut(dewarId, payload); + await DewarsService.updateDewarProtectedDewarsDewarIdPut(dewarId, payload); setFeedbackMessage('Changes saved successfully.'); setChangesMade(false); } catch (error) { @@ -408,7 +405,7 @@ const DewarDetails: React.FC = ({ if (!dewar) return; try { - const response = await DewarsService.generateDewarQrcodeDewarsDewarIdGenerateQrcodePost(dewar.id); + const response = await DewarsService.generateDewarQrcodeProtectedDewarsDewarIdGenerateQrcodePost(dewar.id); const newQrCodeValue = response.unique_id; setQrCodeValue(newQrCodeValue); setIsQRCodeGenerated(true); @@ -426,7 +423,7 @@ const DewarDetails: React.FC = ({ if (!dewar) return; try { - const response = await DewarsService.downloadDewarLabelDewarsDewarIdDownloadLabelGet(dewar.id); + const response = await DewarsService.downloadDewarLabelProtectedDewarsDewarIdDownloadLabelGet(dewar.id); // The response object might need parsing const blob = new Blob([response as any], { type: 'application/pdf' }); diff --git a/frontend/src/components/SampleSpreadsheetGrid.tsx b/frontend/src/components/SampleSpreadsheetGrid.tsx index 2a0134e..ce07c86 100644 --- a/frontend/src/components/SampleSpreadsheetGrid.tsx +++ b/frontend/src/components/SampleSpreadsheetGrid.tsx @@ -19,7 +19,7 @@ const SampleSpreadsheet: React.FC = ({ dewarId }) => { const fetchSamples = async () => { try { - const response = await DewarsService.getDewarSamplesDewarsDewarsDewarIdSamplesGet(dewarId); + const response = await DewarsService.getDewarSamplesProtectedDewarsDewarsDewarIdSamplesGet(dewarId); console.log("Response from backend:", response); const dewarData = response; // Assume response is already in correct structure @@ -173,7 +173,7 @@ const SampleSpreadsheet: React.FC = ({ dewarId }) => { ); // API call to persist changes - await DewarsService.updateSampleDewarsSamplesSampleIdPut(Number(id), updatedSample); + await DewarsService.updateSampleProtectedDewarsSamplesSampleIdPut(Number(id), updatedSample); console.log(`Sample with ID ${id} successfully updated.`); } catch (error) { @@ -237,7 +237,7 @@ const SampleSpreadsheet: React.FC = ({ dewarId }) => { ); // Send the payload to the backend - await DewarsService.updateSampleDewarsSamplesSampleIdPut(Number(newRow.id), payload); + await DewarsService.updateSampleProtectedDewarsSamplesSampleIdPut(Number(newRow.id), payload); console.log(`Successfully updated sample with ID ${newRow.id}.`); return payload; // Return the updated row diff --git a/frontend/src/components/ShipmentDetails.tsx b/frontend/src/components/ShipmentDetails.tsx index 0523184..2844f45 100644 --- a/frontend/src/components/ShipmentDetails.tsx +++ b/frontend/src/components/ShipmentDetails.tsx @@ -48,11 +48,6 @@ const ShipmentDetails: React.FC = ({ tracking_number: '', number_of_pucks: 0, number_of_samples: 0, - status: 'In preparation', - ready_date: null, - shipping_date: null, - arrival_date: null, - returning_date: null, contact_id: selectedShipment?.contact?.id, return_address_id: selectedShipment?.return_address?.id, }; @@ -126,16 +121,18 @@ const ShipmentDetails: React.FC = ({ const newDewarToPost: Dewar = { ...initialNewDewarState, ...newDewar, + pgroups:activePgroup, dewar_name: newDewar.dewar_name.trim(), contact_id: selectedShipment?.contact?.id, - return_address_id: selectedShipment?.return_address?.id + return_address_id: selectedShipment?.return_address?.id, + status: 'active', } as Dewar; if (!newDewarToPost.dewar_name || !newDewarToPost.status) { throw new Error('Missing required fields'); } - const createdDewar = await DewarsService.createOrUpdateDewarDewarsPost(selectedShipment.id, newDewarToPost); + const createdDewar = await DewarsService.createOrUpdateDewarProtectedDewarsPost(selectedShipment.id, newDewarToPost); if (createdDewar && selectedShipment) { const updatedShipment = await ShipmentsService.addDewarToShipmentProtectedShipmentsShipmentIdAddDewarPost(selectedShipment.id, createdDewar.id); diff --git a/frontend/src/components/ShipmentPanel.tsx b/frontend/src/components/ShipmentPanel.tsx index cf9d78b..0ee569b 100644 --- a/frontend/src/components/ShipmentPanel.tsx +++ b/frontend/src/components/ShipmentPanel.tsx @@ -197,6 +197,7 @@ const ShipmentPanel: React.FC = ({ ); })} { const [localErrors, setLocalErrors] = useState(errors || []); const [editingCell, setEditingCell] = useState({}); @@ -59,17 +60,14 @@ const SpreadsheetTable = ({ console.log("Addinfo:", addinfo); }, [correctionMetadata, addinfo]); const initialNewDewarState = { + pgroups: activePgroup, number_of_pucks: 0, number_of_samples: 0, - ready_date: null, - shipping_date: null, - arrival_date: null, - returning_date: null, contact_id: selectedShipment?.contact?.id, return_address_id: selectedShipment?.return_address?.id, dewar_name: '', tracking_number: 'UNKNOWN', - status: 'In preparation', + status: 'active', pucks: [] // Ensure 'pucks' array exists }; @@ -242,7 +240,7 @@ const SpreadsheetTable = ({ try { // Fetch dewars related to the current shipment via API - const shipDewars = await ShipmentsService.getDewarsByShipmentIdShipmentsShipmentIdDewarsGet(selectedShipment.id); + const shipDewars = await ShipmentsService.getDewarsByShipmentIdProtectedShipmentsShipmentIdDewarsGet(selectedShipment.id); // Search for dewar by name within the shipment const existingDewar = shipDewars.find((d) => d.dewar_name === dewarName); @@ -258,8 +256,8 @@ const SpreadsheetTable = ({ } }; - const createOrUpdateDewarsFromSheet = async (data, contactPerson, returnAddress) => { - if (!contact?.id || !returnAddress?.id) { + const createOrUpdateDewarsFromSheet = async (data, contact, address) => { + if (!contact?.id || !address?.id) { console.error('contact_id or return_address_id is missing'); return null; } @@ -287,9 +285,10 @@ const SpreadsheetTable = ({ // Initialize new dewar object dewar = { ...initialNewDewarState, + pgroups: activePgroup, dewar_name: dewarName, - contact_id: contactPerson.id, - return_address_id: returnAddress.id, + contact_id: contact.id, + return_address_id: address.id, pucks: [], }; dewars.set(dewarName, dewar); @@ -442,7 +441,7 @@ const SpreadsheetTable = ({ const { number_of_pucks, number_of_samples, ...payload } = dewar; // Attempt to create or update a dewar - await DewarsService.createOrUpdateDewarDewarsPost(selectedShipment.id, payload); + await DewarsService.createOrUpdateDewarProtectedDewarsPost(selectedShipment.id, payload); console.log(`Dewar "${dewar.dewar_name}" created/updated successfully.`); } catch (error: any) { diff --git a/frontend/src/components/UploadDialog.tsx b/frontend/src/components/UploadDialog.tsx index 4e47dc7..4aaddf1 100644 --- a/frontend/src/components/UploadDialog.tsx +++ b/frontend/src/components/UploadDialog.tsx @@ -12,12 +12,13 @@ import SpreadsheetTable from './SpreadsheetTable'; import Modal from './Modal'; interface UploadDialogProps { + activePgroup: string; open: boolean; onClose: () => void; selectedShipment: any; } -const UploadDialog: React.FC = ({ open, onClose, selectedShipment }) => { +const UploadDialog: React.FC = ({ open, onClose, selectedShipment, activePgroup }) => { const [uploadError, setUploadError] = useState(null); const [fileSummary, setFileSummary] = useState(null); const [fileBlob, setFileBlob] = useState(null); @@ -180,6 +181,7 @@ const UploadDialog: React.FC = ({ open, onClose, selectedShip onCancel={handleCancel} fileBlob={fileBlob} selectedShipment={selectedShipment} + activePgroup={activePgroup} addinfo={fileSummary.addinfo} /> diff --git a/logistics/src/pages/LogisticsView.tsx b/logistics/src/pages/LogisticsView.tsx index 1c7ae3a..e77c626 100644 --- a/logistics/src/pages/LogisticsView.tsx +++ b/logistics/src/pages/LogisticsView.tsx @@ -53,7 +53,7 @@ interface SlotData extends SlotSchema { retrievedTimestamp?: string; beamlineLocation?: string; shipment_name?: string; - contact_person?: string; + contact?: string; local_contact?: string; } @@ -155,7 +155,7 @@ const LogisticsView: React.FC = () => { needsRefillWarning: true, beamlineLocation: undefined, shipmnet_name: undefined, - contact_person: undefined, + contact: undefined, local_contact: undefined, }; } else { @@ -174,7 +174,7 @@ const LogisticsView: React.FC = () => { needsRefillWarning: !associatedDewar || slot.time_until_refill === undefined, beamlineLocation: slot.beamlineLocation, shipment_name: slot.shipment_name, - contact_person: slot.contact_person, + contact: slot.contact, local_contact: slot.local_contact, }; }); @@ -456,7 +456,7 @@ const LogisticsView: React.FC = () => { {selectedSlotData.label} {`Shipment: ${selectedSlotData.shipment_name}`} {`Dewar: ${selectedSlotData.dewar_name || 'N/A'}`} - {`Contact Person: ${selectedSlotData.contact_person}`} + {`Contact Person: ${selectedSlotData.contact}`} {`QR Code: ${selectedSlotData.qr_code}`} {`Occupied: ${selectedSlotData.occupied ? 'Yes' : 'No'}`} {`Needs Refill: ${selectedSlotData.needsRefillWarning ? 'Yes' : 'No'}`} diff --git a/pyproject.toml b/pyproject.toml index 4f89339..5dc6c5d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "aareDB" -version = "0.1.0a19" +version = "0.1.0a20" description = "Backend for next gen sample management system" authors = [{name = "Guillaume Gotthard", email = "guillaume.gotthard@psi.ch"}] license = {text = "MIT"} diff --git a/testfunctions.ipynb b/testfunctions.ipynb index d44191c..7e862b4 100644 --- a/testfunctions.ipynb +++ b/testfunctions.ipynb @@ -6,8 +6,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2025-01-20T14:44:37.526742Z", - "start_time": "2025-01-20T14:44:37.522704Z" + "end_time": "2025-01-30T11:29:38.703954Z", + "start_time": "2025-01-30T11:29:38.307050Z" } }, "source": [ @@ -42,18 +42,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "0.1.0a19\n", + "0.1.0a20\n", "https://127.0.0.1:8000\n" ] } ], - "execution_count": 2 + "execution_count": 1 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-01-20T14:44:58.875370Z", - "start_time": "2025-01-20T14:44:58.805520Z" + "end_time": "2025-01-30T11:07:14.795059Z", + "start_time": "2025-01-30T11:07:14.783786Z" } }, "cell_type": "code", @@ -95,52 +95,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Shipment ID: 2, Shipment Name: Shipment from Mordor\n", - " Dewar ID: 1, Dewar Name: Dewar One, Dewar Unique ID: 83041c3f8844ff39 \n", - " Puck ID: 1, Puck Name: PUCK-001\n", - " Puck ID: 2, Puck Name: PUCK002\n", - " Puck ID: 3, Puck Name: PUCK003\n", - " Puck ID: 4, Puck Name: PUCK004\n", - " Puck ID: 5, Puck Name: PUCK005\n", - " Puck ID: 6, Puck Name: PUCK006\n", - " Puck ID: 7, Puck Name: PUCK007\n", - " Dewar ID: 2, Dewar Name: Dewar Two, Dewar Unique ID: 62baccecdd37c033 \n", - " Puck ID: 8, Puck Name: PK001\n", - " Puck ID: 9, Puck Name: PK002\n", - " Puck ID: 10, Puck Name: PK003\n", - " Puck ID: 11, Puck Name: PK004\n", - " Puck ID: 12, Puck Name: PK005\n", - " Puck ID: 13, Puck Name: PK006\n", - "Shipment ID: 3, Shipment Name: Shipment from Mordor\n", - " Dewar ID: 3, Dewar Name: Dewar Three, Dewar Unique ID: None \n", - " Puck ID: 14, Puck Name: P001\n", - " Puck ID: 15, Puck Name: P002\n", - " Puck ID: 16, Puck Name: P003\n", - " Puck ID: 17, Puck Name: P004\n", - " Puck ID: 18, Puck Name: P005\n", - " Puck ID: 19, Puck Name: P006\n", - " Puck ID: 20, Puck Name: P007\n", - " Dewar ID: 4, Dewar Name: Dewar Four, Dewar Unique ID: None \n", - " Puck ID: 21, Puck Name: PC002\n", - " Puck ID: 22, Puck Name: PC003\n", - " Puck ID: 23, Puck Name: PC004\n", - " Puck ID: 24, Puck Name: PC005\n", - " Puck ID: 25, Puck Name: PC006\n", - " Puck ID: 26, Puck Name: PC007\n", - "Shipment ID: 1, Shipment Name: Shipment from Mordor\n", - " Dewar ID: 5, Dewar Name: Dewar Five, Dewar Unique ID: 15e3dbe05e78ee83 \n", - " Puck ID: 27, Puck Name: PKK004\n", - " Puck ID: 28, Puck Name: PKK005\n", - " Puck ID: 29, Puck Name: PKK006\n", - " Puck ID: 30, Puck Name: PKK007\n", - "Shipment ID: 4, Shipment Name: testship\n", - " Dewar ID: 6, Dewar Name: Dewar_test, Dewar Unique ID: f352529444d64dd5 \n", - " Puck ID: 43, Puck Name: CPS-4093\n", - " Puck ID: 44, Puck Name: CPS-4178\n", - " Puck ID: 45, Puck Name: PSIMX-122\n", - " Puck ID: 46, Puck Name: CPS-6597\n", - " Puck ID: 47, Puck Name: PSIMX-078\n", - " Puck ID: 48, Puck Name: 1002\n" + "Exception when calling ShipmentsApi->fetch_shipments_shipments_get: (404)\n", + "Reason: Not Found\n", + "HTTP response headers: HTTPHeaderDict({'date': 'Thu, 30 Jan 2025 11:07:14 GMT', 'server': 'uvicorn', 'content-length': '22', 'content-type': 'application/json'})\n", + "HTTP response body: {\"detail\":\"Not Found\"}\n", + "\n" ] }, { @@ -152,7 +111,7 @@ ] } ], - "execution_count": 3 + "execution_count": 6 }, { "metadata": { @@ -253,8 +212,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-01-17T14:07:51.580993Z", - "start_time": "2025-01-17T14:07:51.565128Z" + "end_time": "2025-01-30T11:35:20.036682Z", + "start_time": "2025-01-30T11:35:20.018284Z" } }, "cell_type": "code", @@ -283,12 +242,13 @@ "text": [ "The response of PucksApi->get_pucks_by_slot_pucks_slot_slot_identifier_get:\n", "\n", - "[PuckWithTellPosition(id=43, puck_name='CPS-4093', puck_type='unipuck', puck_location_in_dewar=1, dewar_id=6, dewar_name='Dewar_test', user='e16371', samples=None, tell_position=None),\n", - " PuckWithTellPosition(id=44, puck_name='CPS-4178', puck_type='unipuck', puck_location_in_dewar=2, dewar_id=6, dewar_name='Dewar_test', user='e16371', samples=None, tell_position=None),\n", - " PuckWithTellPosition(id=45, puck_name='PSIMX-122', puck_type='unipuck', puck_location_in_dewar=3, dewar_id=6, dewar_name='Dewar_test', user='e16371', samples=None, tell_position=None),\n", - " PuckWithTellPosition(id=46, puck_name='CPS-6597', puck_type='unipuck', puck_location_in_dewar=4, dewar_id=6, dewar_name='Dewar_test', user='e16371', samples=None, tell_position=None),\n", - " PuckWithTellPosition(id=47, puck_name='PSIMX-078', puck_type='unipuck', puck_location_in_dewar=5, dewar_id=6, dewar_name='Dewar_test', user='e16371', samples=None, tell_position=None),\n", - " PuckWithTellPosition(id=48, puck_name='1002', puck_type='unipuck', puck_location_in_dewar=6, dewar_id=6, dewar_name='Dewar_test', user='e16371', samples=None, tell_position=None)]\n" + "[PuckWithTellPosition(id=1, puck_name='PUCK-001', puck_type='Unipuck', puck_location_in_dewar=1, dewar_id=1, dewar_name='Dewar One', pgroup='p20001', samples=None, tell_position=None),\n", + " PuckWithTellPosition(id=2, puck_name='PUCK002', puck_type='Unipuck', puck_location_in_dewar=2, dewar_id=1, dewar_name='Dewar One', pgroup='p20001', samples=None, tell_position=None),\n", + " PuckWithTellPosition(id=3, puck_name='PUCK003', puck_type='Unipuck', puck_location_in_dewar=3, dewar_id=1, dewar_name='Dewar One', pgroup='p20001', samples=None, tell_position=None),\n", + " PuckWithTellPosition(id=4, puck_name='PUCK004', puck_type='Unipuck', puck_location_in_dewar=4, dewar_id=1, dewar_name='Dewar One', pgroup='p20001', samples=None, tell_position=None),\n", + " PuckWithTellPosition(id=5, puck_name='PUCK005', puck_type='Unipuck', puck_location_in_dewar=5, dewar_id=1, dewar_name='Dewar One', pgroup='p20001', samples=None, tell_position=None),\n", + " PuckWithTellPosition(id=6, puck_name='PUCK006', puck_type='Unipuck', puck_location_in_dewar=6, dewar_id=1, dewar_name='Dewar One', pgroup='p20001', samples=None, tell_position=None),\n", + " PuckWithTellPosition(id=7, puck_name='PUCK007', puck_type='Unipuck', puck_location_in_dewar=7, dewar_id=1, dewar_name='Dewar One', pgroup='p20001', samples=None, tell_position=None)]\n" ] }, { @@ -300,17 +260,19 @@ ] } ], - "execution_count": 42 + "execution_count": 5 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-01-17T14:09:55.141237Z", - "start_time": "2025-01-17T14:09:55.117843Z" + "end_time": "2025-01-30T12:30:46.242711Z", + "start_time": "2025-01-30T12:30:46.215343Z" } }, "cell_type": "code", "source": [ + "from aareDBclient import SetTellPosition\n", + "\n", "# Attribute a puck to a position in the TELL dewar\n", "\n", "with aareDBclient.ApiClient(configuration) as api_client:\n", @@ -321,7 +283,7 @@ " # This part is commented but will be used to attribute a puck to a position of the TELL\n", " # Define the puck ID and payload\n", "\n", - " payload = [SetTellPosition(puck_name='CPS-4178', segment='A', puck_in_segment=2),SetTellPosition(puck_name='PSIMX122', segment='C', puck_in_segment=3)]\n", + " payload = [SetTellPosition(puck_name='PUCK006', segment='A', puck_in_segment=2),SetTellPosition(puck_name='PUCK005', segment='C', puck_in_segment=3)]\n", " #payload = []\n", "\n", " try:\n", @@ -344,12 +306,12 @@ "[{'message': 'The tell_position was updated successfully.',\n", " 'new_position': 'A2',\n", " 'previous_position': None,\n", - " 'puck_name': 'CPS-4178',\n", + " 'puck_name': 'PUCK006',\n", " 'status': 'updated'},\n", " {'message': 'The tell_position was updated successfully.',\n", " 'new_position': 'C3',\n", " 'previous_position': None,\n", - " 'puck_name': 'PSIMX-122',\n", + " 'puck_name': 'PUCK005',\n", " 'status': 'updated'}]\n" ] }, @@ -362,13 +324,13 @@ ] } ], - "execution_count": 43 + "execution_count": 8 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-01-20T10:00:34.361066Z", - "start_time": "2025-01-20T10:00:34.339557Z" + "end_time": "2025-01-30T12:35:40.734188Z", + "start_time": "2025-01-30T12:35:40.679071Z" } }, "cell_type": "code", @@ -412,34 +374,28 @@ "name": "stdout", "output_type": "stream", "text": [ - "Puck ID: 44, Puck Name: CPS-4178\n", - " Sample ID: 433, Sample Name: Dtpase_1\n", - " Sample ID: 434, Sample Name: Dtpase_2\n", - " Sample ID: 435, Sample Name: Dtpase_3\n", - " Sample ID: 436, Sample Name: Dtpase_4\n", - " Sample ID: 437, Sample Name: Dtpase_5\n", - " Sample ID: 438, Sample Name: Dtpase_6\n", - " Sample ID: 439, Sample Name: Dtpase_7\n", - " Sample ID: 440, Sample Name: Fckase_1\n", - " Sample ID: 441, Sample Name: Fckase_2\n", - " Sample ID: 442, Sample Name: Fckase_3\n", - " Sample ID: 443, Sample Name: Fckase_4\n", - " Sample ID: 444, Sample Name: Fckase_5\n", - " Sample ID: 445, Sample Name: Fckase_6\n", - " Sample ID: 446, Sample Name: Fckase_7\n", - " Sample ID: 447, Sample Name: Fckase_8\n", - " Sample ID: 448, Sample Name: Fckase_9\n", - "Puck ID: 45, Puck Name: PSIMX-122\n", - " Sample ID: 449, Sample Name: PopoI_1\n", - " Sample ID: 450, Sample Name: PopoI_2\n", - " Sample ID: 451, Sample Name: PopoI_3\n", - " Sample ID: 452, Sample Name: PopoI_4\n", - " Sample ID: 453, Sample Name: PopoI_5\n", - " Sample ID: 454, Sample Name: PopoI_6\n", - " Sample ID: 455, Sample Name: PopoI_7\n", - " Sample ID: 456, Sample Name: PopoI_8\n", - " Sample ID: 457, Sample Name: PopoI_9\n", - " Sample ID: 458, Sample Name: PopoI_10\n" + "Puck ID: 6, Puck Name: PUCK006\n", + " Sample ID: 28, Sample Name: Sample028\n", + " Sample ID: 29, Sample Name: Sample029\n", + " Sample ID: 30, Sample Name: Sample030\n", + " Sample ID: 31, Sample Name: Sample031\n", + " Sample ID: 32, Sample Name: Sample032\n", + " Sample ID: 33, Sample Name: Sample033\n", + " Sample ID: 34, Sample Name: Sample034\n", + " Sample ID: 35, Sample Name: Sample035\n", + " Sample ID: 36, Sample Name: Sample036\n", + " Sample ID: 37, Sample Name: Sample037\n", + " Sample ID: 38, Sample Name: Sample038\n", + " Sample ID: 39, Sample Name: Sample039\n", + " Sample ID: 40, Sample Name: Sample040\n", + "Puck ID: 5, Puck Name: PUCK005\n", + " Sample ID: 21, Sample Name: Sample021\n", + " Sample ID: 22, Sample Name: Sample022\n", + " Sample ID: 23, Sample Name: Sample023\n", + " Sample ID: 24, Sample Name: Sample024\n", + " Sample ID: 25, Sample Name: Sample025\n", + " Sample ID: 26, Sample Name: Sample026\n", + " Sample ID: 27, Sample Name: Sample027\n" ] }, { @@ -451,13 +407,13 @@ ] } ], - "execution_count": 53 + "execution_count": 10 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-01-20T14:45:11.812597Z", - "start_time": "2025-01-20T14:45:11.793309Z" + "end_time": "2025-01-30T12:36:50.600728Z", + "start_time": "2025-01-30T12:36:50.581752Z" } }, "cell_type": "code", @@ -473,8 +429,8 @@ " try:\n", " # Define the payload with only `event_type`\n", " sample_event_create = SampleEventCreate(\n", - " sample_id=433,\n", - " event_type=\"Mounted\" # Valid event type\n", + " sample_id=27,\n", + " event_type=\"Unmounted\" # Valid event type\n", " )\n", "\n", " # Debug the payload before sending\n", @@ -483,7 +439,7 @@ "\n", " # Call the API\n", " api_response = api_instance.create_sample_event_samples_samples_sample_id_events_post(\n", - " sample_id=433, # Ensure this matches a valid sample ID in the database\n", + " sample_id=27, # Ensure this matches a valid sample ID in the database\n", " sample_event_create=sample_event_create\n", " )\n", "\n", @@ -503,9 +459,9 @@ "output_type": "stream", "text": [ "Payload being sent to API:\n", - "{\"event_type\":\"Mounted\"}\n", + "{\"event_type\":\"Unmounted\"}\n", "API response:\n", - "Sample(id=433, sample_name='Dtpase_1', position=1, puck_id=44, crystalname=None, proteinname=None, positioninpuck=None, priority=1, comments=None, data_collection_parameters=DataCollectionParameters(directory='{sgPuck}/{sgPosition}', oscillation=None, exposure=None, totalrange=None, transmission=None, targetresolution=None, aperture=None, datacollectiontype=None, processingpipeline='', spacegroupnumber=None, cellparameters=None, rescutkey=None, rescutvalue=None, userresolution=None, pdbid='', autoprocfull=False, procfull=False, adpenabled=False, noano=False, ffcscampaign=False, trustedhigh=None, autoprocextraparams=None, chiphiangles=None, dose=None), events=[SampleEventResponse(id=386, sample_id=433, event_type='Mounted', timestamp=datetime.datetime(2025, 1, 20, 11, 35, 38)), SampleEventResponse(id=387, sample_id=433, event_type='Mounted', timestamp=datetime.datetime(2025, 1, 20, 11, 40, 11)), SampleEventResponse(id=388, sample_id=433, event_type='Mounted', timestamp=datetime.datetime(2025, 1, 20, 11, 45, 4)), SampleEventResponse(id=389, sample_id=433, event_type='Mounted', timestamp=datetime.datetime(2025, 1, 20, 11, 45, 24)), SampleEventResponse(id=390, sample_id=433, event_type='Mounted', timestamp=datetime.datetime(2025, 1, 20, 11, 50, 38)), SampleEventResponse(id=391, sample_id=433, event_type='Mounted', timestamp=datetime.datetime(2025, 1, 20, 11, 52, 28)), SampleEventResponse(id=392, sample_id=433, event_type='Mounted', timestamp=datetime.datetime(2025, 1, 20, 12, 10, 20)), SampleEventResponse(id=393, sample_id=433, event_type='Mounted', timestamp=datetime.datetime(2025, 1, 20, 13, 39, 24)), SampleEventResponse(id=394, sample_id=433, event_type='Mounted', timestamp=datetime.datetime(2025, 1, 20, 15, 45, 12))], mount_count=9, unmount_count=0)\n" + "Sample(id=27, sample_name='Sample027', position=15, puck_id=5, crystalname=None, proteinname=None, positioninpuck=None, priority=None, comments=None, data_collection_parameters=None, events=[SampleEventResponse(id=406, sample_id=27, event_type='Mounted', timestamp=datetime.datetime(2025, 1, 30, 13, 36, 34)), SampleEventResponse(id=407, sample_id=27, event_type='Unmounted', timestamp=datetime.datetime(2025, 1, 30, 13, 36, 51))], mount_count=1, unmount_count=1)\n" ] }, { @@ -517,13 +473,13 @@ ] } ], - "execution_count": 4 + "execution_count": 13 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-01-16T19:45:46.332149Z", - "start_time": "2025-01-16T19:45:46.320963Z" + "end_time": "2025-01-30T12:37:14.520342Z", + "start_time": "2025-01-30T12:37:14.508460Z" } }, "cell_type": "code", @@ -545,24 +501,24 @@ "id": "6a808ee09f97ae13", "outputs": [ { - "ename": "NameError", - "evalue": "name 'aareDBclient' is not defined", + "ename": "AttributeError", + "evalue": "'SamplesApi' object has no attribute 'get_last_sample_event_samples_samples_sample_id_events_last_get'", "output_type": "error", "traceback": [ "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mNameError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[0;32mIn[7], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[38;5;28;01mwith\u001B[39;00m \u001B[43maareDBclient\u001B[49m\u001B[38;5;241m.\u001B[39mApiClient(configuration) \u001B[38;5;28;01mas\u001B[39;00m api_client:\n\u001B[1;32m 2\u001B[0m \u001B[38;5;66;03m# Create an instance of the Samples API class\u001B[39;00m\n\u001B[1;32m 3\u001B[0m api_instance \u001B[38;5;241m=\u001B[39m aareDBclient\u001B[38;5;241m.\u001B[39mSamplesApi(api_client)\n\u001B[1;32m 5\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[1;32m 6\u001B[0m \u001B[38;5;66;03m# Get the last sample event\u001B[39;00m\n", - "\u001B[0;31mNameError\u001B[0m: name 'aareDBclient' is not defined" + "\u001B[0;31mAttributeError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[0;32mIn[14], line 7\u001B[0m\n\u001B[1;32m 3\u001B[0m api_instance \u001B[38;5;241m=\u001B[39m aareDBclient\u001B[38;5;241m.\u001B[39mSamplesApi(api_client)\n\u001B[1;32m 5\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[1;32m 6\u001B[0m \u001B[38;5;66;03m# Get the last sample event\u001B[39;00m\n\u001B[0;32m----> 7\u001B[0m last_event_response \u001B[38;5;241m=\u001B[39m \u001B[43mapi_instance\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget_last_sample_event_samples_samples_sample_id_events_last_get\u001B[49m(\u001B[38;5;241m14\u001B[39m)\n\u001B[1;32m 8\u001B[0m \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mThe response of get_last_sample_event:\u001B[39m\u001B[38;5;130;01m\\n\u001B[39;00m\u001B[38;5;124m\"\u001B[39m)\n\u001B[1;32m 9\u001B[0m pprint(last_event_response)\n", + "\u001B[0;31mAttributeError\u001B[0m: 'SamplesApi' object has no attribute 'get_last_sample_event_samples_samples_sample_id_events_last_get'" ] } ], - "execution_count": 7 + "execution_count": 14 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-01-20T15:43:54.575154Z", - "start_time": "2025-01-20T15:43:54.539295Z" + "end_time": "2025-01-30T12:38:46.149389Z", + "start_time": "2025-01-30T12:38:46.110767Z" } }, "cell_type": "code", @@ -575,7 +531,7 @@ "file_path = \"backend/tests/sample_image/IMG_1942.jpg\"\n", "\n", "# Sample ID\n", - "sample_id = 433 # Replace with a valid sample_id from your FastAPI backend\n", + "sample_id = 27 # Replace with a valid sample_id from your FastAPI backend\n", "\n", "# Initialize the API client\n", "with ApiClient(configuration) as api_client:\n", @@ -613,18 +569,18 @@ "traceback": [ "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", "\u001B[0;31mValueError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[0;32mIn[77], line 22\u001B[0m\n\u001B[1;32m 19\u001B[0m mime_type, _ \u001B[38;5;241m=\u001B[39m mimetypes\u001B[38;5;241m.\u001B[39mguess_type(file_path)\n\u001B[1;32m 21\u001B[0m \u001B[38;5;66;03m# Call the API method for uploading sample images\u001B[39;00m\n\u001B[0;32m---> 22\u001B[0m response \u001B[38;5;241m=\u001B[39m \u001B[43mapi_instance\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mupload_sample_images_samples_samples_sample_id_upload_images_post\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 23\u001B[0m \u001B[43m \u001B[49m\u001B[43msample_id\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msample_id\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 24\u001B[0m \u001B[43m \u001B[49m\u001B[43muploaded_files\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m[\u001B[49m\u001B[43mfile\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mread\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[43m]\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;66;43;03m# Pass raw bytes as a list\u001B[39;49;00m\n\u001B[1;32m 25\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 27\u001B[0m \u001B[38;5;66;03m# Print the response from the API\u001B[39;00m\n\u001B[1;32m 28\u001B[0m \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mAPI Response:\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n", + "Cell \u001B[0;32mIn[19], line 22\u001B[0m\n\u001B[1;32m 19\u001B[0m mime_type, _ \u001B[38;5;241m=\u001B[39m mimetypes\u001B[38;5;241m.\u001B[39mguess_type(file_path)\n\u001B[1;32m 21\u001B[0m \u001B[38;5;66;03m# Call the API method for uploading sample images\u001B[39;00m\n\u001B[0;32m---> 22\u001B[0m response \u001B[38;5;241m=\u001B[39m \u001B[43mapi_instance\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mupload_sample_images_samples_samples_sample_id_upload_images_post\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 23\u001B[0m \u001B[43m \u001B[49m\u001B[43msample_id\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msample_id\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 24\u001B[0m \u001B[43m \u001B[49m\u001B[43muploaded_files\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m[\u001B[49m\u001B[43mfile\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mread\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[43m]\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;66;43;03m# Pass raw bytes as a list\u001B[39;49;00m\n\u001B[1;32m 25\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 27\u001B[0m \u001B[38;5;66;03m# Print the response from the API\u001B[39;00m\n\u001B[1;32m 28\u001B[0m \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mAPI Response:\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n", "File \u001B[0;32m/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/pydantic/validate_call_decorator.py:60\u001B[0m, in \u001B[0;36mvalidate_call..validate..wrapper_function\u001B[0;34m(*args, **kwargs)\u001B[0m\n\u001B[1;32m 58\u001B[0m \u001B[38;5;129m@functools\u001B[39m\u001B[38;5;241m.\u001B[39mwraps(function)\n\u001B[1;32m 59\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[38;5;21mwrapper_function\u001B[39m(\u001B[38;5;241m*\u001B[39margs, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs):\n\u001B[0;32m---> 60\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mvalidate_call_wrapper\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43margs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n", "File \u001B[0;32m/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/pydantic/_internal/_validate_call.py:96\u001B[0m, in \u001B[0;36mValidateCallWrapper.__call__\u001B[0;34m(self, *args, **kwargs)\u001B[0m\n\u001B[1;32m 95\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[38;5;21m__call__\u001B[39m(\u001B[38;5;28mself\u001B[39m, \u001B[38;5;241m*\u001B[39margs: Any, \u001B[38;5;241m*\u001B[39m\u001B[38;5;241m*\u001B[39mkwargs: Any) \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m>\u001B[39m Any:\n\u001B[0;32m---> 96\u001B[0m res \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m__pydantic_validator__\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mvalidate_python\u001B[49m\u001B[43m(\u001B[49m\u001B[43mpydantic_core\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mArgsKwargs\u001B[49m\u001B[43m(\u001B[49m\u001B[43margs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 97\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m__return_pydantic_validator__:\n\u001B[1;32m 98\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m__return_pydantic_validator__(res)\n", - "File \u001B[0;32m~/PycharmProjects/heidi-v2/backend/aareDBclient/api/samples_api.py:875\u001B[0m, in \u001B[0;36mSamplesApi.upload_sample_images_samples_samples_sample_id_upload_images_post\u001B[0;34m(self, sample_id, uploaded_files, _request_timeout, _request_auth, _content_type, _headers, _host_index)\u001B[0m\n\u001B[1;32m 827\u001B[0m \u001B[38;5;129m@validate_call\u001B[39m\n\u001B[1;32m 828\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[38;5;21mupload_sample_images_samples_samples_sample_id_upload_images_post\u001B[39m(\n\u001B[1;32m 829\u001B[0m \u001B[38;5;28mself\u001B[39m,\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 843\u001B[0m _host_index: Annotated[StrictInt, Field(ge\u001B[38;5;241m=\u001B[39m\u001B[38;5;241m0\u001B[39m, le\u001B[38;5;241m=\u001B[39m\u001B[38;5;241m0\u001B[39m)] \u001B[38;5;241m=\u001B[39m \u001B[38;5;241m0\u001B[39m,\n\u001B[1;32m 844\u001B[0m ) \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m>\u001B[39m \u001B[38;5;28mobject\u001B[39m:\n\u001B[1;32m 845\u001B[0m \u001B[38;5;250m \u001B[39m\u001B[38;5;124;03m\"\"\"Upload Sample Images\u001B[39;00m\n\u001B[1;32m 846\u001B[0m \n\u001B[1;32m 847\u001B[0m \u001B[38;5;124;03m Uploads images for a sample and stores them in a directory structure: images/user/date/dewar_name/puck_name/position/. Args: sample_id (int): ID of the sample. uploaded_files (Union[List[UploadFile], List[bytes]]): List of image files (as UploadFile or raw bytes). db (Session): SQLAlchemy database session.\u001B[39;00m\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 872\u001B[0m \u001B[38;5;124;03m :return: Returns the result object.\u001B[39;00m\n\u001B[1;32m 873\u001B[0m \u001B[38;5;124;03m \"\"\"\u001B[39;00m \u001B[38;5;66;03m# noqa: E501\u001B[39;00m\n\u001B[0;32m--> 875\u001B[0m _param \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_upload_sample_images_samples_samples_sample_id_upload_images_post_serialize\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 876\u001B[0m \u001B[43m \u001B[49m\u001B[43msample_id\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msample_id\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 877\u001B[0m \u001B[43m \u001B[49m\u001B[43muploaded_files\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43muploaded_files\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 878\u001B[0m \u001B[43m \u001B[49m\u001B[43m_request_auth\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_request_auth\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 879\u001B[0m \u001B[43m \u001B[49m\u001B[43m_content_type\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_content_type\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 880\u001B[0m \u001B[43m \u001B[49m\u001B[43m_headers\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_headers\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 881\u001B[0m \u001B[43m \u001B[49m\u001B[43m_host_index\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_host_index\u001B[49m\n\u001B[1;32m 882\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 884\u001B[0m _response_types_map: Dict[\u001B[38;5;28mstr\u001B[39m, Optional[\u001B[38;5;28mstr\u001B[39m]] \u001B[38;5;241m=\u001B[39m {\n\u001B[1;32m 885\u001B[0m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m200\u001B[39m\u001B[38;5;124m'\u001B[39m: \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mobject\u001B[39m\u001B[38;5;124m\"\u001B[39m,\n\u001B[1;32m 886\u001B[0m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m422\u001B[39m\u001B[38;5;124m'\u001B[39m: \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mHTTPValidationError\u001B[39m\u001B[38;5;124m\"\u001B[39m,\n\u001B[1;32m 887\u001B[0m }\n\u001B[1;32m 888\u001B[0m response_data \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mapi_client\u001B[38;5;241m.\u001B[39mcall_api(\n\u001B[1;32m 889\u001B[0m \u001B[38;5;241m*\u001B[39m_param,\n\u001B[1;32m 890\u001B[0m _request_timeout\u001B[38;5;241m=\u001B[39m_request_timeout\n\u001B[1;32m 891\u001B[0m )\n", - "File \u001B[0;32m~/PycharmProjects/heidi-v2/backend/aareDBclient/api/samples_api.py:1099\u001B[0m, in \u001B[0;36mSamplesApi._upload_sample_images_samples_samples_sample_id_upload_images_post_serialize\u001B[0;34m(self, sample_id, uploaded_files, _request_auth, _content_type, _headers, _host_index)\u001B[0m\n\u001B[1;32m 1095\u001B[0m \u001B[38;5;66;03m# authentication setting\u001B[39;00m\n\u001B[1;32m 1096\u001B[0m _auth_settings: List[\u001B[38;5;28mstr\u001B[39m] \u001B[38;5;241m=\u001B[39m [\n\u001B[1;32m 1097\u001B[0m ]\n\u001B[0;32m-> 1099\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mapi_client\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mparam_serialize\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 1100\u001B[0m \u001B[43m \u001B[49m\u001B[43mmethod\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43mPOST\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1101\u001B[0m \u001B[43m \u001B[49m\u001B[43mresource_path\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43m/samples/samples/\u001B[39;49m\u001B[38;5;132;43;01m{sample_id}\u001B[39;49;00m\u001B[38;5;124;43m/upload-images\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1102\u001B[0m \u001B[43m \u001B[49m\u001B[43mpath_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_path_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1103\u001B[0m \u001B[43m \u001B[49m\u001B[43mquery_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_query_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1104\u001B[0m \u001B[43m \u001B[49m\u001B[43mheader_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_header_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1105\u001B[0m \u001B[43m \u001B[49m\u001B[43mbody\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_body_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1106\u001B[0m \u001B[43m \u001B[49m\u001B[43mpost_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_form_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1107\u001B[0m \u001B[43m \u001B[49m\u001B[43mfiles\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_files\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1108\u001B[0m \u001B[43m \u001B[49m\u001B[43mauth_settings\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_auth_settings\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1109\u001B[0m \u001B[43m \u001B[49m\u001B[43mcollection_formats\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_collection_formats\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1110\u001B[0m \u001B[43m \u001B[49m\u001B[43m_host\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_host\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1111\u001B[0m \u001B[43m \u001B[49m\u001B[43m_request_auth\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_request_auth\u001B[49m\n\u001B[1;32m 1112\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n", + "File \u001B[0;32m~/PycharmProjects/heidi-v2/backend/aareDBclient/api/samples_api.py:874\u001B[0m, in \u001B[0;36mSamplesApi.upload_sample_images_samples_samples_sample_id_upload_images_post\u001B[0;34m(self, sample_id, uploaded_files, _request_timeout, _request_auth, _content_type, _headers, _host_index)\u001B[0m\n\u001B[1;32m 827\u001B[0m \u001B[38;5;129m@validate_call\u001B[39m\n\u001B[1;32m 828\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[38;5;21mupload_sample_images_samples_samples_sample_id_upload_images_post\u001B[39m(\n\u001B[1;32m 829\u001B[0m \u001B[38;5;28mself\u001B[39m,\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 843\u001B[0m _host_index: Annotated[StrictInt, Field(ge\u001B[38;5;241m=\u001B[39m\u001B[38;5;241m0\u001B[39m, le\u001B[38;5;241m=\u001B[39m\u001B[38;5;241m0\u001B[39m)] \u001B[38;5;241m=\u001B[39m \u001B[38;5;241m0\u001B[39m,\n\u001B[1;32m 844\u001B[0m ) \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m>\u001B[39m \u001B[38;5;28mobject\u001B[39m:\n\u001B[1;32m 845\u001B[0m \u001B[38;5;250m \u001B[39m\u001B[38;5;124;03m\"\"\"Upload Sample Images\u001B[39;00m\n\u001B[1;32m 846\u001B[0m \n\u001B[1;32m 847\u001B[0m \n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 871\u001B[0m \u001B[38;5;124;03m :return: Returns the result object.\u001B[39;00m\n\u001B[1;32m 872\u001B[0m \u001B[38;5;124;03m \"\"\"\u001B[39;00m \u001B[38;5;66;03m# noqa: E501\u001B[39;00m\n\u001B[0;32m--> 874\u001B[0m _param \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_upload_sample_images_samples_samples_sample_id_upload_images_post_serialize\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 875\u001B[0m \u001B[43m \u001B[49m\u001B[43msample_id\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43msample_id\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 876\u001B[0m \u001B[43m \u001B[49m\u001B[43muploaded_files\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43muploaded_files\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 877\u001B[0m \u001B[43m \u001B[49m\u001B[43m_request_auth\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_request_auth\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 878\u001B[0m \u001B[43m \u001B[49m\u001B[43m_content_type\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_content_type\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 879\u001B[0m \u001B[43m \u001B[49m\u001B[43m_headers\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_headers\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 880\u001B[0m \u001B[43m \u001B[49m\u001B[43m_host_index\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_host_index\u001B[49m\n\u001B[1;32m 881\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 883\u001B[0m _response_types_map: Dict[\u001B[38;5;28mstr\u001B[39m, Optional[\u001B[38;5;28mstr\u001B[39m]] \u001B[38;5;241m=\u001B[39m {\n\u001B[1;32m 884\u001B[0m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m200\u001B[39m\u001B[38;5;124m'\u001B[39m: \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mobject\u001B[39m\u001B[38;5;124m\"\u001B[39m,\n\u001B[1;32m 885\u001B[0m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124m422\u001B[39m\u001B[38;5;124m'\u001B[39m: \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mHTTPValidationError\u001B[39m\u001B[38;5;124m\"\u001B[39m,\n\u001B[1;32m 886\u001B[0m }\n\u001B[1;32m 887\u001B[0m response_data \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mapi_client\u001B[38;5;241m.\u001B[39mcall_api(\n\u001B[1;32m 888\u001B[0m \u001B[38;5;241m*\u001B[39m_param,\n\u001B[1;32m 889\u001B[0m _request_timeout\u001B[38;5;241m=\u001B[39m_request_timeout\n\u001B[1;32m 890\u001B[0m )\n", + "File \u001B[0;32m~/PycharmProjects/heidi-v2/backend/aareDBclient/api/samples_api.py:1096\u001B[0m, in \u001B[0;36mSamplesApi._upload_sample_images_samples_samples_sample_id_upload_images_post_serialize\u001B[0;34m(self, sample_id, uploaded_files, _request_auth, _content_type, _headers, _host_index)\u001B[0m\n\u001B[1;32m 1092\u001B[0m \u001B[38;5;66;03m# authentication setting\u001B[39;00m\n\u001B[1;32m 1093\u001B[0m _auth_settings: List[\u001B[38;5;28mstr\u001B[39m] \u001B[38;5;241m=\u001B[39m [\n\u001B[1;32m 1094\u001B[0m ]\n\u001B[0;32m-> 1096\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mapi_client\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mparam_serialize\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 1097\u001B[0m \u001B[43m \u001B[49m\u001B[43mmethod\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43mPOST\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1098\u001B[0m \u001B[43m \u001B[49m\u001B[43mresource_path\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43m/samples/samples/\u001B[39;49m\u001B[38;5;132;43;01m{sample_id}\u001B[39;49;00m\u001B[38;5;124;43m/upload-images\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1099\u001B[0m \u001B[43m \u001B[49m\u001B[43mpath_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_path_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1100\u001B[0m \u001B[43m \u001B[49m\u001B[43mquery_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_query_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1101\u001B[0m \u001B[43m \u001B[49m\u001B[43mheader_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_header_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1102\u001B[0m \u001B[43m \u001B[49m\u001B[43mbody\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_body_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1103\u001B[0m \u001B[43m \u001B[49m\u001B[43mpost_params\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_form_params\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1104\u001B[0m \u001B[43m \u001B[49m\u001B[43mfiles\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_files\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1105\u001B[0m \u001B[43m \u001B[49m\u001B[43mauth_settings\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_auth_settings\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1106\u001B[0m \u001B[43m \u001B[49m\u001B[43mcollection_formats\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_collection_formats\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1107\u001B[0m \u001B[43m \u001B[49m\u001B[43m_host\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_host\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1108\u001B[0m \u001B[43m \u001B[49m\u001B[43m_request_auth\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43m_request_auth\u001B[49m\n\u001B[1;32m 1109\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n", "File \u001B[0;32m~/PycharmProjects/heidi-v2/backend/aareDBclient/api_client.py:214\u001B[0m, in \u001B[0;36mApiClient.param_serialize\u001B[0;34m(self, method, resource_path, path_params, query_params, header_params, body, post_params, files, auth_settings, collection_formats, _host, _request_auth)\u001B[0m\n\u001B[1;32m 209\u001B[0m post_params \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mparameters_to_tuples(\n\u001B[1;32m 210\u001B[0m post_params,\n\u001B[1;32m 211\u001B[0m collection_formats\n\u001B[1;32m 212\u001B[0m )\n\u001B[1;32m 213\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m files:\n\u001B[0;32m--> 214\u001B[0m post_params\u001B[38;5;241m.\u001B[39mextend(\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mfiles_parameters\u001B[49m\u001B[43m(\u001B[49m\u001B[43mfiles\u001B[49m\u001B[43m)\u001B[49m)\n\u001B[1;32m 216\u001B[0m \u001B[38;5;66;03m# auth setting\u001B[39;00m\n\u001B[1;32m 217\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mupdate_params_for_auth(\n\u001B[1;32m 218\u001B[0m header_params,\n\u001B[1;32m 219\u001B[0m query_params,\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 224\u001B[0m request_auth\u001B[38;5;241m=\u001B[39m_request_auth\n\u001B[1;32m 225\u001B[0m )\n", "File \u001B[0;32m~/PycharmProjects/heidi-v2/backend/aareDBclient/api_client.py:554\u001B[0m, in \u001B[0;36mApiClient.files_parameters\u001B[0;34m(self, files)\u001B[0m\n\u001B[1;32m 552\u001B[0m filedata \u001B[38;5;241m=\u001B[39m v\n\u001B[1;32m 553\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[0;32m--> 554\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mValueError\u001B[39;00m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mUnsupported file value\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[1;32m 555\u001B[0m mimetype \u001B[38;5;241m=\u001B[39m (\n\u001B[1;32m 556\u001B[0m mimetypes\u001B[38;5;241m.\u001B[39mguess_type(filename)[\u001B[38;5;241m0\u001B[39m]\n\u001B[1;32m 557\u001B[0m \u001B[38;5;129;01mor\u001B[39;00m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mapplication/octet-stream\u001B[39m\u001B[38;5;124m'\u001B[39m\n\u001B[1;32m 558\u001B[0m )\n\u001B[1;32m 559\u001B[0m params\u001B[38;5;241m.\u001B[39mappend(\n\u001B[1;32m 560\u001B[0m \u001B[38;5;28mtuple\u001B[39m([k, \u001B[38;5;28mtuple\u001B[39m([filename, filedata, mimetype])])\n\u001B[1;32m 561\u001B[0m )\n", "\u001B[0;31mValueError\u001B[0m: Unsupported file value" ] } ], - "execution_count": 77 + "execution_count": 19 }, { "metadata": {