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:
2026-03-08 20:21:02 +01:00
parent 6fe61b9a42
commit cb90f9e483
10 changed files with 99 additions and 288 deletions
+13 -85
View File
@@ -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):
"""
+28 -73
View File
@@ -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)