"""User repository""" from typing import Optional from datetime import datetime, timedelta from sqlalchemy import select, update from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload from app.repositories.base import BaseRepository from app.db.models import User class UserRepository(BaseRepository[User]): """User repository with custom methods""" def __init__(self, session: AsyncSession): super().__init__(User, session) async def get_by_did(self, did: str) -> Optional[User]: """Get user by DID""" result = await self.session.execute( select(User).where(User.did == did) ) return result.scalar_one_or_none() async def get_or_create(self, did: str, handle: Optional[str] = None) -> User: """Get existing user or create new one""" user = await self.get_by_did(did) if not user: user = await self.create(did=did, handle=handle) return user async def get_with_cards(self, user_id: int) -> Optional[User]: """Get user with all their cards""" result = await self.session.execute( select(User) .options(selectinload(User.cards)) .where(User.id == user_id) ) return result.scalar_one_or_none() async def can_draw_card(self, did: str, draw_limit_days: int = 2) -> bool: """ Check if user can draw a card based on daily limit Args: did: User's DID draw_limit_days: Number of days between allowed draws (default: 2 days) Returns: True if user can draw, False if still in cooldown period """ user = await self.get_by_did(did) if not user: # New user can always draw return True if not user.last_draw_date: # User has never drawn before return True # Check if enough time has passed since last draw time_since_last_draw = datetime.utcnow() - user.last_draw_date return time_since_last_draw >= timedelta(days=draw_limit_days) async def get_next_draw_time(self, did: str, draw_limit_days: int = 2) -> Optional[datetime]: """ Get the next time the user can draw a card Args: did: User's DID draw_limit_days: Number of days between allowed draws Returns: Next draw time if user is in cooldown, None if user can draw now """ user = await self.get_by_did(did) if not user or not user.last_draw_date: return None next_draw_time = user.last_draw_date + timedelta(days=draw_limit_days) if datetime.utcnow() >= next_draw_time: return None return next_draw_time async def update_last_draw_date(self, user_id: int) -> None: """ Update user's last draw date to current time Args: user_id: User's database ID """ await self.session.execute( update(User) .where(User.id == user_id) .values(last_draw_date=datetime.utcnow()) )