fix(M6): corrections d'audit FIXME_M6 — normalisation, secret, tests, doc
Corrige les 5 points de l'audit FIXME_M6 : 1. Normalisation des matières : fonction normalize_subject (NFKC + unification des espaces + suppression ponctuation + minuscule) partagée par la génération d'ID et le futur comparateur M8. 2. Expurgation du secret dans l'erreur de collision d'IDs : redact_secrets enveloppe l'identifiant dans le message. 3. Test even/odd avec même matière pour isoler la parité comme seul différenciateur d'ID ; tests de normalisation (casse, espaces, Unicode) ; test de non-fuite de secret. 4. TODO.md M6 : 8 items cochés après validation. 5. GUIDE_DEV §8.4 : bloc de code corrigé (clôture, types Lesson/ TheoreticalLesson, comparaison des horaires en minutes, début ET fin, référence à normalize_subject). Co-authored-by: opencode/coder <coder@agents.invalid> Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>
This commit is contained in:
@@ -3505,7 +3505,7 @@ def week_parity(
|
||||
2. **Comparaison exacte** : Les créneaux horaires et la matière normalisée doivent correspondre.
|
||||
3. **Choix de la première correspondance** : En cas de multiples correspondances admissibles, choisir la **première** après tri déterministe.
|
||||
|
||||
**Exemple de tri** :
|
||||
**Exemple de tri** :
|
||||
```python
|
||||
# Tri des cours théoriques par ID stable (pour un matching déterministe)
|
||||
theoretical_lessons_sorted = sorted(
|
||||
@@ -3517,24 +3517,43 @@ theoretical_lessons_sorted = sorted(
|
||||
lesson.subject.lower(),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
**Exemple de matching avec départage déterministe** :
|
||||
```python
|
||||
def match_theoretical_lesson(
|
||||
real_lesson: PronoteLesson,
|
||||
theoretical_lessons: list[TheoreticalLesson],
|
||||
real_lesson: Lesson,
|
||||
theoretical_events: list[TheoreticalLesson],
|
||||
tolerance_minutes: int = 15,
|
||||
) -> TheoreticalLesson | None:
|
||||
"""Trouve le cours théorique correspondant, avec départage déterministe."""
|
||||
"""Trouve la leçon théorique correspondant à une leçon réelle.
|
||||
|
||||
:param real_lesson: Leçon réelle depuis Pronote.
|
||||
:param theoretical_events: Liste des leçons théoriques candidates.
|
||||
:param tolerance_minutes: Tolérance en minutes pour le créneau horaire.
|
||||
:return: La leçon théorique correspondante, ou None.
|
||||
:rtype: TheoreticalLesson | None
|
||||
"""
|
||||
real_start = real_lesson.start
|
||||
real_day = real_start.weekday()
|
||||
|
||||
def to_minutes(t: time) -> int:
|
||||
return t.hour * 60 + t.minute
|
||||
|
||||
# ``normalize_subject`` sera défini dans ``sync/diff.py`` (M8) ou dans le
|
||||
# module théorique ; il normalise les matières pour un matching déterministe.
|
||||
start_minutes = real_start.hour * 60 + real_start.minute
|
||||
end_minutes = real_lesson.end.hour * 60 + real_lesson.end.minute
|
||||
candidates = [
|
||||
t for t in theoretical_lessons
|
||||
if t.day_of_week == real_lesson.start.weekday()
|
||||
and abs((t.start_time - real_lesson.start.time()).total_seconds()) <= tolerance_minutes * 60
|
||||
t
|
||||
for t in theoretical_events
|
||||
if t.day_of_week == real_day
|
||||
and abs(to_minutes(t.start_time) - start_minutes) <= tolerance_minutes
|
||||
and abs(to_minutes(t.end_time) - end_minutes) <= tolerance_minutes
|
||||
and normalize_subject(t.subject) == normalize_subject(real_lesson.subject)
|
||||
]
|
||||
if not candidates:
|
||||
return None
|
||||
# Tri déterministe par ID stable, puis par créneau
|
||||
candidates.sort(key=lambda t: (t.id, t.start_time))
|
||||
return candidates[0]
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user