Refactor: Code-Duplikation reduziert und Dead Code entfernt
- blake2b-Hash-Berechnung in zentrale Utility-Funktion extrahiert (src/utils.py) mit chunk-basiertem Hashing für bessere RAM-Effizienz - _transform_all_xml_files und _transform_all_xml_files_force zu einer Methode mit force-Parameter zusammengeführt - Project-Lookup-Methoden (getXsl, getJavaVm, etc.) über gemeinsame _lookup()-Hilfsmethode konsolidiert - Duplizierte XML-Sammel-Methoden entfernt, Set-basierte Duplikatsprüfung eingeführt - Ungenutzte Imports, Dead Code und wirkungslose Ausdrücke entfernt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -291,50 +291,3 @@ class PdfProjectDlg(QDialog):
|
||||
self.project_data = project_data
|
||||
self._load_project_data()
|
||||
|
||||
|
||||
# Convenience-Funktionen für einfache Verwendung
|
||||
def create_project_dialog(parent=None):
|
||||
"""
|
||||
Erstellt einen neuen Projekt-Dialog für ein neues Projekt.
|
||||
|
||||
Args:
|
||||
parent: Übergeordnetes Widget
|
||||
|
||||
Returns:
|
||||
PdfProjectDlg: Der Dialog
|
||||
"""
|
||||
return PdfProjectDlg(parent)
|
||||
|
||||
|
||||
def edit_project_dialog(parent=None, project_data=None):
|
||||
"""
|
||||
Erstellt einen Projekt-Dialog zum Bearbeiten eines bestehenden Projekts.
|
||||
|
||||
Args:
|
||||
parent: Übergeordnetes Widget
|
||||
project_data: Bestehende Projektdaten
|
||||
|
||||
Returns:
|
||||
PdfProjectDlg: Der Dialog
|
||||
"""
|
||||
return PdfProjectDlg(parent, project_data)
|
||||
|
||||
|
||||
def show_project_dialog(parent=None, project_data=None):
|
||||
"""
|
||||
Zeigt einen Projekt-Dialog an und gibt die Ergebnisse zurück.
|
||||
|
||||
Args:
|
||||
parent: Übergeordnetes Widget
|
||||
project_data: Bestehende Projektdaten (optional)
|
||||
|
||||
Returns:
|
||||
tuple: (accepted: bool, project_data: dict)
|
||||
"""
|
||||
dialog = PdfProjectDlg(parent, project_data)
|
||||
result = dialog.exec()
|
||||
|
||||
if result == QDialog.DialogCode.Accepted:
|
||||
return True, dialog.get_project_data()
|
||||
else:
|
||||
return False, None
|
||||
|
||||
@@ -12,12 +12,9 @@ from PySide6.QtWidgets import (
|
||||
QGroupBox,
|
||||
QLabel,
|
||||
QPushButton,
|
||||
QTextEdit,
|
||||
QTabWidget,
|
||||
QWidget,
|
||||
)
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
@@ -5,9 +5,8 @@ Dieses Mixin enthält alle Methoden zur blake2b-Hash-Berechnung,
|
||||
XML-Datei-Zuordnung und Duplikatserkennung für das MainWindow.
|
||||
"""
|
||||
|
||||
import time
|
||||
import hashlib
|
||||
import shutil
|
||||
import time
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
@@ -17,6 +16,7 @@ from PySide6.QtWidgets import QMessageBox
|
||||
from conf import TreeNode, XslFile, XmlFile
|
||||
from ui.XmlToXslAssignDialog import XmlToXslAssignDialog
|
||||
from ui.threads import XmlHashCalculatorThread
|
||||
from utils import calculate_blake2b_hash
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -185,14 +185,14 @@ class HashCalculationMixin:
|
||||
nodes: Liste der zu durchsuchenden Nodes
|
||||
xml_files: Liste zum Sammeln der XML-Dateien
|
||||
"""
|
||||
seen_paths = {xf.xml for xf in xml_files}
|
||||
for node in nodes:
|
||||
if isinstance(node, XslFile) and node.xmls:
|
||||
# Füge alle XML-Dateien dieser XSL-Datei hinzu
|
||||
for xml_file in node.xmls:
|
||||
if xml_file not in xml_files: # Vermeide Duplikate
|
||||
if xml_file.xml not in seen_paths:
|
||||
xml_files.append(xml_file)
|
||||
seen_paths.add(xml_file.xml)
|
||||
elif isinstance(node, TreeNode) and node.children:
|
||||
# Rekursiv in Kinder-Nodes suchen
|
||||
self._collect_xml_files_recursive(node.children, xml_files)
|
||||
|
||||
def _on_hash_calculated(self, xml_file: XmlFile, hash_value: str):
|
||||
@@ -253,68 +253,22 @@ class HashCalculationMixin:
|
||||
logger.debug(f"Hash bereits vorhanden für {xml_file.xml}: {xml_file.hashsum}")
|
||||
return
|
||||
|
||||
# Prüfe ob Projekt verfügbar ist
|
||||
if not self.project or not self.project.project_dir:
|
||||
logger.warning("Kein Projekt-Verzeichnis für Hash-Berechnung verfügbar")
|
||||
return
|
||||
|
||||
xml_file_path = Path(self.project.project_dir) / xml_file.xml
|
||||
|
||||
if not xml_file_path.exists():
|
||||
logger.warning(f"XML-Datei nicht gefunden: {xml_file_path}")
|
||||
return
|
||||
|
||||
# Datei binär lesen und Hash berechnen
|
||||
with open(xml_file_path, "rb") as f:
|
||||
file_content = f.read()
|
||||
hash_obj = hashlib.blake2b(file_content)
|
||||
hash_hex = hash_obj.hexdigest()
|
||||
|
||||
# Hash mit Präfix setzen
|
||||
xml_file.hashsum = f"blake2b:{hash_hex}"
|
||||
logger.debug(f"Hash berechnet für {xml_file.xml}: {xml_file.hashsum}")
|
||||
hash_value = calculate_blake2b_hash(xml_file_path)
|
||||
if hash_value:
|
||||
xml_file.hashsum = hash_value
|
||||
logger.debug(f"Hash berechnet für {xml_file.xml}: {xml_file.hashsum}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Berechnen des Hash für {xml_file.xml}: {e}")
|
||||
|
||||
def _get_all_project_xml_files(self) -> List[XmlFile]:
|
||||
"""
|
||||
Sammelt alle XmlFile-Objekte aus dem gesamten Projekt für Hash-Vergleiche.
|
||||
|
||||
Returns:
|
||||
List[XmlFile]: Liste aller XML-Dateien im Projekt
|
||||
"""
|
||||
xml_files = []
|
||||
|
||||
try:
|
||||
if self.pdf_project and self.pdf_project.nodes:
|
||||
self._collect_xml_files_for_hash_comparison(self.pdf_project.nodes, xml_files)
|
||||
|
||||
logger.debug(f"Hash-Vergleich: {len(xml_files)} XML-Dateien im Projekt gefunden")
|
||||
return xml_files
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Sammeln der XML-Dateien für Hash-Vergleich: {e}")
|
||||
return []
|
||||
|
||||
def _collect_xml_files_for_hash_comparison(self, nodes, xml_files: List[XmlFile]):
|
||||
"""
|
||||
Sammelt rekursiv alle XML-Dateien aus den Nodes für Hash-Vergleiche.
|
||||
|
||||
Args:
|
||||
nodes: Liste der zu durchsuchenden Nodes
|
||||
xml_files: Liste zum Sammeln der XML-Dateien
|
||||
"""
|
||||
for node in nodes:
|
||||
if isinstance(node, XslFile) and node.xmls:
|
||||
# Füge alle XML-Dateien dieser XSL-Datei hinzu
|
||||
for xml_file in node.xmls:
|
||||
# Vermeide Duplikate basierend auf Pfad
|
||||
if not any(existing.xml == xml_file.xml for existing in xml_files):
|
||||
xml_files.append(xml_file)
|
||||
elif isinstance(node, TreeNode) and node.children:
|
||||
# Rekursiv in Kinder-Nodes suchen
|
||||
self._collect_xml_files_for_hash_comparison(node.children, xml_files)
|
||||
"""Sammelt alle XmlFile-Objekte aus dem gesamten Projekt."""
|
||||
return self._collect_all_xml_files()
|
||||
|
||||
def _find_xml_file_by_hash(self, target_hash: str) -> XmlFile | None:
|
||||
"""
|
||||
@@ -410,34 +364,8 @@ class HashCalculationMixin:
|
||||
return True # Im Zweifelsfall annehmen, dass der Name verwendet wird
|
||||
|
||||
def _calculate_hash_for_file(self, file_path: Path) -> str | None:
|
||||
"""
|
||||
Berechnet synchron den blake2b-Hash für eine Datei.
|
||||
|
||||
Args:
|
||||
file_path: Pfad zur Datei
|
||||
|
||||
Returns:
|
||||
str|None: Hash-Wert mit blake2b: Präfix oder None bei Fehler
|
||||
"""
|
||||
try:
|
||||
if not file_path.exists():
|
||||
logger.warning(f"Datei für Hash-Berechnung nicht gefunden: {file_path}")
|
||||
return None
|
||||
|
||||
# Datei binär lesen und Hash berechnen
|
||||
with open(file_path, "rb") as f:
|
||||
file_content = f.read()
|
||||
hash_obj = hashlib.blake2b(file_content)
|
||||
hash_hex = hash_obj.hexdigest()
|
||||
|
||||
# Hash mit Präfix zurückgeben
|
||||
hash_value = f"blake2b:{hash_hex}"
|
||||
logger.debug(f"Hash berechnet für {file_path}: {hash_value}")
|
||||
return hash_value
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Berechnen des Hash für {file_path}: {e}")
|
||||
return None
|
||||
"""Berechnet synchron den blake2b-Hash für eine Datei."""
|
||||
return calculate_blake2b_hash(file_path)
|
||||
|
||||
def _assign_existing_xml_to_nodes(self, existing_xml: XmlFile, selected_xsl_nodes: list):
|
||||
"""
|
||||
|
||||
@@ -579,9 +579,6 @@ class TransformationMixin:
|
||||
|
||||
# Progress Bar anzeigen
|
||||
map_key = f"{xml_file_name}|{xsl_id_str}"
|
||||
if map_key not in self.xml_item_map and self.xml_item_map:
|
||||
# Zeige erste Keys zur Diagnose
|
||||
list(self.xml_item_map.keys())[:3]
|
||||
logger.info(f"Suche TreeWidget-Item für: '{map_key}'")
|
||||
logger.info(f"Map hat {len(self.xml_item_map)} Einträge")
|
||||
tree_item = self.xml_item_map.get(map_key)
|
||||
@@ -741,10 +738,12 @@ class TransformationMixin:
|
||||
f"{successful_count} von {total_count} Transformationen erfolgreich\n{failed_count} fehlgeschlagen\n\nGesamtdauer: {duration_str}",
|
||||
)
|
||||
|
||||
def _transform_all_xml_files(self):
|
||||
def _transform_all_xml_files(self, force: bool = False):
|
||||
"""
|
||||
Transformiert ALLE XML-Dateien in allen TreeNodes des TreeWidgets.
|
||||
Nur Dateien, die nicht up-to-date sind, werden transformiert.
|
||||
|
||||
Args:
|
||||
force: Wenn True, werden alle Dateien unabhängig vom Änderungsstatus neu transformiert.
|
||||
"""
|
||||
try:
|
||||
if not self.project or not self.pdf_project:
|
||||
@@ -781,87 +780,43 @@ class TransformationMixin:
|
||||
return
|
||||
|
||||
# Frage Benutzer um Bestätigung
|
||||
if force:
|
||||
title = "Alle XML-Dateien neu transformieren (force)"
|
||||
message = (
|
||||
f"Möchten Sie wirklich ALLE {len(all_jobs)} XML-Dateien NEU transformieren?\n\n"
|
||||
f"⚠ WARNUNG: Im Force-Modus werden alle Dateien unabhängig von ihrem Status neu verarbeitet!\n"
|
||||
f"Dies kann längere Zeit in Anspruch nehmen."
|
||||
)
|
||||
default_button = QMessageBox.StandardButton.No
|
||||
else:
|
||||
title = "Alle XML-Dateien transformieren"
|
||||
message = (
|
||||
f"Möchten Sie wirklich alle {len(all_jobs)} XML-Dateien transformieren?\n\n"
|
||||
f"Nur Dateien mit Änderungen werden verarbeitet."
|
||||
)
|
||||
default_button = QMessageBox.StandardButton.Yes
|
||||
|
||||
reply = QMessageBox.question(
|
||||
self,
|
||||
"Alle XML-Dateien transformieren",
|
||||
f"Möchten Sie wirklich alle {len(all_jobs)} XML-Dateien transformieren?\n\n"
|
||||
f"Nur Dateien mit Änderungen werden verarbeitet.",
|
||||
title,
|
||||
message,
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
QMessageBox.StandardButton.Yes,
|
||||
default_button,
|
||||
)
|
||||
|
||||
if reply != QMessageBox.StandardButton.Yes:
|
||||
logger.info("Transformation abgebrochen durch Benutzer")
|
||||
logger.info(f"{'Force-' if force else ''}Transformation abgebrochen durch Benutzer")
|
||||
return
|
||||
|
||||
logger.info(f"Starte Transformation für alle {len(all_jobs)} XML-Dateien (nicht-force)")
|
||||
logger.info(f"Starte {'Force-' if force else ''}Transformation für alle {len(all_jobs)} XML-Dateien")
|
||||
|
||||
# Starte Transformation in separatem Thread
|
||||
self._start_transformation(all_jobs, force=False)
|
||||
self._start_transformation(all_jobs, force=force)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Transformieren aller XML-Dateien: {e}")
|
||||
QMessageBox.critical(self, "Fehler", f"Fehler beim Starten der Transformation: {str(e)}")
|
||||
|
||||
def _transform_all_xml_files_force(self):
|
||||
"""
|
||||
Transformiert ALLE XML-Dateien in allen TreeNodes des TreeWidgets (force).
|
||||
Alle Dateien werden unabhängig vom Änderungsstatus neu transformiert.
|
||||
"""
|
||||
try:
|
||||
if not self.project or not self.pdf_project:
|
||||
QMessageBox.warning(self, "Fehler", "Kein Projekt geladen")
|
||||
return
|
||||
|
||||
# Sammle alle XSL/XML-Paare aus allen Root-Nodes
|
||||
all_jobs = []
|
||||
root = self.ui.treeWidget.invisibleRootItem()
|
||||
|
||||
for i in range(root.childCount()):
|
||||
root_item = root.child(i)
|
||||
root_node = root_item.data(0, Qt.ItemDataRole.UserRole)
|
||||
|
||||
if isinstance(root_node, TreeNode):
|
||||
# Sammle alle XSL/XML-Paare rekursiv
|
||||
xsl_xml_pairs = self._collect_all_xsl_xml_pairs_recursive(root_node, root_item)
|
||||
|
||||
# Erstelle TransformationJobs
|
||||
for xsl_file_obj, xml_file_obj, xsl_file_item in xsl_xml_pairs:
|
||||
job = self._create_transformation_job(xsl_file_obj, xml_file_obj, xsl_file_item)
|
||||
if job:
|
||||
all_jobs.append(job)
|
||||
|
||||
elif isinstance(root_node, XslFile):
|
||||
# Direkt XslFile als Root-Element
|
||||
for xml_file_obj in root_node.xmls:
|
||||
job = self._create_transformation_job(root_node, xml_file_obj, root_item)
|
||||
if job:
|
||||
all_jobs.append(job)
|
||||
|
||||
if not all_jobs:
|
||||
QMessageBox.information(self, "Info", "Keine XML-Dateien zum Transformieren gefunden")
|
||||
return
|
||||
|
||||
# Frage Benutzer um Bestätigung (mit Warnung wegen force)
|
||||
reply = QMessageBox.question(
|
||||
self,
|
||||
"Alle XML-Dateien neu transformieren (force)",
|
||||
f"Möchten Sie wirklich ALLE {len(all_jobs)} XML-Dateien NEU transformieren?\n\n"
|
||||
f"⚠ WARNUNG: Im Force-Modus werden alle Dateien unabhängig von ihrem Status neu verarbeitet!\n"
|
||||
f"Dies kann längere Zeit in Anspruch nehmen.",
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
QMessageBox.StandardButton.No,
|
||||
)
|
||||
|
||||
if reply != QMessageBox.StandardButton.Yes:
|
||||
logger.info("Force-Transformation abgebrochen durch Benutzer")
|
||||
return
|
||||
|
||||
logger.info(f"Starte Force-Transformation für alle {len(all_jobs)} XML-Dateien")
|
||||
|
||||
# Starte Transformation in separatem Thread (mit force=True)
|
||||
self._start_transformation(all_jobs, force=True)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Force-Transformieren aller XML-Dateien: {e}")
|
||||
QMessageBox.critical(self, "Fehler", f"Fehler beim Starten der Force-Transformation: {str(e)}")
|
||||
"""Transformiert ALLE XML-Dateien im Force-Modus."""
|
||||
self._transform_all_xml_files(force=True)
|
||||
|
||||
+5
-44
@@ -7,16 +7,14 @@ Dieses Modul enthält alle QThread-Klassen, die für Hintergrundoperationen verw
|
||||
- TransformationThread: Ausführung von XSL-Transformationen
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import logging
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from PySide6.QtCore import QThread, Signal
|
||||
|
||||
from conf import TreeNode, XslFile, XmlFile
|
||||
from transform import TransformationJob
|
||||
from utils import calculate_blake2b_hash
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -32,7 +30,7 @@ class XmlHashCalculatorThread(QThread):
|
||||
calculation_finished = Signal(int, int) # processed_count, total_count
|
||||
error_occurred = Signal(str, str) # xml_file_path, error_message
|
||||
|
||||
def __init__(self, project_dir: Path, xml_files: List[XmlFile]):
|
||||
def __init__(self, project_dir: Path, xml_files: list[XmlFile]):
|
||||
"""
|
||||
Initialisiert den Hash-Berechnungs-Thread.
|
||||
|
||||
@@ -81,32 +79,8 @@ class XmlHashCalculatorThread(QThread):
|
||||
logger.info(f"Hash-Berechnung abgeschlossen: {self.processed_count}/{len(self.xml_files)} verarbeitet")
|
||||
|
||||
def _calculate_blake2b_hash(self, file_path: Path) -> str | None:
|
||||
"""
|
||||
Berechnet den blake2b-Hash einer XML-Datei.
|
||||
|
||||
Args:
|
||||
file_path: Pfad zur XML-Datei
|
||||
|
||||
Returns:
|
||||
str: Hash-Wert mit "blake2b:" Präfix oder None bei Fehler
|
||||
"""
|
||||
try:
|
||||
if not file_path.exists():
|
||||
logger.warning(f"XML-Datei nicht gefunden: {file_path}")
|
||||
return None
|
||||
|
||||
# Datei binär lesen und Hash berechnen
|
||||
with open(file_path, "rb") as f:
|
||||
file_content = f.read()
|
||||
hash_obj = hashlib.blake2b(file_content)
|
||||
hash_hex = hash_obj.hexdigest()
|
||||
|
||||
# Präfix hinzufügen
|
||||
return f"blake2b:{hash_hex}"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Berechnen des Hash für {file_path}: {e}")
|
||||
return None
|
||||
"""Berechnet den blake2b-Hash einer XML-Datei."""
|
||||
return calculate_blake2b_hash(file_path)
|
||||
|
||||
|
||||
class XmlBatchProcessingThread(QThread):
|
||||
@@ -217,20 +191,7 @@ class XmlBatchProcessingThread(QThread):
|
||||
|
||||
def _calculate_hash_for_file(self, file_path: Path) -> str | None:
|
||||
"""Berechnet blake2b Hash für eine Datei."""
|
||||
try:
|
||||
if not file_path.exists():
|
||||
return None
|
||||
|
||||
with open(file_path, "rb") as f:
|
||||
file_content = f.read()
|
||||
hash_obj = hashlib.blake2b(file_content)
|
||||
hash_hex = hash_obj.hexdigest()
|
||||
|
||||
return f"blake2b:{hash_hex}"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Berechnen des Hash für {file_path}: {e}")
|
||||
return None
|
||||
return calculate_blake2b_hash(file_path)
|
||||
|
||||
def _find_xml_file_by_hash(self, hash_value: str) -> XmlFile | None:
|
||||
"""Sucht eine XML-Datei anhand ihres Hash-Werts."""
|
||||
|
||||
Reference in New Issue
Block a user