Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions scripts/notebook_tools/check_split_reading_cells.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,10 +426,10 @@ def detect_added_readings(head_nb: dict, base_nb: dict | None) -> list[dict]:
# que la campagne a produit des cellules sans id ET des ids
# dupliques -- l'id est donc consomme une fois, ce qui borne les
# doublons sans reouvrir la porte ;
# (c) meme position ET les deux cellules sont des lectures : la tete
# a revise celle qui occupait ce slot (#17044). Signal
# topologique, indispensable sur les carnets SANS id (le corpus
# en contient -- cf. les deux carnets du controle positif).
# (c) meme position ET les deux cellules sont markdown : la tete
# a revise celle qui occupait ce slot (#17044, etendu #17747).
# Signal topologique, indispensable sur les carnets SANS id (le
# corpus en contient -- cf. les deux carnets du controle positif).
# La regle user dit « fusionner / reecrire, pas empiler » : la
# reecriture EST l'action prescrite. Ne pas la signaler.
is_rewrite = False
Expand All @@ -441,18 +441,28 @@ def detect_added_readings(head_nb: dict, base_nb: dict | None) -> list[dict]:
and base_cells[idx].get("cell_type") == "markdown"
and cell_source(base_cells[idx]) == src):
is_rewrite = True
# (c) meme position ET les DEUX cellules sont des lectures : la tete
# a REVISE celle qui occupait ce slot (#17044). Le mandat prescrit
# (c) meme position ET les DEUX cellules sont MARKDOWN : la tete a
# REVISE celle qui occupait ce slot (#17044). Le mandat prescrit
# cette revision ; sans ce signal elle n'etait reconnue que sur
# les carnets porteurs d'ids, donc le remede etait puni des que
# les cellules n'en avaient pas (le corpus en contient : les deux
# carnets du controle positif #17028 sont dans ce cas). Un
# EMPILEMENT reel a cote n'est pas vu ici : la lecture empilee
# arrive a un index ou la base porte autre chose (ou rien), et
# le compte de paires, lui, monte.
#
# Le signal ne depend PAS de la classification lecture/exercice
# (#17747) : une revision de prose en place n'est pas un
# empilement, quel que soit le titre. Exiger « les deux sont des
# lectures » rouvrait sur les carnets SANS id le faux positif que
# #17044 avait ferme pour les lectures. Mesure fondatrice : un
# simple echappement de `$` (``39,66 $`` -> ``39,66 \$``) dans
# une cellule sans id titree « ### Exercice 3 » suffisait a la
# faire passer pour un ajout, et le cliquet rougissait le geste
# que le mandat PRESCRIT.
if (not is_rewrite and idx < len(base_cells)
and is_reading_cell(base_cells[idx])
and is_reading_cell(cell)):
and base_cells[idx].get("cell_type") == "markdown"
and cell.get("cell_type") == "markdown"):
is_rewrite = True
if is_rewrite:
base_counter[src] += 1
Expand Down
48 changes: 48 additions & 0 deletions scripts/tests/test_check_split_reading_cells.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,54 @@ def test_diff_exempte_reecriture_en_place_d_une_lecture():
assert findings == []


def test_diff_exempte_reecriture_en_place_d_une_cellule_sans_id_non_lecture():
"""#17747 -- revision en place d'une cellule markdown SANS id et NON
classee lecture (titre « Exercice 3 ») : ce n'est pas un ajout.

Mesure fondatrice (OR-tools-Stiegler, tranche SymbolicAI de #17498) : un
simple echappement de `$` (``39,66 $`` -> ``39,66 \\$``) dans cette
cellule suffisait a la faire passer pour un ajout -- les trois signaux
REWRITE manquaient (source modifiee, pas d'id, pas deux lectures) -- et le
cliquet rougissait le geste que le mandat user PRESCRIT : « si on rajoute
une lecture, on modifie le paragraphe de lecture existant ».
"""
base = nb(
code("solver.Solve();"),
md("### Exercice 3 : Analyse de sensibilite du regime optimal\n"
"La solution optimale indique 5 aliments pour un cout de 39,66 $/an."),
)
head = nb(
code("solver.Solve();"),
md("### Exercice 3 : Analyse de sensibilite du regime optimal\n"
"La solution optimale indique 5 aliments pour un cout de 39,66 \\$/an."),
)
assert detect_added_readings(head, base) == []


def test_diff_mord_si_l_empilement_remplace_le_slot_voisin():
"""Controle NEGATIF de #17747 : le signal de revision ne couvre QUE le
slot qu'il occupe. Un empilement qui pousse une lecture la ou la base
portait du code reste rouge, meme si la cellule du dessus a ete revisee
en place dans la meme PR -- sinon le signal serait un robinet ouvert.
"""
base = nb(
code("print(1)"),
md("### Analyse du resultat\nAncienne formulation."),
code("print(2)"),
)
head = nb(
code("print(1)"),
md("### Analyse du resultat\nNouvelle formulation."),
md("### Lecture chiffree : le score atteint 0.94"),
code("print(2)"),
)
findings = detect_added_readings(head, base)
# La revision du slot 1 est exemptee ; la lecture empilee au slot 2 (ou la
# base portait du code) reste signalee.
assert len(findings) == 1
assert findings[0]["cells"] == [2]


def test_diff_mute_si_ordre_inchange_et_contenu_identique():
"""Une PR qui ne touche PAS au notebook ne signale rien (sanity check)."""
base = head = nb(code("print(1)"))
Expand Down
Loading