308 lines
11 KiB
Markdown
308 lines
11 KiB
Markdown
|
|
# XML-Hash-Duplikatserkennung und intelligente Dateinamen-Verwaltung
|
||
|
|
|
||
|
|
## Übersicht
|
||
|
|
|
||
|
|
Diese Dokumentation beschreibt die erweiterte Funktionalität zur Hash-basierten Duplikatserkennung von XML-Dateien und intelligenten Dateinamen-Verwaltung in der XSL-Validator-Anwendung.
|
||
|
|
|
||
|
|
## Neue Funktionalitäten
|
||
|
|
|
||
|
|
### 1. Hash-basierte Duplikatserkennung
|
||
|
|
|
||
|
|
Beim Hinzufügen neuer XML-Dateien wird automatisch geprüft, ob bereits eine Datei mit identischem Inhalt (basierend auf blake2b-Hash) im Projekt vorhanden ist.
|
||
|
|
|
||
|
|
**Vorteile:**
|
||
|
|
- Vermeidung von Datei-Duplikaten
|
||
|
|
- Automatische Zuordnung vorhandener Dateien
|
||
|
|
- Speicherplatz-Optimierung
|
||
|
|
- Konsistente Datenintegrität
|
||
|
|
|
||
|
|
### 2. Intelligente Dateinamen-Verwaltung
|
||
|
|
|
||
|
|
Bei Dateinamen-Konflikten werden automatisch alternative Namen im Format `datei_1.xml`, `datei_2.xml`, etc. generiert.
|
||
|
|
|
||
|
|
**Features:**
|
||
|
|
- Automatische Generierung alternativer Dateinamen
|
||
|
|
- Benutzerfreundlicher Auswahl-Dialog
|
||
|
|
- Vermeidung von Überschreibungen
|
||
|
|
- Konsistente Namenskonventionen
|
||
|
|
|
||
|
|
### 3. Nahtlose Integration
|
||
|
|
|
||
|
|
Die neuen Funktionalitäten sind vollständig in bestehende Workflows integriert:
|
||
|
|
- **Drag & Drop**: Automatische Hash-Prüfung beim Ziehen von XML-Dateien
|
||
|
|
- **Kontextmenü**: Hash-Prüfung beim manuellen Hinzufügen über "XML-Datei hinzufügen"
|
||
|
|
|
||
|
|
## Technische Implementierung
|
||
|
|
|
||
|
|
### Kern-Architektur
|
||
|
|
|
||
|
|
```python
|
||
|
|
def _assign_xml_to_xsl_nodes(self, xml_file_path: Path, selected_xsl_nodes: list):
|
||
|
|
# 1. Hash berechnen
|
||
|
|
file_hash = self._calculate_hash_for_file(xml_file_path)
|
||
|
|
|
||
|
|
# 2. Duplikatsprüfung
|
||
|
|
existing_xml = self._find_xml_file_by_hash(file_hash)
|
||
|
|
|
||
|
|
if existing_xml:
|
||
|
|
# 3. Automatische Zuordnung bei Hash-Match
|
||
|
|
self._assign_existing_xml_to_nodes(existing_xml, selected_xsl_nodes)
|
||
|
|
else:
|
||
|
|
# 4. Neue Datei verarbeiten
|
||
|
|
self._process_new_xml_file(xml_file_path, selected_xsl_nodes, file_hash)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Neue Hilfsmethoden
|
||
|
|
|
||
|
|
#### Hash-Vergleich und Suche
|
||
|
|
|
||
|
|
```python
|
||
|
|
def _get_all_project_xml_files(self) -> List[XmlFile]:
|
||
|
|
"""Sammelt alle XML-Dateien aus dem gesamten Projekt."""
|
||
|
|
|
||
|
|
def _find_xml_file_by_hash(self, target_hash: str) -> XmlFile | None:
|
||
|
|
"""Sucht XML-Datei mit spezifischem Hash im Projekt."""
|
||
|
|
|
||
|
|
def _calculate_hash_for_file(self, file_path: Path) -> str | None:
|
||
|
|
"""Berechnet blake2b-Hash für eine Datei."""
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Dateinamen-Verwaltung
|
||
|
|
|
||
|
|
```python
|
||
|
|
def _generate_alternative_filename(self, original_path: Path, xml_dir: Path) -> Path:
|
||
|
|
"""Generiert alternative Dateinamen im Format: datei_1.xml, datei_2.xml, ..."""
|
||
|
|
|
||
|
|
def _is_filename_used_in_project(self, relative_xml_path: Path) -> bool:
|
||
|
|
"""Prüft ob Dateiname bereits im Projekt verwendet wird."""
|
||
|
|
|
||
|
|
def _show_filename_selection_dialog(self, original_name: str, alternative_paths: List[Path]) -> Path | None:
|
||
|
|
"""Zeigt Dialog zur Auswahl alternativer Dateinamen."""
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Verarbeitungslogik
|
||
|
|
|
||
|
|
```python
|
||
|
|
def _assign_existing_xml_to_nodes(self, existing_xml: XmlFile, selected_xsl_nodes: list):
|
||
|
|
"""Ordnet vorhandene XML-Datei (Hash-Match) den XSL-Knoten zu."""
|
||
|
|
|
||
|
|
def _process_new_xml_file(self, xml_file_path: Path, selected_xsl_nodes: list, file_hash: str | None):
|
||
|
|
"""Verarbeitet neue XML-Datei (kein Hash-Match)."""
|
||
|
|
```
|
||
|
|
|
||
|
|
## Benutzer-Workflows
|
||
|
|
|
||
|
|
### Workflow 1: Hash-Duplikat gefunden
|
||
|
|
|
||
|
|
1. Benutzer fügt XML-Datei hinzu (Drag&Drop oder Kontextmenü)
|
||
|
|
2. System berechnet Hash der neuen Datei
|
||
|
|
3. Hash-Match mit vorhandener Datei gefunden
|
||
|
|
4. **Automatische Zuordnung**: Vorhandene Datei wird den ausgewählten XSL-Knoten zugeordnet
|
||
|
|
5. Erfolgsmeldung: "XML-Datei mit gleichem Inhalt bereits vorhanden - automatisch zugeordnet"
|
||
|
|
|
||
|
|
### Workflow 2: Neue Datei, Dateiname-Konflikt
|
||
|
|
|
||
|
|
1. Benutzer fügt XML-Datei hinzu
|
||
|
|
2. Kein Hash-Match gefunden (neue Datei)
|
||
|
|
3. Dateiname bereits vorhanden
|
||
|
|
4. **Dialog angezeigt**: Auswahl alternativer Dateinamen
|
||
|
|
5. Benutzer wählt gewünschten Namen
|
||
|
|
6. Datei wird kopiert und zugeordnet
|
||
|
|
7. Erfolgsmeldung mit Hinweis auf Umbenennung
|
||
|
|
|
||
|
|
### Workflow 3: Neue Datei, kein Konflikt
|
||
|
|
|
||
|
|
1. Benutzer fügt XML-Datei hinzu
|
||
|
|
2. Kein Hash-Match gefunden
|
||
|
|
3. Dateiname verfügbar
|
||
|
|
4. **Direkte Verarbeitung**: Datei wird kopiert und zugeordnet
|
||
|
|
5. Standard-Erfolgsmeldung
|
||
|
|
|
||
|
|
## Benutzeroberfläche
|
||
|
|
|
||
|
|
### Hash-Duplikat Dialog
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────┐
|
||
|
|
│ XML-Datei zugeordnet │
|
||
|
|
├─────────────────────────────────────────┤
|
||
|
|
│ Eine XML-Datei mit gleichem Inhalt war │
|
||
|
|
│ bereits im Projekt vorhanden. │
|
||
|
|
│ │
|
||
|
|
│ Die vorhandene Datei 'dokument.xml' │
|
||
|
|
│ wurde automatisch zu 2 XSL-Knoten │
|
||
|
|
│ zugeordnet. │
|
||
|
|
├─────────────────────────────────────────┤
|
||
|
|
│ [OK] │
|
||
|
|
└─────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
### Dateiname-Auswahl Dialog
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────┐
|
||
|
|
│ Dateiname auswählen │
|
||
|
|
├─────────────────────────────────────────┤
|
||
|
|
│ Eine Datei mit dem Namen 'test.xml' │
|
||
|
|
│ existiert bereits. │
|
||
|
|
│ │
|
||
|
|
│ Bitte wählen Sie einen alternativen │
|
||
|
|
│ Dateinamen: │
|
||
|
|
│ │
|
||
|
|
│ ○ test_1.xml │
|
||
|
|
│ ● test_2.xml │
|
||
|
|
│ ○ test_3.xml │
|
||
|
|
│ ○ test_4.xml │
|
||
|
|
├─────────────────────────────────────────┤
|
||
|
|
│ [OK] [Abbrechen] │
|
||
|
|
└─────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
## Performance-Optimierungen
|
||
|
|
|
||
|
|
### Hash-Berechnung
|
||
|
|
- **Synchrone Berechnung** für neue Dateien (akzeptable Verzögerung)
|
||
|
|
- **Effiziente blake2b-Implementierung** aus Python's hashlib
|
||
|
|
- **Caching** von Hash-Werten in XmlFile-Objekten
|
||
|
|
|
||
|
|
### Projekt-weite Suche
|
||
|
|
- **Einmalige Sammlung** aller XML-Dateien pro Operation
|
||
|
|
- **Optimierte Rekursion** durch die Node-Struktur
|
||
|
|
- **Duplikat-Vermeidung** bei der Sammlung
|
||
|
|
|
||
|
|
### Dateinamen-Generierung
|
||
|
|
- **Sequenzielle Suche** mit Sicherheitsgrenze (max. 1000 Versuche)
|
||
|
|
- **Fallback-Mechanismus** mit Zeitstempel
|
||
|
|
- **Kombinierte Prüfung** von physischer Existenz und Projekt-Verwendung
|
||
|
|
|
||
|
|
## Fehlerbehandlung
|
||
|
|
|
||
|
|
### Hash-Berechnung Fehler
|
||
|
|
```python
|
||
|
|
try:
|
||
|
|
file_hash = self._calculate_hash_for_file(xml_file_path)
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"Hash-Berechnung fehlgeschlagen: {e}")
|
||
|
|
# Fortsetzung ohne Hash (Fallback-Verhalten)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Datei-Zugriff Fehler
|
||
|
|
```python
|
||
|
|
if not file_path.exists():
|
||
|
|
logger.warning(f"Datei nicht gefunden: {file_path}")
|
||
|
|
return None
|
||
|
|
```
|
||
|
|
|
||
|
|
### Dialog-Fehler
|
||
|
|
```python
|
||
|
|
try:
|
||
|
|
selected_path = self._show_filename_selection_dialog(...)
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"Dialog-Fehler: {e}")
|
||
|
|
# Fallback: Ersten alternativen Namen verwenden
|
||
|
|
return alternative_paths[0] if alternative_paths else None
|
||
|
|
```
|
||
|
|
|
||
|
|
## Logging
|
||
|
|
|
||
|
|
Das System verwendet strukturiertes Logging für alle Operationen:
|
||
|
|
|
||
|
|
```python
|
||
|
|
logger.info(f"Hash-Duplikat gefunden: {existing_xml.xml} hat gleichen Hash wie {xml_file_path.name}")
|
||
|
|
logger.debug(f"Hash-Vergleich: {len(xml_files)} XML-Dateien im Projekt gefunden")
|
||
|
|
logger.warning(f"Hash-Berechnung für {xml_file_path} fehlgeschlagen")
|
||
|
|
logger.error(f"Fehler beim Zuordnen der XML-Datei: {str(e)}")
|
||
|
|
```
|
||
|
|
|
||
|
|
## Testing
|
||
|
|
|
||
|
|
### Umfassende Test-Suite
|
||
|
|
|
||
|
|
Die Implementierung wird durch eine umfassende Test-Suite validiert:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
uv run python test_xml_hash_duplicate_detection.py
|
||
|
|
```
|
||
|
|
|
||
|
|
**Test-Abdeckung:**
|
||
|
|
- Hash-Berechnung und -Konsistenz
|
||
|
|
- XmlFile-Modell mit Hash-Unterstützung
|
||
|
|
- Duplikatserkennung-Logik
|
||
|
|
- Alternative Dateinamen-Generierung
|
||
|
|
- Integration Workflow
|
||
|
|
|
||
|
|
### Test-Ergebnisse
|
||
|
|
|
||
|
|
```
|
||
|
|
=== Test: Hash-Berechnung ===
|
||
|
|
[OK] Hash-Berechnung funktioniert korrekt
|
||
|
|
|
||
|
|
=== Test: XmlFile-Modell mit Hash ===
|
||
|
|
[OK] XmlFile-Modell mit Hash funktioniert korrekt
|
||
|
|
|
||
|
|
=== Test: Duplikatserkennung-Logik ===
|
||
|
|
[OK] Duplikatserkennung-Logik funktioniert korrekt
|
||
|
|
|
||
|
|
=== Test: Alternative Dateinamen-Generierung ===
|
||
|
|
[OK] Alternative Dateinamen-Generierung funktioniert korrekt
|
||
|
|
|
||
|
|
=== Test: Integration Workflow ===
|
||
|
|
[OK] Integration Workflow funktioniert korrekt
|
||
|
|
|
||
|
|
[SUCCESS] Alle Tests erfolgreich abgeschlossen!
|
||
|
|
```
|
||
|
|
|
||
|
|
## Kompatibilität
|
||
|
|
|
||
|
|
### Rückwärtskompatibilität
|
||
|
|
- **Bestehende Projekte** funktionieren unverändert
|
||
|
|
- **Vorhandene XML-Dateien** ohne Hash werden automatisch nachberechnet
|
||
|
|
- **Keine Breaking Changes** in der API
|
||
|
|
|
||
|
|
### Datenformat
|
||
|
|
- **XmlFile.hashsum** ist optional (kann None sein)
|
||
|
|
- **Automatische Migration** beim Projektladen
|
||
|
|
- **Graceful Degradation** bei Hash-Fehlern
|
||
|
|
|
||
|
|
## Wartung und Erweiterung
|
||
|
|
|
||
|
|
### Konfigurierbarkeit
|
||
|
|
Die Implementierung kann einfach erweitert werden:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Verschiedene Hash-Algorithmen
|
||
|
|
def _calculate_hash(self, file_path: Path, algorithm: str = "blake2b"):
|
||
|
|
if algorithm == "blake2b":
|
||
|
|
return self._calculate_blake2b_hash(file_path)
|
||
|
|
elif algorithm == "sha256":
|
||
|
|
return self._calculate_sha256_hash(file_path)
|
||
|
|
|
||
|
|
# Konfigurierbare Dateinamen-Formate
|
||
|
|
def _generate_alternative_filename(self, original_path: Path, format_pattern: str = "{name}_{counter}{ext}"):
|
||
|
|
# Implementierung mit konfigurierbaren Mustern
|
||
|
|
```
|
||
|
|
|
||
|
|
### Monitoring
|
||
|
|
```python
|
||
|
|
# Performance-Metriken
|
||
|
|
start_time = time.time()
|
||
|
|
# ... Operation ...
|
||
|
|
duration = time.time() - start_time
|
||
|
|
logger.info(f"Hash-Vergleich abgeschlossen in {duration:.3f}s")
|
||
|
|
```
|
||
|
|
|
||
|
|
## Changelog
|
||
|
|
|
||
|
|
### Version 1.0.0 (2025-01-20)
|
||
|
|
- ✅ Hash-basierte Duplikatserkennung im gesamten Projekt
|
||
|
|
- ✅ Automatische Zuordnung bei Hash-Match
|
||
|
|
- ✅ Intelligente Dateinamen-Generierung (datei_1.xml Format)
|
||
|
|
- ✅ Integration in Drag&Drop und Kontextmenü
|
||
|
|
- ✅ Benutzerfreundliche Dateiname-Auswahl-Dialoge
|
||
|
|
- ✅ Umfassende Test-Suite
|
||
|
|
- ✅ Strukturiertes Logging
|
||
|
|
- ✅ Fehlerbehandlung und Fallback-Mechanismen
|
||
|
|
|
||
|
|
## Fazit
|
||
|
|
|
||
|
|
Die erweiterte XML-Hash-Duplikatserkennung bietet eine robuste, benutzerfreundliche Lösung für die intelligente Verwaltung von XML-Dateien in XSL-Validator-Projekten. Die Implementierung ist vollständig getestet, performant und nahtlos in bestehende Workflows integriert.
|