#!/usr/bin/env python3 """ Script de setup e instalación para Calculadora MAV - CAS Híbrido """ import sys import subprocess import os from pathlib import Path import importlib.util def check_python_version(): """Verifica que la versión de Python sea compatible""" if sys.version_info < (3, 8): print("❌ Error: Se requiere Python 3.8 o superior") print(f" Versión actual: {sys.version}") return False print(f"✅ Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}") return True def check_module(module_name, package_name=None, optional=False): """Verifica si un módulo está disponible""" try: importlib.import_module(module_name) print(f"✅ {module_name}") return True except ImportError: if optional: print(f"⚠️ {module_name} (opcional)") else: print(f"❌ {module_name} - {'usar: pip install ' + (package_name or module_name)}") return False def install_requirements(): """Instala las dependencias requeridas""" print("\n=== Instalando dependencias ===") requirements = [ "sympy>=1.12", "matplotlib>=3.7.0", "numpy>=1.24.0" ] for req in requirements: try: print(f"Instalando {req}...") subprocess.check_call([ sys.executable, "-m", "pip", "install", req ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) print(f"✅ {req}") except subprocess.CalledProcessError: print(f"❌ Error instalando {req}") return False return True def check_tkinter(): """Verifica que tkinter esté disponible""" try: import tkinter print("✅ tkinter") return True except ImportError: print("❌ tkinter - instalar python3-tk en Linux/Ubuntu") print(" Ubuntu/Debian: sudo apt-get install python3-tk") print(" CentOS/RHEL: sudo yum install tkinter") print(" macOS: tkinter debe estar incluido con Python") return False def check_optional_modules(): """Verifica módulos opcionales""" optional_modules = [ ("markdown", "markdown", True), ("tkinterweb", "tkinterweb", True), ("tkhtmlview", "tkhtmlview", True), ("pytest", "pytest", True) ] for module_name, package_name, optional in optional_modules: check_module(module_name, package_name, optional) def create_desktop_shortcut(): """Crea acceso directo en el escritorio (Linux/Windows)""" current_dir = Path.cwd() main_script = current_dir / "hybrid_calc_app.py" if not main_script.exists(): print("⚠️ No se encontró hybrid_calc_app.py en el directorio actual") return False try: if sys.platform.startswith('linux'): # Linux desktop entry desktop_dir = Path.home() / "Desktop" if desktop_dir.exists(): shortcut_path = desktop_dir / "CalculadoraMAV.desktop" shortcut_content = f"""[Desktop Entry] Version=1.0 Type=Application Name=Calculadora MAV - CAS Híbrido Comment=Sistema de álgebra computacional híbrido Exec={sys.executable} "{main_script}" Icon=calculator Terminal=false Categories=Education;Science;Math; """ with open(shortcut_path, 'w') as f: f.write(shortcut_content) # Hacer ejecutable os.chmod(shortcut_path, 0o755) print(f"✅ Acceso directo creado: {shortcut_path}") return True elif sys.platform == 'win32': # Windows shortcut (requiere pywin32) try: import win32com.client desktop = Path.home() / "Desktop" shortcut_path = desktop / "Calculadora MAV.lnk" shell = win32com.client.Dispatch("WScript.Shell") shortcut = shell.CreateShortCut(str(shortcut_path)) shortcut.Targetpath = sys.executable shortcut.Arguments = f'"{main_script}"' shortcut.WorkingDirectory = str(current_dir) shortcut.IconLocation = sys.executable shortcut.save() print(f"✅ Acceso directo creado: {shortcut_path}") return True except ImportError: print("⚠️ Para crear acceso directo en Windows instalar: pip install pywin32") except Exception as e: print(f"⚠️ Error creando acceso directo: {e}") return False def run_tests(): """Ejecuta tests básicos""" print("\n=== Ejecutando tests básicos ===") try: # Test de importación from bracket_parser import BracketParser from hybrid_base_types import Hex, Bin, IP4 from hybrid_evaluation_engine import HybridEvaluationEngine print("✅ Importaciones básicas") # Test de parser parser = BracketParser() result, info = parser.parse_line("Hex[FF]") assert result == 'Hex("FF")', f"Parser test failed: {result}" print("✅ Bracket parser") # Test de clases híbridas h = Hex("FF") assert str(h) == "0xFF", f"Hex test failed: {h}" print("✅ Clases híbridas") # Test de motor de evaluación engine = HybridEvaluationEngine() result = engine.evaluate_line("2 + 3") assert result.result == 5, f"Engine test failed: {result.result}" print("✅ Motor de evaluación") print("✅ Todos los tests básicos pasaron") return True except Exception as e: print(f"❌ Error en tests: {e}") return False def main(): """Función principal de setup""" print("=== Calculadora MAV - CAS Híbrido - Setup ===\n") # Verificar Python if not check_python_version(): sys.exit(1) print("\n=== Verificando dependencias ===") # Verificar tkinter if not check_tkinter(): print("\n❌ tkinter es requerido para la interfaz gráfica") sys.exit(1) # Verificar dependencias principales deps_ok = True deps_ok &= check_module("sympy") deps_ok &= check_module("matplotlib") deps_ok &= check_module("numpy") # Si faltan dependencias, intentar instalar if not deps_ok: print("\n=== Faltan dependencias requeridas ===") response = input("¿Instalar automáticamente? (s/n): ").lower().strip() if response in ['s', 'si', 'y', 'yes']: if not install_requirements(): print("❌ Error instalando dependencias") sys.exit(1) else: print("❌ Instale las dependencias manualmente:") print(" pip install sympy matplotlib numpy") sys.exit(1) # Verificar módulos opcionales print("\n=== Módulos opcionales ===") check_optional_modules() # Ejecutar tests if not run_tests(): print("❌ Tests fallaron") sys.exit(1) # Crear acceso directo print("\n=== Configuración adicional ===") response = input("¿Crear acceso directo en el escritorio? (s/n): ").lower().strip() if response in ['s', 'si', 'y', 'yes']: create_desktop_shortcut() print("\n✅ ¡Setup completado exitosamente!") print("\nPara ejecutar la calculadora:") print(f" python {Path.cwd() / 'hybrid_calc_app.py'}") print("\nO usa el acceso directo si lo creaste.") if __name__ == "__main__": main()