diff --git a/.gitignore b/.gitignore index 9263db3..0e9e459 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ __pycache__/ # Persönliche Claude-Code-Einstellungen .claude/settings.local.json + +# Eigene Ergebnisse und Testläufe +output/ diff --git a/aenderungen.py b/aenderungen.py index 7c16a0f..3faf12f 100755 --- a/aenderungen.py +++ b/aenderungen.py @@ -11,9 +11,12 @@ import re from vtt2txt import vtt_to_lines -REVIEW_MARK = re.compile(r"\[prüfen:[^\]]*\]") -TOKEN = re.compile(r"\[prüfen:[^\]]*\]|\S+") +# "[prüfen: …]" in German speeches, "[check: …]" in English ones +REVIEW_MARK = re.compile(r"\[(?:prüfen|check):[^\]]*\]") +TOKEN = re.compile(rf"{REVIEW_MARK.pattern}|\S+") NON_WORD = re.compile(r"[^\w]+") +NON_WORD_KEEP_APOSTROPHE = re.compile(r"[^\w']+") +APOSTROPHES = str.maketrans("’‘`´", "''''") ABBREVIATION = re.compile(r"\w\.") # one part of "z. B.", "d. h." ABBREVIATION_END = re.compile(r"(?:^|\s)\w\.$") EMPTY = "∅" @@ -32,8 +35,13 @@ def add_token(words, token): def normalize(word): - """Compare words without case and punctuation; those changes are expected.""" - return NON_WORD.sub("", word.lower()) or word + """Compare words without case and punctuation; those changes are expected. + + Apostrophes inside a word count ("Bahr’s" is not "Bahrs"), quotes at its + edges do not. + """ + word = word.lower().translate(APOSTROPHES) + return NON_WORD_KEEP_APOSTROPHE.sub("", word).strip("'") or word @dataclass diff --git a/tests/test_aenderungen.py b/tests/test_aenderungen.py index ba01aaf..2dcd7d3 100644 --- a/tests/test_aenderungen.py +++ b/tests/test_aenderungen.py @@ -31,6 +31,26 @@ def test_expected_edits_are_no_changes(raw, corrected): assert changes(raw, corrected) == [] +@pytest.mark.parametrize("raw, corrected", [ + ("gibt's", "gibt’s"), # apostrophe variants + ("er sagte Frieden", "Er sagte ‚Frieden‘."), # quotes at the edges +]) +def test_apostrophes_and_quotes_unchanged(raw, corrected): + assert changes(raw, corrected) == [] + + +def test_apostrophe_inside_word_is_a_change(): + assert changes("Egon Bahrs Politik", "Egon Bahr’s Politik") == [ + ("replace", "Bahrs", "Bahr’s"), + ] + + +def test_english_review_marks(): + comparison = ae.compare("his name is Kocher", "His name is Kocher [check: Kocha].") + assert list(comparison.changes()) == [] + assert sum(len(m) for m in comparison.marks.values()) == 1 + + def test_replace_insert_delete(): raw = "die gedeiliche Zusammenarbeit war das gut heute" corrected = "die gedeihliche Zusammenarbeit war doch gut"