Files
docu_build/doc/plan_fortna_extraktion.md
T
Michael StanglandClaude Opus 4.8 bbfe17ec37 feat: Symbole zentralisieren + Fragment-Ueberschriften filtern
Perceptual-Hash-basierte Symbol-/Duplikaterkennung in lib/pdf_extract.py:

- average_hash() (aHash) + Hamming-Distanz statt exaktem Byte-Hash: erkennt
  visuell gleiche Bilder trotz unterschiedlicher Skalierung/Kompression
- Symbole (klein <=260px UND mehrfach vorkommend, z.B. Warndreiecke) werden
  EINMAL zentral nach assets/shared/symbols/symbol_<hash>.png abgelegt und im
  RST als .. image:: (12mm, zentriert) auf die eine Quelle referenziert -
  dokumentuebergreifend (registry). Ein Warndreieck: 1x gespeichert statt 58x
- grossformatige Inhaltsbild-Duplikate ebenfalls ueber aHash zusammengefuehrt
- Fragment-"Ueberschriften" (POS, li., re., "bar, max. 10 bar") unterdrueckt
- Altbestand assets/shared/symbols/img_*.png (durcheinander benannt) entfernt

Wirkung: omniflo images/ 83 -> 62, 14 zentrale Symbole statt zig Duplikate.
Voller PDF-Build sauber (lualatex exit 0, 719 Seiten).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-23 14:10:33 +02:00

18 KiB
Raw Blame History

Konzept: Fortna-Extraktion nach chapters/ + Projektordner fortna/

Status: Umgesetzt (erster vollständiger Durchlauf inkl. PDF-Build). Antworten aus der Rückfragerunde sind eingearbeitet (siehe Abschnitt "Entscheidungen"). Der tatsächliche Ist-Stand ist unten unter "Umsetzung / Ist-Stand" dokumentiert.

Umsetzung / Ist-Stand

Implementiert wurden vier Python-Skripte in lib/ mit bin/-Wrappern (.bat + .sh):

Skript Zweck Wrapper
lib/fortna_inventory.py Scannt das Rohmaterial, ordnet jede Datei Sprache + Zielkapitel + Aktion zu, schreibt results/fortna_inventory.json. bin/fortna_inventory.*
lib/fortna_convert_chapters.py Volltext+Bilder für die 5 DE-Hauptanleitungen, Verzeichnislisten (index.de.rst) für alle übrigen Kapitel. --dry-run (Default) / --apply. bin/fortna_convert_chapters.*
lib/fortna_init_project.py Legt projekte/fortna/ an (anlage.json, overrides/, images/, README.md). bin/fortna_init_project.*
lib/build_index.py Generiert index.rst mit toctrees aus allen chapters/**/*.de.rst. (in build_pdf.*)
bin/build_pdf.* Kompletter PDF-Build: index → sphinx-build -b latex → latexmk -lualatex. —

Ergebnis des ersten Laufs:

  • 5 Hauptanleitungen volltextig konvertiert (Betriebs-/Wartungsanleitung, Funktionsbeschreibung ILS/OMNIFLO/LoadingLoop), zusammen ~13.500 Textblöcke + mehrere Hundert entduplizierte Bilder.
  • 9 Verzeichnislisten erzeugt (Zertifikate, Lieferantendoku nach Gewerk, Notstopp, Prüfberichte).
  • projekte/fortna/ mit anlage.json-Platzhalter angelegt.
  • Vollständiger PDF-Build erfolgreich: _build/latex/Fortna_Dokumentation_DE.pdf, ~670 Seiten, latexmk-Exit 0, keine LaTeX-Fehler.

Umgebung / Build-Voraussetzungen (bereits erfüllt)

  • Sphinx 9.1.0 + myst-parser 5.1.0 im .venv (ergänzt in requirements.txt).
  • LaTeX: MiKTeX unter C:\Program Files\MiKTeX\ (lualatex/latexmk vorhanden).
  • conf.py erzeugt: LaTeX-Engine lualatex, babel statt polyglossia (polyglossia 2.13 wirft unter lualatex einen bidi-Fehler), iconpackage=none (FontAwesome7 in MiKTeX bricht unter lualatex ab). Beides in conf.py kommentiert.

Extraktion: neues Modul lib/pdf_extract.py

Die Volltext-Konvertierung der 5 Hauptanleitungen läuft über das neue Modul lib/pdf_extract.py (extract_manual()), das gegenüber der ersten Version deutlich sauberere .rst erzeugt:

  • Keine doppelte Nummerierung mehr: Die im PDF vorhandene Gliederungsnummer (z. B. „1.7 Wichtige Informationen") wird nur zur Bestimmung der Überschriften-Ebene ausgewertet und im RST-Text entfernt. Sphinx/LaTeX nummeriert selbst → vorher „3.2.1 1.1 Wichtige Informationen", jetzt nur noch „3.2.1 Wichtige Informationen".
  • Überschriften-Erkennung nur bei führender PDF-Gliederungsnummer (kein „fett allein"), damit Tabellenzellen nicht fälschlich zu Überschriften werden.
  • Bilder positionsbasiert extrahiert und referenziert: pro Seite werden Text und Bilder nach Y-Position verwoben; jedes Bild wird als .. figure:: mit korrektem Pfad und – falls vorhanden – der folgenden „Abbildung N: …"-Zeile als Caption eingebaut. (625 figure-Refs, 0 fehlende Bilddateien.) Duplikate werden per MD5 entfernt, Titelseiten-Deko und Vollseiten-Hintergründe herausgefiltert.
  • Symbole/Warnzeichen zentral: Kleine (≤ 260 px), mehrfach vorkommende Bilder (Warndreiecke, Piktogramme) werden per Perceptual-Hash (aHash + Hamming-Distanz ≤ 6) erkannt, einmal zentral nach assets/shared/symbols/symbol_<hash>.png abgelegt und im RST als .. image:: (12 mm, zentriert) auf die eine Quelle verwiesen – dokumentübergreifend. Vorher lag dasselbe Warndreieck z. B. 8× als img_003/018/020/025/026/031/035… in einem einzigen Kapitel. Auch großformatige Inhaltsbild-Duplikate werden über den aHash zusammengeführt (nur einmal abgelegt, mehrfach referenziert). Wirkung: omniflo-images/ von 83 → 62, Warndreieck 1× statt 58× gespeichert.
  • Fragment-„Überschriften" unterdrückt: Bildlegenden/Tabellenzellen mit führender Nummer („POS", „li.", „re.", „bar, max. 10 bar") werden nicht mehr als Überschrift gesetzt.
  • Fließtext zu Absätzen zusammengeführt: PDF-Zeilenumbrüche werden nicht mehr zu je einem Absatz; zusammenhängende Zeilen bilden einen Absatz, Satzende beendet ihn.
  • Aufzählungen als RST-Listen: einzeln stehende Bullet-Zeichen werden mit der Folgezeile zusammengeführt, in eine Zeile zusammengelaufene Bullets aufgesplittet → --Listenpunkte.
  • Änderungshistorie-/Dokumentinfo-Tabellen am Dokumentanfang werden übersprungen (zerfielen vorher in sinnlose Einzelzellen), ebenso Inhaltsverzeichnis-Seiten und Kopf-/Fußzeilen.

Behobene Extraktionsprobleme

  • Symbolfont-Zeichen (Wingdings/Symbol Private-Use-Area, z. B. Aufzählungs-Bullet U+F0B7): via clean_pua() auf echte Unicode-Zeichen (•, →, ✓) gemappt, restliche PUA-Zeichen entfernt. Vorher erschienen leere Kästchen im PDF. (In lib/pdf_extract.py; die Variante in extract_products.py bleibt für die Produktprogramm-Extraktion bestehen.)
  • Fremd-Altdateien aus früheren convert_to_rst.py-Testläufen (NORD-Getriebe/-Motor im Anhang, alte Risikobewertung/Contents/Konfig/Ersatzteile-Volltexte) wurden entfernt; die betroffenen Kapitel liegen konzeptkonform als Verzeichnisliste vor.

Bekannte Restpunkte (nicht Teil des Auftrags-Scopes)

  • Zwei Fremd-Altdateien aus früheren convert_to_rst.py-Testläufen sind noch im Baum und vom Build ausgeschlossen (conf.py + build_index.py SKIP-Liste): 02_sicherheit/risikobewertung/rb_fortna_...de.rst (unvollständiger Bildbestand) und 00_inhaltsverzeichnis/contens_obsah_...de.rst (keine RST-Überschrift). Zwei weitere Altdateien in 10_anhang/ (Motor/Getriebe) sind noch im toctree und zeigen typische Extraktionsartefakte – bei Bedarf mit fortna_convert_chapters.py sauber neu erzeugen.
  • Tabellen (z. B. Änderungshistorie) werden weiterhin als Einzelzeilen extrahiert, nicht als RST-Tabelle rekonstruiert (wie im Konzept angekündigt).

Ausgangslage

  • Reales Fortna-Rohmaterial liegt in c:\10-Develop\projekte\Fortna\ (zusätzliches Arbeitsverzeichnis dieser Session, nicht im Repo). Struktur wie in doc/Fortna.txt dokumentiert (00_CONTENTS_NOTES … 05_SUPPLIER_DOC), Versionsstände dort sind aktueller als in doc/Fortna.txt (z. B. Betriebsanleitung V2.4 statt V2.3, Wartungsanleitung V2.3, ILS V2.2, OMNIFLO V1.1, LoadingLoop V2.3). doc/Fortna.txt/doc/Fortna-size.txt sind nur noch als Struktur-Referenz zu verstehen, nicht als Quelle für den Extraktionslauf.
  • chapters/ (Base-Repo-Gerüst) existiert bereits vollständig mit .gitkeep-Platzhaltern gemäß doc/kapitelstruktur.md. 06_steuerung/loading_loop/ wurde testweise mit convert_to_rst.py aus der LoadingLoop-Word-Datei befüllt (2 RST + 74 Bilder). Qualität ist noch unzureichend: Kopf-/Fußzeilen (Firmenadresse, Kontaktdaten) landen als normale Absätze im Fließtext, Tabellen (Änderungshistorie) werden zu einzelnen Zeilen ohne Tabellenstruktur zerlegt. Das ist ein bekanntes Problem der aktuellen _html_to_blocks-Logik in convert_to_rst.py und wird im Zuge dieser Arbeit mit adressiert (siehe Abschnitt "Bekannte Schwachstellen").
  • Vorhandene Skripte in lib/ (siehe doc/lib_skripte.md):
    • convert_to_rst.py: .docx/Word-PDF → RST, erkennt Zielkapitel nur anhand Dateiname-Pattern im Root eines Ordners, kein Verständnis für den verschachtelten Fortna-Sprachordner-Baum.
    • extract_products.py: Produktprogramm-PDFs → Bilder + RST (bereits fertig genutzt für data/Produktprogramm).
    • jsonFromXlsx.py, extract_ids.py, project_from_xlsx.py: Stücklisten-Auswertung, für diese Aufgabe nicht direkt relevant.
  • Kein Skript kennt bisher die komplette Fortna-Ordnerstruktur (Lieferantendoku-Baum, Layouts, Schaltpläne, Prüfberichte) und keines erzeugt den projektspezifischen Ordner (anlage.json, overrides/, images/) gemäß doc/Kernkonzepte.md.

Entscheidungen (aus Rückfragen)

Frage Entscheidung
Lieferantendoku (~250 PDFs: SEW, Siemens, Leuze, Cognex, MiTek, Aventics, …) Nur Verzeichnisliste, keine Kopie. RST-Referenztabelle mit Pfaden auf das Originalverzeichnis.
Sonstige PDFs (Zertifikate, Konfigurationsbeschreibung, Layouts, Schaltpläne, Notstopp/Bereichsstopp, Prüfberichte, Ersatzteilliste) Gleiche Behandlung wie Lieferantendoku: nur Verzeichnisliste, keine Kopie nach chapters/.
Volltext-Konvertierung (Text + Bilder → RST) Nur die 5 Hauptanleitungen: Betriebsanleitung, Wartungsanleitung, Funktionsbeschreibung ILS, Funktionsbeschreibung OMNIFLO, Funktionsbeschreibung LoadingLoop.
Projektordner-Ort projekte/fortna/ innerhalb dieses Repos (docu_build), pragmatisch für jetzt; spätere Auslagerung in eigenes Repo bleibt möglich.
Sprachumfang Nur Deutsch in diesem Durchlauf. Namenskonvention {name}.de.rst einhalten, EN/CZ als späterer zweiter Durchlauf vorgesehen.
Quelle c:\10-Develop\projekte\Fortna\ (aktueller Stand), nicht doc/Fortna.txt.
Umgang mit bestehendem chapters/06_steuerung/loading_loop/ Überschreiben/neu erzeugen – konsistent aus einem Guss statt Altbestand stehen lassen.

Zielstruktur (Ergebnis nach Lauf)

chapters/
  01_zertifikate/
    konformitaetserklaerung/index.de.rst        # Verzeichnisliste -> Originalpfade
    konfigurationsbeschreibung/index.de.rst
  02_sicherheit/                                  # unverändert (kein Fortna-Rohmaterial 1:1 zuordenbar)
  03_anlagenbeschreibung/                         # unverändert (Modul-Mechanismus, nicht Teil dieser Aufgabe)
  04_betrieb/betriebsanleitung/
    betriebsanleitung_500592_135776_v2_4.de.rst   # Volltext + Bilder
    images/img_001.png ...
  05_wartung/wartungsanleitung/
    wartungsanleitung_500592_v2_3.de.rst          # Volltext + Bilder
    images/...
  06_steuerung/
    ils/funktion_steuerung_ils_500592_v2_2.de.rst # Volltext + Bilder
    omniflo/funktion_steuerung_omniflo_500592_v1_1.de.rst
    loading_loop/funktion_steuerung_loadingloop_500592_v2_3.de.rst  # neu erzeugt, ersetzt Altbestand
  07_ersatzteile/index.de.rst                     # Verzeichnisliste -> Originalpfad SPARE_PARTS PDF
  08_lieferantendoku/
    antriebe/index.de.rst                         # Verzeichnisliste SEW, NORD, Leantechnik, DTS
    elektrik/index.de.rst                         # Siemens, Leuze, Cognex, Werma, ...
    pneumatik/index.de.rst                        # Aventics, ITS
    stahlbau/index.de.rst                         # MiTek (As-Built, Statik, Zertifikate, ...)
    sicherheitseinrichtungen/index.de.rst          # ABS Sicherheitslinie
  09_technische_unterlagen/
    layouts/mechanisch/index.de.rst
    layouts/elektrisch/index.de.rst
    schaltplaene/index.de.rst
    notstopp/index.de.rst
    bereichsstopp/index.de.rst
  10_anhang/
    pruefberichte/index.de.rst

projekte/
  fortna/
    anlage.json            # Projektmetadaten (Platzhalter/Vorschlag, manuell nachzuschärfen)
    overrides/              # leer angelegt, Struktur gemäß Kernkonzepte.md
    images/                 # leer angelegt (projektspezifische Bilder, Kundenlogo etc.)
    README.md               # kurze Erläuterung was hier reinkommt

Jede index.de.rst-Verzeichnisliste enthält eine Tabelle: Dateiname, Hersteller/Kategorie (aus Ordnername abgeleitet), Originalpfad (relativ zu einer konfigurierbaren Basis, z. B. PV_FORTNA_SOURCE), Dateigröße. Kein PDF wird kopiert oder eingebettet – das hält den Kapitelbaum schlank und vermeidet Redundanz zum Rohmaterial, das ohnehin an anderer Stelle (Nextcloud/Projektordner) verwaltet wird.

Geplante Skripte (in lib/)

1. fortna_inventory.py (neu)

Scannt c:\10-Develop\projekte\Fortna\ rekursiv und baut ein Inventar (JSON) aller Dateien mit: Pfad, Größe, erkannte Sprache (aus Ordnername: *_DE*/*_EN*/*_CZ*/*original* → de), vermutetem Fortna-Kapitel (oberste zwei Ebenen) und vermutetem Ziel-Kapitel gemäß Mapping-Tabelle aus doc/kapitelstruktur.md. Dient als Kontrollartefakt (results/fortna_inventory.json), auf dessen Basis sich beide folgenden Skripte konfigurieren bzw. dessen Output sich manuell prüfen lässt, bevor tatsächlich geschrieben wird (--dry-run Default, --apply zum Ausführen).

Grund für ein separates Inventarskript: 387 Dateien über 6 Top-Level-Ordner mit inkonsistenter Sprachordner-Benennung (01_DE_original, 01_EN_original, 1_EN_original [Tippfehler im Quellbaum], 02_CZ, 03_CZ_pro_celé_zařízení, …) – ein Trockenlauf mit Report ist nötig, um Mapping-Lücken zu erkennen, bevor Dateien verschoben/verarbeitet werden.

2. fortna_convert_chapters.py (neu)

Nutzt das Inventar aus Schritt 1 (oder scannt bei Bedarf direkt) und führt zwei Aktionsarten aus:

  • Volltext-Konvertierung für die 5 Hauptanleitungen: ruft die vorhandene Konvertierungslogik aus convert_to_rst.py wieder, aber mit expliziter Quelldatei-Liste (kein Verzeichnis-Scan) und fest zugeordnetem Zielkapitel (aus der Tabelle unten), statt der generischen CHAPTER_MAPPING-Heuristik. Überschreibt bestehende Ziel-RSTs/Bilder vollständig (Ordnerinhalt vorher leeren, dann neu schreiben).
  • Verzeichnislisten-Generierung für alle übrigen Kapitel (Zertifikate, Layouts, Schaltpläne, Notstopp/Bereichsstopp, Prüfberichte, Ersatzteile, Lieferantendoku): läuft über die im Inventar erkannten Dateien je Zielordner und rendert eine index.de.rst mit RST-list-table (Dateiname, Pfad, Größe).

Fest zugeordnete Hauptanleitungen (Quelle → Ziel):

Quelldatei (DE, aktuellster Stand in projekte/Fortna) Zielkapitel
02_INSTRUCTIONS.../01_DE_original/01_BETRIEBSANLEITUNG_500592_135776_V2.4.pdf 04_betrieb/betriebsanleitung/
02_INSTRUCTIONS.../01_DE_original/02_WARTUNGSANLEITUNG_500592_V2.3.pdf 05_wartung/wartungsanleitung/
02_INSTRUCTIONS.../01_DE_original/03_FUNKTION_STEUERUNG_ILS_500592_V2.2.pdf 06_steuerung/ils/
02_INSTRUCTIONS.../01_DE_original/04_FUNKTION_STEUERUNG_OMNIFLO_500592_V1.1.pdf 06_steuerung/omniflo/
02_INSTRUCTIONS.../01_DE_original/05_FUNKTION_STEUERUNG_LOADING_LOOP_500592_V2.3.pdf 06_steuerung/loading_loop/ (ersetzt Altbestand)

Alle fünf sind PDFs (keine .docx im Quellordner mehr vorhanden), daher wird der is_word_pdf()-Pfad aus convert_to_rst.py verwendet (pdfminer-Textextraktion). Die extra extract_products.py-Textheuristik (Schriftgrößen-basierte Überschriftenerkennung, Kopf-/Fußzeilen-Ausschluss nach Y-Position) ist für Word-erstellte PDFs wie diese robuster als die aktuelle convert_to_rst.py-Textheuristik (siehe Bekannte Schwachstellen) – Vorschlag: die Extraktionsroutine aus extract_products.py (extract_text_to_rst, _is_noise, extract_images) wiederverwenden/verallgemeinern statt der schwächeren pdfminer-Variante aus convert_to_rst.py.

3. fortna_init_project.py (neu)

Legt projekte/fortna/ an: anlage.json (Platzhalter mit Projektnummer 500592/KA135776, Sprache ["de"], normen_region offen als TODO), leere overrides/- und images/-Ordner mit .gitkeep, sowie ein kurzes README.md. Einmaliger Lauf, kein Datei-Scan nötig – im Wesentlichen ein Gerüst-Generator.

Bekannte Schwachstellen der bisherigen Konvertierung (zu beheben)

Der Altbestand in chapters/06_steuerung/loading_loop/ zeigt zwei konkrete Probleme der aktuellen convert_to_rst.py-Logik, die für die 5 Hauptanleitungen behoben werden sollten:

  1. Kopf-/Fußzeilen als Fließtext: Firmenadresse, Kontaktdaten, Projektnummer erscheinen als normale Absätze mitten im Dokument (weil pdfminer.high_level.extract_text keine Positionsinformation liefert, mit der man Kopf-/Fußzeilen herausfiltern könnte). extract_products.py löst das bereits über page.get_text("dict") mit Y-Positions-Filter (bbox[1] < 100 / bbox[3] > page_height - 50) – diese Technik übernehmen.
  2. Tabellen zerfallen in Einzelzeilen: Die Änderungshistorie-Tabelle wird zu einer Folge einzelner Absätze ohne Tabellenstruktur. Vorschlag für ersten Wurf: Tabellen nicht als RST-Table rekonstruieren (zu fehleranfällig aus PDF-Text), sondern als eingerückten .. code-block:: oder einfache Aufzählung kennzeichnen, mit .. todo:: Tabelle manuell nachbearbeiten-Marker – ehrlich statt falsch-schön.

Offene Punkte / Annahmen, die ich getroffen habe

  • Ich gehe davon aus, dass PV_FORTNA_SOURCE (oder ein --source-dir-Argument) als neue Umgebungsvariable/Parameter eingeführt wird, die auf c:\10-Develop\projekte\Fortna\ zeigt, da dieser Pfad außerhalb des Repos liegt und nicht über PV_DATA/PV_EXAMPLES erreichbar ist.
  • 02_sicherheit/ und 03_anlagenbeschreibung/ bleiben in diesem Durchlauf unangetastet – es gibt keine 1:1-Quelle dafür in projekte/Fortna/ (Risikobewertung liegt als separate .docx-Dateien direkt im Repo-Root von chapters/, siehe Git-Status, nicht Teil dieser Aufgabe).
  • Die generierten index.de.rst-Verzeichnislisten verweisen auf absolute oder repo-relative-konfigurierbare Pfade zum Rohmaterial – das bedeutet, das Zielsystem (Sphinx) bekommt keine eigenständig lauffähige Doku ohne Zugriff auf projekte/Fortna/. Das ist so gewollt gemäß der Entscheidung "nur Verzeichnisliste", sollte aber vor dem produktiven PDF-Build nochmal bewusst bestätigt werden (spätestens beim ersten sphinx-build-Versuch fehlen dann die Downloads/Links).

Nächste Schritte

  1. Dieses Konzept abstimmen (insb. Umgang mit 02_sicherheit/Risikobewertung-Docx im Root von chapters/, sowie die Pfad-Referenzierung in den Verzeichnislisten).
  2. fortna_inventory.py implementieren und Trockenlauf-Report gegen projekte/Fortna/ prüfen.
  3. fortna_convert_chapters.py implementieren (inkl. Wiederverwendung der extract_products.py-Textextraktion), auf 5 Hauptanleitungen anwenden, Ergebnis stichprobenhaft gegenlesen.
  4. fortna_init_project.py implementieren, projekte/fortna/ anlegen.
  5. bin/-Wrapper-Skripte (.bat/.sh) für alle drei ergänzen, analog bestehender Konvention.