Spaces:
Sleeping
Sleeping
""" | |
Gamification utilities for the TutorX MCP server | |
""" | |
from typing import Dict, Any, List, Optional | |
from datetime import datetime | |
import random | |
# Dictionary to store badges for students | |
# In a real application, this would be stored in a database | |
BADGES_DB = {} | |
# Dictionary to store leaderboards | |
# In a real application, this would be stored in a database | |
LEADERBOARDS_DB = { | |
"weekly_points": {}, | |
"monthly_streak": {}, | |
"problem_solving_speed": {} | |
} | |
# Badge definitions | |
BADGES = { | |
"beginner": { | |
"name": "Beginner", | |
"description": "Completed your first lesson", | |
"icon": "๐ฐ", | |
"points": 10 | |
}, | |
"persistent": { | |
"name": "Persistent Learner", | |
"description": "Completed 5 lessons in a row", | |
"icon": "๐", | |
"points": 25 | |
}, | |
"math_whiz": { | |
"name": "Math Whiz", | |
"description": "Scored 100% on a math assessment", | |
"icon": "๐งฎ", | |
"points": 50 | |
}, | |
"science_explorer": { | |
"name": "Science Explorer", | |
"description": "Completed 10 science modules", | |
"icon": "๐ฌ", | |
"points": 50 | |
}, | |
"speed_demon": { | |
"name": "Speed Demon", | |
"description": "Solved 5 problems in under 2 minutes each", | |
"icon": "โก", | |
"points": 35 | |
}, | |
"accuracy_master": { | |
"name": "Accuracy Master", | |
"description": "Maintained 90% accuracy over 20 problems", | |
"icon": "๐ฏ", | |
"points": 40 | |
}, | |
"helping_hand": { | |
"name": "Helping Hand", | |
"description": "Helped 5 other students in the forum", | |
"icon": "๐ค", | |
"points": 30 | |
}, | |
"night_owl": { | |
"name": "Night Owl", | |
"description": "Studied for 3 hours after 8 PM", | |
"icon": "๐", | |
"points": 20 | |
}, | |
"early_bird": { | |
"name": "Early Bird", | |
"description": "Studied for 3 hours before 9 AM", | |
"icon": "๐ ", | |
"points": 20 | |
}, | |
"perfect_streak": { | |
"name": "Perfect Streak", | |
"description": "Logged in for 7 days straight", | |
"icon": "๐ฅ", | |
"points": 45 | |
} | |
} | |
def award_badge(student_id: str, badge_id: str) -> Dict[str, Any]: | |
""" | |
Award a badge to a student | |
Args: | |
student_id: The student's unique identifier | |
badge_id: The badge's unique identifier | |
Returns: | |
Badge information | |
""" | |
if badge_id not in BADGES: | |
return { | |
"error": "Invalid badge ID", | |
"timestamp": datetime.now().isoformat() | |
} | |
if student_id not in BADGES_DB: | |
BADGES_DB[student_id] = {} | |
# Check if student already has the badge | |
if badge_id in BADGES_DB[student_id]: | |
return { | |
"message": "Badge already awarded", | |
"badge": BADGES[badge_id], | |
"timestamp": BADGES_DB[student_id][badge_id]["timestamp"] | |
} | |
# Award the badge | |
BADGES_DB[student_id][badge_id] = { | |
"timestamp": datetime.now().isoformat() | |
} | |
return { | |
"message": "Badge awarded!", | |
"badge": BADGES[badge_id], | |
"timestamp": datetime.now().isoformat() | |
} | |
def get_student_badges(student_id: str) -> Dict[str, Any]: | |
""" | |
Get all badges for a student | |
Args: | |
student_id: The student's unique identifier | |
Returns: | |
Dictionary of badges and points | |
""" | |
if student_id not in BADGES_DB or not BADGES_DB[student_id]: | |
return { | |
"student_id": student_id, | |
"badges": [], | |
"total_points": 0, | |
"timestamp": datetime.now().isoformat() | |
} | |
badges_list = [] | |
total_points = 0 | |
for badge_id in BADGES_DB[student_id]: | |
if badge_id in BADGES: | |
badge_info = BADGES[badge_id].copy() | |
badge_info["awarded_at"] = BADGES_DB[student_id][badge_id]["timestamp"] | |
badges_list.append(badge_info) | |
total_points += badge_info["points"] | |
return { | |
"student_id": student_id, | |
"badges": badges_list, | |
"total_points": total_points, | |
"timestamp": datetime.now().isoformat() | |
} | |
def update_leaderboard(leaderboard_id: str, student_id: str, score: float) -> Dict[str, Any]: | |
""" | |
Update a leaderboard with a new score for a student | |
Args: | |
leaderboard_id: The leaderboard to update | |
student_id: The student's unique identifier | |
score: The score to record | |
Returns: | |
Updated leaderboard information | |
""" | |
if leaderboard_id not in LEADERBOARDS_DB: | |
return { | |
"error": "Invalid leaderboard ID", | |
"timestamp": datetime.now().isoformat() | |
} | |
# Update student's score | |
LEADERBOARDS_DB[leaderboard_id][student_id] = { | |
"score": score, | |
"timestamp": datetime.now().isoformat() | |
} | |
# Get top 10 students | |
top_students = sorted( | |
LEADERBOARDS_DB[leaderboard_id].items(), | |
key=lambda x: x[1]["score"], | |
reverse=True | |
)[:10] | |
# Format leaderboard | |
leaderboard = [] | |
for i, (sid, data) in enumerate(top_students): | |
leaderboard.append({ | |
"rank": i + 1, | |
"student_id": sid, | |
"score": data["score"], | |
"last_updated": data["timestamp"] | |
}) | |
# Find student's rank | |
student_rank = next( | |
(i + 1 for i, (sid, _) in enumerate(top_students) if sid == student_id), | |
len(LEADERBOARDS_DB[leaderboard_id]) + 1 | |
) | |
return { | |
"leaderboard_id": leaderboard_id, | |
"leaderboard": leaderboard, | |
"student_rank": student_rank, | |
"student_score": score, | |
"timestamp": datetime.now().isoformat() | |
} | |
def get_leaderboard(leaderboard_id: str) -> Dict[str, Any]: | |
""" | |
Get the current state of a leaderboard | |
Args: | |
leaderboard_id: The leaderboard to get | |
Returns: | |
Leaderboard information | |
""" | |
if leaderboard_id not in LEADERBOARDS_DB: | |
return { | |
"error": "Invalid leaderboard ID", | |
"timestamp": datetime.now().isoformat() | |
} | |
# Get top 10 students | |
top_students = sorted( | |
LEADERBOARDS_DB[leaderboard_id].items(), | |
key=lambda x: x[1]["score"], | |
reverse=True | |
)[:10] | |
# Format leaderboard | |
leaderboard = [] | |
for i, (sid, data) in enumerate(top_students): | |
leaderboard.append({ | |
"rank": i + 1, | |
"student_id": sid, | |
"score": data["score"], | |
"last_updated": data["timestamp"] | |
}) | |
return { | |
"leaderboard_id": leaderboard_id, | |
"leaderboard": leaderboard, | |
"total_students": len(LEADERBOARDS_DB[leaderboard_id]), | |
"timestamp": datetime.now().isoformat() | |
} | |
def check_achievements(student_id: str, activity_data: Dict[str, Any]) -> List[Dict[str, Any]]: | |
""" | |
Check if a student's activity unlocks any new badges | |
Args: | |
student_id: The student's unique identifier | |
activity_data: Data about the student's activity | |
Returns: | |
List of newly awarded badges | |
""" | |
new_badges = [] | |
# Initialize student badge record if needed | |
if student_id not in BADGES_DB: | |
BADGES_DB[student_id] = {} | |
# Check for potential badge earnings based on activity | |
if "activity_type" in activity_data: | |
activity_type = activity_data["activity_type"] | |
# Beginner badge - first lesson | |
if activity_type == "lesson_completed" and "beginner" not in BADGES_DB[student_id]: | |
badge_result = award_badge(student_id, "beginner") | |
if "error" not in badge_result: | |
new_badges.append(badge_result) | |
# Math Whiz - perfect math assessment | |
if (activity_type == "assessment_completed" and | |
activity_data.get("subject") == "math" and | |
activity_data.get("score") == 1.0 and | |
"math_whiz" not in BADGES_DB[student_id]): | |
badge_result = award_badge(student_id, "math_whiz") | |
if "error" not in badge_result: | |
new_badges.append(badge_result) | |
# Speed Demon - fast problem solving | |
if (activity_type == "problem_solved" and | |
activity_data.get("time_seconds", 999) < 120): | |
# In a real system, we'd track the count over time | |
# Here we'll simulate it | |
if random.random() < 0.2 and "speed_demon" not in BADGES_DB[student_id]: | |
badge_result = award_badge(student_id, "speed_demon") | |
if "error" not in badge_result: | |
new_badges.append(badge_result) | |
return new_badges | |