Contexte
Les colonnes annotations.properties et corrections.metadata_extra
sont des JSONB libres. Aucune validation de schéma. Au fil du temps,
plusieurs représentations de la même donnée vont coexister
({"grade": 2} vs {"tnm": {"grade": "II"}}), rendant les requêtes
et les exports training-ready (N3) instables.
Référence : docs/architecture/ANNOTATION_DATA_MODEL_AUDIT.md — gap G9.
Objectif
Imposer des schémas Pydantic explicites pour les JSONB en fonction de
l'annotation_type, valider à l'écriture (route POST/PATCH).
Approche technique proposée
Modèles Pydantic dédiés
# schemas/annotation_properties.py
class ManualAnnotationProperties(BaseModel):
grade: Optional[Literal["I", "II", "III", "IV"]] = None
tnm_t: Optional[str] = None
tnm_n: Optional[str] = None
tnm_m: Optional[str] = None
differentiation: Optional[Literal["well", "moderate", "poor", "undifferentiated"]] = None
free_form: Optional[dict[str, Any]] = None
class AutoAnnotationProperties(BaseModel):
model_logits: Optional[list[float]] = None
attention_score: Optional[float] = Field(None, ge=0, le=1)
free_form: Optional[dict[str, Any]] = None
class CorrectionMetadata(BaseModel):
original_geometry_id: Optional[UUID] = None
correction_duration_ms: Optional[int] = Field(None, ge=0)
tool_used: Optional[Literal["drag_vertex", "redraw", "delete", "split"]] = None
free_form: Optional[dict[str, Any]] = None
Validation à l'écriture
Dans routes/annotations.py::create_annotation et update_annotation :
if payload.annotation_type == "manual":
validated = ManualAnnotationProperties.model_validate(payload.properties or {})
elif payload.annotation_type in ("auto", "auto_confirmed"):
validated = AutoAnnotationProperties.model_validate(payload.properties or {})
payload.properties = validated.model_dump(exclude_none=True)
Échappatoire free_form
free_form: dict permet de garder l'extensibilité sans rouvrir la porte
au drift. Toute clé non-prévue doit y aller — visible en review.
Acceptance criteria (fonctionnel)
Review checklist (conditions de validation pour le reviewer PR)
Code
Migration de données
Sécurité
Documentation
Dépendances
- Indépendante — peut démarrer immédiatement
- Bloque N4 (bulk import — réutilise les schémas) et N3 (export training-ready)
Hors-scope
- Migration des annotations historiques vers les nouveaux schémas (DB vide)
- Génération automatique de JSONSchema depuis Pydantic
Références
docs/architecture/ANNOTATION_DATA_MODEL_AUDIT.md §3 (G9)
- Pydantic v2
model_validate
Contexte
Les colonnes
annotations.propertiesetcorrections.metadata_extrasont des JSONB libres. Aucune validation de schéma. Au fil du temps,
plusieurs représentations de la même donnée vont coexister
(
{"grade": 2}vs{"tnm": {"grade": "II"}}), rendant les requêteset les exports training-ready (N3) instables.
Objectif
Imposer des schémas Pydantic explicites pour les JSONB en fonction de
l'
annotation_type, valider à l'écriture (route POST/PATCH).Approche technique proposée
Modèles Pydantic dédiés
Validation à l'écriture
Dans
routes/annotations.py::create_annotationetupdate_annotation:Échappatoire
free_formfree_form: dictpermet de garder l'extensibilité sans rouvrir la porteau drift. Toute clé non-prévue doit y aller — visible en review.
Acceptance criteria (fonctionnel)
backend/schemas/annotation_properties.pyavec les 3 schémasPOST /api/v1/annotationsetPATCH /api/v1/annotations/{id}POST /api/v1/correctionsGET /api/v1/admin/annotations/properties-statsagrégeant la distribution réelle des clésdocs/architecture/ANNOTATION_PROPERTIES_SCHEMAS.mdReview checklist (conditions de validation pour le reviewer PR)
Code
manualavec champ inconnu non-prévu doit échouer (sauf viafree_form)Migration de données
scripts/analyze_annotation_properties.pyqui scan la DB et liste les clés observées + fréquenceSécurité
free_form(validation au minimum sur types primitifs)Documentation
backend/schemas/info.mdmis à jourdocs/architecture/ANNOTATION_DATA_MODEL_AUDIT.md: G9 marquéRESOLVEDDépendances
Hors-scope
Références
docs/architecture/ANNOTATION_DATA_MODEL_AUDIT.md§3 (G9)model_validate