feat(M3): modèles de données Pydantic (22 modèles, 10 modules)
Modèles créés : - models/agenda.py : Status, LessonStatus, HomeworkBlock, Lesson (frozen), SchoolEventKind, SchoolEvent (frozen), TheoreticalLesson (frozen) - models/homework.py : Homework (frozen) - models/message.py : MessageType, Message (frozen) - models/blog.py : BlogArticle (frozen), ExternalInfo (mutable) - models/diff.py : AgendaChangeType, AgendaChange (frozen), AgendaDiff (frozen) - models/pronote.py : PronoteData (mutable) - models/sync.py : CalDAVSyncStatus, CalDAVSyncPlan, CalDAVSyncResult (mutable) - models/synthesis.py : SynthesisInput, SynthesisResult (mutable) - models/xmpp.py : XmppMessage (frozen, external_info: ExternalInfo | None) - models/__init__.py : ré-export des 22 modèles via __all__ Conventions appliquées : - Pydantic v2 : model_config = ConfigDict(frozen=True), pas de json_encoders - StrEnum au lieu de (str, Enum) (ruff UP042) - Alias _date/_time pour éviter collision champ date/type date - Sphinx/reST docstrings sur toutes les classes - TODO.md : 11 items M3 cochés Validations (@verifier) : - ruff check : PASS (0 error) - ruff format --check : PASS (10 fichiers) - mypy strict : PASS (10 fichiers) - bandit : PASS (0 issue) - import 22 modèles : OK - sérialisation JSON (frozen + mutable) : OK - frozen immuable, PronoteData/CalDAVSyncResult mutable : OK - StrEnum isinstance str : OK - pytest : 0 test (infrastructure OK) Co-authored-by: OpenCode/orchestrator <opencode-orchestrator@agents.invalid>
This commit is contained in:
90
pronote_sync/models/agenda.py
Normal file
90
pronote_sync/models/agenda.py
Normal file
@@ -0,0 +1,90 @@
|
||||
"""Modèles de données pour l'agenda Pronote (cours, événements, agenda théorique)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date as _date
|
||||
from datetime import datetime
|
||||
from datetime import time as _time
|
||||
from enum import StrEnum
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class Status(StrEnum):
|
||||
"""Statut générique pour les événements."""
|
||||
|
||||
NORMAL = "normal"
|
||||
CANCELLED = "cancelled"
|
||||
MOVED = "moved"
|
||||
|
||||
|
||||
class LessonStatus(StrEnum):
|
||||
"""Statut d'un cours."""
|
||||
|
||||
NORMAL = "normal"
|
||||
CANCELLED = "cancelled"
|
||||
MOVED = "moved"
|
||||
|
||||
|
||||
class HomeworkBlock(BaseModel):
|
||||
"""Représente un bloc de devoir extrait de la description d'un cours."""
|
||||
|
||||
kind: Literal["due", "assigned"] = Field(
|
||||
..., description="Type de bloc (échéance ou attribution)"
|
||||
)
|
||||
date: _date = Field(..., description="Date associée au bloc")
|
||||
text: str = Field(..., description="Texte du devoir (brut)")
|
||||
html: str = Field(default="", description="Texte du devoir (HTML)")
|
||||
|
||||
|
||||
class Lesson(BaseModel):
|
||||
"""Représente un cours dans l'agenda Pronote."""
|
||||
|
||||
model_config = ConfigDict(frozen=True)
|
||||
|
||||
id: str = Field(..., description="UID stable du cours (normalisé)")
|
||||
start: datetime = Field(..., description="Date/heure de début")
|
||||
end: datetime = Field(..., description="Date/heure de fin")
|
||||
subject: str = Field(..., description="Matière")
|
||||
teachers: list[str] = Field(default_factory=list, description="Liste des professeurs")
|
||||
rooms: list[str] = Field(default_factory=list, description="Liste des salles")
|
||||
group: str | None = Field(None, description="Groupe (ex: Classe entière)")
|
||||
status: LessonStatus = Field(default=LessonStatus.NORMAL, description="Statut du cours")
|
||||
content: str | None = Field(None, description="Contenu pédagogique")
|
||||
homework_blocks: list[HomeworkBlock] = Field(
|
||||
default_factory=list,
|
||||
description="Blocs de devoirs extraits de la description",
|
||||
)
|
||||
|
||||
|
||||
class SchoolEventKind(StrEnum):
|
||||
"""Type d'événement scolaire."""
|
||||
|
||||
HOLIDAY = "holiday"
|
||||
PUBLIC_HOLIDAY = "public_holiday"
|
||||
|
||||
|
||||
class SchoolEvent(BaseModel):
|
||||
"""Représente un événement scolaire (vacances, jours fériés)."""
|
||||
|
||||
model_config = ConfigDict(frozen=True)
|
||||
|
||||
kind: SchoolEventKind = Field(..., description="Type d'événement")
|
||||
label: str = Field(..., description="Libellé (ex: Vacances de Noël)")
|
||||
from_date: _date = Field(..., description="Date de début (inclusive)")
|
||||
to_date: _date = Field(..., description="Date de fin (exclusive)")
|
||||
|
||||
|
||||
class TheoreticalLesson(BaseModel):
|
||||
"""Représente un cours dans l'agenda théorique."""
|
||||
|
||||
model_config = ConfigDict(frozen=True)
|
||||
|
||||
id: str = Field(..., description="Identifiant unique")
|
||||
day_of_week: int = Field(..., description="Jour de la semaine (0=lundi, 6=dimanche)")
|
||||
start_time: _time = Field(..., description="Heure de début")
|
||||
end_time: _time = Field(..., description="Heure de fin")
|
||||
subject: str = Field(..., description="Matière")
|
||||
teachers: list[str] = Field(default_factory=list, description="Liste des professeurs")
|
||||
rooms: list[str] = Field(default_factory=list, description="Liste des salles")
|
||||
Reference in New Issue
Block a user