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:
+12
-24
@@ -104,41 +104,29 @@ class Project(BaseModel):
|
|||||||
postgre_sql_db_id: int = Field(..., description="ID der PostgreSQL Datenbank", gt=0)
|
postgre_sql_db_id: int = Field(..., description="ID der PostgreSQL Datenbank", gt=0)
|
||||||
fop_config_dir: Path | None = Field(None, description="Optionaler Pfad zum Apache FOP Config-Verzeichnis")
|
fop_config_dir: Path | None = Field(None, description="Optionaler Pfad zum Apache FOP Config-Verzeichnis")
|
||||||
|
|
||||||
def getXsl(self) -> str:
|
@staticmethod
|
||||||
global app_settings
|
def _lookup(collection, item_id: int, attr: str) -> str:
|
||||||
value = [x.name for x in app_settings.xsl_dirs if x.id == self.xsl_dir_id]
|
"""Sucht einen Wert in einer Konfigurationsliste anhand der ID."""
|
||||||
|
value = [getattr(x, attr) for x in collection if x.id == item_id]
|
||||||
|
return value[0] if value else ""
|
||||||
|
|
||||||
return value[0] if len(value) else ""
|
def getXsl(self) -> str:
|
||||||
|
return self._lookup(app_settings.xsl_dirs, self.xsl_dir_id, "name")
|
||||||
|
|
||||||
def getJavaVm(self) -> str:
|
def getJavaVm(self) -> str:
|
||||||
global app_settings
|
return self._lookup(app_settings.java_vms, self.java_vm_id, "version")
|
||||||
value = [x.version for x in app_settings.java_vms if x.id == self.java_vm_id]
|
|
||||||
|
|
||||||
return value[0] if len(value) else ""
|
|
||||||
|
|
||||||
def getSaxon(self) -> str:
|
def getSaxon(self) -> str:
|
||||||
global app_settings
|
return self._lookup(app_settings.saxon_jars, self.saxon_jar_id, "version")
|
||||||
value = [x.version for x in app_settings.saxon_jars if x.id == self.saxon_jar_id]
|
|
||||||
|
|
||||||
return value[0] if len(value) else ""
|
|
||||||
|
|
||||||
def getApacheFop(self) -> str:
|
def getApacheFop(self) -> str:
|
||||||
global app_settings
|
return self._lookup(app_settings.apache_fops, self.apache_fop_id, "version")
|
||||||
value = [x.version for x in app_settings.apache_fops if x.id == self.apache_fop_id]
|
|
||||||
|
|
||||||
return value[0] if len(value) else ""
|
|
||||||
|
|
||||||
def getDiffPdf(self) -> str:
|
def getDiffPdf(self) -> str:
|
||||||
global app_settings
|
return self._lookup(app_settings.diff_pdfs, self.diff_pdf_id, "version")
|
||||||
value = [x.version for x in app_settings.diff_pdfs if x.id == self.diff_pdf_id]
|
|
||||||
|
|
||||||
return value[0] if len(value) else ""
|
|
||||||
|
|
||||||
def getPostgreSqlDb(self) -> str:
|
def getPostgreSqlDb(self) -> str:
|
||||||
global app_settings
|
return self._lookup(app_settings.postgresql_dbs, self.postgre_sql_db_id, "name")
|
||||||
value = [x.name for x in app_settings.postgresql_dbs if x.id == self.postgre_sql_db_id]
|
|
||||||
|
|
||||||
return value[0] if len(value) else ""
|
|
||||||
|
|
||||||
|
|
||||||
class AppSettings(BaseSettings):
|
class AppSettings(BaseSettings):
|
||||||
|
|||||||
+1
-4
@@ -11,7 +11,6 @@ import threading
|
|||||||
import time
|
import time
|
||||||
import psutil
|
import psutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from queue import Queue
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
@@ -196,10 +195,8 @@ class FopWorkerPool:
|
|||||||
self.fop_config_file = fop_config_file
|
self.fop_config_file = fop_config_file
|
||||||
self.log_dir = log_dir
|
self.log_dir = log_dir
|
||||||
|
|
||||||
# Worker-Prozesse und Queues
|
# Worker-Prozesse
|
||||||
self.workers: list[subprocess.Popen] = []
|
self.workers: list[subprocess.Popen] = []
|
||||||
self.job_queue: Queue = Queue()
|
|
||||||
self.result_queue: Queue = Queue()
|
|
||||||
self.worker_locks: list[threading.Lock] = []
|
self.worker_locks: list[threading.Lock] = []
|
||||||
|
|
||||||
# Temporäres Verzeichnis für kompilierte Java-Klasse
|
# Temporäres Verzeichnis für kompilierte Java-Klasse
|
||||||
|
|||||||
+1
-4
@@ -11,7 +11,6 @@ import threading
|
|||||||
import time
|
import time
|
||||||
import psutil
|
import psutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from queue import Queue
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
@@ -207,10 +206,8 @@ class SaxonWorkerPool:
|
|||||||
self.classpath_cache = classpath_cache
|
self.classpath_cache = classpath_cache
|
||||||
self.log_dir = log_dir
|
self.log_dir = log_dir
|
||||||
|
|
||||||
# Worker-Prozesse und Queues
|
# Worker-Prozesse
|
||||||
self.workers: list[subprocess.Popen] = []
|
self.workers: list[subprocess.Popen] = []
|
||||||
self.job_queue: Queue = Queue()
|
|
||||||
self.result_queue: Queue = Queue()
|
|
||||||
self.worker_locks: list[threading.Lock] = []
|
self.worker_locks: list[threading.Lock] = []
|
||||||
|
|
||||||
# Temporäres Verzeichnis für kompilierte Java-Klasse
|
# Temporäres Verzeichnis für kompilierte Java-Klasse
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import threading
|
|||||||
import time
|
import time
|
||||||
import psutil
|
import psutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from queue import Queue
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
@@ -188,10 +187,8 @@ class SaxonWorkerPoolS9Api:
|
|||||||
self.classpath_cache = classpath_cache
|
self.classpath_cache = classpath_cache
|
||||||
self.log_dir = log_dir
|
self.log_dir = log_dir
|
||||||
|
|
||||||
# Worker-Prozesse und Queues
|
# Worker-Prozesse
|
||||||
self.workers: list[subprocess.Popen] = []
|
self.workers: list[subprocess.Popen] = []
|
||||||
self.job_queue: Queue = Queue()
|
|
||||||
self.result_queue: Queue = Queue()
|
|
||||||
self.worker_locks: list[threading.Lock] = []
|
self.worker_locks: list[threading.Lock] = []
|
||||||
|
|
||||||
# Temporäres Verzeichnis für kompilierte Java-Klasse
|
# Temporäres Verzeichnis für kompilierte Java-Klasse
|
||||||
|
|||||||
@@ -291,50 +291,3 @@ class PdfProjectDlg(QDialog):
|
|||||||
self.project_data = project_data
|
self.project_data = project_data
|
||||||
self._load_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,
|
QGroupBox,
|
||||||
QLabel,
|
QLabel,
|
||||||
QPushButton,
|
QPushButton,
|
||||||
QTextEdit,
|
|
||||||
QTabWidget,
|
QTabWidget,
|
||||||
QWidget,
|
QWidget,
|
||||||
)
|
)
|
||||||
from PySide6.QtCore import Qt
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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.
|
XML-Datei-Zuordnung und Duplikatserkennung für das MainWindow.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import time
|
|
||||||
import hashlib
|
|
||||||
import shutil
|
import shutil
|
||||||
|
import time
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List
|
from typing import List
|
||||||
@@ -17,6 +16,7 @@ from PySide6.QtWidgets import QMessageBox
|
|||||||
from conf import TreeNode, XslFile, XmlFile
|
from conf import TreeNode, XslFile, XmlFile
|
||||||
from ui.XmlToXslAssignDialog import XmlToXslAssignDialog
|
from ui.XmlToXslAssignDialog import XmlToXslAssignDialog
|
||||||
from ui.threads import XmlHashCalculatorThread
|
from ui.threads import XmlHashCalculatorThread
|
||||||
|
from utils import calculate_blake2b_hash
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -185,14 +185,14 @@ class HashCalculationMixin:
|
|||||||
nodes: Liste der zu durchsuchenden Nodes
|
nodes: Liste der zu durchsuchenden Nodes
|
||||||
xml_files: Liste zum Sammeln der XML-Dateien
|
xml_files: Liste zum Sammeln der XML-Dateien
|
||||||
"""
|
"""
|
||||||
|
seen_paths = {xf.xml for xf in xml_files}
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
if isinstance(node, XslFile) and node.xmls:
|
if isinstance(node, XslFile) and node.xmls:
|
||||||
# Füge alle XML-Dateien dieser XSL-Datei hinzu
|
|
||||||
for xml_file in node.xmls:
|
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)
|
xml_files.append(xml_file)
|
||||||
|
seen_paths.add(xml_file.xml)
|
||||||
elif isinstance(node, TreeNode) and node.children:
|
elif isinstance(node, TreeNode) and node.children:
|
||||||
# Rekursiv in Kinder-Nodes suchen
|
|
||||||
self._collect_xml_files_recursive(node.children, xml_files)
|
self._collect_xml_files_recursive(node.children, xml_files)
|
||||||
|
|
||||||
def _on_hash_calculated(self, xml_file: XmlFile, hash_value: str):
|
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}")
|
logger.debug(f"Hash bereits vorhanden für {xml_file.xml}: {xml_file.hashsum}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Prüfe ob Projekt verfügbar ist
|
|
||||||
if not self.project or not self.project.project_dir:
|
if not self.project or not self.project.project_dir:
|
||||||
logger.warning("Kein Projekt-Verzeichnis für Hash-Berechnung verfügbar")
|
logger.warning("Kein Projekt-Verzeichnis für Hash-Berechnung verfügbar")
|
||||||
return
|
return
|
||||||
|
|
||||||
xml_file_path = Path(self.project.project_dir) / xml_file.xml
|
xml_file_path = Path(self.project.project_dir) / xml_file.xml
|
||||||
|
hash_value = calculate_blake2b_hash(xml_file_path)
|
||||||
if not xml_file_path.exists():
|
if hash_value:
|
||||||
logger.warning(f"XML-Datei nicht gefunden: {xml_file_path}")
|
xml_file.hashsum = hash_value
|
||||||
return
|
logger.debug(f"Hash berechnet für {xml_file.xml}: {xml_file.hashsum}")
|
||||||
|
|
||||||
# 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}")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Fehler beim Berechnen des Hash für {xml_file.xml}: {e}")
|
logger.error(f"Fehler beim Berechnen des Hash für {xml_file.xml}: {e}")
|
||||||
|
|
||||||
def _get_all_project_xml_files(self) -> List[XmlFile]:
|
def _get_all_project_xml_files(self) -> List[XmlFile]:
|
||||||
"""
|
"""Sammelt alle XmlFile-Objekte aus dem gesamten Projekt."""
|
||||||
Sammelt alle XmlFile-Objekte aus dem gesamten Projekt für Hash-Vergleiche.
|
return self._collect_all_xml_files()
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def _find_xml_file_by_hash(self, target_hash: str) -> XmlFile | None:
|
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
|
return True # Im Zweifelsfall annehmen, dass der Name verwendet wird
|
||||||
|
|
||||||
def _calculate_hash_for_file(self, file_path: Path) -> str | None:
|
def _calculate_hash_for_file(self, file_path: Path) -> str | None:
|
||||||
"""
|
"""Berechnet synchron den blake2b-Hash für eine Datei."""
|
||||||
Berechnet synchron den blake2b-Hash für eine Datei.
|
return calculate_blake2b_hash(file_path)
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
def _assign_existing_xml_to_nodes(self, existing_xml: XmlFile, selected_xsl_nodes: list):
|
def _assign_existing_xml_to_nodes(self, existing_xml: XmlFile, selected_xsl_nodes: list):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -579,9 +579,6 @@ class TransformationMixin:
|
|||||||
|
|
||||||
# Progress Bar anzeigen
|
# Progress Bar anzeigen
|
||||||
map_key = f"{xml_file_name}|{xsl_id_str}"
|
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"Suche TreeWidget-Item für: '{map_key}'")
|
||||||
logger.info(f"Map hat {len(self.xml_item_map)} Einträge")
|
logger.info(f"Map hat {len(self.xml_item_map)} Einträge")
|
||||||
tree_item = self.xml_item_map.get(map_key)
|
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}",
|
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.
|
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:
|
try:
|
||||||
if not self.project or not self.pdf_project:
|
if not self.project or not self.pdf_project:
|
||||||
@@ -781,87 +780,43 @@ class TransformationMixin:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Frage Benutzer um Bestätigung
|
# 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(
|
reply = QMessageBox.question(
|
||||||
self,
|
self,
|
||||||
"Alle XML-Dateien transformieren",
|
title,
|
||||||
f"Möchten Sie wirklich alle {len(all_jobs)} XML-Dateien transformieren?\n\n"
|
message,
|
||||||
f"Nur Dateien mit Änderungen werden verarbeitet.",
|
|
||||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||||
QMessageBox.StandardButton.Yes,
|
default_button,
|
||||||
)
|
)
|
||||||
|
|
||||||
if reply != QMessageBox.StandardButton.Yes:
|
if reply != QMessageBox.StandardButton.Yes:
|
||||||
logger.info("Transformation abgebrochen durch Benutzer")
|
logger.info(f"{'Force-' if force else ''}Transformation abgebrochen durch Benutzer")
|
||||||
return
|
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
|
# Starte Transformation in separatem Thread
|
||||||
self._start_transformation(all_jobs, force=False)
|
self._start_transformation(all_jobs, force=force)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Fehler beim Transformieren aller XML-Dateien: {e}")
|
logger.error(f"Fehler beim Transformieren aller XML-Dateien: {e}")
|
||||||
QMessageBox.critical(self, "Fehler", f"Fehler beim Starten der Transformation: {str(e)}")
|
QMessageBox.critical(self, "Fehler", f"Fehler beim Starten der Transformation: {str(e)}")
|
||||||
|
|
||||||
def _transform_all_xml_files_force(self):
|
def _transform_all_xml_files_force(self):
|
||||||
"""
|
"""Transformiert ALLE XML-Dateien im Force-Modus."""
|
||||||
Transformiert ALLE XML-Dateien in allen TreeNodes des TreeWidgets (force).
|
self._transform_all_xml_files(force=True)
|
||||||
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)}")
|
|
||||||
|
|||||||
+5
-44
@@ -7,16 +7,14 @@ Dieses Modul enthält alle QThread-Klassen, die für Hintergrundoperationen verw
|
|||||||
- TransformationThread: Ausführung von XSL-Transformationen
|
- TransformationThread: Ausführung von XSL-Transformationen
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import hashlib
|
|
||||||
import logging
|
import logging
|
||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PySide6.QtCore import QThread, Signal
|
from PySide6.QtCore import QThread, Signal
|
||||||
|
|
||||||
from conf import TreeNode, XslFile, XmlFile
|
from conf import TreeNode, XslFile, XmlFile
|
||||||
from transform import TransformationJob
|
from transform import TransformationJob
|
||||||
|
from utils import calculate_blake2b_hash
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -32,7 +30,7 @@ class XmlHashCalculatorThread(QThread):
|
|||||||
calculation_finished = Signal(int, int) # processed_count, total_count
|
calculation_finished = Signal(int, int) # processed_count, total_count
|
||||||
error_occurred = Signal(str, str) # xml_file_path, error_message
|
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.
|
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")
|
logger.info(f"Hash-Berechnung abgeschlossen: {self.processed_count}/{len(self.xml_files)} verarbeitet")
|
||||||
|
|
||||||
def _calculate_blake2b_hash(self, file_path: Path) -> str | None:
|
def _calculate_blake2b_hash(self, file_path: Path) -> str | None:
|
||||||
"""
|
"""Berechnet den blake2b-Hash einer XML-Datei."""
|
||||||
Berechnet den blake2b-Hash einer XML-Datei.
|
return calculate_blake2b_hash(file_path)
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class XmlBatchProcessingThread(QThread):
|
class XmlBatchProcessingThread(QThread):
|
||||||
@@ -217,20 +191,7 @@ class XmlBatchProcessingThread(QThread):
|
|||||||
|
|
||||||
def _calculate_hash_for_file(self, file_path: Path) -> str | None:
|
def _calculate_hash_for_file(self, file_path: Path) -> str | None:
|
||||||
"""Berechnet blake2b Hash für eine Datei."""
|
"""Berechnet blake2b Hash für eine Datei."""
|
||||||
try:
|
return calculate_blake2b_hash(file_path)
|
||||||
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
|
|
||||||
|
|
||||||
def _find_xml_file_by_hash(self, hash_value: str) -> XmlFile | None:
|
def _find_xml_file_by_hash(self, hash_value: str) -> XmlFile | None:
|
||||||
"""Sucht eine XML-Datei anhand ihres Hash-Werts."""
|
"""Sucht eine XML-Datei anhand ihres Hash-Werts."""
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
"""
|
||||||
|
Gemeinsame Utility-Funktionen für DocuMentor.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
HASH_PREFIX = "blake2b"
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_blake2b_hash(file_path: Path) -> str | None:
|
||||||
|
"""
|
||||||
|
Berechnet den blake2b-Hash einer Datei.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path: Pfad zur Datei
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 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
|
||||||
|
|
||||||
|
hash_obj = hashlib.blake2b()
|
||||||
|
with open(file_path, "rb") as f:
|
||||||
|
for chunk in iter(lambda: f.read(8192), b""):
|
||||||
|
hash_obj.update(chunk)
|
||||||
|
|
||||||
|
return f"{HASH_PREFIX}:{hash_obj.hexdigest()}"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Fehler beim Berechnen des Hash für {file_path}: {e}")
|
||||||
|
return None
|
||||||
Reference in New Issue
Block a user