GotthardG f588bc0cda Add endpoint for creating local contacts with access control
Introduced a new `local_contact_router` to handle creation of local contacts. The endpoint enforces role-based access control and ensures no duplication of email addresses. Updated the router exports for consistency and cleaned up a large test file to improve readability.
2025-02-26 09:58:19 +01:00

155 lines
4.9 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("/{sample_id}/upload-images")
async def upload_sample_image(
sample_id: int,
uploaded_file: UploadFile = File(...),
db: Session = Depends(get_db),
):
logging.info(f"Received file: {uploaded_file.filename}")
"""
Uploads an image for a given sample and saves it to a directory structure.
Args:
sample_id (int): ID of the sample.
uploaded_file (UploadFile): The file 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. Validate MIME type and Save the File
if not uploaded_file.content_type.startswith("image/"):
raise HTTPException(
status_code=400,
detail=f"Invalid file type: {uploaded_file.filename}."
f" Only images are accepted.",
)
file_path = base_dir / uploaded_file.filename
logging.debug(f"Saving file {uploaded_file.filename} to {file_path}")
try:
with file_path.open("wb") as buffer:
shutil.copyfileobj(uploaded_file.file, buffer)
logging.info(f"File saved: {file_path}")
except Exception as e:
logging.error(f"Error saving file {uploaded_file.filename}: {str(e)}")
raise HTTPException(
status_code=500,
detail=f"Could not save file {uploaded_file.filename}."
f" Ensure the server has correct permissions.",
)
# 4. Return Saved File Information
logging.info(f"Uploaded 1 file for sample {sample_id}.")
return {
"message": "1 image uploaded successfully.",
"file": str(file_path),
}