first commit
This commit is contained in:
204
app/main.py
Normal file
204
app/main.py
Normal file
@@ -0,0 +1,204 @@
|
||||
"""
|
||||
IRT Bank Soal - Adaptive Question Bank System
|
||||
|
||||
Main FastAPI application entry point.
|
||||
|
||||
Features:
|
||||
- CTT (Classical Test Theory) scoring with exact Excel formulas
|
||||
- IRT (Item Response Theory) support for adaptive testing
|
||||
- Multi-website support for WordPress integration
|
||||
- AI-powered question generation
|
||||
"""
|
||||
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import AsyncGenerator
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from app.admin import admin as admin_app
|
||||
from app.core.config import get_settings
|
||||
from app.database import close_db, init_db
|
||||
from app.routers import (
|
||||
admin_router,
|
||||
ai_router,
|
||||
import_export_router,
|
||||
reports_router,
|
||||
sessions_router,
|
||||
tryouts_router,
|
||||
wordpress_router,
|
||||
)
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
"""
|
||||
Application lifespan manager.
|
||||
|
||||
Handles startup and shutdown events.
|
||||
"""
|
||||
# Startup: Initialize database
|
||||
await init_db()
|
||||
|
||||
yield
|
||||
|
||||
# Shutdown: Close database connections
|
||||
await close_db()
|
||||
|
||||
|
||||
# Initialize FastAPI application
|
||||
app = FastAPI(
|
||||
title="IRT Bank Soal",
|
||||
description="""
|
||||
## Adaptive Question Bank System with IRT/CTT Scoring
|
||||
|
||||
This API provides a comprehensive backend for adaptive assessment systems.
|
||||
|
||||
### Features
|
||||
- **CTT Scoring**: Classical Test Theory with exact Excel formula compatibility
|
||||
- **IRT Support**: Item Response Theory for adaptive testing (1PL Rasch model)
|
||||
- **Multi-Site**: Single backend serving multiple WordPress sites
|
||||
- **AI Generation**: Automatic question variant generation
|
||||
|
||||
### Scoring Formulas (PRD Section 13.1)
|
||||
- **CTT p-value**: `p = Σ Benar / Total Peserta`
|
||||
- **CTT Bobot**: `Bobot = 1 - p`
|
||||
- **CTT NM**: `NM = (Total_Bobot_Siswa / Total_Bobot_Max) × 1000`
|
||||
- **CTT NN**: `NN = 500 + 100 × ((NM - Rataan) / SB)`
|
||||
|
||||
### Authentication
|
||||
Most endpoints require `X-Website-ID` header for multi-site isolation.
|
||||
""",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
redoc_url="/redoc",
|
||||
openapi_url="/openapi.json",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
# Configure CORS middleware
|
||||
# Parse ALLOWED_ORIGINS from settings (comma-separated string)
|
||||
allowed_origins = settings.ALLOWED_ORIGINS
|
||||
if isinstance(allowed_origins, str):
|
||||
allowed_origins = [origin.strip() for origin in allowed_origins.split(",") if origin.strip()]
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=allowed_origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
# Health check endpoint
|
||||
@app.get(
|
||||
"/",
|
||||
summary="Health check",
|
||||
description="Returns API status and version information.",
|
||||
tags=["health"],
|
||||
)
|
||||
async def root():
|
||||
"""
|
||||
Health check endpoint.
|
||||
|
||||
Returns basic API information for monitoring and load balancer checks.
|
||||
"""
|
||||
return {
|
||||
"status": "healthy",
|
||||
"service": "IRT Bank Soal",
|
||||
"version": "1.0.0",
|
||||
"docs": "/docs",
|
||||
}
|
||||
|
||||
|
||||
@app.get(
|
||||
"/health",
|
||||
summary="Detailed health check",
|
||||
description="Returns detailed health status including database connectivity.",
|
||||
tags=["health"],
|
||||
)
|
||||
async def health_check():
|
||||
"""
|
||||
Detailed health check endpoint.
|
||||
|
||||
Includes database connectivity verification.
|
||||
"""
|
||||
from app.database import engine
|
||||
from sqlalchemy import text
|
||||
|
||||
db_status = "unknown"
|
||||
try:
|
||||
async with engine.connect() as conn:
|
||||
await conn.execute(text("SELECT 1"))
|
||||
db_status = "connected"
|
||||
except Exception as e:
|
||||
db_status = f"error: {str(e)}"
|
||||
|
||||
return {
|
||||
"status": "healthy" if db_status == "connected" else "degraded",
|
||||
"service": "IRT Bank Soal",
|
||||
"version": "1.0.0",
|
||||
"database": db_status,
|
||||
"environment": settings.ENVIRONMENT,
|
||||
}
|
||||
|
||||
|
||||
# Include API routers with version prefix
|
||||
app.include_router(
|
||||
import_export_router,
|
||||
)
|
||||
app.include_router(
|
||||
sessions_router,
|
||||
prefix=f"{settings.API_V1_STR}",
|
||||
)
|
||||
app.include_router(
|
||||
tryouts_router,
|
||||
prefix=f"{settings.API_V1_STR}",
|
||||
)
|
||||
app.include_router(
|
||||
wordpress_router,
|
||||
prefix=f"{settings.API_V1_STR}",
|
||||
)
|
||||
app.include_router(
|
||||
ai_router,
|
||||
prefix=f"{settings.API_V1_STR}",
|
||||
)
|
||||
app.include_router(
|
||||
reports_router,
|
||||
prefix=f"{settings.API_V1_STR}",
|
||||
)
|
||||
|
||||
|
||||
# Mount FastAPI Admin panel
|
||||
app.mount("/admin", admin_app)
|
||||
|
||||
|
||||
# Include admin API router for custom actions
|
||||
app.include_router(
|
||||
admin_router,
|
||||
prefix=f"{settings.API_V1_STR}",
|
||||
)
|
||||
|
||||
|
||||
# Placeholder routers for future implementation
|
||||
# These will be implemented in subsequent phases
|
||||
|
||||
# app.include_router(
|
||||
# items_router,
|
||||
# prefix=f"{settings.API_V1_STR}",
|
||||
# tags=["items"],
|
||||
# )
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
uvicorn.run(
|
||||
"app.main:app",
|
||||
host="0.0.0.0",
|
||||
port=8000,
|
||||
reload=settings.ENVIRONMENT == "development",
|
||||
)
|
||||
Reference in New Issue
Block a user