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:
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<!-- Paket-Definition (ersetzt Product in v4) -->
|
<!-- Paket-Definition (ersetzt Product in v4) -->
|
||||||
<Package
|
<Package
|
||||||
Name="DocuMentor"
|
Name="DocuMentor"
|
||||||
Version="1.5.0"
|
Version="1.5.1"
|
||||||
Manufacturer="Vitali Graf / Software- und Datenbankentwicklung"
|
Manufacturer="Vitali Graf / Software- und Datenbankentwicklung"
|
||||||
UpgradeCode="F498B66C-726D-44AA-95F4-CB4FBDCEF26E"
|
UpgradeCode="F498B66C-726D-44AA-95F4-CB4FBDCEF26E"
|
||||||
Language="1031"
|
Language="1031"
|
||||||
|
|||||||
@@ -253,5 +253,5 @@ HINWEISE
|
|||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
Stand: April 2026
|
Stand: April 2026
|
||||||
Erstellt für: DocuMentor v1.5.0
|
Erstellt für: DocuMentor v1.5.1
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
; Build-Befehl: iscc installer.iss
|
; Build-Befehl: iscc installer.iss
|
||||||
|
|
||||||
#define MyAppName "DocuMentor"
|
#define MyAppName "DocuMentor"
|
||||||
#define MyAppVersion "1.5.0"
|
#define MyAppVersion "1.5.1"
|
||||||
#define MyAppPublisher "Ihr Name/Organisation"
|
#define MyAppPublisher "Ihr Name/Organisation"
|
||||||
#define MyAppURL "https://github.com/yourusername/xsl-validator"
|
#define MyAppURL "https://github.com/yourusername/xsl-validator"
|
||||||
#define MyAppExeName "DocuMentor.exe"
|
#define MyAppExeName "DocuMentor.exe"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "DocuMentor"
|
name = "DocuMentor"
|
||||||
version = "1.5.0"
|
version = "1.5.1"
|
||||||
description = "Professionelle XSL-Transformations-Verwaltung und PDF-Generierung"
|
description = "Professionelle XSL-Transformations-Verwaltung und PDF-Generierung"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = {text = "MIT"}
|
license = {text = "MIT"}
|
||||||
|
|||||||
@@ -223,7 +223,6 @@ class AppSettings(BaseSettings):
|
|||||||
return (JsonConfigSettingsSource(settings_cls),)
|
return (JsonConfigSettingsSource(settings_cls),)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
global config_path
|
|
||||||
# Ordner existert nicht
|
# Ordner existert nicht
|
||||||
if not config_path.parent.exists():
|
if not config_path.parent.exists():
|
||||||
config_path.parent.mkdir(parents=True, exist_ok=True)
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|||||||
+5
-6
@@ -35,11 +35,10 @@ def cleanup_old_logs(log_dir, max_age_hours=24):
|
|||||||
log_file.unlink()
|
log_file.unlink()
|
||||||
deleted_count += 1
|
deleted_count += 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Fehler beim Löschen ignorieren und fortfahren
|
logging.warning(f"Fehler beim Löschen von {log_file}: {e}")
|
||||||
print(f"Fehler beim Löschen von {log_file}: {e}")
|
|
||||||
|
|
||||||
if deleted_count > 0:
|
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():
|
def main():
|
||||||
@@ -53,9 +52,6 @@ def main():
|
|||||||
log_dir = config_path.parent / "logs"
|
log_dir = config_path.parent / "logs"
|
||||||
log_dir.mkdir(parents=True, exist_ok=True)
|
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
|
# Log-Dateiname mit Timestamp
|
||||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
log_file = log_dir / f"documentor_{timestamp}.log"
|
log_file = log_dir / f"documentor_{timestamp}.log"
|
||||||
@@ -81,6 +77,9 @@ def main():
|
|||||||
|
|
||||||
logging.info(f"Logging initialisiert: {log_file}")
|
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
|
# QApplication-Instanz erstellen
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
|||||||
+6
-5
@@ -163,12 +163,12 @@ class TransformationJob:
|
|||||||
Returns:
|
Returns:
|
||||||
bool: True wenn New-PDF existiert und aktueller ist als alle Inputs
|
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}")
|
logger.debug(f"New-PDF existiert nicht: {self.new_pdf}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
output_mtime = self.new_pdf.stat().st_mtime
|
|
||||||
|
|
||||||
# Prüfe XML-Datei
|
# Prüfe XML-Datei
|
||||||
xml_abs = self.project_dir / self.xml_file
|
xml_abs = self.project_dir / self.xml_file
|
||||||
if xml_abs.exists() and xml_abs.stat().st_mtime > output_mtime:
|
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)
|
# Fallback: Traditionelle subprocess-Methode (langsamer, aber robuster)
|
||||||
|
|
||||||
# Apache FOP Kommandozeile
|
# Apache FOP Kommandozeile
|
||||||
|
fop_conf_exists = self.fop_conf.exists()
|
||||||
cmd_line = [
|
cmd_line = [
|
||||||
str(self.fop_cmd),
|
str(self.fop_cmd),
|
||||||
"-c",
|
"-c",
|
||||||
str(self.fop_conf) if self.fop_conf.exists() else "",
|
str(self.fop_conf) if fop_conf_exists else "",
|
||||||
"-r",
|
"-r",
|
||||||
"-fo",
|
"-fo",
|
||||||
str(self.temp_fo),
|
str(self.temp_fo),
|
||||||
@@ -396,7 +397,7 @@ class TransformationJob:
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Entferne leere Config-Parameter wenn fop.xconf nicht existiert
|
# 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", ""]]
|
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}")
|
logger.info(f"Starte Apache FOP PDF-Generierung: {self.xml_file.name}")
|
||||||
|
|||||||
@@ -72,11 +72,6 @@ class DatabaseMixin:
|
|||||||
QMessageBox.warning(self, "Warnung", "Kein Projekt geladen. Bitte öffnen Sie zuerst ein Projekt.")
|
QMessageBox.warning(self, "Warnung", "Kein Projekt geladen. Bitte öffnen Sie zuerst ein Projekt.")
|
||||||
return
|
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
|
# Hole die PostgreSQL-Datenbank-Konfiguration
|
||||||
db_config = self._get_database_config(self.project.postgre_sql_db_id)
|
db_config = self._get_database_config(self.project.postgre_sql_db_id)
|
||||||
if not db_config:
|
if not db_config:
|
||||||
|
|||||||
@@ -9,12 +9,8 @@ import shutil
|
|||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from PySide6.QtWidgets import QMessageBox
|
|
||||||
|
|
||||||
from conf import TreeNode, XslFile, XmlFile
|
from conf import TreeNode, XslFile, XmlFile
|
||||||
from ui.XmlToXslAssignDialog import XmlToXslAssignDialog
|
|
||||||
from ui.threads import XmlHashCalculatorThread
|
from ui.threads import XmlHashCalculatorThread
|
||||||
from utils import calculate_blake2b_hash
|
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
|
- 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):
|
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.
|
Ordnet eine XML-Datei den ausgewählten XSL-Knoten zu.
|
||||||
@@ -157,18 +110,19 @@ class HashCalculationMixin:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Fehler beim Starten der Hash-Berechnung: {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.
|
Sammelt alle XmlFile-Objekte aus der Projektstruktur.
|
||||||
|
|
||||||
Returns:
|
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:
|
try:
|
||||||
if self.pdf_project and self.pdf_project.nodes:
|
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")
|
logger.debug(f"Gesammelt: {len(xml_files)} XML-Dateien")
|
||||||
return xml_files
|
return xml_files
|
||||||
@@ -177,15 +131,15 @@ class HashCalculationMixin:
|
|||||||
logger.error(f"Fehler beim Sammeln der XML-Dateien: {e}")
|
logger.error(f"Fehler beim Sammeln der XML-Dateien: {e}")
|
||||||
return []
|
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.
|
Sammelt rekursiv alle XML-Dateien aus den Nodes.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
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: Set bereits gesehener Pfade (verhindert Duplikate)
|
||||||
"""
|
"""
|
||||||
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:
|
||||||
for xml_file in node.xmls:
|
for xml_file in node.xmls:
|
||||||
@@ -193,7 +147,7 @@ class HashCalculationMixin:
|
|||||||
xml_files.append(xml_file)
|
xml_files.append(xml_file)
|
||||||
seen_paths.add(xml_file.xml)
|
seen_paths.add(xml_file.xml)
|
||||||
elif isinstance(node, TreeNode) and node.children:
|
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):
|
def _on_hash_calculated(self, xml_file: XmlFile, hash_value: str):
|
||||||
"""
|
"""
|
||||||
@@ -266,10 +220,6 @@ class HashCalculationMixin:
|
|||||||
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]:
|
|
||||||
"""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:
|
def _find_xml_file_by_hash(self, target_hash: str) -> XmlFile | None:
|
||||||
"""
|
"""
|
||||||
Sucht eine XML-Datei mit dem angegebenen Hash im gesamten Projekt.
|
Sucht eine XML-Datei mit dem angegebenen Hash im gesamten Projekt.
|
||||||
@@ -284,7 +234,7 @@ class HashCalculationMixin:
|
|||||||
if not target_hash:
|
if not target_hash:
|
||||||
return None
|
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:
|
for xml_file in all_xml_files:
|
||||||
if xml_file.hashsum == target_hash:
|
if xml_file.hashsum == target_hash:
|
||||||
@@ -314,7 +264,7 @@ class HashCalculationMixin:
|
|||||||
extension = original_path.suffix # ".xml"
|
extension = original_path.suffix # ".xml"
|
||||||
|
|
||||||
# Sammle einmalig alle verwendeten Dateinamen (Performance-Optimierung)
|
# 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}
|
used_names = {xml_file.xml.name for xml_file in all_xml_files}
|
||||||
|
|
||||||
counter = 1
|
counter = 1
|
||||||
@@ -351,7 +301,7 @@ class HashCalculationMixin:
|
|||||||
bool: True wenn der Dateiname bereits verwendet wird
|
bool: True wenn der Dateiname bereits verwendet wird
|
||||||
"""
|
"""
|
||||||
try:
|
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:
|
for xml_file in all_xml_files:
|
||||||
if xml_file.xml == relative_xml_path:
|
if xml_file.xml == relative_xml_path:
|
||||||
@@ -508,7 +458,7 @@ class HashCalculationMixin:
|
|||||||
logger.error(error_msg)
|
logger.error(error_msg)
|
||||||
return {"status": "error", "error_msg": 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.
|
Zeigt einen Dialog zur Auswahl eines alternativen Dateinamens.
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "documentor"
|
name = "documentor"
|
||||||
version = "1.5.0"
|
version = "1.5.1"
|
||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "connectorx" },
|
{ name = "connectorx" },
|
||||||
|
|||||||
Reference in New Issue
Block a user