GotthardG c2215860bf 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.
2025-01-30 13:39:49 +01:00

158 lines
5.1 KiB
Python

from fastapi import APIRouter, HTTPException, Depends, UploadFile, File
from sqlalchemy.orm import Session
from pathlib import Path
from typing import List
from datetime import datetime
import shutil
from app.schemas import (
Puck as PuckSchema,
Sample as SampleSchema,
SampleEventCreate,
Sample,
)
from app.models import (
Puck as PuckModel,
Sample as SampleModel,
SampleEvent as SampleEventModel,
)
from app.dependencies import get_db
import logging
from sqlalchemy.orm import joinedload
router = APIRouter()
@router.get("/{puck_id}/samples", response_model=List[SampleSchema])
async def get_samples_with_events(puck_id: int, db: Session = Depends(get_db)):
puck = db.query(PuckModel).filter(PuckModel.id == puck_id).first()
if not puck:
raise HTTPException(status_code=404, detail="Puck not found")
samples = db.query(SampleModel).filter(SampleModel.puck_id == puck_id).all()
for sample in samples:
sample.events = (
db.query(SampleEventModel)
.filter(SampleEventModel.sample_id == sample.id)
.all()
)
return samples
@router.get("/pucks-samples", response_model=List[PuckSchema])
async def get_all_pucks_with_samples_and_events(db: Session = Depends(get_db)):
logging.info("Fetching all pucks with samples and events")
pucks = (
db.query(PuckModel)
.options(
joinedload(PuckModel.samples).joinedload(
SampleModel.events
), # Correct nested relationship
joinedload(PuckModel.events), # If Puck has its own events relationship
)
.all()
)
if not pucks:
raise HTTPException(status_code=404, detail="No pucks found in the database")
return pucks
# Route to post a new sample event
@router.post("/samples/{sample_id}/events", response_model=Sample)
async def create_sample_event(
sample_id: int, event: SampleEventCreate, db: Session = Depends(get_db)
):
# Ensure the sample exists
sample = db.query(SampleModel).filter(SampleModel.id == sample_id).first()
if not sample:
raise HTTPException(status_code=404, detail="Sample not found")
# Create the event
sample_event = SampleEventModel(
sample_id=sample_id,
event_type=event.event_type,
timestamp=datetime.now(), # Use the current timestamp
)
db.add(sample_event)
db.commit()
db.refresh(sample_event)
# Load events for the sample to be serialized in the response
sample.events = (
db.query(SampleEventModel).filter(SampleEventModel.sample_id == sample_id).all()
)
return sample # Return the sample, now including `mount_count`
@router.post("/samples/{sample_id}/upload-images")
async def upload_sample_images(
sample_id: int,
uploaded_files: list[UploadFile] = File(...),
db: Session = Depends(get_db),
):
logging.info(f"Received files: {[file.filename for file in uploaded_files]}")
"""
Uploads images for a given sample and saves them to a directory structure.
Args:
sample_id (int): ID of the sample.
uploaded_files (list[UploadFile]): A list of files uploaded with the request.
db (Session): Database session.
"""
# 1. Validate Sample
sample = db.query(SampleModel).filter(SampleModel.id == sample_id).first()
if not sample:
raise HTTPException(status_code=404, detail="Sample not found")
# 2. Define Directory Structure
pgroup = sample.puck.dewar.pgroups
today = datetime.now().strftime("%Y-%m-%d")
dewar_name = (
sample.puck.dewar.dewar_name
if sample.puck and sample.puck.dewar
else "default_dewar"
)
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/{pgroup}/{today}/{dewar_name}/{puck_name}/{position}")
base_dir.mkdir(parents=True, exist_ok=True)
# 3. Process and Save Each File
saved_files = []
for file in uploaded_files:
# Validate MIME type
if not file.content_type.startswith("image/"):
raise HTTPException(
status_code=400,
detail=f"Invalid file type: {file.filename}. Only images are accepted.",
)
# Save file to the base directory
file_path = base_dir / file.filename
# Save the file from the file stream
try:
with file_path.open("wb") as buffer:
shutil.copyfileobj(file.file, buffer)
saved_files.append(str(file_path)) # Track saved file paths
except Exception as e:
logging.error(f"Error saving file {file.filename}: {str(e)}")
raise HTTPException(
status_code=500,
detail=f"Could not save file {file.filename}."
f" Ensure the server has correct permissions.",
)
# 4. Return Saved Files Information
logging.info(f"Uploaded {len(saved_files)} files for sample {sample_id}.")
return {
"message": f"{len(saved_files)} images uploaded successfully.",
"files": saved_files,
}