Immuabilité : - Collections des modèles frozen : list → tuple (Lesson, TheoreticalLesson, SchoolEvent, AgendaDiff, Homework, XmppMessage, ExternalInfo) - Gel de HomeworkBlock et ExternalInfo (sous-modèles imbriqués dans contrats) - Valeurs par défaut immuables : Field(default=(), ...) Validation de domaine : - TheoreticalLesson.day_of_week : Field(ge=0, le=6) - Lesson/TheoreticalLesson : start < end (validateur de modèle) - SchoolEvent : from_date <= to_date - AgendaChange : cohérence type/payload (ADDED/MODIFIED → lesson, REMOVED → theoretical_lesson) - CalDAVSyncResult : compteurs Field(ge=0) sur added/updated/removed Tests : 66 tests (construction/sérialisation, immuabilité, invariants) couvrant le gel, la mutabilité légitime, et les cas invalides par invariant. Co-authored-by: opencode/coder <coder@agents.invalid> Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>
48 lines
1.7 KiB
Python
48 lines
1.7 KiB
Python
"""Modèles de données pour le blog du collège (RSS) et les informations externes."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
|
|
from pronote_sync.models.message import Message
|
|
|
|
|
|
class BlogArticle(BaseModel):
|
|
"""Représente un article du blog du collège."""
|
|
|
|
model_config = ConfigDict(frozen=True)
|
|
|
|
id: str = Field(..., description="GUID de l'article (clé de déduplication)")
|
|
title: str = Field(..., description="Titre de l'article")
|
|
url: str = Field(..., description="URL canonique de l'article")
|
|
published_at: datetime = Field(..., description="Date de publication (UTC)")
|
|
updated_at: datetime | None = Field(None, description="Date de dernière mise à jour")
|
|
category: str | None = Field(None, description="Catégorie de l'article")
|
|
author: str | None = Field(None, description="Auteur (si disponible)")
|
|
content_html: str = Field(..., description="Contenu HTML complet")
|
|
content_text: str = Field(..., description="Contenu en texte brut (pour XMPP)")
|
|
|
|
|
|
class ExternalInfo(BaseModel):
|
|
"""Agrège les informations externes (blog, messages Pronote) pour le message XMPP.
|
|
|
|
Modèle figé (``frozen``) : les instances sont immuables après création.
|
|
"""
|
|
|
|
model_config = ConfigDict(frozen=True)
|
|
|
|
blog_articles: tuple[BlogArticle, ...] = Field(
|
|
default=(),
|
|
description="Liste des nouveaux articles du blog",
|
|
)
|
|
pronote_messages: tuple[Message, ...] = Field(
|
|
default=(),
|
|
description="Liste des messages Pronote",
|
|
)
|
|
other_info: tuple[str, ...] = Field(
|
|
default=(),
|
|
description="Autres informations (extensible)",
|
|
)
|