|
|
""" |
|
|
FastAPI Marine Species Identification API |
|
|
|
|
|
Main application entry point for the marine species identification API. |
|
|
""" |
|
|
|
|
|
import asyncio |
|
|
from contextlib import asynccontextmanager |
|
|
from fastapi import FastAPI, HTTPException, Request |
|
|
from fastapi.middleware.cors import CORSMiddleware |
|
|
from fastapi.responses import JSONResponse, HTMLResponse |
|
|
from fastapi.staticfiles import StaticFiles |
|
|
from fastapi.templating import Jinja2Templates |
|
|
import uvicorn |
|
|
|
|
|
from app.core.config import settings |
|
|
from app.core.logging import setup_logging, get_logger |
|
|
from app.api.v1.api import api_router |
|
|
from app.services.model_service import model_service |
|
|
|
|
|
|
|
|
setup_logging() |
|
|
logger = get_logger(__name__) |
|
|
|
|
|
|
|
|
@asynccontextmanager |
|
|
async def lifespan(app: FastAPI): |
|
|
""" |
|
|
Application lifespan manager. |
|
|
Handles startup and shutdown events. |
|
|
""" |
|
|
|
|
|
logger.info("Starting Marine Species Identification API...") |
|
|
|
|
|
try: |
|
|
|
|
|
await model_service.ensure_model_available() |
|
|
logger.info("Model loaded successfully") |
|
|
except Exception as e: |
|
|
logger.error(f"Failed to load model during startup: {str(e)}") |
|
|
|
|
|
|
|
|
logger.info("API startup completed") |
|
|
|
|
|
yield |
|
|
|
|
|
|
|
|
logger.info("Shutting down Marine Species Identification API...") |
|
|
|
|
|
|
|
|
|
|
|
app = FastAPI( |
|
|
title=settings.PROJECT_NAME, |
|
|
version=settings.VERSION, |
|
|
description=settings.DESCRIPTION, |
|
|
openapi_url=f"{settings.API_V1_STR}/openapi.json", |
|
|
docs_url="/docs", |
|
|
redoc_url="/redoc", |
|
|
lifespan=lifespan |
|
|
) |
|
|
|
|
|
|
|
|
app.mount("/static", StaticFiles(directory="static"), name="static") |
|
|
templates = Jinja2Templates(directory="templates") |
|
|
|
|
|
|
|
|
app.add_middleware( |
|
|
CORSMiddleware, |
|
|
allow_origins=["*"], |
|
|
allow_credentials=True, |
|
|
allow_methods=["*"], |
|
|
allow_headers=["*"], |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
@app.exception_handler(Exception) |
|
|
async def global_exception_handler(request: Request, exc: Exception): |
|
|
"""Global exception handler for unhandled errors.""" |
|
|
logger.error(f"Unhandled exception: {str(exc)}", exc_info=True) |
|
|
return JSONResponse( |
|
|
status_code=500, |
|
|
content={ |
|
|
"error": "Internal Server Error", |
|
|
"message": "An unexpected error occurred", |
|
|
"details": str(exc) if settings.DEBUG else None |
|
|
} |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
app.include_router(api_router, prefix=settings.API_V1_STR) |
|
|
|
|
|
|
|
|
|
|
|
@app.get("/", response_class=HTMLResponse, tags=["Dashboard"]) |
|
|
async def dashboard(request: Request): |
|
|
""" |
|
|
Main dashboard for species identification. |
|
|
Provides a web interface for testing the API. |
|
|
""" |
|
|
return templates.TemplateResponse("dashboard.html", { |
|
|
"request": request, |
|
|
"active_tab": "dashboard" |
|
|
}) |
|
|
|
|
|
|
|
|
@app.get("/api", tags=["Root"]) |
|
|
async def api_root(): |
|
|
""" |
|
|
API root endpoint providing basic API information. |
|
|
""" |
|
|
return { |
|
|
"message": "Seamo Species API", |
|
|
"version": settings.VERSION, |
|
|
"docs": "/docs", |
|
|
"health": f"{settings.API_V1_STR}/health", |
|
|
"api_info": f"{settings.API_V1_STR}/info", |
|
|
"dashboard": "/" |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@app.get("/health", tags=["Health"]) |
|
|
async def root_health(): |
|
|
"""Simple health check at root level.""" |
|
|
return {"status": "ok"} |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
|
|
uvicorn.run( |
|
|
"app.main:app", |
|
|
host=settings.HOST, |
|
|
port=settings.PORT, |
|
|
reload=False, |
|
|
log_level="info" |
|
|
) |
|
|
|