test: eigene Beispielrede statt echter Reden, testdaten/ nur noch lokal
Die Abschriften echter Reden sind urheberrechtlich geschützt und gehören nicht in ein öffentliches Repository. Die Tests nutzen jetzt eine frei erfundene Festrede (tests/daten/beispiel.de.vtt/.txt, YouTube-Format mit rollendem Fenster) mit eigenem Glossar. testdaten/ steht in .gitignore; liegen die Reden dort lokal, laufen die Tests ihrer Glossare zusätzlich, sonst werden sie übersprungen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
0106c307e7
commit
93fb99eda2
7 changed files with 3134 additions and 62 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -12,3 +12,6 @@ __pycache__/
|
|||
|
||||
# Eigene Ergebnisse und Testläufe
|
||||
output/
|
||||
|
||||
# Echte Reden (Urheberrecht): nur lokal; die Tests nutzen tests/daten/
|
||||
testdaten/
|
||||
|
|
|
|||
38
README.md
38
README.md
|
|
@ -415,24 +415,30 @@ werden in `<Entwurf>.parts/` zwischengespeichert.
|
|||
|
||||
## Testdaten
|
||||
|
||||
`testdaten/` enthält drei Reden, jeweils als automatische YouTube-Untertitel
|
||||
(`.vtt`, Original) und daraus mit `vtt2txt.py` gewonnener Rohtext (`.txt`, Eingabe
|
||||
für das Programm):
|
||||
**Im Repository:** `tests/daten/` enthält eine frei erfundene Beispielrede
|
||||
(Festrede zum Jubiläum der Gemeinde „Auental“; Ort und Personen gibt es nicht), als
|
||||
Untertitel im Format der YouTube-Auto-Untertitel (`beispiel.de.vtt`, rollendes
|
||||
Fenster, Wort-Zeitstempel), daraus gewonnener Rohtext (`beispiel.de.txt`) und ein
|
||||
passendes Glossar (`beispiel.glossar.txt`) für die eingebauten Hörfehler wie
|
||||
„Auen Tal“ oder „Haber Kamp“. Die Tests nutzen nur diese Daten.
|
||||
|
||||
```bash
|
||||
./abschrift_korrigieren.py -i tests/daten/beispiel.de.txt -g tests/daten/beispiel.glossar.txt -o /tmp/beispiel.korrigiert.txt
|
||||
```
|
||||
|
||||
**Nur lokal:** Echte Reden gehören wegen des Urheberrechts nicht ins Repository;
|
||||
`testdaten/` ist deshalb in `.gitignore`. Liegen dort die drei Reden, mit denen das
|
||||
Programm entwickelt wurde, laufen zusätzlich die Tests, die ihre Glossare prüfen;
|
||||
sonst werden diese übersprungen. Die Glossare dazu sind im Repository:
|
||||
|
||||
- `ramstein.de.*`: Rede über die US Air Base Ramstein (Glossar `ramstein.glossar.txt`)
|
||||
- `magdeburg.de-orig.*`: Rede in Magdeburg (Glossar `magdeburg.glossar.txt`)
|
||||
- `Lahnstein.de-orig.*`: Rede „Respekt geht anders“ in Lahnstein (Glossar
|
||||
`lahnstein.glossar.txt`, erstellt mit Hilfe von `glossar_vorschlag.py`)
|
||||
`lahnstein.glossar.txt`, erstellt mit Hilfe von `glossar_vorschlag.py`), zu laden mit
|
||||
`./yt_untertitel.py -u 'https://www.youtube.com/watch?v=rm6lADgf6GQ' -n Lahnstein -d testdaten`
|
||||
|
||||
Alle drei mit dem gemeinsamen `krone-schmalz.glossar.txt` davor.
|
||||
|
||||
```bash
|
||||
./vtt2txt.py -i "testdaten/*.vtt" # erzeugt alle .txt neu
|
||||
./abschrift_korrigieren.py -i testdaten/ramstein.de.txt -g krone-schmalz.glossar.txt -g ramstein.glossar.txt
|
||||
# → testdaten/ramstein.de.korrigiert.txt (nicht versioniert)
|
||||
```
|
||||
|
||||
Ein Lauf dauert mit `qwen3:32b` vollständig auf einer RTX 3090 etwa 6 Minuten.
|
||||
Alle drei mit dem gemeinsamen `krone-schmalz.glossar.txt` davor. Ein Lauf über eine
|
||||
Rede dauert mit `qwen3:32b` vollständig auf einer RTX 3090 etwa 6–8 Minuten.
|
||||
|
||||
## Tests
|
||||
|
||||
|
|
@ -440,7 +446,7 @@ Ein Lauf dauert mit `qwen3:32b` vollständig auf einer RTX 3090 etwa 6 Minuten.
|
|||
python3 -m pytest
|
||||
```
|
||||
|
||||
Die Tests (323) brauchen weder Ollama noch Netz: Modellaufrufe und `yt-dlp` werden
|
||||
Die Tests (325, ohne die lokalen Reden 318) brauchen weder Ollama noch Netz: Modellaufrufe und `yt-dlp` werden
|
||||
durch Fakes ersetzt. `tests/test_abschrift_korrigieren.py` prüft Abschnittsbildung,
|
||||
Satzgrenzen (Abkürzungen und Ordnungszahlen sind kein Satzende), Glossar (inkl.
|
||||
„jede Ersetzung im Glossar einer Rede trifft diese Rede, jede im gemeinsamen Glossar
|
||||
|
|
@ -458,8 +464,8 @@ Die Tests nutzen eine kleine eigene Wortliste statt der des Systems.
|
|||
(Original-Audio, manuell vor automatisch) und Fehlerfälle.
|
||||
`tests/test_yt_untertitel.py` prüft Spurauswahl, Aufruf von `yt-dlp` und
|
||||
Fehlerfälle (ohne Netz, `yt-dlp` wird durch Fakes ersetzt).
|
||||
`tests/test_vtt2txt.py` prüft die VTT-Umwandlung: jede `.vtt` in `testdaten/`
|
||||
ergibt genau die zugehörige `.txt`, echte Wiederholungen bleiben erhalten, auch bei
|
||||
`tests/test_vtt2txt.py` prüft die VTT-Umwandlung: die Beispiel-`.vtt` (und lokal
|
||||
jede Rede in `testdaten/`) ergibt genau die zugehörige `.txt`, echte Wiederholungen bleiben erhalten, auch bei
|
||||
Untertiteln ohne rollendes Fenster, dazu Zeitauswahl, Muster, Ausgabeverzeichnis und
|
||||
Fehlerfälle.
|
||||
|
||||
|
|
|
|||
335
tests/daten/beispiel.de.txt
Normal file
335
tests/daten/beispiel.de.txt
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
[applaus]
|
||||
Vielen Dank. Liebe Gäste, liebe
|
||||
Bürgerinnen und Bürger von Auen Tal, ich
|
||||
freue mich sehr, dass Sie heute
|
||||
so zahlreich gekommen sind. Wir feiern ein besonderes
|
||||
Jubiläum. Vor 750 Jahren wurde unser
|
||||
Ort zum ersten Mal
|
||||
in einer Urkunde erwähnt. Das war im
|
||||
Jahr 12 74. Damals war Auen
|
||||
Tal ein kleiner Weiler mit
|
||||
drei Höfen und einer Mühle am Bach.
|
||||
Heute leben hier fast viertausend Menschen.
|
||||
[applaus]
|
||||
Ich möchte Ihnen heute keine lange Geschichtsstunde halten.
|
||||
Dafür haben wir Frau Dr. Jorinde
|
||||
Haber Kamp vom Kreisarchiv
|
||||
eingeladen, die gleich noch zu Ihnen sprechen
|
||||
wird. Ich möchte lieber erzählen, was
|
||||
dieser Ort für uns bedeutet.
|
||||
Und ich möchte erzählen, was wir in
|
||||
den nächsten Jahren vorhaben.
|
||||
Fangen wir mit der Mühle an. Die alte
|
||||
Mühle am Linden Bach steht noch
|
||||
heute. Sie hat viele
|
||||
Besitzer gehabt. Sie ist zweimal abgebrannt und
|
||||
zweimal wieder aufgebaut worden. Seit dem
|
||||
3. Oktober 1990 gehört sie
|
||||
der Gemeinde. Viele von Ihnen kennen sie
|
||||
als Heimatmuseum. Manche kennen sie auch
|
||||
als den Ort, an dem sie ihren ersten
|
||||
Kuss bekommen haben. Ich nenne keine
|
||||
Namen.
|
||||
[lachen]
|
||||
Die Mühle ist ein gutes Beispiel für
|
||||
das, was Auen Tal ausmacht. Wir
|
||||
bauen wieder auf, wenn etwas
|
||||
kaputtgeht. Wir geben nicht auf. Und wir
|
||||
machen das gemeinsam. Das klingt vielleicht
|
||||
nach einer Floskel. Aber ich habe es selbst
|
||||
erlebt. Als vor zwölf Jahren das
|
||||
Hochwasser kam, standen am
|
||||
nächsten Morgen über zweihundert Menschen mit Schaufeln
|
||||
und Eimern vor dem Rathaus. Niemand
|
||||
hatte sie gerufen. Sie waren
|
||||
einfach da.
|
||||
Ich erinnere mich an eine ältere
|
||||
Dame aus der Bachstraße. Ihr Keller stand bis
|
||||
zur Decke unter Wasser. Sie hat
|
||||
trotzdem für alle Helfer
|
||||
Kaffee gekocht. Sie hat gesagt, das Wasser
|
||||
geht wieder, aber die Nachbarn bleiben.
|
||||
Diesen Satz habe ich mir
|
||||
gemerkt. Er hängt heute als Spruch in
|
||||
meinem Büro.
|
||||
Liebe Gäste, ein Jubiläum ist ein guter Anlass,
|
||||
um zurückzuschauen. Es ist aber auch
|
||||
ein guter Anlass, um
|
||||
nach vorn zu schauen. Und da gibt
|
||||
es einiges zu tun. Ich will
|
||||
Ihnen drei Dinge nennen, die
|
||||
uns in den nächsten Jahren beschäftigen werden.
|
||||
Das erste ist die Schule. Unsere
|
||||
Grundschule ist über hundert Jahre alt. Das Gebäude
|
||||
ist schön, aber es ist zu
|
||||
klein geworden. Wir haben
|
||||
im letzten Jahr so viele Kinder eingeschult
|
||||
wie seit Jahrzehnten nicht mehr. Das
|
||||
ist eine gute Nachricht. Es
|
||||
bedeutet aber auch, dass wir anbauen müssen.
|
||||
Der Gemeinderat hat den Anbau im
|
||||
Frühjahr beschlossen. Die Bauarbeiten beginnen im nächsten Sommer.
|
||||
Wenn alles gut geht, ziehen die
|
||||
Kinder in zwei Jahren
|
||||
in die neuen Räume.
|
||||
Das zweite ist der Verkehr. Viele
|
||||
von Ihnen ärgern sich über
|
||||
die Lastwagen, die durch die Hauptstraße fahren.
|
||||
Ich ärgere mich auch. Seit Jahren
|
||||
reden wir über eine Umgehungsstraße. Seit Jahren passiert
|
||||
wenig. Ich will Ihnen nichts versprechen,
|
||||
was ich nicht halten
|
||||
kann. Aber ich kann Ihnen sagen, dass
|
||||
das Land die Planung jetzt endlich
|
||||
aufgenommen hat. Das ist ein
|
||||
erster Schritt. Es werden noch viele Schritte
|
||||
folgen müssen.
|
||||
Das dritte ist die Nahversorgung. Seit der Bäcker
|
||||
an der Kirche geschlossen hat, gibt
|
||||
es im Ortskern kein
|
||||
Geschäft mehr. Wer kein Auto hat, muss
|
||||
mit dem Bus nach Linden Brunn
|
||||
fahren. Das ist für viele
|
||||
ältere Menschen ein Problem. Wir haben deshalb
|
||||
mit einer Genossenschaft gesprochen, die Dorfläden
|
||||
betreibt. Die Gespräche sind gut gelaufen. Ich hoffe,
|
||||
dass wir Ihnen im Herbst mehr
|
||||
dazu sagen können.
|
||||
[applaus]
|
||||
Ich weiß, dass das alles nicht spektakulär
|
||||
klingt. Eine Schule, eine Straße, ein
|
||||
Laden. Aber genau darum geht
|
||||
es in einer Gemeinde wie unserer. Es
|
||||
geht nicht um große Worte. Es
|
||||
geht darum, dass das Leben hier gut funktioniert.
|
||||
Für die Kinder, für die Familien,
|
||||
für die Älteren.
|
||||
Lassen Sie mich noch etwas zum Ehrenamt
|
||||
sagen. Ohne die Vereine wäre Auen
|
||||
Tal ein anderer Ort. Wir
|
||||
haben einen Sportverein, einen Gesangverein, eine freiwillige
|
||||
Feuerwehr, einen Heimatverein und einen Kleintierzuchtverein.
|
||||
Wir haben eine Theatergruppe, die jedes Jahr im
|
||||
Winter ein Stück aufführt. Und wir
|
||||
haben seit drei Jahren
|
||||
ein Repaircafé, in dem Menschen ihre kaputten
|
||||
Toaster und Fahrräder reparieren lassen können.
|
||||
Das alles funktioniert nur, weil
|
||||
Menschen ihre freie Zeit hergeben. Sie tun
|
||||
das ohne Bezahlung und oft ohne
|
||||
Dank. Deshalb möchte ich heute ausdrücklich Danke sagen.
|
||||
Danke an alle, die sich engagieren.
|
||||
Sie sind das Herz
|
||||
dieser Gemeinde.
|
||||
[applaus]
|
||||
Ein besonderer Dank geht an die
|
||||
Freiwillige Feuerwehr. Sie feiert in
|
||||
diesem Jahr ebenfalls ein Jubiläum, nämlich ihr
|
||||
hundertfünfzigjähriges. Der Wehrführer Bernd Ols Hausen
|
||||
hat mir erzählt, dass die erste Spritze noch
|
||||
von Pferden gezogen wurde. Heute hat
|
||||
die Wehr zwei moderne
|
||||
Fahrzeuge. Aber eines ist gleich geblieben. Wenn
|
||||
die Sirene geht, lassen die Frauen
|
||||
und Männer alles stehen und
|
||||
liegen und rücken aus. Das verdient unseren
|
||||
Respekt.
|
||||
Liebe Gäste, wir haben für das Jubiläumsjahr ein
|
||||
Programm vorbereitet, das sich über den
|
||||
ganzen Sommer zieht. Ich
|
||||
will Ihnen nur einige Höhepunkte nennen. Im
|
||||
Juni gibt es ein Mühlenfest mit
|
||||
Führungen und einem Markt. Im
|
||||
Juli zeigt die Theatergruppe ein Stück über
|
||||
die Geschichte des Ortes. Im August
|
||||
feiern wir ein Dorffest auf dem Anger mit
|
||||
Musik und Tanz. Und im September
|
||||
gibt es eine Ausstellung
|
||||
im Rathaus mit alten Fotos aus Auen
|
||||
Tal.
|
||||
Für diese Ausstellung suchen wir
|
||||
übrigens noch Bilder. Wenn Sie zu Hause
|
||||
alte Fotos haben, z. B. von
|
||||
Hochzeiten, von Festen oder einfach vom Alltag, dann
|
||||
bringen Sie sie bitte ins Rathaus.
|
||||
Wir scannen sie und
|
||||
geben sie Ihnen sofort zurück. Frau Haber
|
||||
Kamp hat mir versichert, dass solche
|
||||
Bilder für das Archiv ein
|
||||
großer Schatz sind.
|
||||
Ich möchte auch etwas zu unseren
|
||||
Partnern sagen. Seit über dreißig Jahren haben wir
|
||||
eine Partnerschaft mit der Gemeinde Saint
|
||||
Maur in Frankreich. Eine
|
||||
Delegation aus Saint Maur ist heute hier.
|
||||
Bienvenue, liebe Freunde. Wir freuen uns
|
||||
sehr, dass Sie die weite
|
||||
Reise auf sich genommen haben.
|
||||
[applaus]
|
||||
Die Partnerschaft hat mit einem Schüleraustausch
|
||||
angefangen. Inzwischen gibt es Besuche der Vereine, der
|
||||
Feuerwehren und der Chöre. Es gibt
|
||||
sogar zwei Ehen, die
|
||||
aus dieser Partnerschaft entstanden sind. Auch hier
|
||||
nenne ich keine Namen.
|
||||
Meine Damen und Herren, ich
|
||||
habe vorhin gesagt, dass wir wieder aufbauen,
|
||||
wenn etwas kaputtgeht. Das gilt nicht
|
||||
nur für Gebäude. Es gilt auch für das
|
||||
Miteinander. Auch bei uns gibt es
|
||||
Streit. Es gibt Streit
|
||||
über den Windpark, über die Parkplätze am
|
||||
Friedhof und über die Frage, ob
|
||||
der Weihnachtsbaum auf dem Marktplatz
|
||||
echt sein muss oder aus Plastik sein
|
||||
darf.
|
||||
[lachen]
|
||||
Streit gehört dazu. Aber wir sollten uns dabei
|
||||
nicht aus den Augen verlieren. Wir
|
||||
sollten miteinander reden und
|
||||
nicht übereinander. Wir sollten zuhören, auch wenn
|
||||
es schwerfällt. Das ist nicht immer
|
||||
einfach. Ich schaffe das auch
|
||||
nicht immer. Aber ich bemühe mich.
|
||||
Lassen Sie mich zum Schluss noch
|
||||
eine kleine Geschichte erzählen. Als ich vor sechs
|
||||
Jahren zur Bürgermeisterin gewählt wurde, hat
|
||||
mir mein Vorgänger einen
|
||||
Schlüssel gegeben. Es war kein Schlüssel für
|
||||
das Rathaus. Es war der Schlüssel
|
||||
für den Kirchturm. Er hat
|
||||
gesagt, wenn du einmal nicht weiterweißt, dann
|
||||
steig da hoch und schau dir
|
||||
den Ort von oben an.
|
||||
Ich habe das ein paar Mal
|
||||
gemacht. Von oben sieht
|
||||
man die Dächer, die Gärten, den Bach
|
||||
und die Mühle. Man sieht die
|
||||
Felder und den Wald. Man
|
||||
sieht, wie klein der Ort ist und
|
||||
wie viel trotzdem darin steckt. Und
|
||||
man merkt, dass die meisten Probleme von dort
|
||||
oben ein bisschen kleiner aussehen.
|
||||
Ich möchte, dass dieser
|
||||
Ort auch in fünfzig Jahren noch ein
|
||||
Ort ist, an dem man gern
|
||||
lebt. Ein Ort, an dem
|
||||
Kinder zur Schule laufen können. Ein Ort,
|
||||
an dem ältere Menschen nicht allein
|
||||
sind. Ein Ort, an dem man sich kennt
|
||||
und sich hilft. Daran arbeiten wir.
|
||||
Und dafür brauchen wir
|
||||
Sie alle.
|
||||
Bevor ich jetzt das Wort an
|
||||
Frau Dr. Haber Kamp übergebe,
|
||||
möchte ich noch einigen Menschen danken. Ich
|
||||
danke dem Festausschuss, der seit über
|
||||
einem Jahr dieses Jubiläum vorbereitet. Ich danke dem
|
||||
Bauhof, der den Platz so schön
|
||||
hergerichtet hat. Ich danke
|
||||
dem Gesangverein für die Musik. Und ich
|
||||
danke den Landfrauen für den Kuchen,
|
||||
den Sie nachher alle probieren
|
||||
sollten.
|
||||
[lachen]
|
||||
Ich wünsche uns allen ein schönes
|
||||
Fest und ein gutes Jubiläumsjahr. Auf die nächsten
|
||||
750 Jahre. Vielen Dank.
|
||||
[applaus]
|
||||
Ja, vielen Dank, Frau
|
||||
Bürgermeisterin. Liebe Gäste, ich bin Jorinde Haber
|
||||
Kamp und leite das Kreisarchiv. Ich
|
||||
darf Ihnen heute ein wenig
|
||||
über die Geschichte von Auen Tal erzählen.
|
||||
Keine Sorge, ich halte mich kurz.
|
||||
Die Urkunde aus dem Jahr 12 74 liegt
|
||||
bei uns im Archiv. Sie ist
|
||||
auf Pergament geschrieben, in
|
||||
lateinischer Sprache, und sie ist erstaunlich gut
|
||||
erhalten. Es geht darin um einen
|
||||
Streit über Wasserrechte. Zwei Klöster
|
||||
stritten sich darüber, wer die Mühle am
|
||||
Bach nutzen darf. Der Ort selbst
|
||||
wird nur nebenbei erwähnt. Aber er wird erwähnt,
|
||||
und deshalb feiern wir heute.
|
||||
Was wissen wir über
|
||||
die Menschen, die damals hier gelebt haben?
|
||||
Leider nicht viel. Sie konnten nicht
|
||||
schreiben, und so haben sie
|
||||
uns keine Briefe oder Tagebücher hinterlassen. Was
|
||||
wir wissen, stammt aus Abgaberegistern, aus
|
||||
Gerichtsakten und aus Kirchenbüchern. Daraus können wir aber
|
||||
erstaunlich viel ablesen.
|
||||
Wir wissen z. B.,
|
||||
dass im Jahr 1580 in Auen Tal
|
||||
elf Familien lebten. Wir wissen, dass
|
||||
sie vor allem Roggen und
|
||||
Hafer anbauten. Wir wissen, dass es im
|
||||
Jahr 1636 eine schwere Pest gab,
|
||||
an der fast die Hälfte der Menschen starb.
|
||||
Und wir wissen, dass der Ort
|
||||
sich danach nur langsam
|
||||
erholt hat.
|
||||
Ein besonderer Fund ist ein Gerichtsprotokoll
|
||||
aus dem Jahr 1712. Darin
|
||||
klagt ein Müller namens Kaspar Ols Hausen
|
||||
gegen seinen Nachbarn, weil dessen Ziegen
|
||||
immer wieder in seinen Garten eingedrungen seien. Das
|
||||
Gericht gab dem Müller recht. Der
|
||||
Nachbar musste einen Zaun
|
||||
bauen und zwei Hühner als Entschädigung zahlen.
|
||||
[lachen]
|
||||
Ich erzähle Ihnen das nicht nur,
|
||||
weil es lustig ist. Ich
|
||||
erzähle es, weil es zeigt, dass die
|
||||
Menschen damals ganz ähnliche Sorgen hatten
|
||||
wie wir heute. Es ging um den Garten,
|
||||
um den Nachbarn, um ein bisschen
|
||||
Gerechtigkeit. Der Name Ols
|
||||
Hausen ist übrigens bis heute im Ort
|
||||
zu finden. Vielleicht ist der Wehrführer
|
||||
ja ein Nachfahre des Müllers.
|
||||
Im neunzehnten Jahrhundert hat sich Auen Tal
|
||||
stark verändert. Im Jahr 1868 wurde
|
||||
die Bahnstrecke nach Linden Brunn gebaut. Plötzlich konnten
|
||||
die Bauern ihre Waren in die
|
||||
Stadt bringen. Es entstanden
|
||||
eine Ziegelei und eine kleine Weberei. Die
|
||||
Einwohnerzahl hat sich in wenigen Jahrzehnten
|
||||
verdoppelt.
|
||||
Das zwanzigste Jahrhundert war auch hier eine
|
||||
Zeit der Brüche. Zwei Weltkriege haben
|
||||
viele Familien getroffen. Auf dem Friedhof erinnert ein
|
||||
Denkmal an die Gefallenen. Nach dem
|
||||
2. Weltkrieg kamen viele
|
||||
Vertriebene in den Ort. Sie mussten untergebracht
|
||||
werden, oft in sehr engen Verhältnissen.
|
||||
Für die Alteingesessenen war das
|
||||
nicht leicht, und für die Neuen noch
|
||||
weniger.
|
||||
Aber auch hier hat sich gezeigt, was die
|
||||
Bürgermeisterin vorhin beschrieben hat. Mit der
|
||||
Zeit sind die Neuen
|
||||
zu Nachbarn geworden. Sie haben Häuser gebaut,
|
||||
Vereine gegründet und Familien gegründet. Heute
|
||||
weiß kaum noch jemand, wessen
|
||||
Großeltern damals gekommen sind und wessen Großeltern
|
||||
schon immer hier waren.
|
||||
Liebe Gäste, Archive sind keine staubigen Orte. Sie
|
||||
sind das Gedächtnis einer Gemeinschaft. Und
|
||||
dieses Gedächtnis lebt davon,
|
||||
dass Menschen etwas hineingeben. Deshalb unterstütze ich
|
||||
den Aufruf der Bürgermeisterin ausdrücklich. Bringen
|
||||
Sie Ihre Fotos, Ihre Briefe,
|
||||
Ihre Erinnerungen. Was heute alltäglich erscheint, ist
|
||||
in hundert Jahren ein wertvolles Zeugnis.
|
||||
Ich habe Ihnen eine kleine Auswahl mitgebracht. Im
|
||||
Foyer des Rathauses hängen ab heute
|
||||
Kopien der Urkunde und
|
||||
einiger anderer Dokumente. Schauen Sie sie sich
|
||||
gern an. Und wenn Sie Fragen
|
||||
haben, sprechen Sie mich an.
|
||||
Ich bin den ganzen Nachmittag hier.
|
||||
Ich wünsche Ihnen ein schönes Fest.
|
||||
Vielen Dank für Ihre Aufmerksamkeit.
|
||||
[applaus]
|
||||
2683
tests/daten/beispiel.de.vtt
Normal file
2683
tests/daten/beispiel.de.vtt
Normal file
File diff suppressed because it is too large
Load diff
14
tests/daten/beispiel.glossar.txt
Normal file
14
tests/daten/beispiel.glossar.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Glossar für die Beispielrede tests/daten/beispiel.de.* (frei erfunden,
|
||||
# Gemeinde und Personen gibt es nicht). Jede Ersetzung trifft die Rede.
|
||||
|
||||
# --- Feste Ersetzungen ---
|
||||
Auen Tal => Auental
|
||||
Haber Kamp => Haberkamp
|
||||
Linden Brunn => Lindenbrunn
|
||||
Linden Bach => Lindenbach
|
||||
Ols Hausen => Olshausen
|
||||
12 74 => 1274
|
||||
|
||||
# --- Begriffe ---
|
||||
Saint-Maur
|
||||
Jorinde Haberkamp
|
||||
|
|
@ -11,9 +11,11 @@ import pytest
|
|||
import abschrift_korrigieren as ak
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
SAMPLE = REPO / "testdaten" / "ramstein.de.txt"
|
||||
GLOSSARY = REPO / "ramstein.glossar.txt"
|
||||
# Own, made-up sample speech; real speeches are only kept locally in testdaten/
|
||||
SAMPLE = REPO / "tests" / "daten" / "beispiel.de.txt"
|
||||
GLOSSARY = REPO / "tests" / "daten" / "beispiel.glossar.txt"
|
||||
SHARED_GLOSSARY = REPO / "krone-schmalz.glossar.txt"
|
||||
SPEECHES = REPO / "testdaten"
|
||||
# The autouse fixture below replaces it; tests of the real function use this
|
||||
REAL_FIND_MERGE_SIDE = ak.find_merge_side
|
||||
|
||||
|
|
@ -130,19 +132,34 @@ SAMPLE_GLOSSARIES = [
|
|||
]
|
||||
|
||||
|
||||
# The real speeches are not in the repository (copyright); without them these skip
|
||||
needs_speeches = pytest.mark.skipif(
|
||||
not all((SPEECHES / sample).is_file() for _, sample in SAMPLE_GLOSSARIES),
|
||||
reason="echte Reden nur lokal in testdaten/",
|
||||
)
|
||||
|
||||
|
||||
def test_own_sample_glossary_replacements_all_match(capsys):
|
||||
text = flat(SAMPLE.read_text(encoding="utf-8"))
|
||||
ak.apply_replacements(text, ak.read_glossary(GLOSSARY)[1])
|
||||
assert "ohne Treffer" not in capsys.readouterr().out
|
||||
|
||||
|
||||
@needs_speeches
|
||||
@pytest.mark.parametrize("glossary, sample", SAMPLE_GLOSSARIES)
|
||||
def test_sample_glossary_replacements_all_match(capsys, glossary, sample):
|
||||
"""Every rule in a speech glossary must hit its speech, after the shared one."""
|
||||
text = flat((REPO / "testdaten" / sample).read_text(encoding="utf-8"))
|
||||
text = flat((SPEECHES / sample).read_text(encoding="utf-8"))
|
||||
text = ak.apply_replacements(text, ak.read_glossary(SHARED_GLOSSARY)[1])
|
||||
capsys.readouterr()
|
||||
ak.apply_replacements(text, ak.read_glossary(REPO / glossary)[1])
|
||||
assert "ohne Treffer" not in capsys.readouterr().out
|
||||
|
||||
|
||||
@needs_speeches
|
||||
def test_shared_glossary_rules_match_some_speech():
|
||||
texts = [
|
||||
flat((REPO / "testdaten" / sample).read_text(encoding="utf-8"))
|
||||
flat((SPEECHES / sample).read_text(encoding="utf-8"))
|
||||
for _, sample in SAMPLE_GLOSSARIES
|
||||
]
|
||||
for wrong, right in ak.read_glossary(SHARED_GLOSSARY)[1]:
|
||||
|
|
@ -392,7 +409,7 @@ def test_build_paragraphs_asks_where_short_paragraphs_belong(tmp_path, monkeypat
|
|||
|
||||
@pytest.fixture
|
||||
def sample_copy(tmp_path):
|
||||
path = tmp_path / "ramstein.de.txt"
|
||||
path = tmp_path / "beispiel.de.txt"
|
||||
shutil.copy(SAMPLE, path)
|
||||
return path
|
||||
|
||||
|
|
@ -400,19 +417,19 @@ def sample_copy(tmp_path):
|
|||
def test_main_default_output_resume_and_glossary(sample_copy, monkeypatch, capsys):
|
||||
monkeypatch.setattr(ak, "correct_chunk", identity_correct)
|
||||
monkeypatch.setattr(ak, "find_paragraph_starts", every_nth_start(5))
|
||||
run_main(monkeypatch, "-i", sample_copy, "-g", SHARED_GLOSSARY, "-g", GLOSSARY)
|
||||
run_main(monkeypatch, "-i", sample_copy, "-g", GLOSSARY)
|
||||
|
||||
target = sample_copy.with_name("ramstein.de.korrigiert.txt")
|
||||
target = sample_copy.with_name("beispiel.de.korrigiert.txt")
|
||||
result = target.read_text(encoding="utf-8")
|
||||
assert "Ramstein" in result and "Rammstein" not in result
|
||||
assert "Kreml" in result and "1983" in result
|
||||
assert (sample_copy.parent / "ramstein.de.korrigiert.parts").is_dir()
|
||||
assert "Auental" in result and "Auen Tal" not in result
|
||||
assert "Haberkamp" in result and "1274" in result
|
||||
assert (sample_copy.parent / "beispiel.de.korrigiert.parts").is_dir()
|
||||
assert "WARNUNG" not in capsys.readouterr().out
|
||||
|
||||
# Second run: everything cached, no model calls
|
||||
monkeypatch.setattr(ak, "correct_chunk", no_model_calls)
|
||||
monkeypatch.setattr(ak, "find_paragraph_starts", no_model_calls)
|
||||
run_main(monkeypatch, "-i", sample_copy, "-g", SHARED_GLOSSARY, "-g", GLOSSARY)
|
||||
run_main(monkeypatch, "-i", sample_copy, "-g", GLOSSARY)
|
||||
assert target.read_text(encoding="utf-8") == result
|
||||
|
||||
|
||||
|
|
@ -425,7 +442,7 @@ def test_main_overwrites_own_output(sample_copy, monkeypatch, capsys):
|
|||
monkeypatch.setattr(ak, "correct_chunk", identity_correct)
|
||||
monkeypatch.setattr(ak, "find_paragraph_starts", every_nth_start(5))
|
||||
run_main(monkeypatch, "-i", sample_copy)
|
||||
target = sample_copy.with_name("ramstein.de.korrigiert.txt")
|
||||
target = sample_copy.with_name("beispiel.de.korrigiert.txt")
|
||||
|
||||
# Unchanged output of the last run is replaced by a different result
|
||||
monkeypatch.setattr(ak, "correct_chunk", shouting)
|
||||
|
|
@ -439,7 +456,7 @@ def test_main_keeps_hand_edited_output(sample_copy, monkeypatch, capsys):
|
|||
monkeypatch.setattr(ak, "correct_chunk", identity_correct)
|
||||
monkeypatch.setattr(ak, "find_paragraph_starts", every_nth_start(5))
|
||||
run_main(monkeypatch, "-i", sample_copy)
|
||||
target = sample_copy.with_name("ramstein.de.korrigiert.txt")
|
||||
target = sample_copy.with_name("beispiel.de.korrigiert.txt")
|
||||
edited = target.read_text(encoding="utf-8") + "Von Hand ergänzt.\n"
|
||||
target.write_text(edited, encoding="utf-8")
|
||||
capsys.readouterr()
|
||||
|
|
@ -447,10 +464,10 @@ def test_main_keeps_hand_edited_output(sample_copy, monkeypatch, capsys):
|
|||
monkeypatch.setattr(ak, "correct_chunk", shouting)
|
||||
run_main(monkeypatch, "-i", sample_copy, "-m", "anderes:modell")
|
||||
|
||||
new = sample_copy.with_name("ramstein.de.neu.korrigiert.txt")
|
||||
new = sample_copy.with_name("beispiel.de.neu.korrigiert.txt")
|
||||
assert target.read_text(encoding="utf-8") == edited
|
||||
assert "VIELEN DANK" in new.read_text(encoding="utf-8")
|
||||
assert sample_copy.with_name("ramstein.de.neu.aenderungen.html").is_file()
|
||||
assert sample_copy.with_name("beispiel.de.neu.aenderungen.html").is_file()
|
||||
out = capsys.readouterr().out
|
||||
assert f"WARNUNG: {target} wurde seit dem letzten Lauf geändert" in out
|
||||
assert f"Fertig: {new}" in out
|
||||
|
|
@ -461,14 +478,14 @@ def test_main_keeps_hand_edited_output(sample_copy, monkeypatch, capsys):
|
|||
|
||||
|
||||
def test_main_keeps_differing_output_without_checksum(sample_copy, monkeypatch, capsys):
|
||||
target = sample_copy.with_name("ramstein.de.korrigiert.txt")
|
||||
target = sample_copy.with_name("beispiel.de.korrigiert.txt")
|
||||
target.write_text("Älteres Ergebnis, Herkunft unbekannt.\n", encoding="utf-8")
|
||||
monkeypatch.setattr(ak, "correct_chunk", identity_correct)
|
||||
monkeypatch.setattr(ak, "find_paragraph_starts", every_nth_start(5))
|
||||
run_main(monkeypatch, "-i", sample_copy)
|
||||
|
||||
assert target.read_text(encoding="utf-8") == "Älteres Ergebnis, Herkunft unbekannt.\n"
|
||||
assert sample_copy.with_name("ramstein.de.neu.korrigiert.txt").is_file()
|
||||
assert sample_copy.with_name("beispiel.de.neu.korrigiert.txt").is_file()
|
||||
assert "WARNUNG" in capsys.readouterr().out
|
||||
|
||||
|
||||
|
|
@ -498,36 +515,36 @@ def test_main_output_option_creates_directories(sample_copy, monkeypatch):
|
|||
def test_main_vtt_input_matches_txt_input(sample_copy, monkeypatch):
|
||||
monkeypatch.setattr(ak, "correct_chunk", identity_correct)
|
||||
monkeypatch.setattr(ak, "find_paragraph_starts", every_nth_start(5))
|
||||
vtt = sample_copy.parent / "vtt" / "ramstein.de.vtt"
|
||||
vtt = sample_copy.parent / "vtt" / "beispiel.de.vtt"
|
||||
vtt.parent.mkdir()
|
||||
shutil.copy(SAMPLE.with_suffix(".vtt"), vtt)
|
||||
|
||||
run_main(monkeypatch, "-i", sample_copy)
|
||||
run_main(monkeypatch, "-i", vtt)
|
||||
expected = sample_copy.with_name("ramstein.de.korrigiert.txt")
|
||||
assert vtt.with_name("ramstein.de.korrigiert.txt").read_text(
|
||||
expected = sample_copy.with_name("beispiel.de.korrigiert.txt")
|
||||
assert vtt.with_name("beispiel.de.korrigiert.txt").read_text(
|
||||
encoding="utf-8"
|
||||
) == expected.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_main_writes_change_report(sample_copy, monkeypatch, capsys):
|
||||
def correct(chunk, index, system):
|
||||
return chunk.strip().replace("Rammstein", "Ramstein")
|
||||
return chunk.strip().replace("Linden Brunn", "Lindenbrunn")
|
||||
|
||||
monkeypatch.setattr(ak, "correct_chunk", correct)
|
||||
monkeypatch.setattr(ak, "find_paragraph_starts", every_nth_start(5))
|
||||
run_main(monkeypatch, "-i", sample_copy)
|
||||
|
||||
report = sample_copy.with_name("ramstein.de.aenderungen.html")
|
||||
assert "title='Original: Rammstein" in report.read_text(encoding="utf-8")
|
||||
report = sample_copy.with_name("beispiel.de.aenderungen.html")
|
||||
assert "title='Original: Linden Brunn" in report.read_text(encoding="utf-8")
|
||||
out = capsys.readouterr().out
|
||||
assert "„Rammstein“ → „Ramstein“" in out
|
||||
assert "„Linden Brunn“ → „Lindenbrunn“" in out
|
||||
assert f"Bericht: {report}" in out
|
||||
|
||||
|
||||
def test_main_report_suspects_only_model_changes(sample_copy, monkeypatch, capsys):
|
||||
glossary = sample_copy.with_name("g.txt")
|
||||
glossary.write_text("Rammstein => Ramstein\n", encoding="utf-8")
|
||||
glossary.write_text("Linden Brunn => Lindenbrunn\n", encoding="utf-8")
|
||||
|
||||
def correct(chunk, index, system):
|
||||
return chunk.strip().replace(" das ", " dies ", 1)
|
||||
|
|
@ -536,9 +553,9 @@ def test_main_report_suspects_only_model_changes(sample_copy, monkeypatch, capsy
|
|||
monkeypatch.setattr(ak, "find_paragraph_starts", every_nth_start(5))
|
||||
run_main(monkeypatch, "-i", sample_copy, "-g", glossary)
|
||||
|
||||
page = sample_copy.with_name("ramstein.de.aenderungen.html").read_text(encoding="utf-8")
|
||||
page = sample_copy.with_name("beispiel.de.aenderungen.html").read_text(encoding="utf-8")
|
||||
section = page.split("<h2>Zu prüfen")[1].split("<h2>Alle Änderungen")[0]
|
||||
assert "Rammstein" in page and "Rammstein" not in section
|
||||
assert "Linden Brunn" in page and "Linden Brunn" not in section
|
||||
assert "dies" in section
|
||||
out = capsys.readouterr().out
|
||||
assert "gültiges Wort ersetzt: „das“ → „dies“" in out
|
||||
|
|
@ -549,7 +566,7 @@ def test_main_without_change_report(sample_copy, monkeypatch):
|
|||
monkeypatch.setattr(ak, "correct_chunk", identity_correct)
|
||||
monkeypatch.setattr(ak, "find_paragraph_starts", every_nth_start(5))
|
||||
run_main(monkeypatch, "-i", sample_copy, "--kein-bericht")
|
||||
assert not sample_copy.with_name("ramstein.de.aenderungen.html").exists()
|
||||
assert not sample_copy.with_name("beispiel.de.aenderungen.html").exists()
|
||||
|
||||
|
||||
def test_main_model_option(sample_copy, monkeypatch):
|
||||
|
|
@ -562,7 +579,7 @@ def test_main_model_option(sample_copy, monkeypatch):
|
|||
|
||||
monkeypatch.setattr(ak, "correct_chunk", correct)
|
||||
monkeypatch.setattr(ak, "find_paragraph_starts", every_nth_start(5))
|
||||
parts = sample_copy.parent / "ramstein.de.korrigiert.parts"
|
||||
parts = sample_copy.parent / "beispiel.de.korrigiert.parts"
|
||||
|
||||
run_main(monkeypatch, "-i", sample_copy)
|
||||
default_parts = {p.name for p in parts.glob("part*")}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ import pytest
|
|||
import vtt2txt
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
SAMPLE_VTT = REPO / "testdaten" / "ramstein.de.vtt"
|
||||
SAMPLE_TXT = REPO / "testdaten" / "ramstein.de.txt"
|
||||
# Own, made-up sample speech; real speeches are only kept locally in testdaten/
|
||||
SAMPLE_VTT = REPO / "tests" / "daten" / "beispiel.de.vtt"
|
||||
SAMPLE_TXT = REPO / "tests" / "daten" / "beispiel.de.txt"
|
||||
SPEECHES = REPO / "testdaten"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -107,15 +109,27 @@ def test_is_rolling(cues, expected):
|
|||
|
||||
def test_main_default_output_matches_sample(sample_copy, monkeypatch, capsys):
|
||||
run_main(monkeypatch, "-i", sample_copy)
|
||||
target = sample_copy.with_name("ramstein.de.txt")
|
||||
target = sample_copy.with_name("beispiel.de.txt")
|
||||
assert target.read_text(encoding="utf-8") == SAMPLE_TXT.read_text(encoding="utf-8")
|
||||
assert f"Erstellt: {target}" in capsys.readouterr().out
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", ["ramstein.de", "magdeburg.de-orig", "Lahnstein.de-orig"])
|
||||
def test_sample_vtt_gives_sample_txt(name):
|
||||
vtt = (REPO / "testdaten" / f"{name}.vtt").read_text(encoding="utf-8-sig")
|
||||
txt = (REPO / "testdaten" / f"{name}.txt").read_text(encoding="utf-8")
|
||||
def speech(name):
|
||||
"""A real speech, only kept locally (copyright): skipped without it."""
|
||||
path = SPEECHES / name
|
||||
return pytest.param(path, marks=pytest.mark.skipif(
|
||||
not path.is_file(), reason="echte Reden nur lokal in testdaten/"
|
||||
))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("vtt_path", [
|
||||
SAMPLE_VTT,
|
||||
speech("ramstein.de.vtt"), speech("magdeburg.de-orig.vtt"),
|
||||
speech("Lahnstein.de-orig.vtt"),
|
||||
])
|
||||
def test_sample_vtt_gives_sample_txt(vtt_path):
|
||||
vtt = vtt_path.read_text(encoding="utf-8-sig")
|
||||
txt = vtt_path.with_suffix(".txt").read_text(encoding="utf-8")
|
||||
assert vtt2txt.vtt_to_lines(vtt) == txt.splitlines()
|
||||
|
||||
|
||||
|
|
@ -128,9 +142,9 @@ def test_main_multiple_inputs(sample_copy, monkeypatch):
|
|||
|
||||
|
||||
def test_main_expands_glob_without_duplicates(sample_copy, monkeypatch, capsys):
|
||||
shutil.copy(sample_copy, sample_copy.with_name("ramstein.en.vtt"))
|
||||
shutil.copy(sample_copy, sample_copy.with_name("beispiel.en.vtt"))
|
||||
shutil.copy(sample_copy, sample_copy.with_name("anders.de.vtt"))
|
||||
run_main(monkeypatch, "-i", sample_copy.parent / "ramstein.*.vtt", sample_copy)
|
||||
run_main(monkeypatch, "-i", sample_copy.parent / "beispiel.*.vtt", sample_copy)
|
||||
out = capsys.readouterr().out
|
||||
assert out.count("Erstellt:") == 2
|
||||
assert not sample_copy.with_name("anders.de.txt").exists()
|
||||
|
|
@ -144,7 +158,7 @@ def test_main_output_directory(sample_copy, monkeypatch, make_dir, suffix):
|
|||
if make_dir:
|
||||
out_dir.mkdir()
|
||||
run_main(monkeypatch, "-i", sample_copy, second, "-o", f"{out_dir}{suffix}")
|
||||
assert (out_dir / "ramstein.de.txt").is_file()
|
||||
assert (out_dir / "beispiel.de.txt").is_file()
|
||||
assert (out_dir / "rede.en.txt").is_file()
|
||||
|
||||
|
||||
|
|
@ -176,7 +190,7 @@ def test_main_bad_file_continues_batch(sample_copy, monkeypatch, capsys, content
|
|||
(["-i", "{txt}"], "Keine VTT-Datei"),
|
||||
(["-i", "{vtt}", "{vtt2}", "-o", "{dir}/x.txt"], "muss -o/--output ein Verzeichnis sein"),
|
||||
(["-i", "{dir}/*.gibtsnicht"], "Keine Datei passt zu"),
|
||||
(["-i", "{vtt}", "{dir}/sub/ramstein.de.vtt", "-o", "{dir}/"], "dieselbe Ausgabedatei"),
|
||||
(["-i", "{vtt}", "{dir}/sub/beispiel.de.vtt", "-o", "{dir}/"], "dieselbe Ausgabedatei"),
|
||||
(["-i", "{vtt}", "-o", "{vtt}"], "nicht die Eingabedatei"),
|
||||
])
|
||||
def test_cli_errors(sample_copy, monkeypatch, capsys, args, message):
|
||||
|
|
@ -417,7 +431,7 @@ def test_split_segments_inverts_join_segments():
|
|||
|
||||
def test_main_time_range_default_name(sample_copy, monkeypatch):
|
||||
run_main(monkeypatch, "-i", sample_copy, "-z", "0:00:30-0:01")
|
||||
target = sample_copy.with_name("ramstein.de_00-00-30_bis_00-01-00.txt")
|
||||
target = sample_copy.with_name("beispiel.de_00-00-30_bis_00-01-00.txt")
|
||||
full = SAMPLE_TXT.read_text(encoding="utf-8").splitlines()
|
||||
part = target.read_text(encoding="utf-8").splitlines()
|
||||
assert vtt2txt.HEADER.fullmatch(part[0])
|
||||
|
|
@ -426,17 +440,17 @@ def test_main_time_range_default_name(sample_copy, monkeypatch):
|
|||
|
||||
def test_main_several_ranges(sample_copy, monkeypatch):
|
||||
run_main(monkeypatch, "-i", sample_copy, "-z", "0:01-0:02 0:05")
|
||||
target = sample_copy.with_name("ramstein.de_00-01-00_bis_00-02-00+ab_00-05-00.txt")
|
||||
target = sample_copy.with_name("beispiel.de_00-01-00_bis_00-02-00+ab_00-05-00.txt")
|
||||
segments = vtt2txt.split_segments(target.read_text(encoding="utf-8"))
|
||||
assert [header for header, _ in segments] == [
|
||||
"[00:00:56 - 00:02:09]", "[00:04:57 - 00:48:07]",
|
||||
"[00:00:59 - 00:02:03]", "[00:04:58 - 00:13:10]",
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ranges", [["0:01-0:02 0:05"], ["0:01-0:02", "0:05"]])
|
||||
def test_main_ranges_with_or_without_quotes(sample_copy, monkeypatch, ranges):
|
||||
run_main(monkeypatch, "-i", sample_copy, "-z", *ranges)
|
||||
target = sample_copy.with_name("ramstein.de_00-01-00_bis_00-02-00+ab_00-05-00.txt")
|
||||
target = sample_copy.with_name("beispiel.de_00-01-00_bis_00-02-00+ab_00-05-00.txt")
|
||||
assert target.is_file()
|
||||
|
||||
|
||||
|
|
@ -456,8 +470,8 @@ def test_range_argv(argv, expected):
|
|||
def test_main_range_from_beginning(sample_copy, monkeypatch, ranges):
|
||||
args = ranges if ranges[0].startswith("-z") else ["-z", *ranges]
|
||||
run_main(monkeypatch, "-i", sample_copy, *args)
|
||||
target = sample_copy.with_name("ramstein.de_bis_00-01-00.txt")
|
||||
assert target.read_text(encoding="utf-8").startswith("[00:00:09 - ")
|
||||
target = sample_copy.with_name("beispiel.de_bis_00-01-00.txt")
|
||||
assert target.read_text(encoding="utf-8").startswith("[00:00:00 - ")
|
||||
|
||||
|
||||
def test_main_time_range_without_captions(sample_copy, monkeypatch, capsys):
|
||||
|
|
|
|||
Loading…
Reference in a new issue