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) 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) 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) 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: def getXsl(self) -> str:
global app_settings global app_settings
+20 -1
View File
@@ -35,6 +35,7 @@ class TransformationJob:
diff_pdf_path: Path, diff_pdf_path: Path,
diff_pdf_params: list[str], diff_pdf_params: list[str],
xsl_id: tuple | None = None, xsl_id: tuple | None = None,
fop_config_dir: Path | None = None,
): ):
""" """
Initialisiert einen Transformations-Job. Initialisiert einen Transformations-Job.
@@ -50,6 +51,7 @@ class TransformationJob:
diff_pdf_path: Pfad zur diff-pdf Binary diff_pdf_path: Pfad zur diff-pdf Binary
diff_pdf_params: Standard-Parameter für diff-pdf diff_pdf_params: Standard-Parameter für diff-pdf
xsl_id: ID der XSL-Datei (als Tuple) 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.project_dir = project_dir
self.xml_file = xml_file # Relativ self.xml_file = xml_file # Relativ
@@ -61,6 +63,7 @@ class TransformationJob:
self.java_vm_path = java_vm_path self.java_vm_path = java_vm_path
self.saxon_jar_path = saxon_jar_path self.saxon_jar_path = saxon_jar_path
self.apache_fop_dir = apache_fop_dir self.apache_fop_dir = apache_fop_dir
self.fop_config_dir = fop_config_dir
self.diff_pdf_path = diff_pdf_path self.diff_pdf_path = diff_pdf_path
self.diff_pdf_params = diff_pdf_params self.diff_pdf_params = diff_pdf_params
@@ -98,7 +101,11 @@ class TransformationJob:
else: else:
self.fop_cmd = self.apache_fop_dir / "fop" 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: def is_up_to_date(self) -> bool:
""" """
@@ -200,6 +207,12 @@ class TransformationJob:
timeout=120, # 2 Minuten Timeout 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: if result.returncode == 0:
logger.info(f"Saxon-Transformation erfolgreich: {self.xml_file.name}") logger.info(f"Saxon-Transformation erfolgreich: {self.xml_file.name}")
return True, "Erfolgreich" return True, "Erfolgreich"
@@ -266,6 +279,12 @@ class TransformationJob:
timeout=180, # 3 Minuten Timeout 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 # Temporäre FO-Datei löschen
if self.temp_fo.exists(): if self.temp_fo.exists():
try: try:
+10 -1
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, 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, 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, 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) self.temp_pdf_projects.append(new_project)
@@ -617,7 +618,9 @@ class AppSettingsDlg(QDialog):
'diff_pdf_id': pdf_project.diff_pdf_id, 'diff_pdf_id': pdf_project.diff_pdf_id,
'saxon_jar_id': pdf_project.saxon_jar_id, 'saxon_jar_id': pdf_project.saxon_jar_id,
'apache_fop_id': pdf_project.apache_fop_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) # 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.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.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.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() self._populate_pdf_project_table()
# Einstellungen speichern
self.settings.pdf_projects = self.temp_pdf_projects.copy()
self.settings.save()
# PostgreSQL Methoden # PostgreSQL Methoden
def _add_postgresql_db(self): def _add_postgresql_db(self):
"""Fügt eine neue PostgreSQL-Datenbank hinzu.""" """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"] postgre_sql_db_id=project_data["postgre_sql_db_id"]
if project_data["postgre_sql_db_id"] != -1 if project_data["postgre_sql_db_id"] != -1
else 1, else 1,
fop_config_dir=Path(project_data["fop_config_dir"]) if project_data.get("fop_config_dir") else None,
) )
# Erstelle Projekt-Ordnerstruktur # Erstelle Projekt-Ordnerstruktur
@@ -3348,6 +3349,7 @@ class MainWindow(QMainWindow):
diff_pdf_path=diff_pdf.path_to_binary_file, diff_pdf_path=diff_pdf.path_to_binary_file,
diff_pdf_params=diff_pdf.default_params, diff_pdf_params=diff_pdf.default_params,
xsl_id=xsl_file_obj.id, xsl_id=xsl_file_obj.id,
fop_config_dir=self.project.fop_config_dir,
) )
return job return job
+26 -1
View File
@@ -50,6 +50,9 @@ class PdfProjectDlg(QDialog):
# Browse-Button für Projekt-Ordner # Browse-Button für Projekt-Ordner
self.ui.pushButton.clicked.connect(self.browse_project_dir) 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 # OK/Cancel Buttons sind bereits in der UI-Datei verbunden
# self.ui.buttonBox.accepted.connect(self.accept) # self.ui.buttonBox.accepted.connect(self.accept)
# self.ui.buttonBox.rejected.connect(self.reject) # self.ui.buttonBox.rejected.connect(self.reject)
@@ -133,6 +136,10 @@ class PdfProjectDlg(QDialog):
if 'postgre_sql_db_id' in self.project_data: 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']) 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): def _select_combo_by_data(self, combo_box, data_value):
""" """
Wählt einen ComboBox-Eintrag basierend auf dem data-Wert aus. Wählt einen ComboBox-Eintrag basierend auf dem data-Wert aus.
@@ -167,6 +174,22 @@ class PdfProjectDlg(QDialog):
project_name = os.path.basename(selected_dir) project_name = os.path.basename(selected_dir)
self.ui.lineProjectName.setText(project_name) 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): def validate_and_accept(self):
"""Validiert die Eingaben und akzeptiert den Dialog.""" """Validiert die Eingaben und akzeptiert den Dialog."""
# Projekt-Name prüfen # Projekt-Name prüfen
@@ -236,6 +259,7 @@ class PdfProjectDlg(QDialog):
Returns: Returns:
dict: Dictionary mit allen Projektdaten dict: Dictionary mit allen Projektdaten
""" """
fop_config_dir = self.ui.lineFopConfigDir.text().strip()
return { return {
'name': self.ui.lineProjectName.text().strip(), 'name': self.ui.lineProjectName.text().strip(),
'project_dir': self.ui.lineProjectDir.text().strip(), 'project_dir': self.ui.lineProjectDir.text().strip(),
@@ -244,7 +268,8 @@ class PdfProjectDlg(QDialog):
'saxon_jar_id': self.ui.cB_SaxonJar.currentData(), 'saxon_jar_id': self.ui.cB_SaxonJar.currentData(),
'apache_fop_id': self.ui.cB_ApacheFop.currentData(), 'apache_fop_id': self.ui.cB_ApacheFop.currentData(),
'diff_pdf_id': self.ui.cB_Diff_Pdf.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): def _configure_edit_mode(self):
+45 -4
View File
@@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>608</width> <width>608</width>
<height>299</height> <height>331</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -109,25 +109,66 @@
<widget class="QComboBox" name="cB_ApacheFop"/> <widget class="QComboBox" name="cB_ApacheFop"/>
</item> </item>
<item row="6" column="0"> <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"> <widget class="QLabel" name="label_7">
<property name="text"> <property name="text">
<string>diff-pdf:</string> <string>diff-pdf:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="7" column="1">
<widget class="QComboBox" name="cB_Diff_Pdf"/> <widget class="QComboBox" name="cB_Diff_Pdf"/>
</item> </item>
<item row="7" column="0"> <item row="8" column="0">
<widget class="QLabel" name="label_8"> <widget class="QLabel" name="label_8">
<property name="text"> <property name="text">
<string>Postgres:</string> <string>Postgres:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1"> <item row="8" column="1">
<widget class="QComboBox" name="cB_Postgres"/> <widget class="QComboBox" name="cB_Postgres"/>
</item> </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> </layout>
</widget> </widget>
</item> </item>
+33 -6
View File
@@ -3,7 +3,7 @@
################################################################################ ################################################################################
## Form generated from reading UI file 'PdfProject.ui' ## 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! ## 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): def setupUi(self, projectDlg):
if not projectDlg.objectName(): if not projectDlg.objectName():
projectDlg.setObjectName(u"projectDlg") projectDlg.setObjectName(u"projectDlg")
projectDlg.resize(608, 299) projectDlg.resize(608, 331)
self.verticalLayout = QVBoxLayout(projectDlg) self.verticalLayout = QVBoxLayout(projectDlg)
self.verticalLayout.setObjectName(u"verticalLayout") self.verticalLayout.setObjectName(u"verticalLayout")
self.widget = QWidget(projectDlg) self.widget = QWidget(projectDlg)
@@ -106,25 +106,50 @@ class Ui_projectDlg(object):
self.formLayout.setWidget(5, QFormLayout.ItemRole.FieldRole, self.cB_ApacheFop) 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 = QLabel(self.widget)
self.label_7.setObjectName(u"label_7") 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 = QComboBox(self.widget)
self.cB_Diff_Pdf.setObjectName(u"cB_Diff_Pdf") 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 = QLabel(self.widget)
self.label_8.setObjectName(u"label_8") 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 = QComboBox(self.widget)
self.cB_Postgres.setObjectName(u"cB_Postgres") 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) 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_4.setText(QCoreApplication.translate("projectDlg", u"Java VM:", None))
self.label_5.setText(QCoreApplication.translate("projectDlg", u"Saxon Jar:", 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_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_7.setText(QCoreApplication.translate("projectDlg", u"diff-pdf:", None))
self.label_8.setText(QCoreApplication.translate("projectDlg", u"Postgres:", None)) self.label_8.setText(QCoreApplication.translate("projectDlg", u"Postgres:", None))
self.btnBrowseFopConfig.setText(QCoreApplication.translate("projectDlg", u"Durchsuchen ...", None))
# retranslateUi # retranslateUi