language-learning-app/api/app/outbound/postgres/repositories/flashcard_repository.py

145 lines
4.8 KiB
Python
Raw Normal View History

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,
source_pack_flashcard_template_id: uuid.UUID | 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,
source_pack_flashcard_template_id=(
str(entity.source_pack_flashcard_template_id)
if entity.source_pack_flashcard_template_id
else None
),
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,
source_pack_flashcard_template_id: uuid.UUID | 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,
source_pack_flashcard_template_id=source_pack_flashcard_template_id,
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)