"""Führt die Begriffsgruppen zu `content/glossar.json` zusammen. Das Glossar entsteht gruppenweise – Umgangsarten, Waffenarten, Erlaubniswesen und so fort. Dieses Skript legt die Teilstücke zusammen, sortiert alphabetisch nach deutscher Regel und setzt den Kopf mit dem tatsächlichen Gesetzesstand. Ein Teilstück ist eine JSON-Datei, entweder mit Umschlag:: { "eintraege": [ { "begriff": "Führen", … } ] } oder unmittelbar als Liste:: [ { "begriff": "Führen", … } ] Aufruf ------ python data-pipeline/glossar_zusammenfuehren.py … Anschließend prüfen – ohne bestandene Prüfung wird nicht ausgeliefert:: python data-pipeline/pruefe_glossar.py """ from __future__ import annotations import argparse import io import json import locale import sys from datetime import date from pathlib import Path WURZEL = Path(__file__).resolve().parent.parent GESETZE = WURZEL / 'content' / 'gesetze' / 'index.json' ZIEL = WURZEL / 'content' / 'glossar.json' HINWEIS = ( 'Eigener redaktioneller Inhalt. Wo das Gesetz einen Begriff selbst bestimmt, ' 'gibt der Eintrag diese Bestimmung wieder; Gesetzestexte sind nach § 5 Abs. 1 ' 'UrhG gemeinfrei. Zusammenstellung und Auswahl sind nicht Teil des amtlichen ' 'Fragenkatalogs. Jede Fundstelle wird von data-pipeline/pruefe_glossar.py ' 'gegen den amtlichen Gesetzestext geprueft.' ) #: Felder einer Fundstelle, die als Zeichenkette vorliegen müssen. FUNDSTELLENFELDER = ('gesetz', 'norm', 'absatz', 'nummer', 'buchstabe', 'satz', 'stelle') def teilstuecke_einlesen(orte: list[str]) -> list[dict]: dateien: list[Path] = [] for ort in orte: pfad = Path(ort) if pfad.is_dir(): dateien.extend(sorted(pfad.glob('*.json'))) elif pfad.is_file(): dateien.append(pfad) else: raise SystemExit(f'Nicht gefunden: {ort}') gesammelt: list[dict] = [] for datei in dateien: if datei.resolve() == ZIEL.resolve(): continue roh = json.load(io.open(datei, encoding='utf-8')) eintraege = roh.get('eintraege') if isinstance(roh, dict) else roh if not isinstance(eintraege, list): raise SystemExit(f'{datei}: erwartet wird eine Liste von Eintraegen.') gesammelt.extend(eintraege) print(f' {datei.name}: {len(eintraege)} Eintraege') return gesammelt def normalisieren(eintraege: list[dict]) -> int: """Macht aus Zahlen Zeichenketten – wie bei den Erklaerungen. Der Lader in der Anwendung prueft den Typ. `"absatz": 2` ist eine naheliegende Verwechslung, inhaltlich harmlos und wuerde dort dennoch die ganze Datei verwerfen. """ geaendert = 0 for eintrag in eintraege: for fundstelle in eintrag.get('fundstellen') or []: if not isinstance(fundstelle, dict): continue for feld in FUNDSTELLENFELDER: wert = fundstelle.get(feld) if isinstance(wert, bool): continue if isinstance(wert, (int, float)): fundstelle[feld] = str(wert) geaendert += 1 return geaendert def sortierschluessel(begriff: str) -> str: """Deutsche Sortierung: Umlaute stehen bei ihren Grundbuchstaben.""" ersatz = {'ä': 'a', 'ö': 'o', 'ü': 'u', 'ß': 'ss', 'Ä': 'A', 'Ö': 'O', 'Ü': 'U'} return ''.join(ersatz.get(z, z) for z in begriff).casefold() def main() -> int: zerleger = argparse.ArgumentParser(description='Glossar zusammenfuehren.') zerleger.add_argument('quellen', nargs='+', help='Dateien oder Verzeichnisse') argumente = zerleger.parse_args() print('Teilstuecke:') eintraege = teilstuecke_einlesen(argumente.quellen) geaendert = normalisieren(eintraege) if geaendert: print(f' {geaendert} Zahlangaben zu Zeichenketten gemacht.') # Doppelte Begriffe zusammenfuehren waere geraten; hier faellt es auf. gesehen: dict[str, int] = {} for eintrag in eintraege: begriff = eintrag.get('begriff') if isinstance(begriff, str): gesehen[begriff] = gesehen.get(begriff, 0) + 1 doppelt = sorted(b for b, n in gesehen.items() if n > 1) if doppelt: print(f'\nFEHLER: mehrfach vorhanden: {", ".join(doppelt)}') return 1 eintraege.sort(key=lambda e: sortierschluessel(str(e.get('begriff', '')))) gesetze = json.load(io.open(GESETZE, encoding='utf-8'))['gesetze'] zitiert = { f.get('gesetz') for e in eintraege for f in (e.get('fundstellen') or []) if isinstance(f, dict) } ergebnis = { 'meta': { 'version': 1, 'stand': date.today().isoformat(), 'hinweis': HINWEIS, 'gesetzesstand': {k: gesetze[k]['stand'] for k in gesetze if k in zitiert}, }, 'eintraege': eintraege, } with io.open(ZIEL, 'w', encoding='utf-8', newline='\n') as datei: json.dump(ergebnis, datei, ensure_ascii=False, indent=1) datei.write('\n') begriffe = sum(1 for e in eintraege if e.get('art') == 'begriff') kuerzel = sum(1 for e in eintraege if e.get('art') == 'abkuerzung') print(f'\n{len(eintraege)} Eintraege ({begriffe} Begriffe, {kuerzel} Abkuerzungen)') print(f'geschrieben: {ZIEL.name}') print('\nJetzt pruefen mit: python data-pipeline/pruefe_glossar.py') return 0 if __name__ == '__main__': sys.stdout.reconfigure(encoding='utf-8', errors='replace') # type: ignore[union-attr] raise SystemExit(main())