Files
xsl-validator/CLAUDE.md
T
info 4733ea6b82 chore: OpenWiki-Dokumentation einrichten, AGENTS.md ergänzen
Fügt OpenWiki-Setup (Workflow, Seiten, CLAUDE.md-Verweis) und AGENTS.md
hinzu, damit wiederkehrende Code-Dokumentation automatisch gepflegt wird.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 14:22:57 +02:00

16 KiB

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 <xsl:import/> bzw. <xsl:include/> 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):

uv sync                    # Abhängigkeiten installieren
uv run python src/main.py  # Anwendung starten

Linting

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:

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):

# 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:

# 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

# 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:

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:

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:

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 <xsl:import/>- und <xsl:include/>-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

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.