97 lines
3.3 KiB
Python
97 lines
3.3 KiB
Python
import uuid
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from pydantic import BaseModel, field_validator
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from ...auth import verify_token
|
|
from ...domain.services.account_service import AccountService
|
|
from ...languages import SUPPORTED_LANGUAGES, SUPPORTED_LEVELS
|
|
from ...outbound.postgres.database import get_db
|
|
|
|
router = APIRouter(prefix="/account", tags=["account"])
|
|
|
|
|
|
class AddLearnableLanguageRequest(BaseModel):
|
|
source_language: str
|
|
target_language: str
|
|
proficiencies: list[str]
|
|
|
|
@field_validator("proficiencies")
|
|
@classmethod
|
|
def validate_proficiencies(cls, v: list[str]) -> list[str]:
|
|
if not (1 <= len(v) <= 2):
|
|
raise ValueError("proficiencies must contain 1 or 2 levels")
|
|
invalid = [p for p in v if p not in SUPPORTED_LEVELS]
|
|
if invalid:
|
|
raise ValueError(f"Invalid proficiency levels: {invalid}. Supported: {sorted(SUPPORTED_LEVELS)}")
|
|
return v
|
|
|
|
|
|
class LearnableLanguageResponse(BaseModel):
|
|
id: str
|
|
source_language: str
|
|
target_language: str
|
|
proficiencies: list[str]
|
|
|
|
|
|
@router.post(
|
|
"/learnable-languages",
|
|
response_model=LearnableLanguageResponse,
|
|
status_code=status.HTTP_201_CREATED,
|
|
)
|
|
async def add_learnable_language(
|
|
body: AddLearnableLanguageRequest,
|
|
db: AsyncSession = Depends(get_db),
|
|
token_data: dict = Depends(verify_token),
|
|
) -> LearnableLanguageResponse:
|
|
if body.source_language not in SUPPORTED_LANGUAGES:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Unsupported source language '{body.source_language}'. Supported: {list(SUPPORTED_LANGUAGES)}",
|
|
)
|
|
if body.target_language not in SUPPORTED_LANGUAGES:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Unsupported target language '{body.target_language}'. Supported: {list(SUPPORTED_LANGUAGES)}",
|
|
)
|
|
if body.source_language == body.target_language:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="source_language and target_language must differ",
|
|
)
|
|
|
|
user_id = uuid.UUID(token_data["sub"])
|
|
lang = await AccountService(db).add_learnable_language(
|
|
user_id=user_id,
|
|
source_language=body.source_language,
|
|
target_language=body.target_language,
|
|
proficiencies=body.proficiencies,
|
|
)
|
|
return LearnableLanguageResponse(
|
|
id=lang.id,
|
|
source_language=lang.source_language,
|
|
target_language=lang.target_language,
|
|
proficiencies=lang.proficiencies,
|
|
)
|
|
|
|
|
|
@router.delete(
|
|
"/learnable-languages/{language_id}",
|
|
status_code=status.HTTP_204_NO_CONTENT,
|
|
)
|
|
async def remove_learnable_language(
|
|
language_id: str,
|
|
db: AsyncSession = Depends(get_db),
|
|
token_data: dict = Depends(verify_token),
|
|
) -> None:
|
|
try:
|
|
lid = uuid.UUID(language_id)
|
|
except ValueError:
|
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid language_id")
|
|
|
|
user_id = uuid.UUID(token_data["sub"])
|
|
try:
|
|
await AccountService(db).remove_learnable_language(user_id=user_id, language_id=lid)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc))
|