cbcae3222f
Die JAXP-basierte SaxonWorkerPool-Implementierung ist nur für XSLT 1.0 vollständig spezifiziert und kann bei XSLT 2.0/3.0 zu fehlerhaften Ausgaben führen. Änderungen: - Neue SaxonWorkerPoolS9Api-Klasse mit Saxon s9api für XSLT 2.0/3.0 - XsltVersion-Enum in conf.py (XSLT_1_0, XSLT_2_0_3_0) - ComboBox in Performance-Einstellungen zur XSLT-Version-Auswahl - MainWindow wählt automatisch richtige Worker-Pool-Variante - Verbesserte Classpath-Behandlung und Fehlerbehandlung Standard-Einstellung: XSLT 2.0/3.0 (s9api) - empfohlen für moderne Stylesheets Fallback: XSLT 1.0 (JAXP) - verfügbar für Legacy-Stylesheets 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
232 lines
7.0 KiB
Python
232 lines
7.0 KiB
Python
import os
|
||
import sys
|
||
from pathlib import Path
|
||
from sys import platform
|
||
from typing import Tuple, Type
|
||
from pydantic import Field
|
||
from pydantic_yaml import to_yaml_str
|
||
from ruamel.yaml import YAML
|
||
from enum import Enum
|
||
import logging
|
||
|
||
from pydantic import BaseModel
|
||
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource, SettingsConfigDict, JsonConfigSettingsSource
|
||
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
app_name = "DocuMentor"
|
||
|
||
|
||
if platform == "win32":
|
||
tmp_config_path = f"%APPDATA%\\{app_name}\\config.json"
|
||
elif platform in ("linux", "linux2"):
|
||
tmp_config_path = f"~/.config/{app_name}/config.json"
|
||
elif platform == "darwin":
|
||
tmp_config_path = f"~/Library/Application Support/{app_name}/͏͏͏͏config.json"
|
||
else:
|
||
tmp_config_path = f"~/.config/{app_name}/config.json"
|
||
|
||
config_path = Path(os.path.expandvars(tmp_config_path)).expanduser()
|
||
|
||
|
||
class JavaVm(BaseModel):
|
||
id: int
|
||
version: str
|
||
path_to_binary_file: Path
|
||
|
||
|
||
class DiffPdf(BaseModel):
|
||
id: int
|
||
version: str
|
||
path_to_binary_file: Path
|
||
default_params: list[str]
|
||
output_file_extension: str = "pdf"
|
||
|
||
|
||
class SaxonJar(BaseModel):
|
||
id: int
|
||
version: str
|
||
path_to_jar_file: Path
|
||
output_file_extension: str = "fo"
|
||
|
||
|
||
class ApacheFop(BaseModel):
|
||
id: int
|
||
version: str
|
||
path_to_dir: Path
|
||
output_file_extension: str = "pdf"
|
||
|
||
|
||
class XslDir(BaseModel):
|
||
id: int
|
||
name: str
|
||
path_to_root_dir: Path
|
||
|
||
|
||
class SSLMode(str, Enum):
|
||
DISABLE = "disable"
|
||
ALLOW = "allow"
|
||
PREFER = "prefer"
|
||
REQUIRE = "require"
|
||
VERIFY_CA = "verify-ca"
|
||
VERIFY_FULL = "verify-full"
|
||
|
||
|
||
class XsltVersion(str, Enum):
|
||
"""XSLT-Version für Saxon-Transformationen."""
|
||
XSLT_1_0 = "1.0" # JAXP API (nur XSLT 1.0)
|
||
XSLT_2_0_3_0 = "2.0/3.0" # s9api (XSLT 2.0 und 3.0)
|
||
|
||
|
||
class PostgreSqlDb(BaseModel):
|
||
id: int
|
||
name: str
|
||
host: str
|
||
port: int = 5432
|
||
database: str
|
||
username: str
|
||
password: str
|
||
ssl_mode: SSLMode = SSLMode.PREFER
|
||
|
||
|
||
class Project(BaseModel):
|
||
id: int = Field(..., description="Eindeutige Projekt-ID", gt=0)
|
||
name: str = Field(..., description="Projekt-Name", min_length=1, max_length=255)
|
||
project_dir: Path = Field(..., description="Pfad zum Projekt-Verzeichnis")
|
||
java_vm_id: int = Field(..., description="ID der Java VM", gt=0)
|
||
diff_pdf_id: int = Field(..., description="ID der diff-pdf Konfiguration", gt=0)
|
||
saxon_jar_id: int = Field(..., description="ID der Saxon JAR 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)
|
||
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
|
||
value = [x.name for x in app_settings.xsl_dirs if x.id == self.xsl_dir_id]
|
||
|
||
return value[0] if len(value) else ""
|
||
|
||
def getJavaVm(self) -> str:
|
||
global app_settings
|
||
value = [x.version for x in app_settings.java_vms if x.id == self.java_vm_id]
|
||
|
||
return value[0] if len(value) else ""
|
||
|
||
def getSaxon(self) -> str:
|
||
global app_settings
|
||
value = [x.version for x in app_settings.saxon_jars if x.id == self.saxon_jar_id]
|
||
|
||
return value[0] if len(value) else ""
|
||
|
||
def getApacheFop(self) -> str:
|
||
global app_settings
|
||
value = [x.version for x in app_settings.apache_fops if x.id == self.apache_fop_id]
|
||
|
||
return value[0] if len(value) else ""
|
||
|
||
def getDiffPdf(self) -> str:
|
||
global app_settings
|
||
value = [x.version for x in app_settings.diff_pdfs if x.id == self.diff_pdf_id]
|
||
|
||
return value[0] if len(value) else ""
|
||
|
||
def getPostgreSqlDb(self) -> str:
|
||
global app_settings
|
||
value = [x.name for x in app_settings.postgresql_dbs if x.id == self.postgre_sql_db_id]
|
||
|
||
return value[0] if len(value) else ""
|
||
|
||
|
||
class AppSettings(BaseSettings):
|
||
java_vms: list[JavaVm] = []
|
||
diff_pdfs: list[DiffPdf] = []
|
||
saxon_jars: list[SaxonJar] = []
|
||
apache_fops: list[ApacheFop] = []
|
||
xsl_dirs: list[XslDir] = []
|
||
pdf_projects: list[Project] = []
|
||
postgresql_dbs: list[PostgreSqlDb] = []
|
||
theme: str | None = None
|
||
max_workers: int = 8 # Anzahl paralleler Worker für Transformationen (Standard: 8)
|
||
use_saxon_worker_pool: bool = True # SaxonWorkerPool aktivieren (schneller, benötigt JDK)
|
||
saxon_xslt_version: XsltVersion = XsltVersion.XSLT_2_0_3_0 # XSLT-Version für Saxon (Standard: 2.0/3.0 mit s9api)
|
||
use_fop_worker_pool: bool = True # FopWorkerPool aktivieren (schneller, benötigt JDK)
|
||
|
||
# UI-Zustand
|
||
window_geometry: tuple[int, int, int, int] | None = None # (x, y, width, height)
|
||
splitter_sizes: list[int] | None = None # Splitter-Positionen
|
||
tree_column_widths: list[int] | None = None # TreeWidget-Spaltenbreiten
|
||
|
||
model_config = SettingsConfigDict(json_file=config_path)
|
||
|
||
@classmethod
|
||
def settings_customise_sources(
|
||
cls,
|
||
settings_cls: Type[BaseSettings],
|
||
init_settings: PydanticBaseSettingsSource,
|
||
env_settings: PydanticBaseSettingsSource,
|
||
dotenv_settings: PydanticBaseSettingsSource,
|
||
file_secret_settings: PydanticBaseSettingsSource,
|
||
) -> Tuple[PydanticBaseSettingsSource, ...]:
|
||
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)
|
||
|
||
if not config_path.parent.is_dir() or not os.access(config_path.parent, os.W_OK):
|
||
logger.exception(f"{config_path.parent} ist kein Verzeichnis oder es gibt keine Schreibrechte")
|
||
sys.exit(1)
|
||
|
||
# Konfiguration speichern
|
||
with open(config_path, "wb") as c:
|
||
c.write(app_settings.model_dump_json(indent=4).encode())
|
||
|
||
|
||
app_settings = AppSettings()
|
||
|
||
|
||
class XmlFile(BaseModel):
|
||
xml: Path
|
||
# blake2b hashsum
|
||
hashsum: str | None = None
|
||
|
||
|
||
class XslFile(BaseModel):
|
||
id: tuple
|
||
bez: str
|
||
xsl_file: Path
|
||
xslt_params: dict[str, str] = {}
|
||
xmls: list[XmlFile] = []
|
||
|
||
|
||
class TreeNode(BaseModel):
|
||
id: tuple
|
||
bez: str
|
||
xslt_params: dict[str, str] = {}
|
||
children: list["TreeNode|XslFile"]
|
||
|
||
|
||
class ProjectData(BaseModel):
|
||
"""
|
||
Speichert die Projekteinstellungen direkt im Projektordner in einer .yaml-Datei.
|
||
"""
|
||
|
||
nodes: list[TreeNode] = []
|
||
|
||
@classmethod
|
||
def readSettings(cls, project_dir: Path):
|
||
# Explizit UTF-8 Encoding verwenden
|
||
project_yaml_path = project_dir / "project.yaml"
|
||
with open(project_yaml_path, "r", encoding="utf-8") as f:
|
||
yaml = YAML(typ="safe")
|
||
yaml_data = yaml.load(f)
|
||
return cls.model_validate(yaml_data)
|
||
|
||
def writeSettings(self, project_dir: Path):
|
||
with open(project_dir / "project.yaml", "w", encoding="utf8") as f:
|
||
f.write(to_yaml_str(self))
|