diff --git a/backend/app/routers/sample.py b/backend/app/routers/sample.py index 2d24b5b..39db2eb 100644 --- a/backend/app/routers/sample.py +++ b/backend/app/routers/sample.py @@ -1,7 +1,13 @@ from fastapi import APIRouter, HTTPException, Depends from sqlalchemy.orm import Session from typing import List -from app.schemas import Puck as PuckSchema, Sample as SampleSchema +from datetime import datetime +from app.schemas import ( + Puck as PuckSchema, + Sample as SampleSchema, + SampleEventResponse, + SampleEventCreate, +) from app.models import ( Puck as PuckModel, Sample as SampleModel, @@ -45,3 +51,50 @@ async def get_all_pucks_with_samples_and_events(db: Session = Depends(get_db)): status_code=404, detail="No pucks found in the database" ) # More descriptive return pucks + + +# Route to post a new sample event +@router.post("/samples/{sample_id}/events", response_model=SampleEventResponse) +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) + + return ( + sample_event # Response will automatically use the SampleEventResponse schema + ) + + +# 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 last_event # Response will automatically use the SampleEventResponse schema diff --git a/backend/app/schemas.py b/backend/app/schemas.py index 854bbce..a46f94d 100644 --- a/backend/app/schemas.py +++ b/backend/app/schemas.py @@ -83,6 +83,16 @@ class SampleEventCreate(BaseModel): event_type: str +class SampleEventResponse(BaseModel): + id: int + sample_id: int + event_type: str + timestamp: datetime + + class Config: + from_attributes = True + + class Results(BaseModel): # Define attributes for Results here pass diff --git a/backend/main.py b/backend/main.py index 903b536..3397eb6 100644 --- a/backend/main.py +++ b/backend/main.py @@ -36,6 +36,28 @@ def get_project_metadata(): ) +def run_server(): + import uvicorn + + print(f"[INFO] Starting server in {environment} environment...") + print(f"[INFO] SSL Certificate Path: {cert_path}") + print(f"[INFO] SSL Key Path: {key_path}") + port = config.get("PORT", os.getenv("PORT")) + if not port: + print("[ERROR] No port defined in config or environment variables. Aborting!") + sys.exit(1) # Exit if no port is defined + port = int(port) + print(f"[INFO] Running on port {port}") + uvicorn.run( + app, + host="127.0.0.1" if environment in ["dev", "test"] else "0.0.0.0", + port=port, + log_level="debug", + ssl_keyfile=key_path, + ssl_certfile=cert_path, + ) + + # Get project metadata from pyproject.toml project_name, project_version = get_project_metadata() app = FastAPI( @@ -145,7 +167,6 @@ app.include_router(sample.router, prefix="/samples", tags=["samples"]) if __name__ == "__main__": import sys - import uvicorn from dotenv import load_dotenv from multiprocessing import Process from time import sleep @@ -169,52 +190,28 @@ if __name__ == "__main__": print("openapi.json generated successfully.") sys.exit(0) # Exit after generating the file - # Default behavior: Run the server + # Default behavior: Run the server based on the environment environment = os.getenv("ENVIRONMENT", "dev") port = int(os.getenv("PORT", 8000)) is_ci = os.getenv("CI", "false").lower() == "true" - def run_server(): - print(f"[INFO] Starting server in {environment} environment...") - print(f"[INFO] SSL Certificate Path: {cert_path}") - print(f"[INFO] SSL Key Path: {key_path}") - port = config.get("PORT", os.getenv("PORT")) - if not port: - print( - "[ERROR] No port defined in config or environment variables. Aborting!" - ) - sys.exit(1) # Exit if no port is defined - port = int(port) - print(f"[INFO] Running on port {port}") - uvicorn.run( - app, - host="127.0.0.1" if environment in ["dev", "test"] else "0.0.0.0", - port=port, - log_level="debug", - ssl_keyfile=key_path, - ssl_certfile=cert_path, - ) - - # Run in CI mode - # Generate or use SSL Key and Certificate - if environment in ["test", "dev", "ci"]: - ssl_dir = Path( - cert_path - ).parent # Ensure we work with the parent directory of the cert path - ssl_dir.mkdir( - parents=True, exist_ok=True - ) # Create the directory structure if it doesn't exist + if is_ci or environment == "test": + # Test or CI Mode: Run server process temporarily for test validation + ssl_dir = Path(cert_path).parent + ssl_dir.mkdir(parents=True, exist_ok=True) # Generate self-signed certs if missing if not Path(cert_path).exists() or not Path(key_path).exists(): print(f"[INFO] Generating self-signed SSL certificates at {ssl_dir}") ssl_heidi.generate_self_signed_cert(cert_path, key_path) + # Start the server as a subprocess, wait, then terminate server_process = Process(target=run_server) server_process.start() - sleep(5) # Wait 5 seconds to ensure the server starts without errors - server_process.terminate() # Terminate the server (test purposes) + sleep(5) # Wait for 5 seconds to verify the server is running + server_process.terminate() # Terminate the server process (for CI) server_process.join() # Ensure proper cleanup print("CI: Server started and terminated successfully for test validation.") else: + # Dev or Prod: Start the server as usual run_server() diff --git a/pyproject.toml b/pyproject.toml index c043e5c..c8832c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "aareDB" -version = "0.1.0a5" +version = "0.1.0a6" description = "Backend for next gen sample management system" authors = [{name = "Guillaume Gotthard", email = "guillaume.gotthard@psi.ch"}] license = {text = "MIT"}