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:
@@ -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!
|
|
||||||
@@ -19,13 +19,11 @@ Die Basis bilden etwa 100 XSL-Dateien. Die meisten sind mittels `<xsl:import/>`
|
|||||||
|
|
||||||
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
|
||||||
|
|
||||||
@@ -34,7 +32,6 @@ Dieses Projekt verwendet den `uv` Paketmanager (nicht pip oder poetry):
|
|||||||
```bash
|
```bash
|
||||||
uv sync # Abhängigkeiten installieren
|
uv sync # Abhängigkeiten installieren
|
||||||
uv run python src/main.py # Anwendung starten
|
uv run python src/main.py # Anwendung starten
|
||||||
uv run python test_hash_implementation.py # Hash-Tests ausführen
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Linting
|
### Linting
|
||||||
@@ -43,6 +40,130 @@ uv run ruff check # Code-Style prüfen (Zeilenlänge: 120)
|
|||||||
uv run ruff format # Code formatieren
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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
|
## Architektur
|
||||||
|
|
||||||
### Konfigurationssystem (src/conf.py)
|
### Konfigurationssystem (src/conf.py)
|
||||||
@@ -96,6 +217,24 @@ Beim Erstellen neuer Dialoge:
|
|||||||
- Die UI-Datei wird automatisch als `.py`-Datei von einer VS Code Extension generiert
|
- Die UI-Datei wird automatisch als `.py`-Datei von einer VS Code Extension generiert
|
||||||
- Die generierte UI-Klasse in der Implementierungsdatei importieren und verwenden
|
- 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)
|
### Hauptfenster (src/ui/MainWindow.py)
|
||||||
|
|
||||||
Zentrale Schaltstelle der Anwendung mit mehreren wichtigen Verantwortlichkeiten:
|
Zentrale Schaltstelle der Anwendung mit mehreren wichtigen Verantwortlichkeiten:
|
||||||
@@ -117,6 +256,13 @@ Zentrale Schaltstelle der Anwendung mit mehreren wichtigen Verantwortlichkeiten:
|
|||||||
- `XmlHashCalculatorThread`: Hintergrund-blake2b-Hash-Berechnung für XML-Dateien
|
- `XmlHashCalculatorThread`: Hintergrund-blake2b-Hash-Berechnung für XML-Dateien
|
||||||
- `DatabaseTestThread` (in PostgreSqlConfigDialog): Asynchrones Testen von Datenbankverbindungen
|
- `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
|
### Hash-Berechnungssystem
|
||||||
|
|
||||||
Die Anwendung verwendet blake2b-Hashing zur Verfolgung von XML-Dateiänderungen:
|
Die Anwendung verwendet blake2b-Hashing zur Verfolgung von XML-Dateiänderungen:
|
||||||
@@ -132,15 +278,40 @@ Die Anwendung verwendet blake2b-Hashing zur Verfolgung von XML-Dateiänderungen:
|
|||||||
Die Anwendung unterstützt mehrere Qt-Themes:
|
Die Anwendung unterstützt mehrere Qt-Themes:
|
||||||
- Theme-Auswahlmenü wird dynamisch aus `QStyleFactory.keys()` befüllt
|
- Theme-Auswahlmenü wird dynamisch aus `QStyleFactory.keys()` befüllt
|
||||||
- Theme-Präferenz wird in `AppSettings.theme` gespeichert
|
- Theme-Präferenz wird in `AppSettings.theme` gespeichert
|
||||||
- Dark-Theme-Unterstützung via `qdarktheme` Paket (aktuell in main.py auskommentiert)
|
|
||||||
|
|
||||||
### Datenbankintegration
|
### Datenbankintegration
|
||||||
|
|
||||||
PostgreSQL-Integration mit Polars und ConnectorX:
|
PostgreSQL-Integration mit Polars und ConnectorX:
|
||||||
- Konfiguration wird im `PostgreSqlDb`-Modell mit SSL-Modus-Unterstützung gespeichert
|
- Konfiguration wird im `PostgreSqlDb`-Modell mit SSL-Modus-Unterstützung gespeichert
|
||||||
- SQL-Abfragen werden via `_execute_sql_query()` im MainWindow ausgeführt
|
- SQL-Abfragen werden asynchron via `DatabaseQueryThread` im `DatabaseMixin` ausgeführt
|
||||||
- Ergebnisse werden in Polars DataFrames geladen
|
- 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
|
## Wichtige Konventionen
|
||||||
|
|
||||||
### Deutsche Sprache
|
### Deutsche Sprache
|
||||||
@@ -150,11 +321,6 @@ Die Codebasis verwendet Deutsch für:
|
|||||||
- Variablennamen wo kontextuell passend
|
- Variablennamen wo kontextuell passend
|
||||||
- Log-Meldungen
|
- Log-Meldungen
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
### ID-basierte Lookups
|
### 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.
|
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.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user