Fix: Code-Qualität und Effizienz verbessern (v1.5.1)

- main.py: print() durch logging ersetzt, cleanup nach Logger-Init verschoben
- conf.py: funktionsloses global-Statement entfernt
- database.py: unerreichbaren zweiten Projekt-Check entfernt
- hash_calculation.py: deprecated _handle_xml_file_drop entfernt, nutzlosen
  _get_all_project_xml_files-Wrapper entfernt, seen_paths-Scope-Bug in
  rekursiver Traversierung behoben (O(N²) → O(N)), veraltete List[]-Syntax
  und ungenutzte Imports bereinigt
- transform.py: TOCTOU-Muster (exists+stat) durch direktes stat() mit
  FileNotFoundError ersetzt; fop_conf.exists() gecacht

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 17:02:24 +02:00
parent 3d2efe628b
commit b900455d69
10 changed files with 28 additions and 84 deletions
-1
View File
@@ -223,7 +223,6 @@ class AppSettings(BaseSettings):
return (JsonConfigSettingsSource(settings_cls),)
def save(self):
global config_path
# Ordner existert nicht
if not config_path.parent.exists():
config_path.parent.mkdir(parents=True, exist_ok=True)
+5 -6
View File
@@ -35,11 +35,10 @@ def cleanup_old_logs(log_dir, max_age_hours=24):
log_file.unlink()
deleted_count += 1
except Exception as e:
# Fehler beim Löschen ignorieren und fortfahren
print(f"Fehler beim Löschen von {log_file}: {e}")
logging.warning(f"Fehler beim Löschen von {log_file}: {e}")
if deleted_count > 0:
print(f"{deleted_count} alte Log-Datei(en) gelöscht (älter als {max_age_hours} Stunden)")
logging.info(f"{deleted_count} alte Log-Datei(en) gelöscht (älter als {max_age_hours} Stunden)")
def main():
@@ -53,9 +52,6 @@ def main():
log_dir = config_path.parent / "logs"
log_dir.mkdir(parents=True, exist_ok=True)
# Alte Log-Dateien aufräumen
cleanup_old_logs(log_dir, max_age_hours=24)
# Log-Dateiname mit Timestamp
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
log_file = log_dir / f"documentor_{timestamp}.log"
@@ -81,6 +77,9 @@ def main():
logging.info(f"Logging initialisiert: {log_file}")
# Alte Log-Dateien aufräumen (erst nach Logger-Init)
cleanup_old_logs(log_dir, max_age_hours=24)
# QApplication-Instanz erstellen
app = QApplication(sys.argv)
+6 -5
View File
@@ -163,12 +163,12 @@ class TransformationJob:
Returns:
bool: True wenn New-PDF existiert und aktueller ist als alle Inputs
"""
if not self.new_pdf.exists():
try:
output_mtime = self.new_pdf.stat().st_mtime
except FileNotFoundError:
logger.debug(f"New-PDF existiert nicht: {self.new_pdf}")
return False
output_mtime = self.new_pdf.stat().st_mtime
# Prüfe XML-Datei
xml_abs = self.project_dir / self.xml_file
if xml_abs.exists() and xml_abs.stat().st_mtime > output_mtime:
@@ -384,10 +384,11 @@ class TransformationJob:
# Fallback: Traditionelle subprocess-Methode (langsamer, aber robuster)
# Apache FOP Kommandozeile
fop_conf_exists = self.fop_conf.exists()
cmd_line = [
str(self.fop_cmd),
"-c",
str(self.fop_conf) if self.fop_conf.exists() else "",
str(self.fop_conf) if fop_conf_exists else "",
"-r",
"-fo",
str(self.temp_fo),
@@ -396,7 +397,7 @@ class TransformationJob:
]
# Entferne leere Config-Parameter wenn fop.xconf nicht existiert
if not self.fop_conf.exists():
if not fop_conf_exists:
cmd_line = [c for c in cmd_line if c not in ["-c", ""]]
logger.info(f"Starte Apache FOP PDF-Generierung: {self.xml_file.name}")
-5
View File
@@ -72,11 +72,6 @@ class DatabaseMixin:
QMessageBox.warning(self, "Warnung", "Kein Projekt geladen. Bitte öffnen Sie zuerst ein Projekt.")
return
# Hole das aktuelle Projekt aus app_settings
if not self.project:
QMessageBox.warning(self, "Warnung", "Aktuelles Projekt nicht in den Einstellungen gefunden.")
return
# Hole die PostgreSQL-Datenbank-Konfiguration
db_config = self._get_database_config(self.project.postgre_sql_db_id)
if not db_config:
+12 -62
View File
@@ -9,12 +9,8 @@ import shutil
import time
import logging
from pathlib import Path
from typing import List
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
@@ -33,49 +29,6 @@ class HashCalculationMixin:
- self._load_nodes_to_tree(): Methode zum Laden der Nodes in den Tree
"""
def _handle_xml_file_drop(self, xml_file_path: Path):
"""
Verarbeitet eine einzelne XML-Datei, die per Drag&Drop hinzugefügt wurde.
DEPRECATED: Diese Methode wird durch _handle_multiple_xml_files_drop ersetzt.
Args:
xml_file_path: Pfad zur XML-Datei
"""
try:
logger.debug(f"Verarbeite XML-Datei: {xml_file_path}")
# Prüfe ob die Datei existiert
if not xml_file_path.exists():
QMessageBox.critical(self, "Fehler", f"Die XML-Datei existiert nicht:\n{xml_file_path}")
return
# Prüfe ob Projekt-Nodes verfügbar sind
if not self.pdf_project or not self.pdf_project.nodes:
QMessageBox.warning(self, "Warnung", "Keine Projekt-Nodes verfügbar für die Zuordnung.")
return
# Öffne den Dialog zur Zuordnung zu XSL-Knoten
dialog = XmlToXslAssignDialog(
parent=self, xml_file_path=xml_file_path, project_nodes=self.pdf_project.nodes
)
if dialog.exec() == XmlToXslAssignDialog.DialogCode.Accepted:
# Hole die ausgewählten XSL-Knoten
selected_xsl_nodes = dialog.get_selected_xsl_nodes()
if selected_xsl_nodes:
# Verarbeite die Zuordnung
self._assign_xml_to_xsl_nodes(xml_file_path, selected_xsl_nodes)
else:
logger.warning("Keine XSL-Knoten ausgewählt")
else:
logger.debug("Dialog abgebrochen")
except Exception as e:
error_msg = f"Fehler beim Verarbeiten der XML-Datei '{xml_file_path}': {str(e)}"
logger.error(error_msg)
QMessageBox.critical(self, "Fehler", error_msg)
def _assign_xml_to_xsl_nodes(self, xml_file_path: Path, selected_xsl_nodes: list):
"""
Ordnet eine XML-Datei den ausgewählten XSL-Knoten zu.
@@ -157,18 +110,19 @@ class HashCalculationMixin:
except Exception as e:
logger.error(f"Fehler beim Starten der Hash-Berechnung: {e}")
def _collect_all_xml_files(self) -> List[XmlFile]:
def _collect_all_xml_files(self) -> list[XmlFile]:
"""
Sammelt alle XmlFile-Objekte aus der Projektstruktur.
Returns:
List[XmlFile]: Liste aller gefundenen XML-Dateien
list[XmlFile]: Liste aller gefundenen XML-Dateien
"""
xml_files = []
xml_files: list[XmlFile] = []
seen_paths: set = set()
try:
if self.pdf_project and self.pdf_project.nodes:
self._collect_xml_files_recursive(self.pdf_project.nodes, xml_files)
self._collect_xml_files_recursive(self.pdf_project.nodes, xml_files, seen_paths)
logger.debug(f"Gesammelt: {len(xml_files)} XML-Dateien")
return xml_files
@@ -177,15 +131,15 @@ class HashCalculationMixin:
logger.error(f"Fehler beim Sammeln der XML-Dateien: {e}")
return []
def _collect_xml_files_recursive(self, nodes, xml_files: List[XmlFile]):
def _collect_xml_files_recursive(self, nodes, xml_files: list[XmlFile], seen_paths: set):
"""
Sammelt rekursiv alle XML-Dateien aus den Nodes.
Args:
nodes: Liste der zu durchsuchenden Nodes
xml_files: Liste zum Sammeln der XML-Dateien
seen_paths: Set bereits gesehener Pfade (verhindert Duplikate)
"""
seen_paths = {xf.xml for xf in xml_files}
for node in nodes:
if isinstance(node, XslFile) and node.xmls:
for xml_file in node.xmls:
@@ -193,7 +147,7 @@ class HashCalculationMixin:
xml_files.append(xml_file)
seen_paths.add(xml_file.xml)
elif isinstance(node, TreeNode) and node.children:
self._collect_xml_files_recursive(node.children, xml_files)
self._collect_xml_files_recursive(node.children, xml_files, seen_paths)
def _on_hash_calculated(self, xml_file: XmlFile, hash_value: str):
"""
@@ -266,10 +220,6 @@ class HashCalculationMixin:
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."""
return self._collect_all_xml_files()
def _find_xml_file_by_hash(self, target_hash: str) -> XmlFile | None:
"""
Sucht eine XML-Datei mit dem angegebenen Hash im gesamten Projekt.
@@ -284,7 +234,7 @@ class HashCalculationMixin:
if not target_hash:
return None
all_xml_files = self._get_all_project_xml_files()
all_xml_files = self._collect_all_xml_files()
for xml_file in all_xml_files:
if xml_file.hashsum == target_hash:
@@ -314,7 +264,7 @@ class HashCalculationMixin:
extension = original_path.suffix # ".xml"
# Sammle einmalig alle verwendeten Dateinamen (Performance-Optimierung)
all_xml_files = self._get_all_project_xml_files()
all_xml_files = self._collect_all_xml_files()
used_names = {xml_file.xml.name for xml_file in all_xml_files}
counter = 1
@@ -351,7 +301,7 @@ class HashCalculationMixin:
bool: True wenn der Dateiname bereits verwendet wird
"""
try:
all_xml_files = self._get_all_project_xml_files()
all_xml_files = self._collect_all_xml_files()
for xml_file in all_xml_files:
if xml_file.xml == relative_xml_path:
@@ -508,7 +458,7 @@ class HashCalculationMixin:
logger.error(error_msg)
return {"status": "error", "error_msg": error_msg}
def _show_filename_selection_dialog(self, original_name: str, alternative_paths: List[Path]) -> Path | None:
def _show_filename_selection_dialog(self, original_name: str, alternative_paths: list[Path]) -> Path | None:
"""
Zeigt einen Dialog zur Auswahl eines alternativen Dateinamens.