Docs: AGENTS.md mit CLAUDE.md konsolidiert und Einzeldatei entfernt

Code-Style-Richtlinien (Imports, Type Annotations, Naming, Logging,
Docstrings), UI-Import-Pattern, Thread-Pattern, RAM-Optimierung und
Test-Infos aus AGENTS.md übernommen. Veraltete Einträge korrigiert
(qdarktheme entfernt, _execute_sql_query → DatabaseQueryThread,
XslDependencyDialog dokumentiert).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 15:25:10 +01:00
parent 3dcbf783b1
commit 64408157ba
2 changed files with 350 additions and 549 deletions
-365
View File
@@ -1,365 +0,0 @@
# AGENTS.md
**Entwicklerrichtlinien für DocuMentor (KI-Coding-Agenten)**
Diese Datei enthält wichtige Richtlinien für Coding-Agenten, die in diesem Repository arbeiten.
## Sprache
**Alle Kommunikation, Code-Kommentare und UI-Texte auf Deutsch!**
- Kommentare: Deutsch
- Docstrings: Deutsch
- Log-Meldungen: Deutsch
- Variablennamen: Deutsch wo kontextuell passend
- UI-Labels: Deutsch
## Build & Lint Kommandos
### Paketmanager: `uv` (NICHT pip oder poetry!)
```bash
# Abhängigkeiten installieren
uv sync
# Anwendung starten
uv run python src/main.py
# Code-Style prüfen (Zeilenlänge: 120)
uv run ruff check
# Code automatisch formatieren
uv run ruff format
```
### Tests ausführen
```bash
# Alle Hash-Tests
uv run python test_hash_implementation.py
# Duplikatserkennung testen
uv run python test_xml_hash_duplicate_detection.py
# Einzelnen Test ausführen (direkter Python-Aufruf)
uv run python test_hash_implementation.py
```
**Hinweis:** Dieses Projekt verwendet KEINE pytest/unittest-Frameworks. Tests sind standalone Python-Skripte mit if __name__ == "__main__".
## 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 Optional, TYPE_CHECKING
# 2. Drittanbieter (Third-Party)
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
from ui.JavaVmConfigDialog_ui import Ui_JavaVmConfigDialog
```
**Wichtig:**
- Immer `from pathlib import Path` verwenden
- NIEMALS String-Pfade verwenden, IMMER `Path`-Objekte
- `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
```
**Pflicht:**
- Alle Funktionsparameter annotieren
- Alle Rückgabewerte annotieren
- Moderne Syntax: `str | None` statt `Optional[str]`
- Container: `list[str]`, `dict[str, str]`, `tuple[int, int]`
### Naming Conventions
```python
# Klassen: PascalCase
class SaxonWorkerPool:
class TransformationJob:
# Funktionen/Methoden: snake_case
def transform_saxon(xml_file: Path) -> bool:
def calculate_hash(content: bytes) -> str:
# Private Methoden: _snake_case mit Unterstrich
def _create_tree_item(self, node: TreeNode):
def _load_project_data(self):
# Variablen: snake_case
xml_file_path = Path("test.xml")
diff_pdf_count = 0
self.current_zoom = 100
# Konstanten: UPPER_CASE
SAXON_WORKER_JAVA = """..."""
MAX_RETRY_COUNT = 3
```
### Formatierung & Linting
- **Zeilenlänge:** 120 Zeichen (via Ruff konfiguriert)
- **Strings:** Bevorzugt Double-Quotes `"..."`, aber konsistent im File
- **Trailing Commas:** Bei mehrzeiligen Strukturen verwenden
- **Ruff:** Alle Warnings beheben vor Commit
### Error Handling
**IMMER Logging statt print() verwenden:**
```python
import logging
logger = logging.getLogger(__name__)
def transform(xml_path: Path) -> tuple[bool, str]:
try:
# Operation durchführen
logger.info(f"Transformation gestartet: {xml_path}")
result = do_transform(xml_path)
logger.debug(f"Zwischenergebnis: {result}")
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
```
**Pattern:**
- `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
"""
```
**Modul-Docstrings am Dateianfang:**
```python
"""
Saxon Worker Pool - Persistente JVM-Prozesse für schnelle XSLT-Transformationen.
Eliminiert JVM-Startup-Overhead durch Vorinitialisierung von N Worker-Prozessen.
Verwendet multiprocessing.Queue für Thread-sichere Kommunikation.
"""
```
## Pydantic Models
### Definition
```python
from pydantic import BaseModel, Field
class Project(BaseModel):
id: int = Field(..., description="Eindeutige Projekt-ID", gt=0)
name: str = Field(..., description="Projekt-Name", min_length=1, max_length=255)
project_dir: Path = Field(..., description="Pfad zum Projekt-Verzeichnis")
# Helper-Methoden direkt im Model erlaubt
def getJavaVm(self) -> str:
global app_settings
value = [x.version for x in app_settings.java_vms if x.id == self.java_vm_id]
return value[0] if len(value) else ""
```
### Settings speichern
```python
from conf import app_settings, ProjectData
# Globale Einstellungen
app_settings.theme = "Fusion"
app_settings.save() # WICHTIG: Nicht vergessen!
# Projekteinstellungen
project_data = ProjectData.readSettings(project_dir)
project_data.nodes.append(new_node)
project_data.writeSettings(project_dir) # WICHTIG: Persistieren!
```
## PySide6 UI-Integration
### KRITISCH: UI-Dateien nicht manuell bearbeiten!
```
src/ui/
├── MainWindow.ui # Qt Designer Datei (editieren erlaubt)
├── MainWinddow_ui.py # AUTO-GENERIERT (NICHT BEARBEITEN!)
└── MainWindow.py # Implementierung (hier Code schreiben)
```
### UI-Import-Pattern
```python
# In src/ui/JavaVmConfigDialog.py
from PySide6.QtWidgets import QDialog
from ui.JavaVmConfigDialog_ui import Ui_JavaVmConfigDialog
class JavaVmConfigDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
# UI einrichten
self.ui = Ui_JavaVmConfigDialog()
self.ui.setupUi(self)
# Signale NACH setupUi() verbinden
self.ui.browseButton.clicked.connect(self._browse_file)
def _browse_file(self):
# Widgets über self.ui.widgetName zugreifen
current_path = self.ui.pathLineEdit.text()
...
```
**Wichtig:**
- UI-Klassen NIEMALS direkt erben, nur als `self.ui` Member
- Alle Widgets über `self.ui.widgetName` zugreifen
- Signal-Verbindungen immer NACH `setupUi()` aufrufen
## Projektstruktur-Änderungen
Beim Modifizieren der Baumstruktur (TreeNode, XslFile, XmlFile):
```python
# 1. ProjectData modifizieren
self.project_data.nodes.append(new_node)
# 2. SOFORT persistieren
self.project_data.writeSettings(self.project.project_dir)
# 3. UI neu laden
self._load_nodes_to_tree()
```
**Pattern:** Immer in dieser Reihenfolge: Modifizieren → Speichern → UI aktualisieren
## Wichtige Konventionen
### Pathlib IMMER verwenden
```python
# RICHTIG
from pathlib import Path
xml_path = Path("data/test.xml")
xml_path = Path.home() / ".config" / "app" / "config.json"
xml_path = Path(os.path.expandvars("$HOME/data")).expanduser()
if xml_path.exists():
content = xml_path.read_text(encoding="utf-8")
# FALSCH
xml_path = "data/test.xml" # String-Pfad
xml_path = os.path.join("data", "test.xml") # os.path statt pathlib
```
### Globale Singletons
```python
# In conf.py am Modulende
app_settings = AppSettings()
# In anderen Modulen
from conf import app_settings
# Verwendung
java_vm = [x for x in app_settings.java_vms if x.id == vm_id][0]
```
### Thread-basierte Operationen
```python
from PySide6.QtCore import QThread, Signal
class HashCalculatorThread(QThread):
# Signale für Thread-sichere Kommunikation
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!
```
## RAM-Optimierung
Da DocuMentor permanent läuft, sparsam mit RAM umgehen:
- Worker-Pools nach Verwendung herunterfahren
- Große Datenstrukturen frühzeitig freigeben
- Polars DataFrames statt Pandas (geringerer RAM-Verbrauch)
- Lazy Loading wo möglich
---
**Zusammenfassung:** Deutsch sprechen, pathlib verwenden, Typen annotieren, Ruff nutzen, UI-Dateien nicht anfassen!
+350 -184
View File
@@ -1,184 +1,350 @@
# CLAUDE.md # CLAUDE.md
Spreche mit mir auf Deutsch! (Communicate with me in German!) Spreche mit mir auf Deutsch! (Communicate with me in German!)
## Projektübersicht ## Projektübersicht
DocuMentor (ehemals xsl-validator) 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. DocuMentor (ehemals xsl-validator) 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 ## 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. 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. 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:** **Typischer Workflow:**
1. Entwickler führt benötigte Änderungen an den XSL-Dateien durch 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 2. Entwickler startet die Transformation im DocuMentor und begutachtet die generierte PDF-Diff
3. Prüfung: Wurden die richtigen PDF-Dateien geändert? 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? 4. Prüfung: Hat die Änderung der XSL-Dateien die erhoffte Änderung in den PDF-Dateien ergeben?
Diese Schritte können sich mehrfach wiederholen. Diese Schritte können sich mehrfach wiederholen.
Da der DocuMentor permanent im Hintergrund läuft, ist ein sparsamer Umgang mit RAM wichtig. Da der DocuMentor permanent im Hintergrund läuft, ist ein sparsamer Umgang mit RAM wichtig:
- Worker-Pools nach Verwendung herunterfahren
## PySide6-GUI - Große Datenstrukturen frühzeitig freigeben
- Beim Erstellen neuer Dialoge und Fenster sollte immer eine entsprechende UI-Datei erstellt werden - Polars DataFrames statt Pandas (geringerer RAM-Verbrauch)
- Der Entwickler sollte später in der Lage sein, den neuen Dialog bzw. Fenster über diese UI-Datei zu gestalten - Lazy Loading wo möglich
- Aus der UI-Datei wird in Visual Studio Code über eine Erweiterung automatisch eine .py-Datei erzeugt
- Die automatisch generierte .py-Datei muss in den Code eingebunden und verwendet werden ## Entwicklungskommandos
## Entwicklungskommandos ### Paketverwaltung
Dieses Projekt verwendet den `uv` Paketmanager (nicht pip oder poetry):
### Paketverwaltung ```bash
Dieses Projekt verwendet den `uv` Paketmanager (nicht pip oder poetry): uv sync # Abhängigkeiten installieren
```bash uv run python src/main.py # Anwendung starten
uv sync # Abhängigkeiten installieren ```
uv run python src/main.py # Anwendung starten
uv run python test_hash_implementation.py # Hash-Tests ausführen ### Linting
``` ```bash
uv run ruff check # Code-Style prüfen (Zeilenlänge: 120)
### Linting uv run ruff format # Code formatieren
```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
## Architektur uv run python test_hash_implementation.py # Hash-Tests
uv run python test_xml_hash_duplicate_detection.py # Duplikatserkennung
### Konfigurationssystem (src/conf.py) ```
Die Anwendung verwendet ein zentralisiertes Konfigurationsmodell mit Pydantic: ## Code-Style-Richtlinien
- **AppSettings**: Globales Singleton (`app_settings`), das die gesamte Anwendungskonfiguration speichert ### Import-Organisation
- Wird an plattformspezifischen Orten gespeichert:
- Linux: `~/.config/DocuMentor/config.json` Reihenfolge (keine Leerzeilen zwischen Gruppen):
- Windows: `%APPDATA%\DocuMentor\config.json` ```python
- macOS: `~/Library/Application Support/DocuMentor/config.json` # 1. Standard Library
- Enthält Listen von Tools: `java_vms`, `saxon_jars`, `apache_fops`, `diff_pdfs`, `xsl_dirs`, `postgresql_dbs` import os
import sys
- **ProjectData**: Projektspezifische Einstellungen, die in `project.yaml` im jeweiligen Projektverzeichnis gespeichert werden import logging
- Enthält hierarchische Baumstruktur von Transformationsknoten from pathlib import Path
- Verwendet `TreeNode` und `XslFile` zur Organisation from typing import TYPE_CHECKING
- Jede `XmlFile` hat eine optionale `hashsum` (blake2b) zur Änderungsverfolgung
# 2. Drittanbieter
### Wichtige Datenmodelle from PySide6.QtCore import Qt, QThread, Signal
from PySide6.QtWidgets import QDialog, QMainWindow
1. **Tool-Konfigurationsmodelle** (JavaVm, SaxonJar, ApacheFop, DiffPdf, XslDir, PostgreSqlDb): from pydantic import BaseModel, Field
- Jedes hat eine `id` und `version`
- Speichert Pfade zu Binärdateien/Verzeichnissen # 3. Lokale Imports (IMMER absolute Imports, KEINE relativen .imports)
from conf import app_settings, TreeNode, XslFile
2. **Project-Modell**: from ui.MainWindow import MainWindow
- Referenziert Tool-Konfigurationen über ID ```
- Verlinkt zu einem Projektverzeichnis mit `project.yaml`
- Hat Hilfsmethoden wie `getXsl()`, `getJavaVm()` um IDs in Namen/Versionen aufzulösen - `TYPE_CHECKING` für zirkuläre Import-Vermeidung nutzen
- Keine relativen Imports (`.` oder `..`)
3. **Baumstruktur** (TreeNode → XslFile → XmlFile):
- Hierarchische Organisation von Transformations-Workflows ### Type Annotations
- `TreeNode`: Organisationseinheit mit `xslt_params` und Kindknoten/-dateien
- `XslFile`: XSL-Stylesheet mit zugehörigen XML-Dateien und XSLT-Parametern Moderne Union-Syntax verwenden:
- `XmlFile`: XML-Eingabedatei mit optionalem blake2b-Hash ```python
# RICHTIG
### UI-Architektur (src/ui/) def transform(xml_path: Path, params: dict[str, str]) -> tuple[bool, str]:
result: str | None = None
Die Anwendung folgt einem spezifischen PySide6-Muster: files: list[Path] = []
1. **UI-Definitionsdateien** (`*_ui.py`): Automatisch generiert aus UI-Designer-Dateien # FALSCH
- Diese Dateien definieren die UI-Struktur als Klassen (z.B. `Ui_MainWindow`) def transform(xml_path, params): # Keine Annotations
- Sollten NICHT manuell bearbeitet werden result: Optional[str] = None # Alte Union-Syntax
files: List[Path] = [] # Großgeschriebene Types
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 ### Naming Conventions
- Beispiel: `MainWindow.py` verwendet `Ui_MainWindow` aus `MainWinddow_ui.py`
```python
Beim Erstellen neuer Dialoge: # Klassen: PascalCase
- Immer zuerst eine entsprechende UI-Datei erstellen class SaxonWorkerPool:
- Die UI-Datei wird automatisch als `.py`-Datei von einer VS Code Extension generiert
- Die generierte UI-Klasse in der Implementierungsdatei importieren und verwenden # Funktionen/Methoden: snake_case
def transform_saxon(xml_file: Path) -> bool:
### Hauptfenster (src/ui/MainWindow.py)
# Private Methoden: _snake_case mit Unterstrich
Zentrale Schaltstelle der Anwendung mit mehreren wichtigen Verantwortlichkeiten: def _create_tree_item(self, node: TreeNode):
1. **Projektverwaltung**: # Konstanten: UPPER_CASE
- Öffnet und verwaltet PDF-Transformationsprojekte SAXON_WORKER_JAVA = """..."""
- Lädt/speichert `ProjectData` aus `project.yaml` Dateien ```
2. **Tree Widget**: Zeigt hierarchische Struktur von Transformationsknoten an ### Formatierung
- Kontextmenüs zum Hinzufügen/Bearbeiten/Löschen von Knoten, XSL-Dateien und XML-Dateien - **Zeilenlänge:** 120 Zeichen (via Ruff konfiguriert)
- Drag-and-Drop-Unterstützung für XML-Dateien - **Strings:** Bevorzugt Double-Quotes `"..."`, aber konsistent im File
- **Trailing Commas:** Bei mehrzeiligen Strukturen verwenden
3. **PDF-Vergleichsansicht**:
- Drei-Panel-Ansicht (Referenz, Diff, Neu) ### Error Handling
- Alpha-Blending für visuellen Vergleich
- Zoom- und Pan-Funktionalität IMMER Logging statt `print()` verwenden:
```python
4. **Asynchrone Operationen**: import logging
- `XmlHashCalculatorThread`: Hintergrund-blake2b-Hash-Berechnung für XML-Dateien logger = logging.getLogger(__name__)
- `DatabaseTestThread` (in PostgreSqlConfigDialog): Asynchrones Testen von Datenbankverbindungen
def transform(xml_path: Path) -> tuple[bool, str]:
### Hash-Berechnungssystem try:
logger.info(f"Transformation gestartet: {xml_path}")
Die Anwendung verwendet blake2b-Hashing zur Verfolgung von XML-Dateiänderungen: result = do_transform(xml_path)
return True, "Erfolg"
- **Automatisch**: Hashes werden berechnet, wenn Projekte geladen werden (nur für Dateien ohne existierenden Hash) except FileNotFoundError as e:
- **Asynchron**: Hintergrund-Thread (`XmlHashCalculatorThread`) um die UI reaktionsfähig zu halten error_msg = f"XML-Datei nicht gefunden: {xml_path}"
- **Format**: `blake2b:<64-Zeichen-Hexdigest>` logger.error(error_msg)
- **Speicherung**: Persistiert in `project.yaml` innerhalb jedes `XmlFile`-Objekts return False, error_msg
- **Details**: Siehe `docs/blake2b_hash_implementation.md` except Exception as e:
error_msg = f"Fehler bei Transformation: {str(e)}"
### Theme-System logger.exception(error_msg) # Mit Stack Trace
return False, error_msg
Die Anwendung unterstützt mehrere Qt-Themes: ```
- Theme-Auswahlmenü wird dynamisch aus `QStyleFactory.keys()` befüllt
- Theme-Präferenz wird in `AppSettings.theme` gespeichert - `logger.debug()` für Debugging-Infos
- Dark-Theme-Unterstützung via `qdarktheme` Paket (aktuell in main.py auskommentiert) - `logger.info()` für normale Operationen
- `logger.warning()` für Warnungen
### Datenbankintegration - `logger.error()` für Fehler ohne Stack Trace
- `logger.exception()` für Fehler MIT Stack Trace
PostgreSQL-Integration mit Polars und ConnectorX: - Fehlermeldungen auf Deutsch
- Konfiguration wird im `PostgreSqlDb`-Modell mit SSL-Modus-Unterstützung gespeichert
- SQL-Abfragen werden via `_execute_sql_query()` im MainWindow ausgeführt ### Docstrings
- Ergebnisse werden in Polars DataFrames geladen
Google-Style auf Deutsch:
## Wichtige Konventionen ```python
def transform_xml_to_pdf(xml_path: Path, xsl_path: Path, output_dir: Path) -> tuple[bool, str]:
### Deutsche Sprache """
Die Codebasis verwendet Deutsch für: Transformiert eine XML-Datei mit XSL zu PDF.
- UI-Texte und Labels
- Kommentare und Dokumentation Args:
- Variablennamen wo kontextuell passend xml_path: Pfad zur XML-Eingabedatei
- Log-Meldungen xsl_path: Pfad zum XSL-Stylesheet
output_dir: Zielverzeichnis für PDF-Ausgabe
### Pfadbehandlung
- Immer `pathlib.Path`-Objekte verwenden, keine Strings Returns:
- `expanduser()` und `expandvars()` für Benutzer-/Umgebungspfade verwenden tuple[bool, str]: (Erfolg, Fehlermeldung oder Info-Text)
- Projektrelative Pfade werden als relativ gespeichert, zur Laufzeit gegen `project_dir` aufgelöst
Raises:
### ID-basierte Lookups FileNotFoundError: Wenn XML- oder XSL-Datei nicht existiert
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 ### Pfadbehandlung
- Projekteinstellungen: `project_data.writeSettings(project_dir)` nach Änderungen aufrufen - Immer `pathlib.Path`-Objekte verwenden, keine Strings
- `expanduser()` und `expandvars()` für Benutzer-/Umgebungspfade verwenden
## Arbeiten mit der Codebasis - Projektrelative Pfade werden als relativ gespeichert, zur Laufzeit gegen `project_dir` aufgelöst
### Neue Tool-Konfigurationen hinzufügen ## Architektur
1. Modell zu `conf.py` hinzufügen (ähnlich wie `JavaVm`, `SaxonJar`)
2. Listenfeld zu `AppSettings` hinzufügen ### Konfigurationssystem (src/conf.py)
3. Konfigurationsdialog in `src/ui/` erstellen (UI-Datei + Implementierung)
4. Zu `AppSettings.py` Tabs hinzufügen Die Anwendung verwendet ein zentralisiertes Konfigurationsmodell mit Pydantic:
5. `Project`-Modell aktualisieren, falls das Tool projektspezifisch sein soll
- **AppSettings**: Globales Singleton (`app_settings`), das die gesamte Anwendungskonfiguration speichert
### Neue Baumoperationen hinzufügen - Wird an plattformspezifischen Orten gespeichert:
1. Aktion zum Kontextmenü in `_create_context_menu_for_type()` hinzufügen - Linux: `~/.config/DocuMentor/config.json`
2. Handler-Methode implementieren nach Namensschema `_action_tree_node()`, `_action_xsl_file()`, etc. - Windows: `%APPDATA%\DocuMentor\config.json`
3. Baum nach Änderungen mit `_load_nodes_to_tree()` aktualisieren - macOS: `~/Library/Application Support/DocuMentor/config.json`
4. `self.project_data.writeSettings(self.project.project_dir)` aufrufen um Änderungen zu persistieren - Enthält Listen von Tools: `java_vms`, `saxon_jars`, `apache_fops`, `diff_pdfs`, `xsl_dirs`, `postgresql_dbs`
### Projektstruktur modifizieren - **ProjectData**: Projektspezifische Einstellungen, die in `project.yaml` im jeweiligen Projektverzeichnis gespeichert werden
Das `ProjectData`-Modell ist die Quelle der Wahrheit. Alle Änderungen an der Baumstruktur müssen: - Enthält hierarchische Baumstruktur von Transformationsknoten
1. Die `project_data.nodes` Liste modifizieren - Verwendet `TreeNode` und `XslFile` zur Organisation
2. `project_data.writeSettings()` aufrufen um zu persistieren - Jede `XmlFile` hat eine optionale `hashsum` (blake2b) zur Änderungsverfolgung
3. Baum mit `_load_nodes_to_tree()` neu laden um Änderungen in der UI zu reflektieren
### 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 `<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
```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