feat: „Zu prüfen“ zeigt bloße Wortformen getrennt am Ende

Ersetzt das Modell ein gültiges Wort nur durch eine andere Form
(„Punkten“ → „Punkte“, „das“ → „dass“, „sehe“ → „sähe“), heißt der Grund
jetzt „Wortform geändert“. Diese Fälle stehen im Bericht in einer eigenen
Tabelle nach den sinnverändernden, im Text gestrichelt umrahmt, und in
der Konsole am Ende. „-in“ zählt nicht als Endung: „Professor“ →
„Professorin“ bleibt oben.

Außerdem: Die Tabellen scrollen auf schmalen Bildschirmen waagrecht, und
aenderungen.py meldet Zeit-Überschriften nicht als Einfügung, wenn der
Rohtext (z. B. eine .vtt) keine hat.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-10-11 22:01:18 +02:00
commit cd62feab69

View file

@ -124,10 +124,8 @@ def test_cli_missing_file(tmp_path, monkeypatch, capsys):
WORDS = {"professor", "das", "dass", "ist", "haus", "unterschreiben", "bar"}
def suspects(model_input, corrected, wordlist=WORDS, glossary=(), fillers=ae.FILLERS["de"]):
found = ae.review_candidates(
ae.compare(model_input, corrected), wordlist, glossary, fillers
)
def suspects(model_input, corrected, wordlist=WORDS, glossary=(), lang="de"):
found = ae.review_candidates(ae.compare(model_input, corrected), wordlist, glossary, lang)
return [(s.reason, s.old, s.new) for s in found]
@ -182,7 +180,7 @@ def test_review_candidates_fillers_per_language():
assert suspects("dann kam er um zu helfen", "Dann kam, zu helfen.") == [
(ae.DELETED, "er um", "")
]
english = suspects("so er we um go", "So we go.", fillers=ae.FILLERS["en"])
english = suspects("so er we um go", "So we go.", lang="en")
assert english == []
@ -224,7 +222,7 @@ def test_report_suspects_only_against_model_input(tmp_path, capsys):
assert [s.reason for s in comparison.suspects] == [ae.VALID_WORD, ae.DELETED]
assert "Zu prüfen (2)" in page
assert "Professorin" in section and "Kremmel" not in section
assert "<span class=warn title='gültiges Wort ersetzt: „Professor“ → „Professorin“'>" in page
assert "<span class='warn' title='gültiges Wort ersetzt: „Professor“ → „Professorin“'>" in page
assert "<span class=warn title='gestrichen: „heute“ → „∅“'>⚠</span>" in page
# Framing changes nothing about the text itself
assert report_text(page) == "Der Professorin im Kreml ⚠ dort.".split()
@ -242,6 +240,20 @@ def test_report_without_suspects(tmp_path):
assert "Keine auffälligen Änderungen" in suspect_section(target.read_text(encoding="utf-8"))
def test_cli_ignores_time_headers_missing_in_raw(tmp_path, monkeypatch, capsys):
raw = tmp_path / "rede.vtt"
raw.write_text(
"WEBVTT\n\n00:00:01.000 --> 00:00:02.000\ndas Kremmel heute\n", encoding="utf-8"
)
corrected = tmp_path / "rede.korrigiert.txt"
corrected.write_text("[00:00:01 - 00:00:02]\n\nDas Kreml heute.\n", encoding="utf-8")
monkeypatch.setattr(sys, "argv", ["aenderungen.py", "-a", str(raw), "-b", str(corrected)])
ae.main()
out = capsys.readouterr().out
assert "Zu prüfen: 0 Stellen" in out
assert "00:00:01" not in out
def test_cli_glossary(tmp_path, monkeypatch, capsys):
raw = tmp_path / "rede.txt"
raw.write_text("das Kremmel und\nBar heute\n", encoding="utf-8")
@ -261,3 +273,51 @@ def test_cli_glossary(tmp_path, monkeypatch, capsys):
assert "Zu prüfen: 0 Stellen" in out
# The report still compares against the raw transcript
assert "1× „Kremmel“ → „Kreml“" in out
@pytest.mark.parametrize("old, new, expected", [
("Punkten", "Punkte", True),
("das", "dass", True),
("sehe", "sähe", True), # umlaut
("sein", "seien", True), # both endings change
("kommt,", "kommen,", True), # punctuation does not count
("Professor", "Professorin", False), # "-in" is no inflection
("geworben", "geworden", False),
("unterschreiben", "unterscheiden", False),
("ich", "sich", False),
("den", "der", False), # stem too short
("sich dazu", "zu", False), # more than one word
])
def test_word_form_change(old, new, expected):
assert ae.word_form_change(old.split(), new.split()) is expected
def test_word_form_change_english():
assert ae.word_form_change(["walk"], ["walked"], "en")
assert not ae.word_form_change(["walk"], ["walker"], "en")
def test_review_candidates_word_forms():
words = WORDS | {"punkten", "punkte"}
assert suspects("die Punkten und das", "die Punkte und dass", wordlist=words) == [
(ae.WORD_FORM, "Punkten", "Punkte"), (ae.WORD_FORM, "das", "dass"),
]
def test_report_and_summary_put_word_forms_last(tmp_path, capsys):
target = tmp_path / "bericht.html"
comparison = ae.write_report(
"das sagte der Professor", "dass sagte der Professorin", target, "rede"
)
assert [s.reason for s in comparison.suspects] == [ae.WORD_FORM, ae.VALID_WORD]
page = target.read_text(encoding="utf-8")
section = suspect_section(page)
# Meaning change in the main table, the word form in its own group after it
assert section.index("Professorin") < section.index("Wortform geändert (1)")
assert section.index("Wortform geändert (1)") < section.index("<ins>dass</ins>")
assert "<span class='warn form' title='Wortform geändert: „das“ → „dass“'>" in page
ae.print_summary(comparison)
out = capsys.readouterr().out.split("Zu prüfen: ")[1]
assert out.startswith("2 Stellen (davon 1 Wortformen)")
assert out.index("Professorin") < out.index("„das“ → „dass“")