"""Creación automática de config/ y carpetas de trabajo.""" import shutil import sys from pathlib import Path from ..constants import ( APP_DIR, BUNDLE_DIR, BUNDLED_SOURCE_7ZIP, BUNDLED_SOURCE_ODBC, CONFIG_DIR, DATA_DIR, DIR_ENTRADA, DIR_FALLADOS, DIR_PROCESADOS, DIR_TEMP, ENV_PATH, IS_WINDOWS, LOGS_DIR, ODBC_DIR, SEVEN_ZIP_DIR, ) from ..db.database import DatabaseManager from .env_loader import render_env_template def _copy_tree_if_missing(src: Path, dest: Path) -> None: if not src.is_dir(): return # dest puede existir pero VACÍO: ensure_runtime_layout crea ODBC_DIR/SEVEN_ZIP_DIR # antes de llamar aquí. Solo saltar si ya tiene contenido (evita re-copiar en cada # arranque). Antes se saltaba por dest.exists(), dejando 7-Zip/ODBC sin desplegar. if dest.exists() and any(dest.iterdir()): return dest.mkdir(parents=True, exist_ok=True) shutil.copytree(src, dest, dirs_exist_ok=True) def _ensure_seven_zip_executable() -> None: """ Asegura el bit de ejecución del 7-Zip embebido en Linux/macOS. shutil.copytree suele preservar los permisos, pero esto es una salvaguarda barata: si el binario quedó sin 'x', la extracción fallaría con PermissionError. """ if IS_WINDOWS: return for exe_name in ("7zz", "7za", "7z"): exe_path = SEVEN_ZIP_DIR / exe_name if exe_path.is_file(): try: exe_path.chmod(0o755) except OSError: pass def _write_env_if_missing() -> None: if ENV_PATH.exists(): return template_path = BUNDLE_DIR / "packaging" / "templates" / "env.default" if not template_path.is_file(): template_path = APP_DIR / "packaging" / "templates" / "env.default" if template_path.is_file(): content = template_path.read_text(encoding="utf-8") content = content.replace("{APP_DIR}", str(APP_DIR)) else: content = render_env_template(APP_DIR) ENV_PATH.write_text(content, encoding="utf-8") try: ENV_PATH.chmod(0o600) except OSError: pass def ensure_runtime_layout() -> None: """ Idempotente: crea config/, carpetas de trabajo y .env si no existen. No sobrescribe .env ni app.db existentes. """ for directory in ( CONFIG_DIR, DATA_DIR, LOGS_DIR, ODBC_DIR, SEVEN_ZIP_DIR, DIR_ENTRADA, DIR_PROCESADOS, DIR_FALLADOS, DIR_TEMP, ): directory.mkdir(parents=True, exist_ok=True) _write_env_if_missing() _copy_tree_if_missing(BUNDLED_SOURCE_7ZIP, SEVEN_ZIP_DIR) _copy_tree_if_missing(BUNDLED_SOURCE_ODBC, ODBC_DIR) _ensure_seven_zip_executable() from ..constants import DB_PATH if not DB_PATH.exists(): DatabaseManager(DB_PATH) if getattr(sys, "frozen", False): marker = CONFIG_DIR / ".bootstrap_ok" if not marker.exists(): marker.write_text("ok", encoding="utf-8")