[split-required] Split 500-850 LOC files (batch 2)
backend-lehrer (10 files): - game/database.py (785 → 5), correction_api.py (683 → 4) - classroom_engine/antizipation.py (676 → 5) - llm_gateway schools/edu_search already done in prior batch klausur-service (12 files): - orientation_crop_api.py (694 → 5), pdf_export.py (677 → 4) - zeugnis_crawler.py (676 → 5), grid_editor_api.py (671 → 5) - eh_templates.py (658 → 5), mail/api.py (651 → 5) - qdrant_service.py (638 → 5), training_api.py (625 → 4) website (6 pages): - middleware (696 → 8), mail (733 → 6), consent (628 → 8) - compliance/risks (622 → 5), export (502 → 5), brandbook (629 → 7) studio-v2 (3 components): - B2BMigrationWizard (848 → 3), CleanupPanel (765 → 2) - dashboard-experimental (739 → 2) admin-lehrer (4 files): - uebersetzungen (769 → 4), manager (670 → 2) - ChunkBrowserQA (675 → 6), dsfa/page (674 → 5) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
143
backend-lehrer/game/database_models.py
Normal file
143
backend-lehrer/game/database_models.py
Normal file
@@ -0,0 +1,143 @@
|
||||
# ==============================================
|
||||
# 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),
|
||||
]
|
||||
Reference in New Issue
Block a user