"""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()