# ============================================== # Breakpilot Drive - Game Database Models # ============================================== # Data models, enums, and achievement definitions. import os import logging from datetime import datetime from typing import Optional, Dict, Any from dataclasses import dataclass from enum import IntEnum logger = logging.getLogger(__name__) # Database URL from environment GAME_DB_URL = os.getenv( "DATABASE_URL", "postgresql://breakpilot:breakpilot123@localhost:5432/breakpilot" ) class LearningLevel(IntEnum): """Learning level enum mapping to grade ranges.""" BEGINNER = 1 # Klasse 2-3 ELEMENTARY = 2 # Klasse 3-4 INTERMEDIATE = 3 # Klasse 4-5 ADVANCED = 4 # Klasse 5-6 EXPERT = 5 # Klasse 6+ @dataclass class StudentLearningState: """Student learning state data model.""" id: Optional[str] = None student_id: str = "" overall_level: int = 3 math_level: float = 3.0 german_level: float = 3.0 english_level: float = 3.0 total_play_time_minutes: int = 0 total_sessions: int = 0 questions_answered: int = 0 questions_correct: int = 0 created_at: Optional[datetime] = None updated_at: Optional[datetime] = None def to_dict(self) -> Dict[str, Any]: """Convert to dictionary.""" return { "id": self.id, "student_id": self.student_id, "overall_level": self.overall_level, "math_level": self.math_level, "german_level": self.german_level, "english_level": self.english_level, "total_play_time_minutes": self.total_play_time_minutes, "total_sessions": self.total_sessions, "questions_answered": self.questions_answered, "questions_correct": self.questions_correct, "created_at": self.created_at.isoformat() if self.created_at else None, "updated_at": self.updated_at.isoformat() if self.updated_at else None, } @property def accuracy(self) -> float: """Calculate overall accuracy percentage.""" if self.questions_answered == 0: return 0.0 return self.questions_correct / self.questions_answered @dataclass class GameSessionRecord: """Game session record for database storage.""" id: Optional[str] = None student_id: str = "" game_mode: str = "video" duration_seconds: int = 0 distance_traveled: float = 0.0 score: int = 0 questions_answered: int = 0 questions_correct: int = 0 difficulty_level: int = 3 started_at: Optional[datetime] = None ended_at: Optional[datetime] = None metadata: Optional[Dict[str, Any]] = None @dataclass class GameQuizAnswer: """Individual quiz answer record.""" id: Optional[str] = None session_id: Optional[str] = None question_id: str = "" subject: str = "" difficulty: int = 3 is_correct: bool = False answer_time_ms: int = 0 created_at: Optional[datetime] = None @dataclass class Achievement: """Achievement definition and unlock status.""" id: str name: str description: str icon: str = "star" category: str = "general" # general, streak, accuracy, time, score threshold: int = 1 unlocked: bool = False unlocked_at: Optional[datetime] = None progress: int = 0 # Achievement definitions (static, not in DB) ACHIEVEMENTS = [ # Erste Schritte Achievement(id="first_game", name="Erste Fahrt", description="Spiele dein erstes Spiel", icon="rocket", category="general", threshold=1), Achievement(id="five_games", name="Regelmaessiger Fahrer", description="Spiele 5 Spiele", icon="car", category="general", threshold=5), Achievement(id="twenty_games", name="Erfahrener Pilot", description="Spiele 20 Spiele", icon="trophy", category="general", threshold=20), # Serien Achievement(id="streak_3", name="Guter Start", description="3 richtige Antworten hintereinander", icon="fire", category="streak", threshold=3), Achievement(id="streak_5", name="Auf Feuer", description="5 richtige Antworten hintereinander", icon="fire", category="streak", threshold=5), Achievement(id="streak_10", name="Unaufhaltsam", description="10 richtige Antworten hintereinander", icon="fire", category="streak", threshold=10), # Genauigkeit Achievement(id="perfect_game", name="Perfektes Spiel", description="100% richtig in einem Spiel (min. 5 Fragen)", icon="star", category="accuracy", threshold=100), Achievement(id="accuracy_80", name="Scharfschuetze", description="80% Gesamtgenauigkeit (min. 50 Fragen)", icon="target", category="accuracy", threshold=80), # Zeit Achievement(id="play_30min", name="Ausdauer", description="30 Minuten Gesamtspielzeit", icon="clock", category="time", threshold=30), Achievement(id="play_60min", name="Marathon", description="60 Minuten Gesamtspielzeit", icon="clock", category="time", threshold=60), # Score Achievement(id="score_5000", name="Punktejaeger", description="5.000 Punkte in einem Spiel", icon="gem", category="score", threshold=5000), Achievement(id="score_10000", name="Highscore Hero", description="10.000 Punkte in einem Spiel", icon="crown", category="score", threshold=10000), # Level Achievement(id="level_up", name="Aufsteiger", description="Erreiche Level 2", icon="arrow-up", category="level", threshold=2), Achievement(id="master", name="Meister", description="Erreiche Level 5", icon="medal", category="level", threshold=5), ]