# ============================================== # Breakpilot Drive - Learning State DB Methods # ============================================== # Methods for reading/writing student learning state. import logging from typing import Optional from .database_models import StudentLearningState logger = logging.getLogger(__name__) class LearningStateMixin: """Mixin providing learning state database methods for GameDatabase.""" async def get_learning_state(self, student_id: str) -> Optional[StudentLearningState]: """Get learning state for a student.""" await self._ensure_connected() if not self._pool: return None try: async with self._pool.acquire() as conn: row = await conn.fetchrow( """ SELECT id, student_id, overall_level, math_level, german_level, english_level, total_play_time_minutes, total_sessions, questions_answered, questions_correct, created_at, updated_at FROM student_learning_state WHERE student_id = $1 """, student_id ) if row: return StudentLearningState( id=str(row["id"]), student_id=str(row["student_id"]), overall_level=row["overall_level"], math_level=float(row["math_level"]), german_level=float(row["german_level"]), english_level=float(row["english_level"]), total_play_time_minutes=row["total_play_time_minutes"], total_sessions=row["total_sessions"], questions_answered=row["questions_answered"] or 0, questions_correct=row["questions_correct"] or 0, created_at=row["created_at"], updated_at=row["updated_at"], ) except Exception as e: logger.error(f"Failed to get learning state: {e}") return None async def create_or_update_learning_state( self, student_id: str, overall_level: int = 3, math_level: float = 3.0, german_level: float = 3.0, english_level: float = 3.0, ) -> Optional[StudentLearningState]: """Create or update learning state for a student.""" await self._ensure_connected() if not self._pool: return None try: async with self._pool.acquire() as conn: row = await conn.fetchrow( """ INSERT INTO student_learning_state ( student_id, overall_level, math_level, german_level, english_level ) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (student_id) DO UPDATE SET overall_level = EXCLUDED.overall_level, math_level = EXCLUDED.math_level, german_level = EXCLUDED.german_level, english_level = EXCLUDED.english_level, updated_at = NOW() RETURNING id, student_id, overall_level, math_level, german_level, english_level, total_play_time_minutes, total_sessions, questions_answered, questions_correct, created_at, updated_at """, student_id, overall_level, math_level, german_level, english_level ) if row: return StudentLearningState( id=str(row["id"]), student_id=str(row["student_id"]), overall_level=row["overall_level"], math_level=float(row["math_level"]), german_level=float(row["german_level"]), english_level=float(row["english_level"]), total_play_time_minutes=row["total_play_time_minutes"], total_sessions=row["total_sessions"], questions_answered=row["questions_answered"] or 0, questions_correct=row["questions_correct"] or 0, created_at=row["created_at"], updated_at=row["updated_at"], ) except Exception as e: logger.error(f"Failed to create/update learning state: {e}") return None async def update_learning_stats( self, student_id: str, duration_minutes: int, questions_answered: int, questions_correct: int, new_level: Optional[int] = None, ) -> bool: """Update learning stats after a game session.""" await self._ensure_connected() if not self._pool: return False try: async with self._pool.acquire() as conn: if new_level is not None: await conn.execute( """ UPDATE student_learning_state SET total_play_time_minutes = total_play_time_minutes + $2, total_sessions = total_sessions + 1, questions_answered = COALESCE(questions_answered, 0) + $3, questions_correct = COALESCE(questions_correct, 0) + $4, overall_level = $5, updated_at = NOW() WHERE student_id = $1 """, student_id, duration_minutes, questions_answered, questions_correct, new_level ) else: await conn.execute( """ UPDATE student_learning_state SET total_play_time_minutes = total_play_time_minutes + $2, total_sessions = total_sessions + 1, questions_answered = COALESCE(questions_answered, 0) + $3, questions_correct = COALESCE(questions_correct, 0) + $4, updated_at = NOW() WHERE student_id = $1 """, student_id, duration_minutes, questions_answered, questions_correct ) return True except Exception as e: logger.error(f"Failed to update learning stats: {e}") return False