import uuid from datetime import datetime, timezone from typing import Protocol from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from ..entities.flashcard_entities import FlashcardEntity, FlashcardEventEntity from ....domain.models.flashcard import Flashcard, FlashcardEvent class FlashcardRepository(Protocol): async def create_flashcard( self, user_id: uuid.UUID, bank_entry_id: uuid.UUID, source_lang: str, target_lang: str, prompt_text: str, answer_text: str, card_direction: str, prompt_modality: str = "text", prompt_context_text: str | None = None, answer_context_text: str | None = None, ) -> Flashcard: ... async def get_flashcards_for_user(self, user_id: uuid.UUID) -> list[Flashcard]: ... async def get_flashcards_for_entry(self, bank_entry_id: uuid.UUID) -> list[Flashcard]: ... async def record_event( self, flashcard_id: uuid.UUID, user_id: uuid.UUID, event_type: str, user_response: str | None = None, ) -> FlashcardEvent: ... def _flashcard_to_model(entity: FlashcardEntity) -> Flashcard: return Flashcard( id=str(entity.id), user_id=str(entity.user_id), bank_entry_id=str(entity.bank_entry_id), source_lang=entity.source_lang, target_lang=entity.target_lang, prompt_text=entity.prompt_text, answer_text=entity.answer_text, prompt_context_text=entity.prompt_context_text, answer_context_text=entity.answer_context_text, card_direction=entity.card_direction, prompt_modality=entity.prompt_modality, created_at=entity.created_at, ) def _event_to_model(entity: FlashcardEventEntity) -> FlashcardEvent: return FlashcardEvent( id=str(entity.id), flashcard_id=str(entity.flashcard_id), user_id=str(entity.user_id), event_type=entity.event_type, user_response=entity.user_response, created_at=entity.created_at, ) class PostgresFlashcardRepository: def __init__(self, db: AsyncSession) -> None: self.db = db async def create_flashcard( self, user_id: uuid.UUID, bank_entry_id: uuid.UUID, source_lang: str, target_lang: str, prompt_text: str, answer_text: str, card_direction: str, prompt_modality: str = "text", prompt_context_text: str | None = None, answer_context_text: str | None = None, ) -> Flashcard: entity = FlashcardEntity( user_id=user_id, bank_entry_id=bank_entry_id, source_lang=source_lang, target_lang=target_lang, prompt_text=prompt_text, answer_text=answer_text, prompt_context_text=prompt_context_text, answer_context_text=answer_context_text, card_direction=card_direction, prompt_modality=prompt_modality, created_at=datetime.now(timezone.utc), ) self.db.add(entity) await self.db.commit() await self.db.refresh(entity) return _flashcard_to_model(entity) async def get_flashcards_for_user(self, user_id: uuid.UUID) -> list[Flashcard]: result = await self.db.execute( select(FlashcardEntity) .where(FlashcardEntity.user_id == user_id) .order_by(FlashcardEntity.created_at.desc()) ) return [_flashcard_to_model(e) for e in result.scalars().all()] async def get_flashcards_for_entry(self, bank_entry_id: uuid.UUID) -> list[Flashcard]: result = await self.db.execute( select(FlashcardEntity) .where(FlashcardEntity.bank_entry_id == bank_entry_id) .order_by(FlashcardEntity.created_at.desc()) ) return [_flashcard_to_model(e) for e in result.scalars().all()] async def record_event( self, flashcard_id: uuid.UUID, user_id: uuid.UUID, event_type: str, user_response: str | None = None, ) -> FlashcardEvent: entity = FlashcardEventEntity( flashcard_id=flashcard_id, user_id=user_id, event_type=event_type, user_response=user_response, created_at=datetime.now(timezone.utc), ) self.db.add(entity) await self.db.commit() await self.db.refresh(entity) return _event_to_model(entity)