FOP-Config: Projektspezifischer Konfigurationsordner und erweitertes Logging

- Project-Modell um optionales fop_config_dir Feld erweitert
- TransformationJob verwendet nun projektspezifischen FOP-Config-Pfad
- Saxon und FOP stdout/stderr werden nun im Debug-Level geloggt
- UI-Elemente für FOP-Config-Ordner-Auswahl hinzugefügt
- AppSettings und MainWindow unterstützen neues Feld beim Laden/Speichern

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-26 12:45:44 +01:00
parent 92930a3da4
commit 8c7db39f5f
7 changed files with 142 additions and 18 deletions
+1
View File
@@ -93,6 +93,7 @@ class Project(BaseModel):
apache_fop_id: int = Field(..., description="ID der Apache FOP Konfiguration", gt=0)
xsl_dir_id: int = Field(..., description="ID des XSL-Verzeichnisses", 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")
def getXsl(self) -> str:
global app_settings
+20 -1
View File
@@ -35,6 +35,7 @@ class TransformationJob:
diff_pdf_path: Path,
diff_pdf_params: list[str],
xsl_id: tuple | None = None,
fop_config_dir: Path | None = None,
):
"""
Initialisiert einen Transformations-Job.
@@ -50,6 +51,7 @@ class TransformationJob:
diff_pdf_path: Pfad zur diff-pdf Binary
diff_pdf_params: Standard-Parameter für diff-pdf
xsl_id: ID der XSL-Datei (als Tuple)
fop_config_dir: Optionaler Pfad zum FOP-Config-Verzeichnis (überschreibt Standardpfad)
"""
self.project_dir = project_dir
self.xml_file = xml_file # Relativ
@@ -61,6 +63,7 @@ class TransformationJob:
self.java_vm_path = java_vm_path
self.saxon_jar_path = saxon_jar_path
self.apache_fop_dir = apache_fop_dir
self.fop_config_dir = fop_config_dir
self.diff_pdf_path = diff_pdf_path
self.diff_pdf_params = diff_pdf_params
@@ -98,7 +101,11 @@ class TransformationJob:
else:
self.fop_cmd = self.apache_fop_dir / "fop"
self.fop_conf = self.apache_fop_dir / "conf" / "fop.xconf"
# FOP-Konfigurationsdatei: Verwende fop_config_dir falls angegeben, sonst Standardpfad
if self.fop_config_dir:
self.fop_conf = self.fop_config_dir / "fop.xconf"
else:
self.fop_conf = self.apache_fop_dir / "conf" / "fop.xconf"
def is_up_to_date(self) -> bool:
"""
@@ -200,6 +207,12 @@ class TransformationJob:
timeout=120, # 2 Minuten Timeout
)
# Saxon Ausgaben loggen
if result.stdout:
logger.debug(f"Saxon StdOut:\n{result.stdout}")
if result.stderr:
logger.debug(f"Saxon StdErr:\n{result.stderr}")
if result.returncode == 0:
logger.info(f"Saxon-Transformation erfolgreich: {self.xml_file.name}")
return True, "Erfolgreich"
@@ -266,6 +279,12 @@ class TransformationJob:
timeout=180, # 3 Minuten Timeout
)
# Apache FOP Ausgaben loggen
if result.stdout:
logger.debug(f"FOP StdOut:\n{result.stdout}")
if result.stderr:
logger.debug(f"FOP StdErr:\n{result.stderr}")
# Temporäre FO-Datei löschen
if self.temp_fo.exists():
try:
+11 -2
View File
@@ -466,6 +466,7 @@ class AppSettingsDlg(QDialog):
apache_fop_id=project_data['apache_fop_id'] if project_data['apache_fop_id'] != -1 else 1,
xsl_dir_id=project_data['xsl_dir_id'] if project_data['xsl_dir_id'] != -1 else 1,
postgre_sql_db_id=project_data['postgre_sql_db_id'] if project_data['postgre_sql_db_id'] != -1 else 1,
fop_config_dir=Path(project_data['fop_config_dir']) if project_data.get('fop_config_dir') else None,
)
self.temp_pdf_projects.append(new_project)
@@ -617,7 +618,9 @@ class AppSettingsDlg(QDialog):
'diff_pdf_id': pdf_project.diff_pdf_id,
'saxon_jar_id': pdf_project.saxon_jar_id,
'apache_fop_id': pdf_project.apache_fop_id,
'xsl_dir_id': pdf_project.xsl_dir_id
'xsl_dir_id': pdf_project.xsl_dir_id,
'postgre_sql_db_id': pdf_project.postgre_sql_db_id,
'fop_config_dir': str(pdf_project.fop_config_dir) if pdf_project.fop_config_dir else None
}
# Dialog im Edit-Modus öffnen (Projekt-Name und -Ordner deaktiviert)
@@ -632,9 +635,15 @@ class AppSettingsDlg(QDialog):
pdf_project.saxon_jar_id = new_data['saxon_jar_id'] if new_data['saxon_jar_id'] != -1 else pdf_project.saxon_jar_id
pdf_project.apache_fop_id = new_data['apache_fop_id'] if new_data['apache_fop_id'] != -1 else pdf_project.apache_fop_id
pdf_project.xsl_dir_id = new_data['xsl_dir_id'] if new_data['xsl_dir_id'] != -1 else pdf_project.xsl_dir_id
pdf_project.postgre_sql_db_id = new_data['postgre_sql_db_id'] if new_data['postgre_sql_db_id'] != -1 else pdf_project.postgre_sql_db_id
pdf_project.fop_config_dir = Path(new_data['fop_config_dir']) if new_data.get('fop_config_dir') else None
self._populate_pdf_project_table()
# Einstellungen speichern
self.settings.pdf_projects = self.temp_pdf_projects.copy()
self.settings.save()
# PostgreSQL Methoden
def _add_postgresql_db(self):
"""Fügt eine neue PostgreSQL-Datenbank hinzu."""
+2
View File
@@ -1000,6 +1000,7 @@ class MainWindow(QMainWindow):
postgre_sql_db_id=project_data["postgre_sql_db_id"]
if project_data["postgre_sql_db_id"] != -1
else 1,
fop_config_dir=Path(project_data["fop_config_dir"]) if project_data.get("fop_config_dir") else None,
)
# Erstelle Projekt-Ordnerstruktur
@@ -3348,6 +3349,7 @@ class MainWindow(QMainWindow):
diff_pdf_path=diff_pdf.path_to_binary_file,
diff_pdf_params=diff_pdf.default_params,
xsl_id=xsl_file_obj.id,
fop_config_dir=self.project.fop_config_dir,
)
return job
+30 -5
View File
@@ -49,11 +49,14 @@ class PdfProjectDlg(QDialog):
"""Verbindet die Signale mit den entsprechenden Slots."""
# Browse-Button für Projekt-Ordner
self.ui.pushButton.clicked.connect(self.browse_project_dir)
# Browse-Button für FOP-Config-Ordner
self.ui.btnBrowseFopConfig.clicked.connect(self.browse_fop_config_dir)
# OK/Cancel Buttons sind bereits in der UI-Datei verbunden
# self.ui.buttonBox.accepted.connect(self.accept)
# self.ui.buttonBox.rejected.connect(self.reject)
# Überschreibe accept() für Validierung
self.ui.buttonBox.accepted.disconnect()
self.ui.buttonBox.accepted.connect(self.validate_and_accept)
@@ -132,6 +135,10 @@ class PdfProjectDlg(QDialog):
if 'postgre_sql_db_id' in self.project_data:
self._select_combo_by_data(self.ui.cB_Postgres, self.project_data['postgre_sql_db_id'])
# FOP-Config-Ordner
if 'fop_config_dir' in self.project_data and self.project_data['fop_config_dir']:
self.ui.lineFopConfigDir.setText(str(self.project_data['fop_config_dir']))
def _select_combo_by_data(self, combo_box, data_value):
"""
@@ -166,7 +173,23 @@ class PdfProjectDlg(QDialog):
if not self.ui.lineProjectName.text():
project_name = os.path.basename(selected_dir)
self.ui.lineProjectName.setText(project_name)
def browse_fop_config_dir(self):
"""Öffnet einen Dialog zum Auswählen des FOP-Config-Ordners."""
current_dir = self.ui.lineFopConfigDir.text()
if not current_dir or not os.path.exists(current_dir):
current_dir = os.path.expanduser("~")
selected_dir = QFileDialog.getExistingDirectory(
self,
"FOP-Config-Ordner auswählen",
current_dir,
QFileDialog.Option.ShowDirsOnly | QFileDialog.Option.DontResolveSymlinks
)
if selected_dir:
self.ui.lineFopConfigDir.setText(selected_dir)
def validate_and_accept(self):
"""Validiert die Eingaben und akzeptiert den Dialog."""
# Projekt-Name prüfen
@@ -232,10 +255,11 @@ class PdfProjectDlg(QDialog):
def get_project_data(self):
"""
Gibt die eingegebenen Projektdaten zurück.
Returns:
dict: Dictionary mit allen Projektdaten
"""
fop_config_dir = self.ui.lineFopConfigDir.text().strip()
return {
'name': self.ui.lineProjectName.text().strip(),
'project_dir': self.ui.lineProjectDir.text().strip(),
@@ -244,7 +268,8 @@ class PdfProjectDlg(QDialog):
'saxon_jar_id': self.ui.cB_SaxonJar.currentData(),
'apache_fop_id': self.ui.cB_ApacheFop.currentData(),
'diff_pdf_id': self.ui.cB_Diff_Pdf.currentData(),
'postgre_sql_db_id': self.ui.cB_Postgres.currentData()
'postgre_sql_db_id': self.ui.cB_Postgres.currentData(),
'fop_config_dir': fop_config_dir if fop_config_dir else None
}
def _configure_edit_mode(self):
+45 -4
View File
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>608</width>
<height>299</height>
<height>331</height>
</rect>
</property>
<property name="windowTitle">
@@ -109,25 +109,66 @@
<widget class="QComboBox" name="cB_ApacheFop"/>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>FOP-Config-Ordner:</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>diff-pdf:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<widget class="QComboBox" name="cB_Diff_Pdf"/>
</item>
<item row="7" column="0">
<item row="8" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Postgres:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<item row="8" column="1">
<widget class="QComboBox" name="cB_Postgres"/>
</item>
<item row="6" column="1">
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="lineFopConfigDir"/>
</item>
<item>
<widget class="QPushButton" name="btnBrowseFopConfig">
<property name="text">
<string>Durchsuchen ...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
+33 -6
View File
@@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'PdfProject.ui'
##
## Created by: Qt User Interface Compiler version 6.9.1
## Created by: Qt User Interface Compiler version 6.9.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@@ -24,7 +24,7 @@ class Ui_projectDlg(object):
def setupUi(self, projectDlg):
if not projectDlg.objectName():
projectDlg.setObjectName(u"projectDlg")
projectDlg.resize(608, 299)
projectDlg.resize(608, 331)
self.verticalLayout = QVBoxLayout(projectDlg)
self.verticalLayout.setObjectName(u"verticalLayout")
self.widget = QWidget(projectDlg)
@@ -106,25 +106,50 @@ class Ui_projectDlg(object):
self.formLayout.setWidget(5, QFormLayout.ItemRole.FieldRole, self.cB_ApacheFop)
self.label_9 = QLabel(self.widget)
self.label_9.setObjectName(u"label_9")
self.formLayout.setWidget(6, QFormLayout.ItemRole.LabelRole, self.label_9)
self.label_7 = QLabel(self.widget)
self.label_7.setObjectName(u"label_7")
self.formLayout.setWidget(6, QFormLayout.ItemRole.LabelRole, self.label_7)
self.formLayout.setWidget(7, QFormLayout.ItemRole.LabelRole, self.label_7)
self.cB_Diff_Pdf = QComboBox(self.widget)
self.cB_Diff_Pdf.setObjectName(u"cB_Diff_Pdf")
self.formLayout.setWidget(6, QFormLayout.ItemRole.FieldRole, self.cB_Diff_Pdf)
self.formLayout.setWidget(7, QFormLayout.ItemRole.FieldRole, self.cB_Diff_Pdf)
self.label_8 = QLabel(self.widget)
self.label_8.setObjectName(u"label_8")
self.formLayout.setWidget(7, QFormLayout.ItemRole.LabelRole, self.label_8)
self.formLayout.setWidget(8, QFormLayout.ItemRole.LabelRole, self.label_8)
self.cB_Postgres = QComboBox(self.widget)
self.cB_Postgres.setObjectName(u"cB_Postgres")
self.formLayout.setWidget(7, QFormLayout.ItemRole.FieldRole, self.cB_Postgres)
self.formLayout.setWidget(8, QFormLayout.ItemRole.FieldRole, self.cB_Postgres)
self.frame_2 = QFrame(self.widget)
self.frame_2.setObjectName(u"frame_2")
self.frame_2.setFrameShape(QFrame.Shape.StyledPanel)
self.frame_2.setFrameShadow(QFrame.Shadow.Raised)
self.horizontalLayout_2 = QHBoxLayout(self.frame_2)
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.lineFopConfigDir = QLineEdit(self.frame_2)
self.lineFopConfigDir.setObjectName(u"lineFopConfigDir")
self.horizontalLayout_2.addWidget(self.lineFopConfigDir)
self.btnBrowseFopConfig = QPushButton(self.frame_2)
self.btnBrowseFopConfig.setObjectName(u"btnBrowseFopConfig")
self.horizontalLayout_2.addWidget(self.btnBrowseFopConfig)
self.formLayout.setWidget(6, QFormLayout.ItemRole.FieldRole, self.frame_2)
self.verticalLayout.addWidget(self.widget)
@@ -154,7 +179,9 @@ class Ui_projectDlg(object):
self.label_4.setText(QCoreApplication.translate("projectDlg", u"Java VM:", None))
self.label_5.setText(QCoreApplication.translate("projectDlg", u"Saxon Jar:", None))
self.label_6.setText(QCoreApplication.translate("projectDlg", u"Apache FOP:", None))
self.label_9.setText(QCoreApplication.translate("projectDlg", u"FOP-Config-Ordner:", None))
self.label_7.setText(QCoreApplication.translate("projectDlg", u"diff-pdf:", None))
self.label_8.setText(QCoreApplication.translate("projectDlg", u"Postgres:", None))
self.btnBrowseFopConfig.setText(QCoreApplication.translate("projectDlg", u"Durchsuchen ...", None))
# retranslateUi