from __future__ import annotations from datetime import UTC, datetime, timedelta from app.database import RankHistory, UserStatistics from app.database.rank_history import RankTop from app.dependencies.database import engine from app.dependencies.scheduler import get_scheduler from app.models.score import GameMode from sqlmodel import col, exists, select, update from sqlmodel.ext.asyncio.session import AsyncSession @get_scheduler().scheduled_job( "cron", hour=0, minute=0, second=0, id="calculate_user_rank" ) async def calculate_user_rank(is_today: bool = False): today = datetime.now(UTC).date() target_date = today if is_today else today - timedelta(days=1) async with AsyncSession(engine) as session: for gamemode in GameMode: users = await session.exec( select(UserStatistics) .where( UserStatistics.mode == gamemode, UserStatistics.pp > 0, col(UserStatistics.is_ranked).is_(True), ) .order_by( col(UserStatistics.pp).desc(), col(UserStatistics.total_score).desc(), ) ) rank = 1 for user in users: is_exist = ( await session.exec( select(exists()).where( RankHistory.user_id == user.user_id, RankHistory.mode == gamemode, RankHistory.date == target_date, ) ) ).first() if not is_exist: rank_history = RankHistory( user_id=user.user_id, mode=gamemode, rank=rank, date=today, ) session.add(rank_history) else: await session.execute( update(RankHistory) .where( col(RankHistory.user_id) == user.user_id, col(RankHistory.mode) == gamemode, col(RankHistory.date) == target_date, ) .values(rank=rank) ) rank_top = ( await session.exec( select(RankTop).where( RankTop.user_id == user.user_id, RankTop.mode == gamemode, ) ) ).first() if not rank_top: rank_top = RankTop( user_id=user.user_id, mode=gamemode, rank=rank, date=today, ) session.add(rank_top) else: if rank_top.rank > rank: rank_top.rank = rank rank_top.date = today rank += 1 await session.commit()