
Enhance app with active pgroup handling and token updates Added active pgroup state management across the app for user-specific settings. Improved token handling with decoding, saving user data, and setting OpenAPI authorization. Updated components, API calls, and forms to support dynamic pgroup selection and user-specific features.
158 lines
5.1 KiB
Python
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
|
|
username = "e16371" # Hardcoded username; replace with dynamic logic if applicable
|
|
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/{username}/{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,
|
|
}
|