Automated Action 53d9909fb6 Create gamified kids learning API with FastAPI and SQLite
- Set up project structure with FastAPI and SQLite
- Implement user authentication with JWT
- Create models for learning content (subjects, lessons, quizzes)
- Add progress tracking and gamification features
- Implement comprehensive API documentation
- Add error handling and validation
- Set up proper logging and health check endpoint
2025-06-17 18:25:16 +00:00

177 lines
5.9 KiB
Python

from __future__ import annotations
import json
from datetime import datetime
from typing import List, Optional
from sqlalchemy import func
from sqlalchemy.orm import Session
from app.models.achievement import Achievement, AchievementType, UserAchievement
from app.models.progress import ProgressStatus, UserProgress
from app.schemas.achievement import AchievementCreate, AchievementUpdate, UserAchievementCreate
from app.services.user import user as user_service
from app.utils.db import CRUDBase
class CRUDAchievement(CRUDBase[Achievement, AchievementCreate, AchievementUpdate]):
def get_by_type(
self, db: Session, *, achievement_type: AchievementType, skip: int = 0, limit: int = 100
) -> List[Achievement]:
"""
Get achievements by type.
"""
return (
db.query(Achievement)
.filter(Achievement.type == achievement_type)
.offset(skip)
.limit(limit)
.all()
)
def get_active(self, db: Session, *, skip: int = 0, limit: int = 100) -> List[Achievement]:
"""
Get active achievements.
"""
return (
db.query(Achievement)
.filter(Achievement.is_active == True)
.offset(skip)
.limit(limit)
.all()
)
class CRUDUserAchievement(CRUDBase[UserAchievement, UserAchievementCreate, UserAchievementCreate]):
def get_by_user(
self, db: Session, *, user_id: int, skip: int = 0, limit: int = 100
) -> List[UserAchievement]:
"""
Get a user's achievements.
"""
return (
db.query(UserAchievement)
.filter(UserAchievement.user_id == user_id)
.offset(skip)
.limit(limit)
.all()
)
def get_by_user_achievement(
self, db: Session, *, user_id: int, achievement_id: int
) -> Optional[UserAchievement]:
"""
Get a specific user achievement.
"""
return (
db.query(UserAchievement)
.filter(
UserAchievement.user_id == user_id, UserAchievement.achievement_id == achievement_id
)
.first()
)
def award_achievement(
self, db: Session, *, user_id: int, achievement_id: int
) -> UserAchievement:
"""
Award an achievement to a user.
"""
# Check if user already has this achievement
existing = self.get_by_user_achievement(db, user_id=user_id, achievement_id=achievement_id)
if existing:
return existing
# Get the achievement to award points
achievement = db.query(Achievement).filter(Achievement.id == achievement_id).first()
# Create new user achievement
user_achievement = UserAchievement(
user_id=user_id, achievement_id=achievement_id, earned_at=datetime.utcnow()
)
db.add(user_achievement)
# Award points to user
if achievement and achievement.points > 0:
user_service.add_points(db, user_id=user_id, points=achievement.points)
db.commit()
db.refresh(user_achievement)
return user_achievement
def check_achievements(self, db: Session, *, user_id: int) -> List[UserAchievement]:
"""
Check and award achievements based on user's progress.
"""
# Get all active achievements
active_achievements = db.query(Achievement).filter(Achievement.is_active == True).all()
# Get user's current achievements
user_achievements = (
db.query(UserAchievement.achievement_id)
.filter(UserAchievement.user_id == user_id)
.all()
)
already_earned = [ua[0] for ua in user_achievements]
# Get user data for achievement criteria
user = user_service.get(db, id=user_id)
# Counters for various achievement criteria
completed_lessons_count = (
db.query(func.count(UserProgress.id))
.filter(
UserProgress.user_id == user_id, UserProgress.status == ProgressStatus.COMPLETED
)
.scalar()
or 0
)
# List to store newly awarded achievements
newly_awarded = []
# Check each achievement
for achievement in active_achievements:
# Skip if already earned
if achievement.id in already_earned:
continue
# Parse criteria
try:
criteria = json.loads(achievement.criteria)
except json.JSONDecodeError:
# Skip if criteria is invalid JSON
continue
# Check different types of achievements
awarded = False
if achievement.type == AchievementType.COMPLETION:
# Completion achievements (e.g., complete X lessons)
if criteria.get("type") == "lessons_completed" and "count" in criteria:
if completed_lessons_count >= criteria["count"]:
awarded = True
elif achievement.type == AchievementType.MILESTONE:
# Milestone achievements (e.g., reach level X, earn Y points)
if criteria.get("type") == "reach_level" and "level" in criteria:
if user.level >= criteria["level"]:
awarded = True
elif criteria.get("type") == "earn_points" and "points" in criteria:
if user.points >= criteria["points"]:
awarded = True
# Award the achievement if criteria met
if awarded:
user_achievement = self.award_achievement(
db, user_id=user_id, achievement_id=achievement.id
)
newly_awarded.append(user_achievement)
return newly_awarded
achievement = CRUDAchievement(Achievement)
user_achievement = CRUDUserAchievement(UserAchievement)