""" FastAPI application for medical report generation. This API provides a single endpoint that accepts all required files and patient information, then generates a comprehensive medical report. """ import shutil import tempfile from pathlib import Path from fastapi import FastAPI, File, Form, HTTPException, UploadFile from fastapi.responses import FileResponse from pydantic import BaseModel from services.report_generator import ReportGeneratorService app = FastAPI( title="Medical Report Generation API", description="API for generating medical performance reports with analysis and graphs", version="2.0.0", ) # Define output directories GRAPHS_DIR = Path("graphs") GRAPHS_DIR.mkdir(exist_ok=True) REPORTS_DIR = Path("reports") REPORTS_DIR.mkdir(exist_ok=True) # Initialize report generator service report_service = ReportGeneratorService( template_dir="app/report_gen", graphs_dir=str(GRAPHS_DIR), reports_dir=str(REPORTS_DIR), ) class ReportResponse(BaseModel): message: str report_path: str graphs_generated: list analysis_data: dict @app.get("/") async def root(): """Root endpoint with API information""" return { "message": "Medical Report Generation API", "version": "2.0.0", "endpoints": { "generate_report": "POST /generate-report", "download_report": "GET /download-report/{filename}", "health": "GET /health", }, } @app.get("/health") async def health_check(): """Health check endpoint""" return {"status": "healthy", "service": "report-generation-api"} @app.post("/generate-report", response_model=ReportResponse) async def generate_report( patient_name: str = Form(..., description="Patient name"), age: int = Form(..., description="Patient age"), height: str = Form(..., description="Patient height (e.g., 5'4\")"), weight: str = Form(..., description="Patient weight (e.g., 123lbs)"), focus: str = Form(default="Endurance", description="Training focus"), session_id: str = Form(default="default", description="Session ID"), spirometry_pdf: UploadFile = File(..., description="Spirometry PDF file"), pnoe_csv: UploadFile = File(..., description="Pnoe CSV file"), seca_excel: UploadFile = File(..., description="SECA Excel file"), ): """ Generate a comprehensive medical report from uploaded files. This endpoint accepts all required files and patient information, processes the data, generates graphs, and returns a PDF report. Args: spirometry_pdf: Spirometry PDF file pnoe_csv: Pnoe CSV data file seca_excel: SECA body composition Excel file patient_name: Name of the patient age: Patient age height: Patient height weight: Patient weight focus: Training focus (default: Endurance) session_id: Session identifier (default: default) Returns: ReportResponse with report path, graphs generated, and analysis data """ # Validate file types if not spirometry_pdf.filename.endswith(".pdf"): raise HTTPException(status_code=400, detail="Spirometry file must be a PDF") if not pnoe_csv.filename.endswith(".csv"): raise HTTPException(status_code=400, detail="Pnoe file must be a CSV") if not seca_excel.filename.endswith((".xlsx", ".xls")): raise HTTPException( status_code=400, detail="SECA file must be an Excel file (.xlsx or .xls)" ) # Create temporary directory for uploaded files with tempfile.TemporaryDirectory() as temp_dir: temp_path = Path(temp_dir) # Save uploaded files temporarily spirometry_path = temp_path / f"spirometry_{spirometry_pdf.filename}" pnoe_path = temp_path / f"pnoe_{pnoe_csv.filename}" seca_path = temp_path / f"seca_{seca_excel.filename}" try: # Write files with open(spirometry_path, "wb") as f: shutil.copyfileobj(spirometry_pdf.file, f) with open(pnoe_path, "wb") as f: shutil.copyfileobj(pnoe_csv.file, f) with open(seca_path, "wb") as f: shutil.copyfileobj(seca_excel.file, f) # Prepare patient information patient_info = { "patient_name": patient_name, "age": age, "height": height, "weight": weight, "focus": focus, "session_id": session_id, } # Generate report using the service result = report_service.generate_report( spirometry_pdf_path=str(spirometry_path), pnoe_csv_path=str(pnoe_path), seca_excel_path=str(seca_path), patient_info=patient_info, ) return ReportResponse( message="Report generated successfully", report_path=result["report_path"], graphs_generated=result["graphs_generated"], analysis_data=result["analysis_data"], ) except Exception as e: raise HTTPException( status_code=500, detail=f"Error generating report: {str(e)}", ) finally: # Close file handles spirometry_pdf.file.close() pnoe_csv.file.close() seca_excel.file.close() @app.get("/download-report/{filename}") async def download_report(filename: str): """ Download a generated report. Args: filename: Name of the report file Returns: PDF file """ file_path = REPORTS_DIR / filename if not file_path.exists(): raise HTTPException(status_code=404, detail="Report not found") return FileResponse( path=file_path, media_type="application/pdf", filename=filename, ) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)