TutorX-MCP / mcp_server /models /student_profile.py
Meet Patel
Implement storage layer and adaptive learning tools for TutorX-MCP
250bf8c
"""
Student profile data models for TutorX-MCP.
This module defines data structures for storing and managing
student learning profiles, preferences, and characteristics.
"""
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Any
from dataclasses import dataclass, field
from enum import Enum
import json
class LearningStyle(Enum):
"""Learning style preferences."""
VISUAL = "visual"
AUDITORY = "auditory"
KINESTHETIC = "kinesthetic"
READING_WRITING = "reading_writing"
MULTIMODAL = "multimodal"
class LearningPace(Enum):
"""Learning pace preferences."""
SLOW = "slow"
MODERATE = "moderate"
FAST = "fast"
ADAPTIVE = "adaptive"
class DifficultyPreference(Enum):
"""Difficulty progression preferences."""
GRADUAL = "gradual"
MODERATE = "moderate"
AGGRESSIVE = "aggressive"
ADAPTIVE = "adaptive"
class FeedbackPreference(Enum):
"""Feedback delivery preferences."""
IMMEDIATE = "immediate"
DELAYED = "delayed"
SUMMARY = "summary"
MINIMAL = "minimal"
@dataclass
class LearningPreferences:
"""Student learning preferences and settings."""
learning_style: LearningStyle = LearningStyle.MULTIMODAL
learning_pace: LearningPace = LearningPace.ADAPTIVE
difficulty_preference: DifficultyPreference = DifficultyPreference.ADAPTIVE
feedback_preference: FeedbackPreference = FeedbackPreference.IMMEDIATE
# Session preferences
preferred_session_length: int = 30 # minutes
max_session_length: int = 60 # minutes
break_frequency: int = 20 # minutes between breaks
# Content preferences
gamification_enabled: bool = True
hints_enabled: bool = True
explanations_enabled: bool = True
examples_enabled: bool = True
# Notification preferences
reminders_enabled: bool = True
progress_notifications: bool = True
achievement_notifications: bool = True
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for serialization."""
return {
'learning_style': self.learning_style.value,
'learning_pace': self.learning_pace.value,
'difficulty_preference': self.difficulty_preference.value,
'feedback_preference': self.feedback_preference.value,
'preferred_session_length': self.preferred_session_length,
'max_session_length': self.max_session_length,
'break_frequency': self.break_frequency,
'gamification_enabled': self.gamification_enabled,
'hints_enabled': self.hints_enabled,
'explanations_enabled': self.explanations_enabled,
'examples_enabled': self.examples_enabled,
'reminders_enabled': self.reminders_enabled,
'progress_notifications': self.progress_notifications,
'achievement_notifications': self.achievement_notifications
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'LearningPreferences':
"""Create from dictionary."""
return cls(
learning_style=LearningStyle(data.get('learning_style', 'multimodal')),
learning_pace=LearningPace(data.get('learning_pace', 'adaptive')),
difficulty_preference=DifficultyPreference(data.get('difficulty_preference', 'adaptive')),
feedback_preference=FeedbackPreference(data.get('feedback_preference', 'immediate')),
preferred_session_length=data.get('preferred_session_length', 30),
max_session_length=data.get('max_session_length', 60),
break_frequency=data.get('break_frequency', 20),
gamification_enabled=data.get('gamification_enabled', True),
hints_enabled=data.get('hints_enabled', True),
explanations_enabled=data.get('explanations_enabled', True),
examples_enabled=data.get('examples_enabled', True),
reminders_enabled=data.get('reminders_enabled', True),
progress_notifications=data.get('progress_notifications', True),
achievement_notifications=data.get('achievement_notifications', True)
)
@dataclass
class StudentGoals:
"""Student learning goals and objectives."""
target_concepts: List[str] = field(default_factory=list)
target_mastery_level: float = 0.8
target_completion_date: Optional[datetime] = None
daily_time_goal: int = 30 # minutes per day
weekly_concept_goal: int = 2 # concepts per week
# Long-term goals
grade_level_target: Optional[str] = None
subject_focus_areas: List[str] = field(default_factory=list)
career_interests: List[str] = field(default_factory=list)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for serialization."""
return {
'target_concepts': self.target_concepts,
'target_mastery_level': self.target_mastery_level,
'target_completion_date': self.target_completion_date.isoformat() if self.target_completion_date else None,
'daily_time_goal': self.daily_time_goal,
'weekly_concept_goal': self.weekly_concept_goal,
'grade_level_target': self.grade_level_target,
'subject_focus_areas': self.subject_focus_areas,
'career_interests': self.career_interests
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'StudentGoals':
"""Create from dictionary."""
target_date = None
if data.get('target_completion_date'):
target_date = datetime.fromisoformat(data['target_completion_date'])
return cls(
target_concepts=data.get('target_concepts', []),
target_mastery_level=data.get('target_mastery_level', 0.8),
target_completion_date=target_date,
daily_time_goal=data.get('daily_time_goal', 30),
weekly_concept_goal=data.get('weekly_concept_goal', 2),
grade_level_target=data.get('grade_level_target'),
subject_focus_areas=data.get('subject_focus_areas', []),
career_interests=data.get('career_interests', [])
)
@dataclass
class StudentProfile:
"""Comprehensive student learning profile."""
student_id: str
name: Optional[str] = None
grade_level: Optional[str] = None
age: Optional[int] = None
# Learning characteristics
preferences: LearningPreferences = field(default_factory=LearningPreferences)
goals: StudentGoals = field(default_factory=StudentGoals)
# Profile metadata
created_at: datetime = field(default_factory=datetime.utcnow)
last_updated: datetime = field(default_factory=datetime.utcnow)
last_active: Optional[datetime] = None
# Adaptive learning state
current_difficulty_level: float = 0.5
learning_velocity: float = 0.0 # concepts per day
engagement_level: float = 0.5
# Performance summary
total_concepts_attempted: int = 0
total_concepts_mastered: int = 0
total_learning_time: int = 0 # minutes
average_accuracy: float = 0.0
# Strengths and challenges
strength_areas: List[str] = field(default_factory=list)
challenge_areas: List[str] = field(default_factory=list)
# Adaptive learning insights
learning_patterns: List[str] = field(default_factory=list)
recommended_strategies: List[str] = field(default_factory=list)
def update_last_active(self):
"""Update last active timestamp."""
self.last_active = datetime.utcnow()
self.last_updated = datetime.utcnow()
def update_performance_summary(self, concepts_attempted: int, concepts_mastered: int,
learning_time: int, accuracy: float):
"""Update performance summary statistics."""
self.total_concepts_attempted = concepts_attempted
self.total_concepts_mastered = concepts_mastered
self.total_learning_time = learning_time
self.average_accuracy = accuracy
self.last_updated = datetime.utcnow()
def calculate_mastery_rate(self) -> float:
"""Calculate overall mastery rate."""
if self.total_concepts_attempted == 0:
return 0.0
return self.total_concepts_mastered / self.total_concepts_attempted
def calculate_daily_average_time(self, days: int = 30) -> float:
"""Calculate average daily learning time over specified period."""
if days <= 0:
return 0.0
# This would need to be calculated from actual session data
# For now, return a simple estimate
return self.total_learning_time / max(1, days)
def is_active_learner(self, days: int = 7) -> bool:
"""Check if student has been active in recent days."""
if not self.last_active:
return False
cutoff_date = datetime.utcnow() - timedelta(days=days)
return self.last_active >= cutoff_date
def get_learning_efficiency(self) -> float:
"""Calculate learning efficiency (mastery per hour)."""
if self.total_learning_time == 0:
return 0.0
hours = self.total_learning_time / 60.0
return self.total_concepts_mastered / hours
def add_strength_area(self, area: str):
"""Add a strength area if not already present."""
if area not in self.strength_areas:
self.strength_areas.append(area)
self.last_updated = datetime.utcnow()
def add_challenge_area(self, area: str):
"""Add a challenge area if not already present."""
if area not in self.challenge_areas:
self.challenge_areas.append(area)
self.last_updated = datetime.utcnow()
def add_learning_pattern(self, pattern: str):
"""Add a detected learning pattern."""
if pattern not in self.learning_patterns:
self.learning_patterns.append(pattern)
self.last_updated = datetime.utcnow()
def add_recommended_strategy(self, strategy: str):
"""Add a recommended learning strategy."""
if strategy not in self.recommended_strategies:
self.recommended_strategies.append(strategy)
self.last_updated = datetime.utcnow()
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for serialization."""
return {
'student_id': self.student_id,
'name': self.name,
'grade_level': self.grade_level,
'age': self.age,
'preferences': self.preferences.to_dict(),
'goals': self.goals.to_dict(),
'created_at': self.created_at.isoformat(),
'last_updated': self.last_updated.isoformat(),
'last_active': self.last_active.isoformat() if self.last_active else None,
'current_difficulty_level': self.current_difficulty_level,
'learning_velocity': self.learning_velocity,
'engagement_level': self.engagement_level,
'total_concepts_attempted': self.total_concepts_attempted,
'total_concepts_mastered': self.total_concepts_mastered,
'total_learning_time': self.total_learning_time,
'average_accuracy': self.average_accuracy,
'strength_areas': self.strength_areas,
'challenge_areas': self.challenge_areas,
'learning_patterns': self.learning_patterns,
'recommended_strategies': self.recommended_strategies
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'StudentProfile':
"""Create from dictionary."""
created_at = datetime.fromisoformat(data['created_at']) if data.get('created_at') else datetime.utcnow()
last_updated = datetime.fromisoformat(data['last_updated']) if data.get('last_updated') else datetime.utcnow()
last_active = datetime.fromisoformat(data['last_active']) if data.get('last_active') else None
preferences = LearningPreferences.from_dict(data.get('preferences', {}))
goals = StudentGoals.from_dict(data.get('goals', {}))
return cls(
student_id=data['student_id'],
name=data.get('name'),
grade_level=data.get('grade_level'),
age=data.get('age'),
preferences=preferences,
goals=goals,
created_at=created_at,
last_updated=last_updated,
last_active=last_active,
current_difficulty_level=data.get('current_difficulty_level', 0.5),
learning_velocity=data.get('learning_velocity', 0.0),
engagement_level=data.get('engagement_level', 0.5),
total_concepts_attempted=data.get('total_concepts_attempted', 0),
total_concepts_mastered=data.get('total_concepts_mastered', 0),
total_learning_time=data.get('total_learning_time', 0),
average_accuracy=data.get('average_accuracy', 0.0),
strength_areas=data.get('strength_areas', []),
challenge_areas=data.get('challenge_areas', []),
learning_patterns=data.get('learning_patterns', []),
recommended_strategies=data.get('recommended_strategies', [])
)
def to_json(self) -> str:
"""Convert to JSON string."""
return json.dumps(self.to_dict(), indent=2)
@classmethod
def from_json(cls, json_str: str) -> 'StudentProfile':
"""Create from JSON string."""
data = json.loads(json_str)
return cls.from_dict(data)