# app/main.py [v3 โ€” Flask + pywebview suite] import sys import socket import threading import time from pathlib import Path from loguru import logger from app.config_loader import is_debug, get_version from launcher_src.shared_db_sync import ( pull_from_server, start_background_sync, stop_background_sync, push_to_server, pull_ott_db, ) def setup_logging(): import os as _os, socket as _socket log_dir = Path('logs') log_dir.mkdir(exist_ok=True) log_format = '{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}' log_level = 'DEBUG' if is_debug() else 'INFO' logger.remove() logger.add( str(log_dir / 'myicr_suite.log'), rotation='10 MB', retention='7 days', format=log_format, level=log_level, ) try: server_log_dir = Path(r'I:\SOFTWARE\PYTHON_SRV\logs') server_log_dir.mkdir(parents=True, exist_ok=True) hostname = _socket.gethostname() username = _os.environ.get('USERNAME', 'unknown') logger.add( str(server_log_dir / f'{hostname}-{username}.log'), rotation='5 MB', retention='30 days', format=log_format, level='INFO', enqueue=True, catch=True, ) except Exception: pass logger.info('=' * 50) logger.info(f"Avvio MyICR Suite (Debug: {'ON' if is_debug() else 'OFF'})") def _wait_for_server(port: int = 5001, timeout: int = 10) -> bool: deadline = time.time() + timeout while time.time() < deadline: try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(1) if s.connect_ex(('127.0.0.1', port)) == 0: s.close() return True s.close() except Exception: pass time.sleep(0.1) return False def main(): setup_logging() import os as _os _dev = _os.environ.get("MYICR_DEV_MODE") == "1" or '--dev' in sys.argv if _dev: _os.environ["MYICR_DEV_MODE"] = "1" # propaga ai subprocess figli if not _dev: pull_from_server() pull_ott_db() start_background_sync() # Build gemma_unified.db in background โ€” usato solo dal Linker (valutazioni_unified) def _build_gemma_bg(): try: from launcher_src.gemma_unified_builder import ensure_gemma_unified from app.config_loader import get_app_root, load_config _root = get_app_root() _cfg = load_config() parquet_dir = _root / _cfg.get('parquet_dir', 'local_db/parquet') linker_db = _root / 'sync_db' / 'linker.db' ensure_gemma_unified(parquet_dir, linker_db) except Exception as _e: logger.warning(f"Build gemma_unified.db fallita: {_e}") threading.Thread(target=_build_gemma_bg, daemon=True).start() # Avvia Flask suite in thread demone from app.suite.server import app as flask_app, terminate_all_subprocesses def _run_flask(): flask_app.run(host='127.0.0.1', port=5001, debug=False, use_reloader=False, threaded=True) server_thread = threading.Thread(target=_run_flask, daemon=True) server_thread.start() if not _wait_for_server(5001): logger.error('Flask suite non risponde entro 10 secondi โ€” uscita.') sys.exit(1) import webview _ver = get_version() title = f'MyICR Suite v{_ver}' if _ver else 'MyICR Suite' def on_closing(): result = webview.windows[0].create_confirmation_dialog( 'Conferma Uscita', 'Uscire da MyICR Suite?' ) if result and not _dev: stop_background_sync() push_to_server() if result: terminate_all_subprocesses() return result window = webview.create_window( title, 'http://127.0.0.1:5001', width=1400, height=900, resizable=True, min_size=(900, 600), maximized=True, ) window.events.closing += on_closing # Segnala al bootstrap che la finestra รจ pronta try: (Path(__file__).parent.parent / '.launcher_ready').touch() except Exception: pass logger.info('Avvio webview suite') webview.start(debug=is_debug()) if __name__ == '__main__': main()