Files
gt-ai-os-community/apps/tenant-backend/app/models/game.py
HackWeasel b9dfb86260 GT AI OS Community Edition v2.0.33
Security hardening release addressing CodeQL and Dependabot alerts:

- Fix stack trace exposure in error responses
- Add SSRF protection with DNS resolution checking
- Implement proper URL hostname validation (replaces substring matching)
- Add centralized path sanitization to prevent path traversal
- Fix ReDoS vulnerability in email validation regex
- Improve HTML sanitization in validation utilities
- Fix capability wildcard matching in auth utilities
- Update glob dependency to address CVE
- Add CodeQL suppression comments for verified false positives

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 17:04:45 -05:00

383 lines
14 KiB
Python

"""
Game Models for GT 2.0 Tenant Backend - Service-Based Architecture
Pydantic models for game entities using the PostgreSQL + PGVector backend.
Game sessions for AI literacy and strategic thinking development.
Perfect tenant isolation - each tenant has separate game data.
"""
from datetime import datetime
from typing import List, Optional, Dict, Any
from enum import Enum
import uuid
from pydantic import Field, ConfigDict
from app.models.base import BaseServiceModel, BaseCreateModel, BaseUpdateModel, BaseResponseModel
def generate_uuid():
"""Generate a unique identifier"""
return str(uuid.uuid4())
class GameType(str, Enum):
"""Game type enumeration"""
CHESS = "chess"
GO = "go"
LOGIC_PUZZLE = "logic_puzzle"
PHILOSOPHICAL_DILEMMA = "philosophical_dilemma"
TRIVIA = "trivia"
DEBATE = "debate"
class DifficultyLevel(str, Enum):
"""Difficulty level enumeration"""
BEGINNER = "beginner"
INTERMEDIATE = "intermediate"
ADVANCED = "advanced"
EXPERT = "expert"
class GameStatus(str, Enum):
"""Game status enumeration"""
ACTIVE = "active"
COMPLETED = "completed"
PAUSED = "paused"
ABANDONED = "abandoned"
class GameSession(BaseServiceModel):
"""
Game session model for GT 2.0 service-based architecture.
Represents AI literacy and strategic thinking game sessions
with progress tracking and skill development.
"""
# Core game properties
user_id: str = Field(..., description="User playing the game")
tenant_id: str = Field(..., description="Tenant domain identifier")
game_type: GameType = Field(..., description="Type of game")
game_name: str = Field(..., min_length=1, max_length=100, description="Game name")
# Game configuration
difficulty_level: DifficultyLevel = Field(default=DifficultyLevel.INTERMEDIATE, description="Difficulty level")
ai_opponent_config: Dict[str, Any] = Field(default_factory=dict, description="AI opponent settings")
game_rules: Dict[str, Any] = Field(default_factory=dict, description="Game-specific rules")
# Game state
current_state: Dict[str, Any] = Field(default_factory=dict, description="Current game state")
move_history: List[Dict[str, Any]] = Field(default_factory=list, description="History of moves")
game_status: GameStatus = Field(default=GameStatus.ACTIVE, description="Game status")
# Progress tracking
moves_count: int = Field(default=0, description="Number of moves made")
hints_used: int = Field(default=0, description="Number of hints used")
time_spent_seconds: int = Field(default=0, description="Time spent in seconds")
current_rating: int = Field(default=1200, description="ELO-style rating")
# Results
winner: Optional[str] = Field(None, description="Winner of the game")
final_score: Optional[Dict[str, Any]] = Field(None, description="Final score details")
learning_insights: List[str] = Field(default_factory=list, description="Learning insights")
# Model configuration
model_config = ConfigDict(
protected_namespaces=(),
json_encoders={
datetime: lambda v: v.isoformat() if v else None
}
)
@classmethod
def get_table_name(cls) -> str:
"""Get the database table name"""
return "game_sessions"
def add_move(self, move_data: Dict[str, Any]) -> None:
"""Add a move to the game history"""
self.move_history.append(move_data)
self.moves_count += 1
self.update_timestamp()
def use_hint(self) -> None:
"""Record hint usage"""
self.hints_used += 1
self.update_timestamp()
def complete_game(self, winner: str, final_score: Dict[str, Any]) -> None:
"""Mark game as completed"""
self.game_status = GameStatus.COMPLETED
self.winner = winner
self.final_score = final_score
self.update_timestamp()
def pause_game(self) -> None:
"""Pause the game"""
self.game_status = GameStatus.PAUSED
self.update_timestamp()
def resume_game(self) -> None:
"""Resume the game"""
self.game_status = GameStatus.ACTIVE
self.update_timestamp()
class PuzzleSession(BaseServiceModel):
"""
Puzzle session model for logic and problem-solving games.
Tracks puzzle-specific metrics and progress.
"""
# Core puzzle properties
user_id: str = Field(..., description="User solving the puzzle")
tenant_id: str = Field(..., description="Tenant domain identifier")
puzzle_type: str = Field(..., max_length=50, description="Type of puzzle")
puzzle_name: str = Field(..., min_length=1, max_length=100, description="Puzzle name")
# Puzzle configuration
difficulty_level: DifficultyLevel = Field(default=DifficultyLevel.INTERMEDIATE, description="Difficulty level")
puzzle_data: Dict[str, Any] = Field(default_factory=dict, description="Puzzle configuration")
solution_data: Dict[str, Any] = Field(default_factory=dict, description="Solution information")
# Progress tracking
attempts_made: int = Field(default=0, description="Number of attempts")
hints_requested: int = Field(default=0, description="Hints requested")
is_solved: bool = Field(default=False, description="Whether puzzle is solved")
solve_time_seconds: Optional[int] = Field(None, description="Time to solve")
# Learning metrics
skill_points_earned: int = Field(default=0, description="Skill points earned")
concepts_learned: List[str] = Field(default_factory=list, description="Concepts learned")
# Model configuration
model_config = ConfigDict(
protected_namespaces=(),
json_encoders={
datetime: lambda v: v.isoformat() if v else None
}
)
@classmethod
def get_table_name(cls) -> str:
"""Get the database table name"""
return "puzzle_sessions"
def add_attempt(self) -> None:
"""Record a puzzle attempt"""
self.attempts_made += 1
self.update_timestamp()
def solve_puzzle(self, solve_time: int, skill_points: int) -> None:
"""Mark puzzle as solved"""
self.is_solved = True
self.solve_time_seconds = solve_time
self.skill_points_earned = skill_points
self.update_timestamp()
class PhilosophicalDialogue(BaseServiceModel):
"""
Philosophical dialogue model for ethical and critical thinking development.
Tracks philosophical discussions and thinking development.
"""
# Core dialogue properties
user_id: str = Field(..., description="User participating in dialogue")
tenant_id: str = Field(..., description="Tenant domain identifier")
dialogue_topic: str = Field(..., min_length=1, max_length=200, description="Dialogue topic")
dialogue_type: str = Field(..., max_length=50, description="Type of philosophical dialogue")
# Dialogue configuration
ai_persona: str = Field(default="socratic", max_length=50, description="AI dialogue persona")
dialogue_style: str = Field(default="questioning", max_length=50, description="Dialogue style")
target_concepts: List[str] = Field(default_factory=list, description="Target concepts to explore")
# Dialogue content
messages: List[Dict[str, Any]] = Field(default_factory=list, description="Dialogue messages")
key_insights: List[str] = Field(default_factory=list, description="Key insights generated")
# Progress metrics
turns_count: int = Field(default=0, description="Number of dialogue turns")
depth_score: float = Field(default=0.0, description="Depth of philosophical exploration")
critical_thinking_score: float = Field(default=0.0, description="Critical thinking score")
# Status
is_completed: bool = Field(default=False, description="Whether dialogue is completed")
completion_reason: Optional[str] = Field(None, description="Reason for completion")
# Model configuration
model_config = ConfigDict(
protected_namespaces=(),
json_encoders={
datetime: lambda v: v.isoformat() if v else None
}
)
@classmethod
def get_table_name(cls) -> str:
"""Get the database table name"""
return "philosophical_dialogues"
def add_message(self, message_data: Dict[str, Any]) -> None:
"""Add a message to the dialogue"""
self.messages.append(message_data)
self.turns_count += 1
self.update_timestamp()
def complete_dialogue(self, reason: str) -> None:
"""Mark dialogue as completed"""
self.is_completed = True
self.completion_reason = reason
self.update_timestamp()
class LearningAnalytics(BaseServiceModel):
"""
Learning analytics model for tracking educational progress.
Aggregates learning data across all game types.
"""
# Core analytics properties
user_id: str = Field(..., description="User being analyzed")
tenant_id: str = Field(..., description="Tenant domain identifier")
# Skill tracking
chess_rating: int = Field(default=1200, description="Chess skill rating")
go_rating: int = Field(default=1200, description="Go skill rating")
puzzle_solving_level: int = Field(default=1, description="Puzzle solving level")
critical_thinking_level: int = Field(default=1, description="Critical thinking level")
# Activity metrics
total_games_played: int = Field(default=0, description="Total games played")
total_puzzles_solved: int = Field(default=0, description="Total puzzles solved")
total_dialogues_completed: int = Field(default=0, description="Total dialogues completed")
total_time_spent_hours: float = Field(default=0.0, description="Total time spent in hours")
# Learning metrics
concepts_mastered: List[str] = Field(default_factory=list, description="Mastered concepts")
learning_streaks: Dict[str, int] = Field(default_factory=dict, description="Learning streaks")
achievement_badges: List[str] = Field(default_factory=list, description="Achievement badges")
# Progress tracking
last_activity_date: Optional[datetime] = Field(None, description="Last activity date")
learning_goals: List[Dict[str, Any]] = Field(default_factory=list, description="Learning goals")
# Model configuration
model_config = ConfigDict(
protected_namespaces=(),
json_encoders={
datetime: lambda v: v.isoformat() if v else None
}
)
@classmethod
def get_table_name(cls) -> str:
"""Get the database table name"""
return "learning_analytics"
def update_activity(self) -> None:
"""Update last activity timestamp"""
self.last_activity_date = datetime.utcnow()
self.update_timestamp()
def earn_badge(self, badge_name: str) -> None:
"""Earn an achievement badge"""
if badge_name not in self.achievement_badges:
self.achievement_badges.append(badge_name)
self.update_timestamp()
class GameTemplate(BaseServiceModel):
"""
Game template model for configuring game types and rules.
Defines reusable game configurations and templates.
"""
# Core template properties
template_name: str = Field(..., min_length=1, max_length=100, description="Template name")
game_type: GameType = Field(..., description="Game type")
template_description: str = Field(..., max_length=500, description="Template description")
# Template configuration
default_rules: Dict[str, Any] = Field(default_factory=dict, description="Default game rules")
ai_configurations: List[Dict[str, Any]] = Field(default_factory=list, description="AI opponent configs")
difficulty_settings: Dict[str, Any] = Field(default_factory=dict, description="Difficulty settings")
# Educational content
learning_objectives: List[str] = Field(default_factory=list, description="Learning objectives")
skill_categories: List[str] = Field(default_factory=list, description="Skill categories")
educational_notes: Optional[str] = Field(None, description="Educational notes")
# Template metadata
created_by: str = Field(..., description="Creator of the template")
tenant_id: str = Field(..., description="Tenant domain identifier")
is_public: bool = Field(default=False, description="Whether template is publicly available")
usage_count: int = Field(default=0, description="Number of times used")
# Model configuration
model_config = ConfigDict(
protected_namespaces=(),
json_encoders={
datetime: lambda v: v.isoformat() if v else None
}
)
@classmethod
def get_table_name(cls) -> str:
"""Get the database table name"""
return "game_templates"
def increment_usage(self) -> None:
"""Increment usage count"""
self.usage_count += 1
self.update_timestamp()
# Create/Update/Response models
class GameSessionCreate(BaseCreateModel):
"""Model for creating new game sessions"""
user_id: str
tenant_id: str
game_type: GameType
game_name: str = Field(..., min_length=1, max_length=100)
difficulty_level: DifficultyLevel = Field(default=DifficultyLevel.INTERMEDIATE)
ai_opponent_config: Dict[str, Any] = Field(default_factory=dict)
game_rules: Dict[str, Any] = Field(default_factory=dict)
class GameSessionUpdate(BaseUpdateModel):
"""Model for updating game sessions"""
current_state: Optional[Dict[str, Any]] = None
game_status: Optional[GameStatus] = None
time_spent_seconds: Optional[int] = Field(None, ge=0)
current_rating: Optional[int] = Field(None, ge=0, le=3000)
winner: Optional[str] = None
final_score: Optional[Dict[str, Any]] = None
class GameSessionResponse(BaseResponseModel):
"""Model for game session API responses"""
id: str
user_id: str
tenant_id: str
game_type: GameType
game_name: str
difficulty_level: DifficultyLevel
current_state: Dict[str, Any]
move_history: List[Dict[str, Any]]
game_status: GameStatus
moves_count: int
hints_used: int
time_spent_seconds: int
current_rating: int
winner: Optional[str]
final_score: Optional[Dict[str, Any]]
learning_insights: List[str]
created_at: datetime
updated_at: datetime