faais-k's picture
style: run black formatter to fix CI
7e89e5d
"""
MongoDB connection for AI Interview Simulator.
Collections:
sessions β€” one doc per session (metadata + status tracking)
reports β€” final report + analytics (written on interview completion)
violations β€” anti-cheat events (one doc per violation event)
Strategy: HYBRID storage.
- MongoDB stores session metadata and final results (reports, analytics).
- Flat files remain for in-progress interview_state.json because the
state machine does many small reads/writes per question that benefit
from local file I/O speed.
- Falls back gracefully if MONGODB_URL is not set (flat-file mode only).
Usage:
await connect_db() # called in FastAPI lifespan on startup
await disconnect_db() # called in FastAPI lifespan on shutdown
db = get_db() # returns AsyncIOMotorDatabase or None
db_available() # True if connected
"""
import os
from typing import Optional
_client = None
_db = None
MONGODB_URL = os.getenv("MONGODB_URL", "")
DB_NAME = os.getenv("MONGODB_DB", "ai_interview_sim")
async def connect_db() -> None:
"""Connect to MongoDB and create indexes. No-op if MONGODB_URL is unset."""
global _client, _db
if not MONGODB_URL:
print("ℹ️ MONGODB_URL not set β€” running in flat-file mode only")
return
try:
from motor.motor_asyncio import AsyncIOMotorClient
_client = AsyncIOMotorClient(
MONGODB_URL,
serverSelectionTimeoutMS=5000,
maxPoolSize=10,
minPoolSize=1,
)
# Ping to verify connection before declaring success
await _client.admin.command("ping")
_db = _client[DB_NAME]
# Create indexes (idempotent β€” safe to run on every startup)
await _db.sessions.create_index("session_id", unique=True)
await _db.sessions.create_index("created_at")
await _db.sessions.create_index("status")
await _db.reports.create_index("session_id", unique=True)
await _db.violations.create_index("session_id")
await _db.violations.create_index("logged_at")
print(f"βœ… MongoDB connected: {MONGODB_URL[:40]}… / {DB_NAME}")
except Exception as e:
print(f"⚠️ MongoDB connection failed β€” flat-file mode only: {e}")
_client = None
_db = None
async def disconnect_db() -> None:
"""Close the MongoDB connection."""
global _client, _db
if _client:
_client.close()
_client = None
_db = None
print("πŸ”΄ MongoDB disconnected")
def get_db():
"""Return the AsyncIOMotorDatabase instance, or None if not connected."""
return _db
def db_available() -> bool:
"""True if MongoDB is connected and available."""
return _db is not None