151 lines
5.3 KiB
Python
151 lines
5.3 KiB
Python
import uuid
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from pydantic import BaseModel
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from ...auth import verify_token
|
|
from ...domain.services.pack_service import PackService, PackNotFoundError, DuplicateEntryError
|
|
from ...outbound.postgres.database import get_db
|
|
from ...outbound.postgres.repositories.pack_repository import PostgresPackRepository
|
|
from ...outbound.postgres.repositories.vocab_repository import PostgresVocabRepository
|
|
from ...outbound.postgres.repositories.flashcard_repository import PostgresFlashcardRepository
|
|
from ...outbound.postgres.repositories.dictionary_repository import PostgresDictionaryRepository
|
|
|
|
router = APIRouter(prefix="/packs", tags=["packs"])
|
|
|
|
|
|
# ── Response models ───────────────────────────────────────────────────────────
|
|
|
|
class PackSummaryResponse(BaseModel):
|
|
id: str
|
|
name: str
|
|
name_target: str
|
|
description: str
|
|
description_target: str
|
|
source_lang: str
|
|
target_lang: str
|
|
proficiencies: list[str]
|
|
entry_count: int
|
|
|
|
|
|
class PackDetailResponse(PackSummaryResponse):
|
|
surface_texts: list[str]
|
|
|
|
|
|
class AddTobankRequest(BaseModel):
|
|
source_lang: str
|
|
target_lang: str
|
|
|
|
|
|
class AddTobankResponse(BaseModel):
|
|
added: list[str]
|
|
|
|
|
|
# ── Dependency ────────────────────────────────────────────────────────────────
|
|
|
|
def _service(db: AsyncSession) -> PackService:
|
|
return PackService(
|
|
pack_repo=PostgresPackRepository(db),
|
|
vocab_repo=PostgresVocabRepository(db),
|
|
flashcard_repo=PostgresFlashcardRepository(db),
|
|
dict_repo=PostgresDictionaryRepository(db),
|
|
)
|
|
|
|
|
|
def _pack_repo(db: AsyncSession) -> PostgresPackRepository:
|
|
return PostgresPackRepository(db)
|
|
|
|
|
|
# ── Endpoints ─────────────────────────────────────────────────────────────────
|
|
|
|
@router.get("", response_model=list[PackSummaryResponse])
|
|
async def list_packs(
|
|
source_lang: str | None = None,
|
|
target_lang: str | None = None,
|
|
db: AsyncSession = Depends(get_db),
|
|
_: dict = Depends(verify_token),
|
|
) -> list[PackSummaryResponse]:
|
|
repo = _pack_repo(db)
|
|
packs = await repo.list_packs(
|
|
source_lang=source_lang, target_lang=target_lang, published_only=True
|
|
)
|
|
responses = []
|
|
for pack in packs:
|
|
count = await repo.count_entries_for_pack(uuid.UUID(pack.id))
|
|
responses.append(
|
|
PackSummaryResponse(
|
|
id=pack.id,
|
|
name=pack.name,
|
|
name_target=pack.name_target,
|
|
description=pack.description,
|
|
description_target=pack.description_target,
|
|
source_lang=pack.source_lang,
|
|
target_lang=pack.target_lang,
|
|
proficiencies=pack.proficiencies,
|
|
entry_count=count,
|
|
)
|
|
)
|
|
return responses
|
|
|
|
|
|
@router.get("/{pack_id}", response_model=PackDetailResponse)
|
|
async def get_pack(
|
|
pack_id: str,
|
|
db: AsyncSession = Depends(get_db),
|
|
_: dict = Depends(verify_token),
|
|
) -> PackDetailResponse:
|
|
repo = _pack_repo(db)
|
|
pack = await repo.get_pack(_parse_uuid(pack_id))
|
|
if pack is None or not pack.is_published:
|
|
raise HTTPException(status_code=404, detail="Pack not found")
|
|
|
|
entries = await repo.get_entries_for_pack(uuid.UUID(pack.id))
|
|
count = len(entries)
|
|
surface_texts = [e.surface_text for e in entries]
|
|
|
|
return PackDetailResponse(
|
|
id=pack.id,
|
|
name=pack.name,
|
|
name_target=pack.name_target,
|
|
description=pack.description,
|
|
description_target=pack.description_target,
|
|
source_lang=pack.source_lang,
|
|
target_lang=pack.target_lang,
|
|
proficiencies=pack.proficiencies,
|
|
entry_count=count,
|
|
surface_texts=surface_texts,
|
|
)
|
|
|
|
|
|
@router.post("/{pack_id}/add-to-bank", response_model=AddTobankResponse, status_code=201)
|
|
async def add_pack_to_bank(
|
|
pack_id: str,
|
|
request: AddTobankRequest,
|
|
db: AsyncSession = Depends(get_db),
|
|
token_data: dict = Depends(verify_token),
|
|
) -> AddTobankResponse:
|
|
user_id = uuid.UUID(token_data["sub"])
|
|
|
|
try:
|
|
result = await _service(db).add_pack_to_user_bank(
|
|
pack_id=_parse_uuid(pack_id),
|
|
user_id=user_id,
|
|
source_lang=request.source_lang,
|
|
target_lang=request.target_lang,
|
|
)
|
|
except PackNotFoundError:
|
|
raise HTTPException(status_code=404, detail="Pack not found")
|
|
except DuplicateEntryError as exc:
|
|
raise HTTPException(status_code=409, detail=str(exc))
|
|
|
|
return AddTobankResponse(added=result.added_surface_texts)
|
|
|
|
|
|
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
|
|
def _parse_uuid(value: str) -> uuid.UUID:
|
|
try:
|
|
return uuid.UUID(value)
|
|
except ValueError:
|
|
raise HTTPException(status_code=400, detail=f"Invalid UUID: {value!r}")
|