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>
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:
- Entwickler führt benötigte Änderungen an den XSL-Dateien durch
- Entwickler startet die Transformation im DocuMentor und begutachtet die generierte PDF-Diff
- Prüfung: Wurden die richtigen PDF-Dateien geändert?
- 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_CHECKINGfü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-Infoslogger.info()für normale Operationenlogger.warning()für Warnungenlogger.error()für Fehler ohne Stack Tracelogger.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()undexpandvars()für Benutzer-/Umgebungspfade verwenden- Projektrelative Pfade werden als relativ gespeichert, zur Laufzeit gegen
project_diraufgelö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
- Linux:
- Enthält Listen von Tools:
java_vms,saxon_jars,apache_fops,diff_pdfs,xsl_dirs,postgresql_dbs
- Wird an plattformspezifischen Orten gespeichert:
-
ProjectData: Projektspezifische Einstellungen, die in
project.yamlim jeweiligen Projektverzeichnis gespeichert werden- Enthält hierarchische Baumstruktur von Transformationsknoten
- Verwendet
TreeNodeundXslFilezur Organisation - Jede
XmlFilehat eine optionalehashsum(blake2b) zur Änderungsverfolgung
Wichtige Datenmodelle
-
Tool-Konfigurationsmodelle (JavaVm, SaxonJar, ApacheFop, DiffPdf, XslDir, PostgreSqlDb):
- Jedes hat eine
idundversion - Speichert Pfade zu Binärdateien/Verzeichnissen
- Jedes hat eine
-
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
-
Baumstruktur (TreeNode → XslFile → XmlFile):
- Hierarchische Organisation von Transformations-Workflows
TreeNode: Organisationseinheit mitxslt_paramsund Kindknoten/-dateienXslFile: XSL-Stylesheet mit zugehörigen XML-Dateien und XSLT-ParameternXmlFile: XML-Eingabedatei mit optionalem blake2b-Hash
UI-Architektur (src/ui/)
Die Anwendung folgt einem spezifischen PySide6-Muster:
-
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
- Diese Dateien definieren die UI-Struktur als Klassen (z.B.
-
Implementierungsdateien (ohne
_uiSuffix): Tatsächliche Dialog-/Fenster-Implementierungen- Importieren und verwenden die entsprechende
*_ui.pyDatei - Enthalten Business-Logik und Signal/Slot-Verbindungen
- Beispiel:
MainWindow.pyverwendetUi_MainWindowausMainWinddow_ui.py
- Importieren und verwenden die entsprechende
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.uiMember - Alle Widgets über
self.ui.widgetNamezugreifen - Signal-Verbindungen immer NACH
setupUi()aufrufen
Hauptfenster (src/ui/MainWindow.py)
Zentrale Schaltstelle der Anwendung mit mehreren wichtigen Verantwortlichkeiten:
-
Projektverwaltung:
- Öffnet und verwaltet PDF-Transformationsprojekte
- Lädt/speichert
ProjectDataausproject.yamlDateien
-
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
-
PDF-Vergleichsansicht:
- Drei-Panel-Ansicht (Referenz, Diff, Neu)
- Alpha-Blending für visuellen Vergleich
- Zoom- und Pan-Funktionalität
-
Asynchrone Operationen:
XmlHashCalculatorThread: Hintergrund-blake2b-Hash-Berechnung für XML-DateienDatabaseTestThread(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.yamlinnerhalb jedesXmlFile-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.themegespeichert
Datenbankintegration
PostgreSQL-Integration mit Polars und ConnectorX:
- Konfiguration wird im
PostgreSqlDb-Modell mit SSL-Modus-Unterstützung gespeichert - SQL-Abfragen werden asynchron via
DatabaseQueryThreadimDatabaseMixinausgefü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
- Modell zu
conf.pyhinzufügen (ähnlich wieJavaVm,SaxonJar) - Listenfeld zu
AppSettingshinzufügen - Konfigurationsdialog in
src/ui/erstellen (UI-Datei + Implementierung) - Zu
AppSettings.pyTabs hinzufügen Project-Modell aktualisieren, falls das Tool projektspezifisch sein soll
Neue Baumoperationen hinzufügen
- Aktion zum Kontextmenü in
_create_context_menu_for_type()hinzufügen - Handler-Methode implementieren nach Namensschema
_action_tree_node(),_action_xsl_file(), etc. - Baum nach Änderungen mit
_load_nodes_to_tree()aktualisieren 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:
- Die
project_data.nodesListe modifizieren project_data.writeSettings()aufrufen um zu persistieren- 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.