fix(ical): fiabiliser le parsing Pronote 2026
Revue Codex validée. Corrections de parsing iCal, conservation du contexte des devoirs et fixtures anonymisées. Co-authored-by: Codex <codex@antoineve.me> Co-committed-by: Codex <codex@antoineve.me>
This commit was merged in pull request #31.
This commit is contained in:
+115
-5
@@ -294,8 +294,8 @@ def test_collect_homeworks_dedup() -> None:
|
||||
id="lesson2",
|
||||
start=datetime(2026, 9, 10, 10, 0),
|
||||
end=datetime(2026, 9, 10, 11, 0),
|
||||
subject="Physique",
|
||||
teachers=("M. Martin",),
|
||||
subject="Math",
|
||||
teachers=("M. Dupont",),
|
||||
rooms=("205",),
|
||||
group=None,
|
||||
status=LessonStatus.NORMAL,
|
||||
@@ -343,8 +343,8 @@ def test_collect_homeworks_id_stability() -> None:
|
||||
id="lesson2",
|
||||
start=datetime(2026, 9, 10, 10, 0),
|
||||
end=datetime(2026, 9, 10, 11, 0),
|
||||
subject="Physique",
|
||||
teachers=("M. Martin",),
|
||||
subject="Math",
|
||||
teachers=("M. Dupont",),
|
||||
rooms=("205",),
|
||||
group=None,
|
||||
status=LessonStatus.NORMAL,
|
||||
@@ -361,7 +361,117 @@ def test_collect_homeworks_id_stability() -> None:
|
||||
|
||||
homeworks = collect_homeworks([lesson1, lesson2], target_date=date(2026, 9, 10))
|
||||
assert len(homeworks) == 1
|
||||
assert homeworks[0].id == generate_homework_id(date(2026, 9, 10), "devoir commun")
|
||||
assert homeworks[0].id == generate_homework_id(
|
||||
date(2026, 9, 10), "devoir commun", "Math", ("M. Dupont",)
|
||||
)
|
||||
|
||||
|
||||
def test_collect_homeworks_keeps_distinct_subjects() -> None:
|
||||
"""Deux matières différentes conservent deux devoirs homonymes.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
lesson = Lesson(
|
||||
id="lesson1",
|
||||
start=datetime(2026, 9, 10, 8, 0),
|
||||
end=datetime(2026, 9, 10, 9, 0),
|
||||
subject="Math",
|
||||
teachers=("M. Dupont",),
|
||||
group=None,
|
||||
content=None,
|
||||
homework_blocks=(
|
||||
HomeworkBlock(
|
||||
kind="due",
|
||||
date=date(2026, 9, 10),
|
||||
text="Devoir commun",
|
||||
html="<p>Devoir commun</p>",
|
||||
),
|
||||
),
|
||||
)
|
||||
other_lesson = lesson.model_copy(
|
||||
update={"id": "lesson2", "subject": "Physique", "teachers": ("M. Martin",)}
|
||||
)
|
||||
|
||||
homeworks = collect_homeworks([lesson, other_lesson], target_date=date(2026, 9, 10))
|
||||
|
||||
assert len(homeworks) == 2
|
||||
assert {homework.subject for homework in homeworks} == {"Math", "Physique"}
|
||||
|
||||
|
||||
def test_collect_homeworks_deduplicates_teacher_order_variants() -> None:
|
||||
"""Deux ordres d'enseignants équivalents ne créent pas de doublon.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
homework_block = HomeworkBlock(
|
||||
kind="due",
|
||||
date=date(2026, 9, 10),
|
||||
text="Devoir commun",
|
||||
html="<p>Devoir commun</p>",
|
||||
)
|
||||
lesson = Lesson(
|
||||
id="lesson1",
|
||||
start=datetime(2026, 9, 10, 8, 0),
|
||||
end=datetime(2026, 9, 10, 9, 0),
|
||||
subject="Math",
|
||||
teachers=("M. Martin", "M. Dupont"),
|
||||
group=None,
|
||||
content=None,
|
||||
homework_blocks=(homework_block,),
|
||||
)
|
||||
other_lesson = lesson.model_copy(
|
||||
update={"id": "lesson2", "teachers": ("M. Dupont", "M. Martin")}
|
||||
)
|
||||
|
||||
homeworks = collect_homeworks([lesson, other_lesson], target_date=date(2026, 9, 10))
|
||||
|
||||
assert len(homeworks) == 1
|
||||
|
||||
|
||||
def test_parse_body_accepts_html_variants_and_sanitizes() -> None:
|
||||
"""Les attributs HTML, les espaces et le contenu actif sont traités correctement.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
body = (
|
||||
'<strong class="label"> Contenu pédagogique : </strong><p>Leçon & exemple</p>'
|
||||
'<strong data-kind="homework"> Pour le 10/09/2026 : </strong>'
|
||||
'<p onclick="evil()">Lire <em>le chapitre</em></p><script>alert(1)</script>'
|
||||
"<strong> Donné le 05/09/2026 : </strong><p>Noter & relire</p>"
|
||||
)
|
||||
|
||||
content, due_blocks, assigned_blocks = parse_body(body)
|
||||
|
||||
assert content == "Leçon & exemple"
|
||||
assert due_blocks[0].date == date(2026, 9, 10)
|
||||
assert due_blocks[0].text == "Lire le chapitre"
|
||||
assert "<em>le chapitre</em>" in due_blocks[0].html
|
||||
assert "onclick" not in due_blocks[0].html
|
||||
assert "script" not in due_blocks[0].html.lower()
|
||||
assert assigned_blocks[0].date == date(2026, 9, 5)
|
||||
|
||||
|
||||
def test_parse_anonymized_variants_fixture() -> None:
|
||||
"""Vérifie le corpus iCal anonymisé sur les statuts et le contexte des devoirs.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
fixture_path = Path(__file__).parent.parent / "fixtures" / "pronote-variants.ics"
|
||||
lessons, _, school_events = parse_ical(fixture_path.read_text(encoding="utf-8"))
|
||||
|
||||
assert lessons[0].class_part == "Groupe 1"
|
||||
assert lessons[0].homework_blocks[0].html == "<p>Lire le chapitre</p>"
|
||||
assert any(lesson.status == LessonStatus.MOVED for lesson in lessons)
|
||||
assert any(event.kind.value == "public_holiday" for event in school_events)
|
||||
assert any(
|
||||
event.from_date == date(2026, 10, 20) and event.to_date == date(2026, 10, 22)
|
||||
for event in school_events
|
||||
)
|
||||
|
||||
homeworks = collect_homeworks(lessons, target_date=date(2026, 9, 15))
|
||||
|
||||
assert len(homeworks) == 2
|
||||
assert {homework.subject for homework in homeworks} == {"Mathématiques", "Sciences"}
|
||||
|
||||
|
||||
def test_collect_homeworks_sorted() -> None:
|
||||
|
||||
Reference in New Issue
Block a user