""" Classroom API - Database Persistence Service Handles hybrid storage: In-Memory + PostgreSQL for sessions. """ import logging from typing import Dict, Optional from fastapi import HTTPException from classroom_engine import LessonSession, LessonPhase logger = logging.getLogger(__name__) # Database imports (Feature f22) try: from classroom_engine.database import get_db, init_db, SessionLocal from classroom_engine.repository import SessionRepository DB_ENABLED = True except ImportError: DB_ENABLED = False SessionLocal = None logger.warning("Classroom DB not available, using in-memory storage only") # === Hybrid Storage: In-Memory + DB (Feature f22) === # In-Memory fuer schnellen Zugriff, DB fuer Persistenz sessions: Dict[str, LessonSession] = {} _db_initialized = False def init_db_if_needed(): """Initialisiert DB und laedt aktive Sessions beim ersten Aufruf.""" global _db_initialized if _db_initialized or not DB_ENABLED: return try: init_db() load_active_sessions_from_db() _db_initialized = True logger.info("Classroom DB initialized, loaded active sessions") except Exception as e: logger.error(f"Failed to initialize Classroom DB: {e}") def load_active_sessions_from_db(): """Laedt alle aktiven Sessions aus der DB in den Memory-Cache.""" if not DB_ENABLED: return try: db = SessionLocal() repo = SessionRepository(db) # Lade alle nicht-beendeten Sessions from classroom_engine.db_models import LessonSessionDB, LessonPhaseEnum active_db_sessions = db.query(LessonSessionDB).filter( LessonSessionDB.current_phase != LessonPhaseEnum.ENDED ).all() for db_session in active_db_sessions: session = repo.to_dataclass(db_session) sessions[session.session_id] = session logger.info(f"Loaded session {session.session_id} from DB") db.close() except Exception as e: logger.error(f"Failed to load sessions from DB: {e}") def persist_session(session: LessonSession): """Speichert/aktualisiert Session in der DB.""" if not DB_ENABLED: return try: db = SessionLocal() repo = SessionRepository(db) existing = repo.get_by_id(session.session_id) if existing: repo.update(session) else: repo.create(session) db.close() except Exception as e: logger.error(f"Failed to persist session {session.session_id}: {e}") def get_session_or_404(session_id: str) -> LessonSession: """Holt eine Session oder wirft 404. Prueft auch DB bei Cache-Miss.""" init_db_if_needed() session = sessions.get(session_id) if session: return session # Feature f22: Bei Cache-Miss aus DB laden if DB_ENABLED: try: db = SessionLocal() repo = SessionRepository(db) db_session = repo.get_by_id(session_id) if db_session: session = repo.to_dataclass(db_session) sessions[session.session_id] = session # In Cache laden db.close() return session db.close() except Exception as e: logger.error(f"Failed to load session {session_id} from DB: {e}") raise HTTPException(status_code=404, detail="Session nicht gefunden") def delete_session_from_db(session_id: str): """Loescht Session aus der DB.""" if not DB_ENABLED: return try: db = SessionLocal() repo = SessionRepository(db) repo.delete(session_id) db.close() except Exception as e: logger.error(f"Failed to delete session {session_id} from DB: {e}")