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

141 lines
4.7 KiB
Python
Raw Permalink 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,
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,
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,
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,
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)