Files
sufw-taetigkeitsnachweise/1_build-pipeline/run.py
T

110 lines
3.9 KiB
Python

"""Einstiegspunkt für die gebundelte Tätigkeitsnachweis-Generator .exe.
Wird von PyInstaller als Entry-Point verwendet. Im gebundelten Modus
(sys.frozen = True) liegen die Python-Dateien (app.py, src/) in sys._MEIPASS,
während die Daten-Ordner (config/, input/, template/, output/) neben der .exe
erwartet werden.
Dieses Skript:
1. Setzt APP_BASE_DIR auf das Verzeichnis der .exe
2. Fügt sys._MEIPASS zum Python-Pfad hinzu (für src/-Imports)
3. Startet den Streamlit-Server und öffnet den Browser
"""
import os
import subprocess
import sys
import threading
import time
import webbrowser
from pathlib import Path
if not getattr(sys, "frozen", False):
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "0_application"))
from server_lifecycle import stop_existing_server
def _get_base_dir() -> Path:
"""Verzeichnis der .exe (bzw. des Skripts im Entwicklungsmodus)."""
if getattr(sys, 'frozen', False):
return Path(sys.executable).resolve().parent
else:
# Entwicklungsmodus: run.py liegt in 1_build-pipeline/,
# die App in 0_application/
return Path(__file__).resolve().parent.parent / "0_application"
def _get_app_path() -> str:
"""Pfad zur app.py (gebundelt oder im Entwicklungsmodus)."""
if getattr(sys, 'frozen', False):
return os.path.join(sys._MEIPASS, "app.py")
else:
return str(Path(__file__).resolve().parent.parent / "0_application" / "app.py")
def _ensure_dirs(base_dir: Path) -> None:
"""Stellt sicher, dass input/, output/, template/, config/ existieren."""
for folder in ["input", "output", "template", "config"]:
(base_dir / folder).mkdir(exist_ok=True)
def main() -> None:
# Only open the browser/start Streamlit once the previous server has stopped.
try:
if not stop_existing_server():
print("[INFO] Die bisherige Version bleibt geöffnet. Keine neue Instanz gestartet.")
return
except (RuntimeError, OSError, subprocess.SubprocessError) as exc:
print(f"[FEHLER] Server-Neustart fehlgeschlagen: {exc}", file=sys.stderr)
raise SystemExit(1)
base_dir = _get_base_dir()
# Im gebundelten Modus: sys._MEIPASS enthält src/ und app.py
if getattr(sys, 'frozen', False):
if sys._MEIPASS not in sys.path:
sys.path.insert(0, sys._MEIPASS)
# APP_BASE_DIR teilt app.py mit, wo die Daten-Ordner liegen
os.environ["APP_BASE_DIR"] = str(base_dir)
_ensure_dirs(base_dir)
# ── Streamlit-Konfiguration ──────────────────────────
os.environ["STREAMLIT_SERVER_ADDRESS"] = "localhost"
os.environ["STREAMLIT_SERVER_PORT"] = "8501"
os.environ["STREAMLIT_SERVER_HEADLESS"] = "true"
os.environ["STREAMLIT_BROWSER_GATHER_USAGE_STATS"] = "false"
os.environ["STREAMLIT_SERVER_ENABLE_CORS"] = "false"
os.environ["STREAMLIT_SERVER_ENABLE_XSRF_PROTECTION"] = "false"
# Browser manuell öffnen
def _open_browser() -> None:
time.sleep(2)
webbrowser.open("http://localhost:8501")
threading.Thread(target=_open_browser, daemon=True).start()
# ── Streamlit via CLI-Codepfad starten ───────────────
# Dieser Weg ist identisch zu 'streamlit run app.py' und
# funktioniert im PyInstaller-Bundle zuverlässiger als
# bootstrap.run() mit Streamlit >= 1.58.
sys.argv = [
"streamlit", "run", _get_app_path(),
"--server.port=8501",
"--server.address=localhost",
"--server.headless=true",
"--browser.gatherUsageStats=false",
"--server.enableCORS=false",
"--server.enableXsrfProtection=false",
"--global.developmentMode=false",
]
try:
from streamlit.cli import main as cli_main
except ImportError:
from streamlit.web.cli import main as cli_main
cli_main()
if __name__ == "__main__":
main()