# CLAUDE.md Spreche mit mir auf Deutsch! (Communicate with me in German!) ## Projektübersicht DocuMentor ist eine PySide6-basierte Desktop-Anwendung zur Verwaltung und Validierung von XSL-Transformationen mit XML-Dateien. Sie bietet eine GUI zur Konfiguration von Transformations-Toolchains (Saxon, Apache FOP, diff-pdf) und zur Verwaltung von PDF-Generierungsprojekten mit PostgreSQL-Datenbankintegration. ## Anvisiertes Nutzungsszenario Der primäre Einsatz ist die kontinuierliche Weiterentwicklung von PDF-Dokumenten in Flexnow (Software zur Prüfungsverwaltung). Dabei handelt es sich beispielsweise um amtliche Urkunden, Zeugnisse und Bescheide. Die Basis bilden etwa 100 XSL-Dateien. Die meisten sind mittels `` bzw. `` miteinander verknüpft (ähnlich der Klassen-Vererbung). Daher können sich Änderungen in einer XSL-Datei auf (unerwartet) viele andere auswirken. Um diese Auswirkungen im Auge zu behalten, wird DocuMentor entwickelt. **Typischer Workflow:** 1. Entwickler führt benötigte Änderungen an den XSL-Dateien durch 2. Entwickler startet die Transformation im DocuMentor und begutachtet die generierte PDF-Diff 3. Prüfung: Wurden die richtigen PDF-Dateien geändert? 4. Prüfung: Hat die Änderung der XSL-Dateien die erhoffte Änderung in den PDF-Dateien ergeben? Diese Schritte können sich mehrfach wiederholen. Da der DocuMentor permanent im Hintergrund läuft, ist ein sparsamer Umgang mit RAM wichtig: - Worker-Pools nach Verwendung herunterfahren - Große Datenstrukturen frühzeitig freigeben - Polars DataFrames statt Pandas (geringerer RAM-Verbrauch) - Lazy Loading wo möglich ## Entwicklungskommandos ### Paketverwaltung Dieses Projekt verwendet den `uv` Paketmanager (nicht pip oder poetry): ```bash uv sync # Abhängigkeiten installieren uv run python src/main.py # Anwendung starten ``` ### Linting ```bash uv run ruff check # Code-Style prüfen (Zeilenlänge: 120) uv run ruff format # Code formatieren ``` ### Tests Dieses Projekt verwendet KEINE pytest/unittest-Frameworks. Tests sind standalone Python-Skripte: ```bash uv run python test_hash_implementation.py # Hash-Tests uv run python test_xml_hash_duplicate_detection.py # Duplikatserkennung ``` ### Commit Jedes mal bei Commit diese Skills nutzen: - /license-check - /version-bump ## Code-Style-Richtlinien ### Import-Organisation Reihenfolge (keine Leerzeilen zwischen Gruppen): ```python # 1. Standard Library import os import sys import logging from pathlib import Path from typing import TYPE_CHECKING # 2. Drittanbieter from PySide6.QtCore import Qt, QThread, Signal from PySide6.QtWidgets import QDialog, QMainWindow from pydantic import BaseModel, Field # 3. Lokale Imports (IMMER absolute Imports, KEINE relativen .imports) from conf import app_settings, TreeNode, XslFile from ui.MainWindow import MainWindow ``` - `TYPE_CHECKING` für zirkuläre Import-Vermeidung nutzen - Keine relativen Imports (`.` oder `..`) ### Type Annotations Moderne Union-Syntax verwenden: ```python # RICHTIG def transform(xml_path: Path, params: dict[str, str]) -> tuple[bool, str]: result: str | None = None files: list[Path] = [] # FALSCH def transform(xml_path, params): # Keine Annotations result: Optional[str] = None # Alte Union-Syntax files: List[Path] = [] # Großgeschriebene Types ``` ### Naming Conventions ```python # Klassen: PascalCase class SaxonWorkerPool: # Funktionen/Methoden: snake_case def transform_saxon(xml_file: Path) -> bool: # Private Methoden: _snake_case mit Unterstrich def _create_tree_item(self, node: TreeNode): # Konstanten: UPPER_CASE SAXON_WORKER_JAVA = """...""" ``` ### Formatierung - **Zeilenlänge:** 120 Zeichen (via Ruff konfiguriert) - **Strings:** Bevorzugt Double-Quotes `"..."`, aber konsistent im File - **Trailing Commas:** Bei mehrzeiligen Strukturen verwenden ### Error Handling IMMER Logging statt `print()` verwenden: ```python import logging logger = logging.getLogger(__name__) def transform(xml_path: Path) -> tuple[bool, str]: try: logger.info(f"Transformation gestartet: {xml_path}") result = do_transform(xml_path) return True, "Erfolg" except FileNotFoundError as e: error_msg = f"XML-Datei nicht gefunden: {xml_path}" logger.error(error_msg) return False, error_msg except Exception as e: error_msg = f"Fehler bei Transformation: {str(e)}" logger.exception(error_msg) # Mit Stack Trace return False, error_msg ``` - `logger.debug()` für Debugging-Infos - `logger.info()` für normale Operationen - `logger.warning()` für Warnungen - `logger.error()` für Fehler ohne Stack Trace - `logger.exception()` für Fehler MIT Stack Trace - Fehlermeldungen auf Deutsch ### Docstrings Google-Style auf Deutsch: ```python def transform_xml_to_pdf(xml_path: Path, xsl_path: Path, output_dir: Path) -> tuple[bool, str]: """ Transformiert eine XML-Datei mit XSL zu PDF. Args: xml_path: Pfad zur XML-Eingabedatei xsl_path: Pfad zum XSL-Stylesheet output_dir: Zielverzeichnis für PDF-Ausgabe Returns: tuple[bool, str]: (Erfolg, Fehlermeldung oder Info-Text) Raises: FileNotFoundError: Wenn XML- oder XSL-Datei nicht existiert """ ``` ### Pfadbehandlung - Immer `pathlib.Path`-Objekte verwenden, keine Strings - `expanduser()` und `expandvars()` für Benutzer-/Umgebungspfade verwenden - Projektrelative Pfade werden als relativ gespeichert, zur Laufzeit gegen `project_dir` aufgelöst ## Architektur ### Konfigurationssystem (src/conf.py) Die Anwendung verwendet ein zentralisiertes Konfigurationsmodell mit Pydantic: - **AppSettings**: Globales Singleton (`app_settings`), das die gesamte Anwendungskonfiguration speichert - Wird an plattformspezifischen Orten gespeichert: - Linux: `~/.config/DocuMentor/config.json` - Windows: `%APPDATA%\DocuMentor\config.json` - macOS: `~/Library/Application Support/DocuMentor/config.json` - Enthält Listen von Tools: `java_vms`, `saxon_jars`, `apache_fops`, `diff_pdfs`, `xsl_dirs`, `postgresql_dbs` - **ProjectData**: Projektspezifische Einstellungen, die in `project.yaml` im jeweiligen Projektverzeichnis gespeichert werden - Enthält hierarchische Baumstruktur von Transformationsknoten - Verwendet `TreeNode` und `XslFile` zur Organisation - Jede `XmlFile` hat eine optionale `hashsum` (blake2b) zur Änderungsverfolgung ### Wichtige Datenmodelle 1. **Tool-Konfigurationsmodelle** (JavaVm, SaxonJar, ApacheFop, DiffPdf, XslDir, PostgreSqlDb): - Jedes hat eine `id` und `version` - Speichert Pfade zu Binärdateien/Verzeichnissen 2. **Project-Modell**: - Referenziert Tool-Konfigurationen über ID - Verlinkt zu einem Projektverzeichnis mit `project.yaml` - Hat Hilfsmethoden wie `getXsl()`, `getJavaVm()` um IDs in Namen/Versionen aufzulösen 3. **Baumstruktur** (TreeNode → XslFile → XmlFile): - Hierarchische Organisation von Transformations-Workflows - `TreeNode`: Organisationseinheit mit `xslt_params` und Kindknoten/-dateien - `XslFile`: XSL-Stylesheet mit zugehörigen XML-Dateien und XSLT-Parametern - `XmlFile`: XML-Eingabedatei mit optionalem blake2b-Hash ### UI-Architektur (src/ui/) Die Anwendung folgt einem spezifischen PySide6-Muster: 1. **UI-Definitionsdateien** (`*_ui.py`): Automatisch generiert aus UI-Designer-Dateien - Diese Dateien definieren die UI-Struktur als Klassen (z.B. `Ui_MainWindow`) - Sollten NICHT manuell bearbeitet werden 2. **Implementierungsdateien** (ohne `_ui` Suffix): Tatsächliche Dialog-/Fenster-Implementierungen - Importieren und verwenden die entsprechende `*_ui.py` Datei - Enthalten Business-Logik und Signal/Slot-Verbindungen - Beispiel: `MainWindow.py` verwendet `Ui_MainWindow` aus `MainWinddow_ui.py` Beim Erstellen neuer Dialoge: - Immer zuerst eine entsprechende UI-Datei erstellen - Die UI-Datei wird automatisch als `.py`-Datei von einer VS Code Extension generiert - Die generierte UI-Klasse in der Implementierungsdatei importieren und verwenden **UI-Import-Pattern:** ```python from PySide6.QtWidgets import QDialog from ui.JavaVmConfigDialog_ui import Ui_JavaVmConfigDialog class JavaVmConfigDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_JavaVmConfigDialog() self.ui.setupUi(self) # Signale NACH setupUi() verbinden self.ui.browseButton.clicked.connect(self._browse_file) ``` - UI-Klassen NIEMALS direkt erben, nur als `self.ui` Member - Alle Widgets über `self.ui.widgetName` zugreifen - Signal-Verbindungen immer NACH `setupUi()` aufrufen ### Hauptfenster (src/ui/MainWindow.py) Zentrale Schaltstelle der Anwendung mit mehreren wichtigen Verantwortlichkeiten: 1. **Projektverwaltung**: - Öffnet und verwaltet PDF-Transformationsprojekte - Lädt/speichert `ProjectData` aus `project.yaml` Dateien 2. **Tree Widget**: Zeigt hierarchische Struktur von Transformationsknoten an - Kontextmenüs zum Hinzufügen/Bearbeiten/Löschen von Knoten, XSL-Dateien und XML-Dateien - Drag-and-Drop-Unterstützung für XML-Dateien 3. **PDF-Vergleichsansicht**: - Drei-Panel-Ansicht (Referenz, Diff, Neu) - Alpha-Blending für visuellen Vergleich - Zoom- und Pan-Funktionalität 4. **Asynchrone Operationen**: - `XmlHashCalculatorThread`: Hintergrund-blake2b-Hash-Berechnung für XML-Dateien - `DatabaseTestThread` (in PostgreSqlConfigDialog): Asynchrones Testen von Datenbankverbindungen ### XSL-Abhängigkeitsgraph (src/ui/XslDependencyDialog.py) Interaktiver Dialog zur Visualisierung von ``- und ``-Abhängigkeiten zwischen XSL-Dateien: - Sidebar mit Suchfilter zur Navigation - Abhängigkeitsgraph-Darstellung via vis.js - Parsing der XSL-Dateien mit lxml ### Hash-Berechnungssystem Die Anwendung verwendet blake2b-Hashing zur Verfolgung von XML-Dateiänderungen: - **Automatisch**: Hashes werden berechnet, wenn Projekte geladen werden (nur für Dateien ohne existierenden Hash) - **Asynchron**: Hintergrund-Thread (`XmlHashCalculatorThread`) um die UI reaktionsfähig zu halten - **Format**: `blake2b:<64-Zeichen-Hexdigest>` - **Speicherung**: Persistiert in `project.yaml` innerhalb jedes `XmlFile`-Objekts - **Details**: Siehe `docs/blake2b_hash_implementation.md` ### Theme-System Die Anwendung unterstützt mehrere Qt-Themes: - Theme-Auswahlmenü wird dynamisch aus `QStyleFactory.keys()` befüllt - Theme-Präferenz wird in `AppSettings.theme` gespeichert ### Datenbankintegration PostgreSQL-Integration mit Polars und ConnectorX: - Konfiguration wird im `PostgreSqlDb`-Modell mit SSL-Modus-Unterstützung gespeichert - SQL-Abfragen werden asynchron via `DatabaseQueryThread` im `DatabaseMixin` ausgeführt - Ergebnisse werden in Polars DataFrames geladen ### Thread-basierte Operationen ```python from PySide6.QtCore import QThread, Signal class HashCalculatorThread(QThread): progress = Signal(int) finished = Signal(dict) def __init__(self, files: list[Path]): super().__init__() self.files = files def run(self): for i, file_path in enumerate(self.files): hash_value = calculate_hash(file_path) self.progress.emit(i + 1) self.finished.emit(results) # Verwendung thread = HashCalculatorThread(xml_files) thread.progress.connect(self._on_progress) thread.finished.connect(self._on_finished) thread.start() # NICHT run() direkt aufrufen! ``` ## Wichtige Konventionen ### Deutsche Sprache Die Codebasis verwendet Deutsch für: - UI-Texte und Labels - Kommentare und Dokumentation - Variablennamen wo kontextuell passend - Log-Meldungen ### ID-basierte Lookups Konfigurationsentitäten (Tools, Datenbanken) werden in Projekten über ID referenziert. Die Hilfsmethoden des `Project`-Modells (`getXsl()`, `getJavaVm()`, etc.) verwenden, um IDs in Anzeigewerte aufzulösen. ### Einstellungspersistenz - Globale Einstellungen: `app_settings.save()` nach Änderungen aufrufen - Projekteinstellungen: `project_data.writeSettings(project_dir)` nach Änderungen aufrufen ## Arbeiten mit der Codebasis ### Neue Tool-Konfigurationen hinzufügen 1. Modell zu `conf.py` hinzufügen (ähnlich wie `JavaVm`, `SaxonJar`) 2. Listenfeld zu `AppSettings` hinzufügen 3. Konfigurationsdialog in `src/ui/` erstellen (UI-Datei + Implementierung) 4. Zu `AppSettings.py` Tabs hinzufügen 5. `Project`-Modell aktualisieren, falls das Tool projektspezifisch sein soll ### Neue Baumoperationen hinzufügen 1. Aktion zum Kontextmenü in `_create_context_menu_for_type()` hinzufügen 2. Handler-Methode implementieren nach Namensschema `_action_tree_node()`, `_action_xsl_file()`, etc. 3. Baum nach Änderungen mit `_load_nodes_to_tree()` aktualisieren 4. `self.project_data.writeSettings(self.project.project_dir)` aufrufen um Änderungen zu persistieren ### Projektstruktur modifizieren Das `ProjectData`-Modell ist die Quelle der Wahrheit. Alle Änderungen an der Baumstruktur müssen: 1. Die `project_data.nodes` Liste modifizieren 2. `project_data.writeSettings()` aufrufen um zu persistieren 3. Baum mit `_load_nodes_to_tree()` neu laden um Änderungen in der UI zu reflektieren # CLAUDE.md Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed. **Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment. ## 1. Think Before Coding **Don't assume. Don't hide confusion. Surface tradeoffs.** Before implementing: - State your assumptions explicitly. If uncertain, ask. - If multiple interpretations exist, present them - don't pick silently. - If a simpler approach exists, say so. Push back when warranted. - If something is unclear, stop. Name what's confusing. Ask. ## 2. Simplicity First **Minimum code that solves the problem. Nothing speculative.** - No features beyond what was asked. - No abstractions for single-use code. - No "flexibility" or "configurability" that wasn't requested. - No error handling for impossible scenarios. - If you write 200 lines and it could be 50, rewrite it. Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify. ## 3. Surgical Changes **Touch only what you must. Clean up only your own mess.** When editing existing code: - Don't "improve" adjacent code, comments, or formatting. - Don't refactor things that aren't broken. - Match existing style, even if you'd do it differently. - If you notice unrelated dead code, mention it - don't delete it. When your changes create orphans: - Remove imports/variables/functions that YOUR changes made unused. - Don't remove pre-existing dead code unless asked. The test: Every changed line should trace directly to the user's request. ## 4. Goal-Driven Execution **Define success criteria. Loop until verified.** Transform tasks into verifiable goals: - "Add validation" → "Write tests for invalid inputs, then make them pass" - "Fix the bug" → "Write a test that reproduces it, then make it pass" - "Refactor X" → "Ensure tests pass before and after" For multi-step tasks, state a brief plan: ``` 1. [Step] → verify: [check] 2. [Step] → verify: [check] 3. [Step] → verify: [check] ``` Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification. --- **These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes. ## OpenWiki This repository uses OpenWiki for recurring code documentation. Start with `openwiki/quickstart.md`, then follow its links to architecture, workflows, domain concepts, operations, integrations, testing guidance, and source maps. The scheduled OpenWiki GitHub Actions workflow refreshes the repository wiki. Do not hand-edit generated OpenWiki pages unless explicitly asked; prefer updating source code/docs and letting OpenWiki regenerate.