**Commit Message:**

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.
This commit is contained in:
GotthardG
2025-01-22 13:55:26 +01:00
parent 4630bcfac5
commit 6cde57f783
23 changed files with 806 additions and 250 deletions

View File

@ -7,7 +7,6 @@ import shutil
from app.schemas import (
Puck as PuckSchema,
Sample as SampleSchema,
SampleEventResponse,
SampleEventCreate,
Sample,
)
@ -90,106 +89,69 @@ async def create_sample_event(
return sample # Return the sample, now including `mount_count`
# Route to fetch the last (most recent) sample event
@router.get("/samples/{sample_id}/events/last", response_model=SampleEventResponse)
async def get_last_sample_event(sample_id: int, 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")
# Get the most recent event for the sample
last_event = (
db.query(SampleEventModel)
.filter(SampleEventModel.sample_id == sample_id)
.order_by(SampleEventModel.timestamp.desc())
.first()
)
if not last_event:
raise HTTPException(status_code=404, detail="No events found for the sample")
return SampleEventResponse(
id=last_event.id,
sample_id=last_event.sample_id,
event_type=last_event.event_type,
timestamp=last_event.timestamp,
) # Response will automatically use the SampleEventResponse schema
@router.post("/samples/{sample_id}/upload-images")
async def upload_sample_images(
sample_id: int,
uploaded_files: List[UploadFile] = File(...), # Accept multiple files
uploaded_files: list[UploadFile] = File(...),
db: Session = Depends(get_db),
):
"""
Uploads images for a sample and stores them in a directory structure:
images/user/date/dewar_name/puck_name/position/.
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]): List of image files to be uploaded.
db (Session): SQLAlchemy database session.
uploaded_files (list[UploadFile]): A list of files uploaded with the request.
db (Session): Database session.
"""
# Fetch sample details from the database
# 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")
# Retrieve associated dewar_name, puck_name and position
puck = sample.puck
if not puck:
raise HTTPException(
status_code=404, detail=f"No puck associated with sample ID {sample_id}"
)
dewar_name = puck.dewar.dewar_name if puck.dewar else None
if not dewar_name:
raise HTTPException(
status_code=404, detail=f"No dewar associated with puck ID {puck.id}"
)
puck_name = puck.puck_name
position = sample.position
# Retrieve username (hardcoded for now—can be fetched dynamically if needed)
username = "e16371"
# Today's date in the format YYYY-MM-DD
# 2. Define Directory Structure
username = "e16371" # Hardcoded username; replace with dynamic logic if applicable
today = datetime.now().strftime("%Y-%m-%d")
# Generate the directory path based on the structure
base_dir = (
Path("images") / username / today / dewar_name / puck_name / str(position)
dewar_name = (
sample.puck.dewar.dewar_name
if sample.puck and sample.puck.dewar
else "default_dewar"
)
# Create directories if they don't exist
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)
# Save each uploaded image to the directory
# 3. Process and Save Each File
saved_files = []
for file in uploaded_files:
# Validate file content type
# Validate MIME type
if not file.content_type.startswith("image/"):
raise HTTPException(
status_code=400,
detail=f"Invalid file type: {file.filename}. Must be an image.",
detail=f"Invalid file type: {file.filename}. Only images are accepted.",
)
# Create a file path for storing the uploaded file
# Save file to the base directory
file_path = base_dir / file.filename
# Save the file from the file stream
try:
# Save the file
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"Error saving file {file.filename}: {str(e)}",
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(uploaded_files)} images uploaded successfully.",
"path": str(base_dir), # Return the base directory for reference
"message": f"{len(saved_files)} images uploaded successfully.",
"files": saved_files,
}