Feature: Timeout-Einstellung und asynchrone DB-Abfrage mit Abbrechen-Dialog

DB-Abfragen laufen nun in einem Hintergrund-Thread mit QProgressDialog,
sodass die UI nicht mehr einfriert. connect_timeout wird als konfigurierbarer
Parameter (1-300s, Standard: 10) im Connection-String übergeben.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 21:31:40 +01:00
parent 66496c26d8
commit ec33a5b586
5 changed files with 287 additions and 187 deletions
+1
View File
@@ -89,6 +89,7 @@ class PostgreSqlDb(BaseModel):
username: str
password: str
ssl_mode: SSLMode = SSLMode.PREFER
timeout: int = 10
class Project(BaseModel):
+10 -2
View File
@@ -19,8 +19,14 @@ class DatabaseTestThread(QThread):
def run(self):
"""Führt den Datenbanktest in einem separaten Thread aus."""
try:
uri = f"postgresql://{self.connection_data['username']}:{self.connection_data['password']}@{self.connection_data['host']}:{self.connection_data['port']}/{self.connection_data['database']}"
timeout = self.connection_data.get("timeout", 10)
uri = (
f"postgresql://{self.connection_data['username']}:{self.connection_data['password']}"
f"@{self.connection_data['host']}:{self.connection_data['port']}"
f"/{self.connection_data['database']}"
f"?connect_timeout={timeout}"
)
# Datenbankverbindung testen
r = pl.read_database_uri(
query="SELECT 1",
@@ -106,6 +112,7 @@ class PostgreSqlConfigDialog(QDialog):
self.ui.usernameEdit.setText(data.get("username", ""))
self.ui.passwordEdit.setText(data.get("password", ""))
self.ui.sslModeComboBox.setCurrentText(data.get("ssl_mode", "prefer"))
self.ui.timeoutSpinBox.setValue(data.get("timeout", 10))
def get_data(self):
"""Gibt die eingegebenen Daten zurück."""
@@ -126,4 +133,5 @@ class PostgreSqlConfigDialog(QDialog):
"username": self.ui.usernameEdit.text().strip(),
"password": self.ui.passwordEdit.text(), # Passwort kann leer sein
"ssl_mode": self.ui.sslModeComboBox.currentText(),
"timeout": self.ui.timeoutSpinBox.value(),
}
+20
View File
@@ -138,7 +138,27 @@
</item>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="timeoutLabel">
<property name="text">
<string>Timeout (s):</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QSpinBox" name="timeoutSpinBox">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>300</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QPushButton" name="testConnectionButton">
<property name="text">
<string>Verbindung testen</string>
+170 -156
View File
@@ -1,156 +1,170 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'PostgreSqlConfigDialog.ui'
##
## Created by: Qt User Interface Compiler version 6.9.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractButton, QApplication, QComboBox, QDialog,
QDialogButtonBox, QFormLayout, QLabel, QLineEdit,
QPushButton, QSizePolicy, QSpinBox, QVBoxLayout,
QWidget)
class Ui_PostgreSqlConfigDialog(object):
def setupUi(self, PostgreSqlConfigDialog):
if not PostgreSqlConfigDialog.objectName():
PostgreSqlConfigDialog.setObjectName(u"PostgreSqlConfigDialog")
PostgreSqlConfigDialog.resize(397, 268)
PostgreSqlConfigDialog.setModal(True)
self.verticalLayout = QVBoxLayout(PostgreSqlConfigDialog)
self.verticalLayout.setObjectName(u"verticalLayout")
self.formLayout = QFormLayout()
self.formLayout.setObjectName(u"formLayout")
self.nameLabel = QLabel(PostgreSqlConfigDialog)
self.nameLabel.setObjectName(u"nameLabel")
self.formLayout.setWidget(0, QFormLayout.ItemRole.LabelRole, self.nameLabel)
self.nameEdit = QLineEdit(PostgreSqlConfigDialog)
self.nameEdit.setObjectName(u"nameEdit")
self.formLayout.setWidget(0, QFormLayout.ItemRole.FieldRole, self.nameEdit)
self.hostLabel = QLabel(PostgreSqlConfigDialog)
self.hostLabel.setObjectName(u"hostLabel")
self.formLayout.setWidget(1, QFormLayout.ItemRole.LabelRole, self.hostLabel)
self.hostEdit = QLineEdit(PostgreSqlConfigDialog)
self.hostEdit.setObjectName(u"hostEdit")
self.formLayout.setWidget(1, QFormLayout.ItemRole.FieldRole, self.hostEdit)
self.portLabel = QLabel(PostgreSqlConfigDialog)
self.portLabel.setObjectName(u"portLabel")
self.formLayout.setWidget(2, QFormLayout.ItemRole.LabelRole, self.portLabel)
self.portSpinBox = QSpinBox(PostgreSqlConfigDialog)
self.portSpinBox.setObjectName(u"portSpinBox")
self.portSpinBox.setMinimum(1)
self.portSpinBox.setMaximum(65535)
self.portSpinBox.setValue(5432)
self.formLayout.setWidget(2, QFormLayout.ItemRole.FieldRole, self.portSpinBox)
self.databaseLabel = QLabel(PostgreSqlConfigDialog)
self.databaseLabel.setObjectName(u"databaseLabel")
self.formLayout.setWidget(3, QFormLayout.ItemRole.LabelRole, self.databaseLabel)
self.databaseEdit = QLineEdit(PostgreSqlConfigDialog)
self.databaseEdit.setObjectName(u"databaseEdit")
self.formLayout.setWidget(3, QFormLayout.ItemRole.FieldRole, self.databaseEdit)
self.usernameLabel = QLabel(PostgreSqlConfigDialog)
self.usernameLabel.setObjectName(u"usernameLabel")
self.formLayout.setWidget(4, QFormLayout.ItemRole.LabelRole, self.usernameLabel)
self.usernameEdit = QLineEdit(PostgreSqlConfigDialog)
self.usernameEdit.setObjectName(u"usernameEdit")
self.formLayout.setWidget(4, QFormLayout.ItemRole.FieldRole, self.usernameEdit)
self.passwordLabel = QLabel(PostgreSqlConfigDialog)
self.passwordLabel.setObjectName(u"passwordLabel")
self.formLayout.setWidget(5, QFormLayout.ItemRole.LabelRole, self.passwordLabel)
self.passwordEdit = QLineEdit(PostgreSqlConfigDialog)
self.passwordEdit.setObjectName(u"passwordEdit")
self.passwordEdit.setEchoMode(QLineEdit.EchoMode.Password)
self.formLayout.setWidget(5, QFormLayout.ItemRole.FieldRole, self.passwordEdit)
self.sslModeLabel = QLabel(PostgreSqlConfigDialog)
self.sslModeLabel.setObjectName(u"sslModeLabel")
self.formLayout.setWidget(6, QFormLayout.ItemRole.LabelRole, self.sslModeLabel)
self.sslModeComboBox = QComboBox(PostgreSqlConfigDialog)
self.sslModeComboBox.addItem("")
self.sslModeComboBox.addItem("")
self.sslModeComboBox.addItem("")
self.sslModeComboBox.addItem("")
self.sslModeComboBox.addItem("")
self.sslModeComboBox.addItem("")
self.sslModeComboBox.setObjectName(u"sslModeComboBox")
self.formLayout.setWidget(6, QFormLayout.ItemRole.FieldRole, self.sslModeComboBox)
self.testConnectionButton = QPushButton(PostgreSqlConfigDialog)
self.testConnectionButton.setObjectName(u"testConnectionButton")
self.formLayout.setWidget(7, QFormLayout.ItemRole.FieldRole, self.testConnectionButton)
self.verticalLayout.addLayout(self.formLayout)
self.buttonBox = QDialogButtonBox(PostgreSqlConfigDialog)
self.buttonBox.setObjectName(u"buttonBox")
self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
self.buttonBox.setCenterButtons(True)
self.verticalLayout.addWidget(self.buttonBox)
self.retranslateUi(PostgreSqlConfigDialog)
self.buttonBox.accepted.connect(PostgreSqlConfigDialog.accept)
self.buttonBox.rejected.connect(PostgreSqlConfigDialog.reject)
QMetaObject.connectSlotsByName(PostgreSqlConfigDialog)
# setupUi
def retranslateUi(self, PostgreSqlConfigDialog):
PostgreSqlConfigDialog.setWindowTitle(QCoreApplication.translate("PostgreSqlConfigDialog", u"PostgreSQL Datenbank Konfiguration", None))
self.nameLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Name:", None))
self.hostLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Host:", None))
self.hostEdit.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"localhost", None))
self.portLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Port:", None))
self.databaseLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Datenbank:", None))
self.usernameLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Benutzername:", None))
self.passwordLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Passwort:", None))
self.sslModeLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"SSL-Modus:", None))
self.sslModeComboBox.setItemText(0, QCoreApplication.translate("PostgreSqlConfigDialog", u"disable", None))
self.sslModeComboBox.setItemText(1, QCoreApplication.translate("PostgreSqlConfigDialog", u"allow", None))
self.sslModeComboBox.setItemText(2, QCoreApplication.translate("PostgreSqlConfigDialog", u"prefer", None))
self.sslModeComboBox.setItemText(3, QCoreApplication.translate("PostgreSqlConfigDialog", u"require", None))
self.sslModeComboBox.setItemText(4, QCoreApplication.translate("PostgreSqlConfigDialog", u"verify-ca", None))
self.sslModeComboBox.setItemText(5, QCoreApplication.translate("PostgreSqlConfigDialog", u"verify-full", None))
self.testConnectionButton.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Verbindung testen", None))
# retranslateUi
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'PostgreSqlConfigDialog.ui'
##
## Created by: Qt User Interface Compiler version 6.10.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractButton, QApplication, QComboBox, QDialog,
QDialogButtonBox, QFormLayout, QLabel, QLineEdit,
QPushButton, QSizePolicy, QSpinBox, QVBoxLayout,
QWidget)
class Ui_PostgreSqlConfigDialog(object):
def setupUi(self, PostgreSqlConfigDialog):
if not PostgreSqlConfigDialog.objectName():
PostgreSqlConfigDialog.setObjectName(u"PostgreSqlConfigDialog")
PostgreSqlConfigDialog.resize(397, 268)
PostgreSqlConfigDialog.setModal(True)
self.verticalLayout = QVBoxLayout(PostgreSqlConfigDialog)
self.verticalLayout.setObjectName(u"verticalLayout")
self.formLayout = QFormLayout()
self.formLayout.setObjectName(u"formLayout")
self.nameLabel = QLabel(PostgreSqlConfigDialog)
self.nameLabel.setObjectName(u"nameLabel")
self.formLayout.setWidget(0, QFormLayout.ItemRole.LabelRole, self.nameLabel)
self.nameEdit = QLineEdit(PostgreSqlConfigDialog)
self.nameEdit.setObjectName(u"nameEdit")
self.formLayout.setWidget(0, QFormLayout.ItemRole.FieldRole, self.nameEdit)
self.hostLabel = QLabel(PostgreSqlConfigDialog)
self.hostLabel.setObjectName(u"hostLabel")
self.formLayout.setWidget(1, QFormLayout.ItemRole.LabelRole, self.hostLabel)
self.hostEdit = QLineEdit(PostgreSqlConfigDialog)
self.hostEdit.setObjectName(u"hostEdit")
self.formLayout.setWidget(1, QFormLayout.ItemRole.FieldRole, self.hostEdit)
self.portLabel = QLabel(PostgreSqlConfigDialog)
self.portLabel.setObjectName(u"portLabel")
self.formLayout.setWidget(2, QFormLayout.ItemRole.LabelRole, self.portLabel)
self.portSpinBox = QSpinBox(PostgreSqlConfigDialog)
self.portSpinBox.setObjectName(u"portSpinBox")
self.portSpinBox.setMinimum(1)
self.portSpinBox.setMaximum(65535)
self.portSpinBox.setValue(5432)
self.formLayout.setWidget(2, QFormLayout.ItemRole.FieldRole, self.portSpinBox)
self.databaseLabel = QLabel(PostgreSqlConfigDialog)
self.databaseLabel.setObjectName(u"databaseLabel")
self.formLayout.setWidget(3, QFormLayout.ItemRole.LabelRole, self.databaseLabel)
self.databaseEdit = QLineEdit(PostgreSqlConfigDialog)
self.databaseEdit.setObjectName(u"databaseEdit")
self.formLayout.setWidget(3, QFormLayout.ItemRole.FieldRole, self.databaseEdit)
self.usernameLabel = QLabel(PostgreSqlConfigDialog)
self.usernameLabel.setObjectName(u"usernameLabel")
self.formLayout.setWidget(4, QFormLayout.ItemRole.LabelRole, self.usernameLabel)
self.usernameEdit = QLineEdit(PostgreSqlConfigDialog)
self.usernameEdit.setObjectName(u"usernameEdit")
self.formLayout.setWidget(4, QFormLayout.ItemRole.FieldRole, self.usernameEdit)
self.passwordLabel = QLabel(PostgreSqlConfigDialog)
self.passwordLabel.setObjectName(u"passwordLabel")
self.formLayout.setWidget(5, QFormLayout.ItemRole.LabelRole, self.passwordLabel)
self.passwordEdit = QLineEdit(PostgreSqlConfigDialog)
self.passwordEdit.setObjectName(u"passwordEdit")
self.passwordEdit.setEchoMode(QLineEdit.EchoMode.Password)
self.formLayout.setWidget(5, QFormLayout.ItemRole.FieldRole, self.passwordEdit)
self.sslModeLabel = QLabel(PostgreSqlConfigDialog)
self.sslModeLabel.setObjectName(u"sslModeLabel")
self.formLayout.setWidget(6, QFormLayout.ItemRole.LabelRole, self.sslModeLabel)
self.sslModeComboBox = QComboBox(PostgreSqlConfigDialog)
self.sslModeComboBox.addItem("")
self.sslModeComboBox.addItem("")
self.sslModeComboBox.addItem("")
self.sslModeComboBox.addItem("")
self.sslModeComboBox.addItem("")
self.sslModeComboBox.addItem("")
self.sslModeComboBox.setObjectName(u"sslModeComboBox")
self.formLayout.setWidget(6, QFormLayout.ItemRole.FieldRole, self.sslModeComboBox)
self.timeoutLabel = QLabel(PostgreSqlConfigDialog)
self.timeoutLabel.setObjectName(u"timeoutLabel")
self.formLayout.setWidget(7, QFormLayout.ItemRole.LabelRole, self.timeoutLabel)
self.timeoutSpinBox = QSpinBox(PostgreSqlConfigDialog)
self.timeoutSpinBox.setObjectName(u"timeoutSpinBox")
self.timeoutSpinBox.setMinimum(1)
self.timeoutSpinBox.setMaximum(300)
self.timeoutSpinBox.setValue(10)
self.formLayout.setWidget(7, QFormLayout.ItemRole.FieldRole, self.timeoutSpinBox)
self.testConnectionButton = QPushButton(PostgreSqlConfigDialog)
self.testConnectionButton.setObjectName(u"testConnectionButton")
self.formLayout.setWidget(8, QFormLayout.ItemRole.FieldRole, self.testConnectionButton)
self.verticalLayout.addLayout(self.formLayout)
self.buttonBox = QDialogButtonBox(PostgreSqlConfigDialog)
self.buttonBox.setObjectName(u"buttonBox")
self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
self.buttonBox.setCenterButtons(True)
self.verticalLayout.addWidget(self.buttonBox)
self.retranslateUi(PostgreSqlConfigDialog)
self.buttonBox.accepted.connect(PostgreSqlConfigDialog.accept)
self.buttonBox.rejected.connect(PostgreSqlConfigDialog.reject)
QMetaObject.connectSlotsByName(PostgreSqlConfigDialog)
# setupUi
def retranslateUi(self, PostgreSqlConfigDialog):
PostgreSqlConfigDialog.setWindowTitle(QCoreApplication.translate("PostgreSqlConfigDialog", u"PostgreSQL Datenbank Konfiguration", None))
self.nameLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Name:", None))
self.hostLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Host:", None))
self.hostEdit.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"localhost", None))
self.portLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Port:", None))
self.databaseLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Datenbank:", None))
self.usernameLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Benutzername:", None))
self.passwordLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Passwort:", None))
self.sslModeLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"SSL-Modus:", None))
self.sslModeComboBox.setItemText(0, QCoreApplication.translate("PostgreSqlConfigDialog", u"disable", None))
self.sslModeComboBox.setItemText(1, QCoreApplication.translate("PostgreSqlConfigDialog", u"allow", None))
self.sslModeComboBox.setItemText(2, QCoreApplication.translate("PostgreSqlConfigDialog", u"prefer", None))
self.sslModeComboBox.setItemText(3, QCoreApplication.translate("PostgreSqlConfigDialog", u"require", None))
self.sslModeComboBox.setItemText(4, QCoreApplication.translate("PostgreSqlConfigDialog", u"verify-ca", None))
self.sslModeComboBox.setItemText(5, QCoreApplication.translate("PostgreSqlConfigDialog", u"verify-full", None))
self.timeoutLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Timeout (s):", None))
self.testConnectionButton.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Verbindung testen", None))
# retranslateUi
+86 -29
View File
@@ -10,13 +10,35 @@ import logging
from pathlib import Path
import polars as pl
from PySide6.QtWidgets import QMessageBox
from PySide6.QtCore import QThread, Signal, Qt
from PySide6.QtWidgets import QMessageBox, QProgressDialog
from conf import app_settings, TreeNode, XslFile
logger = logging.getLogger(__name__)
class DatabaseQueryThread(QThread):
"""Thread für asynchrone Datenbankabfragen."""
query_completed = Signal(object) # pl.DataFrame
query_failed = Signal(str) # Fehlermeldung
def __init__(self, sql_query, connection_string):
super().__init__()
self.sql_query = sql_query
self.connection_string = connection_string
def run(self):
try:
df = pl.read_database_uri(self.sql_query, self.connection_string, engine="connectorx").sort(
["reporttyp_bez", "report_bez", "repfile_bez"]
)
self.query_completed.emit(df)
except Exception as e:
self.query_failed.emit(str(e))
class DatabaseMixin:
"""
Mixin für Datenbank-Operationen.
@@ -31,7 +53,7 @@ class DatabaseMixin:
def on_load_from_fn2_clicked(self):
"""
Wird ausgeführt, wenn der Button "lade aus FN2" geklickt wird.
Führt SQL-Abfrage aus und aktualisiert die Projekt-Nodes.
Startet SQL-Abfrage asynchron mit Fortschrittsdialog.
"""
logger.debug("Button 'lade aus FN2' wurde geklickt!")
@@ -52,29 +74,68 @@ class DatabaseMixin:
QMessageBox.warning(self, "Warnung", "PostgreSQL-Datenbank-Konfiguration nicht gefunden.")
return
# Führe SQL-Abfrage aus
df = self._execute_sql_query(db_config)
if df is None:
# SQL-Abfrage und Connection-String vorbereiten
sql_query, connection_string = self._prepare_sql_query(db_config)
if sql_query is None:
return # Fehler bereits angezeigt
# Verarbeite die Daten wie in readCsv.py
new_nodes = self._process_sql_data(df)
# Fortschrittsdialog erstellen
self._db_progress = QProgressDialog("Verbinde mit Datenbank...", "Abbrechen", 0, 0, self)
self._db_progress.setWindowTitle("Datenbank-Abfrage")
self._db_progress.setWindowModality(Qt.WindowModal)
self._db_progress.setMinimumDuration(0)
# Merge mit vorhandenen Nodes
self._merge_nodes_with_existing(new_nodes)
# Speichere die aktualisierten Projekt-Einstellungen
self._save_project_settings()
# Lade das Projekt neu
self._load_nodes_to_tree()
# QMessageBox.information(self, "Erfolg", "Daten erfolgreich aus FN2 geladen und Projekt aktualisiert!")
# Query-Thread erstellen und starten
self._db_query_thread = DatabaseQueryThread(sql_query, connection_string)
self._db_query_thread.query_completed.connect(self._on_db_query_completed)
self._db_query_thread.query_failed.connect(self._on_db_query_failed)
self._db_query_thread.finished.connect(self._cleanup_db_query)
self._db_progress.canceled.connect(self._on_db_query_canceled)
self._db_query_thread.start()
except Exception as e:
logger.error(f"Fehler beim Laden aus FN2: {e}")
QMessageBox.critical(self, "Fehler", f"Fehler beim Laden aus FN2:\n{str(e)}")
def _on_db_query_completed(self, df):
"""Wird aufgerufen, wenn die Datenbankabfrage erfolgreich war."""
# Ignoriere Ergebnis, falls der Benutzer abgebrochen hat
if hasattr(self, "_db_progress") and self._db_progress.wasCanceled():
return
try:
new_nodes = self._process_sql_data(df)
self._merge_nodes_with_existing(new_nodes)
self._save_project_settings()
self._load_nodes_to_tree()
except Exception as e:
logger.error(f"Fehler beim Verarbeiten der DB-Daten: {e}")
QMessageBox.critical(self, "Fehler", f"Fehler beim Verarbeiten der Daten:\n{str(e)}")
def _on_db_query_failed(self, error_msg):
"""Wird aufgerufen, wenn die Datenbankabfrage fehlgeschlagen ist."""
if hasattr(self, "_db_progress") and self._db_progress.wasCanceled():
return
error = f"Fehler beim Ausführen der SQL-Abfrage: {error_msg}"
logger.error(error)
QMessageBox.critical(self, "Fehler", error)
def _on_db_query_canceled(self):
"""Wird aufgerufen, wenn der Benutzer die Abfrage abbricht."""
logger.info("Datenbankabfrage vom Benutzer abgebrochen")
def _cleanup_db_query(self):
"""Räumt nach der Datenbankabfrage auf."""
if hasattr(self, "_db_progress"):
self._db_progress.canceled.disconnect(self._on_db_query_canceled)
self._db_progress.close()
self._db_progress.deleteLater()
del self._db_progress
if hasattr(self, "_db_query_thread"):
self._db_query_thread.deleteLater()
del self._db_query_thread
def _get_database_config(self, db_id):
"""
Holt die Datenbank-Konfiguration anhand der ID.
@@ -90,29 +151,27 @@ class DatabaseMixin:
return db
return None
def _execute_sql_query(self, db_config):
def _prepare_sql_query(self, db_config):
"""
Führt die SQL-Abfrage aus der data.sql Datei aus.
Bereitet SQL-Abfrage und Connection-String vor.
Args:
db_config: PostgreSQL-Datenbank-Konfiguration
Returns:
pl.DataFrame|None: Die Abfrageergebnisse oder None bei Fehler
tuple[str, str]|tuple[None, None]: (sql_query, connection_string) oder (None, None) bei Fehler
"""
try:
# Lade SQL-Abfrage aus Datei
sql_file_path = Path("src/res/data.sql")
if not sql_file_path.exists():
QMessageBox.critical(self, "Fehler", f"SQL-Datei nicht gefunden: {sql_file_path}")
return None
return None, None
with open(sql_file_path, "r", encoding="utf-8") as f:
sql_query = f.read()
logger.debug(f"SQL-Abfrage geladen: {len(sql_query)} Zeichen")
# Verbindung zur PostgreSQL-Datenbank herstellen
connection_string = (
"postgresql://"
f"{db_config.username}:"
@@ -121,19 +180,17 @@ class DatabaseMixin:
f"{db_config.port}/"
f"{db_config.database}?"
f"sslmode={db_config.ssl_mode.value}"
f"&connect_timeout={db_config.timeout}"
)
logger.info(f"Verbinde zu PostgreSQL: {db_config.host}:{db_config.port}/{db_config.database}")
df = pl.read_database_uri(sql_query, connection_string, engine="connectorx").sort(
["reporttyp_bez", "report_bez", "repfile_bez"]
)
return df
return sql_query, connection_string
except Exception as e:
error_msg = f"Fehler beim Ausführen der SQL-Abfrage: {str(e)}"
error_msg = f"Fehler beim Vorbereiten der SQL-Abfrage: {str(e)}"
logger.error(error_msg)
QMessageBox.critical(self, "Fehler", error_msg)
return None
return None, None
def _process_sql_data(self, df):
"""