2 # :Copyright: © 2014 Günter Milde.
3 # Released without warranty under the terms of the
4 # GNU General Public License (v. 2 or later)
7 # prepare_patch.py: Helfer für kleine Editieraufgaben
8 # ===================================================
12 Erstelle einen Patch für kleinere Korrekturen der Wortliste.
13 Ausgangspunkt sind Dateien mit einer Korrektur pro Zeile.
14 (Zeilen, die mit ``#`` starten, werden ignoriert.)
17 doppelte: Einträge mit gleichem Schlüssel entfernen,
18 fehleintraege: Einträge entfernen,
19 grossklein: Großschreibung ändern,
20 grossabgleich: Großschreibung der Trennmuster wie erstes Feld,
21 korrektur: Einträge durch alternative Version ersetzen,
22 neu: Einträge hinzufügen,
23 reformschreibung: Eintrag in "nur Reformschreibung" ändern,
24 zusammenfassen: Sprachvarianten zusammenfassen wenn gleich.
27 # Die ``<AKTION>.todo`` Dateien im Unterverzeichnis "tasks/" beschreiben das
28 # jeweils erforderliche Datenformat im Dateikopf.
32 import optparse
, sys
, os
33 from copy
import copy
, deepcopy
36 from wortliste
import WordFile
, WordEntry
, join_word
, udiff
, sortkey_duden
38 def teste_datei(datei
):
39 """Teste, ob Datei geöffnet werden kann."""
42 file = open(datei
, 'r')
45 sys
.stderr
.write("Kann '" + datei
+ u
"' nicht öffnen\n" )
51 # Sprach-Tag nach [BCP47]_::
53 sprachvariante
= 'de-1901' # "traditionell"
54 # sprachvariante = 'de-1996' # Reformschreibung
55 # sprachvariante = 'de-x-versal' # ohne ß (Großbuchstaben und Kapitälchen)
56 # sprachvariante = 'de-1901-x-versal' # ohne ß (Schweiz oder versal)
57 # sprachvariante = 'de-1996-x-versal' # ohne ß (Schweiz oder versal)
58 # sprachvariante = 'de-CH-1901' # ohne ß (Schweiz) ("süssauer")
61 # Allgemeine Korrektur (z.B. Fehltrennung)
64 # * ein Wort mit Trennstellen (für "Sprachvariante"):
65 # * vollständiger Eintrag (für Wörter mit Sprachvarianten).
69 def korrektur(wordfile
, datei
='tasks/korrektur.todo'):
70 """Patch aus korrigierten Einträgen"""
75 for line
in open(datei
, 'r'):
76 if line
.startswith('#'):
78 # Dekodieren, Zeilenende entfernen
82 # Eintrag ggf. komplettieren
84 line
= '%s;%s' % (join_word(line
), line
)
85 entry
= WordEntry(line
)
87 entry
.regelaenderungen() # teste auf Dinge wie s-t/-st
89 korrekturen
[key
] = entry
91 wortliste
= list(wordfile
)
92 wortliste_neu
= [] # korrigierte Liste
94 for entry
in wortliste
:
96 if key
in korrekturen
:
97 entry
= korrekturen
.pop(key
)
98 wortliste_neu
.append(entry
)
101 print(u
"Die folgenden Einträge sind nicht in der Wortliste:")
102 print(korrekturen
) # übrige Einträge
104 return (wortliste
, wortliste_neu
)
111 def fehleintraege(wordfile
, datei
='tasks/fehleintraege.todo'):
112 """Entfernen der Einträge aus einer Liste von Fehleinträgen """
114 # Fehleinträge aus Datei.
117 # Ein Eintrag/Zeile, mit oder ohne Trennzeichen
123 # Dekodieren, Zeilenende entfernen, Trennzeichen entfernen
124 korrekturen
= set(join_word(
125 line
.strip().replace(';', ' ').split()[0])
126 for line
in open(datei
, 'r')
127 if line
.strip() and not line
.startswith('#'))
128 wortliste
= list(wordfile
)
129 wortliste_neu
= [] # korrigierte Liste
130 for entry
in wortliste
:
131 if entry
[0] in korrekturen
: # nicht kopieren
132 korrekturen
.discard(entry
[0]) # erledigt
134 wortliste_neu
.append(entry
)
137 print('nicht gefunden:')
138 for w
in korrekturen
:
141 return (wortliste
, wortliste_neu
)
144 # Groß-/Kleinschreibung ändern
145 # ----------------------------
147 # Umstellen der Groß- oder Kleinschreibung auf die Variante in der Datei
148 # ``grossklein.todo``
151 # ein Eintrag oder Wort pro Zeile, mit vorhandener Groß-/Kleinschreibung.
155 def grossklein(wordfile
, datei
='tasks/grossklein.todo'):
156 """Groß-/Kleinschreibung umstellen"""
160 wortliste
= list(wordfile
)
162 # Dekodieren, Feldtrenner zu Leerzeichen
163 korrekturen
= [line
.replace(';',' ')
164 for line
in open(datei
, 'r')
165 if not line
.startswith('#')]
167 # erstes Feld, Trennzeichen entfernen
168 korrekturen
= [join_word(line
.split()[0]) for line
in korrekturen
169 if line
.strip() and not line
.startswith('#')]
170 korrekturen
= set(korrekturen
)
171 wortliste_neu
= deepcopy(wortliste
) # korrigierte Liste
173 for entry
in wortliste_neu
:
174 if entry
[0] in korrekturen
:
175 korrekturen
.discard(entry
[0]) # gefunden
176 # Anfangsbuchstabe mit geänderter Großschreibung:
177 if entry
[0][0].islower():
178 anfangsbuchstabe
= entry
[0][0].title()
180 anfangsbuchstabe
= entry
[0][0].lower()
181 # Einträge korrigieren:
182 for i
in range(len(entry
)):
183 if entry
[i
].startswith('-'): # -2-, -3-, ...
185 entry
[i
] = anfangsbuchstabe
+ entry
[i
][1:]
188 print(korrekturen
) # übrige Einträge
190 return (wortliste
, wortliste_neu
)
192 # Anpassung der Großschreibung der Trennmuster an das erste Feld
193 # (ungetrenntes Wort). Siehe "wortliste.py" für einen Test auf Differenzen.
194 # (Differenzen sind größtenteils auf unvorsichtiges Ersetzen mit Texteditor
198 def grossabgleich(wordfile
):
199 wortliste
= list(wordfile
)
200 wortliste_neu
= deepcopy(wortliste
) # korrigierte Liste
201 for entry
in wortliste_neu
:
202 # Übertrag des Anfangsbuchstabens
203 for i
in range(1,len(entry
)):
206 entry
[i
] = entry
[0][0] + entry
[i
][1:]
207 return (wortliste
, wortliste_neu
)
210 # Sprachvariante ändern
211 # ---------------------
213 # Einträge mit allgemeingültiger (oder sonstwie mehrfacher) Sprachvariante
214 # in "nur in Reformschreibung" (allgemein) ändern.
217 # ein Wort/(Alt-)Eintrag pro Zeile.
221 def reformschreibung(wordfile
, datei
='tasks/reformschreibung.todo'):
222 """Wörter die nur in (allgemeiner) Reformschreibung existieren"""
225 # Dekodieren, Zeilenende entfernen
226 korrekturen
= [line
.strip()
227 for line
in open(datei
, 'r')
228 if not line
.startswith('#')
231 korrekturen
= [line
.split(';')[0] for line
in korrekturen
]
232 korrekturen
= set(korrekturen
)
234 wortliste
= list(wordfile
)
235 wortliste_neu
= [] # korrigierte Liste
237 for entry
in wortliste
:
238 if entry
[0] in korrekturen
:
240 wort
= entry
.get('de-1996')
242 entry
= WordEntry('%s;-2-;-3-;%s;%s' % (key
, wort
, wort
))
244 entry
= WordEntry('%s;-2-;-3-;%s' % (key
, wort
))
245 korrekturen
.discard(key
) # erledigt
246 wortliste_neu
.append(entry
)
249 print(korrekturen
) # übrige Einträge
250 return wortliste
, wortliste_neu
253 # Getrennte Einträge für Sprachvarianten
254 # --------------------------------------
256 # Korrigiere fehlende Spezifizierung nach Sprachvarianten, z.B.
259 # + System;-2-;Sy-stem;Sys-tem
263 def sprachvariante_split(wordfile
, alt
, neu
,
264 altsprache
='de-1901', neusprache
='de-1996'):
266 wortliste
= list(wordfile
)
267 wortliste_neu
= [] # korrigierte Liste
269 for entry
in wortliste
:
270 if len(entry
) == 2: # Allgemeine Schreibung
271 altwort
= entry
.get(altsprache
)
272 neuwort
= altwort
.replace(alt
, neu
)
273 if altwort
!= neuwort
:
274 entry
= WordEntry('%s;-2-;3;4' % (join_word(altwort
)))
275 entry
.set(altwort
, altsprache
)
276 entry
.set(neuwort
, neusprache
)
277 wortliste_neu
.append(entry
)
278 return (wortliste
, wortliste_neu
)
282 # Neueinträge prüfen und vorbereiten
283 # ----------------------------------
285 # Die in einer Datei (ein Neueintrag pro Zeile) gesammelten Vorschläge auf
286 # auf Neuwert testen (vorhandene Wörter werden ignoriert, unabhängig von der
287 # Groß-/Kleinschreibung) und einsortieren.
289 # Akzeptierte Formate:
291 # * vollständiger Eintrag (für Wörter mit Sprachvarianten oder Kommentaren).
293 # * ein Wort mit Trennstellen:
295 # - Schlüssel wird generiert und vorangestellt (durch Semikolon getrennt).
296 # - Test auf einfache/häufige Reformänderungen 1996:
297 # s-t/-st, {ck/k-k}/c-k
301 def neu(wordfile
, datei
='tasks/neu.todo'):
302 """Neueinträge prüfen und vorbereiten."""
305 neue
= open(datei
, 'r')
306 print_korrekturhinweis
= False
308 wortliste
= list(wordfile
)
309 wortliste_neu
= copy(wortliste
)
310 # vorhandene Einträge:
311 words
= dict((entry
.key().lower(), entry
) for entry
in wortliste
)
314 if line
.startswith('#'):
316 # Dekodieren, Zeilenende entfernen
320 # Eintrag ggf. komplettieren:
322 line
= '%s;%s' % (join_word(line
), line
)
323 entry
= WordEntry(line
)
324 key
= entry
.key().lower()
326 # Test auf "Neuwert":
330 wortliste_neu
.remove(old
)
332 print(key
, 'ignored: schon vorhanden',)
333 # TODO: Einträge vergleichen:
334 if entry
== old
and entry
.comment
== old
.comment
:
335 print('(identisch).')
337 print('(unterschiedlich)')
340 print_korrekturhinweis
= True
343 entry
.regelaenderungen() # teste auf Dinge wie s-t/-st
344 wortliste_neu
.append(entry
)
347 if print_korrekturhinweis
:
348 print('Änderungen können mit "--force" erzwungen werden.')
350 wortliste_neu
.sort(key
=sortkey_duden
)
352 return (wortliste
, wortliste_neu
)
355 def doppelte(wordfile
, use_first
=False):
356 """Doppeleinträge entfernen (ohne Berücksichtigung der Großschreibung).
358 Boolscher Wert `use_first` bestimmt, ob der erste oder der letzte von
359 Einträgen mit gleichem Schlüssel in der Liste verbleibt.
361 Die neue Liste ist sortiert.
363 wortliste
= list(wordfile
)
365 for entry
in wortliste
:
366 key
= entry
[0].lower()
367 if use_first
and key
in worddict
:
369 worddict
[key
] = entry
371 wortliste_neu
= list(worddict
.values()) # korrigierte Liste
372 wortliste_neu
.sort(key
=sortkey_duden
)
374 print(len(wortliste
) - len(wortliste_neu
), u
"Einträge entfernt")
375 return (wortliste
, wortliste_neu
)
378 def prune(wortliste
):
380 wortliste
= list(wordfile
)
381 wortliste_neu
= [] # korrigierte Liste
383 for entry
in wortliste
:
385 # Felder zusammenfassen:
388 wortliste_neu
.append(entry
)
390 return (wortliste
, wortliste_neu
)
395 if __name__
== '__main__':
397 # Pfad zu "../../../wortliste" unabhängig vom Arbeitsverzeichnis::
399 default_wortliste
= os
.path
.relpath(os
.path
.join(
400 os
.path
.dirname(os
.path
.dirname(os
.path
.dirname(os
.path
.dirname(
401 os
.path
.abspath(__file__
))))),
406 usage
= '%prog [Optionen] AKTION\n' + __doc__
408 parser
= optparse
.OptionParser(usage
=usage
)
409 parser
.add_option('-i', '--file', dest
='wortliste',
410 help='Eingangsdatei, Vorgabe "%s"'%default
_wortliste
,
411 default
=default_wortliste
)
412 parser
.add_option('-k', '--todofile', dest
='todo',
413 help='Korrekturdatei, Vorgabe "tasks/<AKTION>.todo"')
414 parser
.add_option('-o', '--outfile', dest
='patchfile',
415 help='Ausgangsdatei, Vorgabe "%s.patch"'
417 default
=default_wortliste
+'.patch')
418 parser
.add_option('-f', '--force',
419 help='überschreibe vorhandene Einträge (Aktion "neu").',
420 action
="store_true", default
=False)
422 (options
, args
) = parser
.parse_args()
427 print('Nichts zu tun: AKTION Argument fehlt.', '\n')
432 wordfile
= WordFile(options
.wortliste
)
434 # Da der Aufruf von `wortliste = list(wordfile)` lange dauert, wird er
435 # in den Aktionsroutinen nach dem Test auf die Eingabedatei ausgeführt.
437 # Die Aufgabenliste::
439 todofile
= options
.todo
or "tasks/%s.todo" % aktion
444 (wortliste
, wortliste_neu
) = neu(wordfile
, todofile
)
445 elif aktion
== 'doppelte':
446 (wortliste
, wortliste_neu
) = doppelte(wordfile
)
447 elif aktion
== 'fehleintraege':
448 (wortliste
, wortliste_neu
) = fehleintraege(wordfile
, todofile
)
449 elif aktion
== 'grossklein':
450 (wortliste
, wortliste_neu
) = grossklein(wordfile
, todofile
)
451 elif aktion
== 'grossabgleich':
452 (wortliste
, wortliste_neu
) = grossabgleich(wordfile
)
453 elif aktion
== 'korrektur':
454 (wortliste
, wortliste_neu
) = korrektur(wordfile
, todofile
)
455 elif aktion
== 'reformschreibung':
456 (wortliste
, wortliste_neu
) = reformschreibung(wordfile
, todofile
)
457 elif aktion
== 'zusammenfassen':
458 (wortliste
, wortliste_neu
) = prune(wordfile
)
460 print('Unbekannte AKTION', '\n')
464 # (wortliste, wortliste_neu) = sprachvariante_split(wordfile,
469 patch
= udiff(wortliste
, wortliste_neu
, 'wortliste', 'wortliste-neu')
472 patchfile
= open(options
.patchfile
, 'w')
473 patchfile
.write(patch
)
474 print('Änderungen nach %s geschrieben' % options
.patchfile
)
476 print('keine Änderungen')