All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m27s
Full audit found 18 issues across backend, frontend, and data files. Root cause: target_language had no single source of truth — 'en' was hardcoded as default in 6+ places while templates are actually multilingual. Fixes applied: - routes/glossary_routes.py: list_glossaries() fallback 'en' → 'multi' - routes/glossary_routes.py: import reads target_lang from index.json (source of truth) instead of template file - data/glossaries/*.json: all 8 template files target_lang 'en' → 'multi' - services/glossary_service.py: get_glossary_terms() now returns target_language field (was missing entirely) - schemas/glossary_schemas.py: all defaults 'en' → 'multi' (GlossaryCreate, GlossaryResponse, GlossaryListItem) - useTranslationConfig.ts: only reset glossary on sourceLang change, not targetLang (multilingual glossaries work with any target) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
117 lines
3.2 KiB
Python
117 lines
3.2 KiB
Python
"""
|
|
Pydantic schemas for glossary endpoints.
|
|
Story 3.9: Glossaires - Endpoint CRUD
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from uuid import UUID
|
|
from typing import Optional
|
|
|
|
from pydantic import BaseModel, Field, field_validator
|
|
|
|
|
|
class GlossaryTermCreate(BaseModel):
|
|
"""Schema for creating a single term."""
|
|
|
|
source: str = Field(..., min_length=1, max_length=500, description="Terme source")
|
|
target: str = Field(
|
|
..., min_length=1, max_length=500, description="Traduction cible"
|
|
)
|
|
translations: Optional[dict[str, str]] = Field(
|
|
None, description="Traductions multilingues: {\"en\": \"coil\", \"de\": \"Spule\", ...}"
|
|
)
|
|
|
|
@field_validator("source", "target")
|
|
@classmethod
|
|
def strip_whitespace(cls, v: str) -> str:
|
|
return v.strip()
|
|
|
|
|
|
class GlossaryTermResponse(BaseModel):
|
|
"""Schema for term in response."""
|
|
|
|
id: str
|
|
source: str
|
|
target: str
|
|
translations: dict[str, str] = {}
|
|
created_at: Optional[datetime] = None
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class GlossaryCreate(BaseModel):
|
|
"""Schema for creating a glossary."""
|
|
|
|
name: str = Field(..., min_length=1, max_length=255, description="Nom du glossaire")
|
|
source_language: str = Field(
|
|
default="fr", max_length=10, description="Langue source (ISO code)"
|
|
)
|
|
target_language: str = Field(
|
|
default="multi", max_length=10, description="Langue cible (ISO code) ou 'multi' pour multilingue"
|
|
)
|
|
terms: list[GlossaryTermCreate] = Field(
|
|
default_factory=list, description="Liste des termes"
|
|
)
|
|
|
|
@field_validator("name")
|
|
@classmethod
|
|
def strip_name(cls, v: str) -> str:
|
|
return v.strip()
|
|
|
|
|
|
class GlossaryUpdate(BaseModel):
|
|
"""Schema for updating a glossary (all fields optional)."""
|
|
|
|
name: Optional[str] = Field(None, min_length=1, max_length=255)
|
|
source_language: Optional[str] = Field(None, max_length=10)
|
|
target_language: Optional[str] = Field(None, max_length=10)
|
|
terms: Optional[list[GlossaryTermCreate]] = Field(None)
|
|
|
|
@field_validator("name")
|
|
@classmethod
|
|
def strip_name(cls, v: Optional[str]) -> Optional[str]:
|
|
return v.strip() if v else None
|
|
|
|
|
|
class GlossaryResponse(BaseModel):
|
|
"""Schema for glossary in response (with full terms)."""
|
|
|
|
id: str
|
|
name: str
|
|
source_language: str = "fr"
|
|
target_language: str = "multi"
|
|
terms: list[GlossaryTermResponse] = []
|
|
created_at: Optional[datetime] = None
|
|
updated_at: Optional[datetime] = None
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class GlossaryListItem(BaseModel):
|
|
"""Schema for glossary in list (without full terms)."""
|
|
|
|
id: str
|
|
name: str
|
|
source_language: str = "fr"
|
|
target_language: str = "multi"
|
|
terms_count: int = Field(
|
|
default=0, description="Nombre de termes dans le glossaire"
|
|
)
|
|
created_at: Optional[datetime] = None
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class GlossaryListResponse(BaseModel):
|
|
"""Schema for glossaries list response."""
|
|
|
|
data: list[GlossaryListItem] = []
|
|
meta: dict = Field(default_factory=dict)
|
|
|
|
|
|
class GlossaryDetailResponse(BaseModel):
|
|
"""Schema for single glossary response."""
|
|
|
|
data: GlossaryResponse
|
|
meta: dict = Field(default_factory=dict)
|