416 lines
17 KiB
Python
416 lines
17 KiB
Python
import os
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from typing import Dict, Any, List, Optional
|
|
from datetime import datetime
|
|
import uuid
|
|
|
|
class LauncherManager:
|
|
def __init__(self, data_path: str):
|
|
self.data_path = data_path
|
|
self.launcher_config_path = os.path.join(data_path, "launcher_scripts.json")
|
|
self.favorites_path = os.path.join(data_path, "launcher_favorites.json")
|
|
self.history_path = os.path.join(data_path, "launcher_history.json")
|
|
|
|
# Inicializar archivos si no existen
|
|
self._initialize_files()
|
|
|
|
def _initialize_files(self):
|
|
"""Crear archivos de configuración por defecto si no existen"""
|
|
# Inicializar launcher_scripts.json
|
|
if not os.path.exists(self.launcher_config_path):
|
|
default_config = {
|
|
"version": "1.0",
|
|
"groups": [],
|
|
"categories": {
|
|
"Herramientas": {
|
|
"color": "#3B82F6",
|
|
"icon": "🔧",
|
|
"subcategories": ["Generales", "Desarrollo", "Sistema"]
|
|
},
|
|
"Análisis": {
|
|
"color": "#10B981",
|
|
"icon": "📊",
|
|
"subcategories": ["Datos", "Estadísticas", "Visualización"]
|
|
},
|
|
"Utilidades": {
|
|
"color": "#8B5CF6",
|
|
"icon": "⚙️",
|
|
"subcategories": ["Archivos", "Texto", "Conversión"]
|
|
},
|
|
"Desarrollo": {
|
|
"color": "#F59E0B",
|
|
"icon": "💻",
|
|
"subcategories": ["Code", "Testing", "Deploy"]
|
|
},
|
|
"Visualización": {
|
|
"color": "#EF4444",
|
|
"icon": "📈",
|
|
"subcategories": ["Gráficos", "Reportes", "Dashboard"]
|
|
},
|
|
"Otros": {
|
|
"color": "#6B7280",
|
|
"icon": "📁",
|
|
"subcategories": ["Misceláneos"]
|
|
}
|
|
},
|
|
"settings": {
|
|
"default_execution_directory": "script_directory",
|
|
"enable_argument_validation": True,
|
|
"max_history_entries": 100,
|
|
"auto_cleanup_days": 30
|
|
}
|
|
}
|
|
with open(self.launcher_config_path, 'w', encoding='utf-8') as f:
|
|
json.dump(default_config, f, indent=2, ensure_ascii=False)
|
|
|
|
# Inicializar launcher_favorites.json
|
|
if not os.path.exists(self.favorites_path):
|
|
default_favorites = {"favorites": []}
|
|
with open(self.favorites_path, 'w', encoding='utf-8') as f:
|
|
json.dump(default_favorites, f, indent=2, ensure_ascii=False)
|
|
|
|
# Inicializar launcher_history.json
|
|
if not os.path.exists(self.history_path):
|
|
default_history = {
|
|
"history": [],
|
|
"settings": {
|
|
"max_entries": 100,
|
|
"auto_cleanup_days": 30,
|
|
"track_execution_time": True,
|
|
"track_arguments": True
|
|
}
|
|
}
|
|
with open(self.history_path, 'w', encoding='utf-8') as f:
|
|
json.dump(default_history, f, indent=2, ensure_ascii=False)
|
|
|
|
def get_launcher_groups(self) -> List[Dict[str, Any]]:
|
|
"""Obtener todos los grupos de scripts GUI"""
|
|
try:
|
|
with open(self.launcher_config_path, 'r', encoding='utf-8') as f:
|
|
config = json.load(f)
|
|
return config.get("groups", [])
|
|
except Exception as e:
|
|
print(f"Error loading launcher groups: {e}")
|
|
return []
|
|
|
|
def get_launcher_group(self, group_id: str) -> Optional[Dict[str, Any]]:
|
|
"""Obtener un grupo específico por ID"""
|
|
groups = self.get_launcher_groups()
|
|
for group in groups:
|
|
if group.get("id") == group_id:
|
|
return group
|
|
return None
|
|
|
|
def add_launcher_group(self, group_data: Dict[str, Any]) -> Dict[str, str]:
|
|
"""Agregar nuevo grupo de scripts GUI"""
|
|
try:
|
|
# Validar datos requeridos
|
|
required_fields = ["name", "directory"]
|
|
for field in required_fields:
|
|
if not group_data.get(field):
|
|
return {"status": "error", "message": f"Campo requerido: {field}"}
|
|
|
|
# Validar que el directorio existe
|
|
if not os.path.isdir(group_data["directory"]):
|
|
return {"status": "error", "message": "El directorio especificado no existe"}
|
|
|
|
# Generar ID único si no se proporciona
|
|
if not group_data.get("id"):
|
|
group_data["id"] = str(uuid.uuid4())[:8]
|
|
|
|
# Verificar que el ID no exista
|
|
if self.get_launcher_group(group_data["id"]):
|
|
return {"status": "error", "message": "Ya existe un grupo con este ID"}
|
|
|
|
# Agregar campos por defecto
|
|
current_time = datetime.now().isoformat() + "Z"
|
|
group_data.setdefault("description", "")
|
|
group_data.setdefault("category", "Otros")
|
|
group_data.setdefault("version", "1.0")
|
|
group_data.setdefault("author", "")
|
|
group_data.setdefault("tags", [])
|
|
group_data.setdefault("created_date", current_time)
|
|
group_data["updated_date"] = current_time
|
|
|
|
# Cargar configuración y agregar grupo
|
|
with open(self.launcher_config_path, 'r', encoding='utf-8') as f:
|
|
config = json.load(f)
|
|
|
|
config["groups"].append(group_data)
|
|
|
|
with open(self.launcher_config_path, 'w', encoding='utf-8') as f:
|
|
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
|
|
return {"status": "success", "message": "Grupo agregado exitosamente"}
|
|
|
|
except Exception as e:
|
|
return {"status": "error", "message": f"Error agregando grupo: {str(e)}"}
|
|
|
|
def update_launcher_group(self, group_id: str, group_data: Dict[str, Any]) -> Dict[str, str]:
|
|
"""Actualizar grupo existente"""
|
|
try:
|
|
with open(self.launcher_config_path, 'r', encoding='utf-8') as f:
|
|
config = json.load(f)
|
|
|
|
# Buscar y actualizar el grupo
|
|
group_found = False
|
|
for i, group in enumerate(config["groups"]):
|
|
if group["id"] == group_id:
|
|
# Mantener ID y fechas de creación
|
|
group_data["id"] = group_id
|
|
group_data["created_date"] = group.get("created_date", datetime.now().isoformat() + "Z")
|
|
group_data["updated_date"] = datetime.now().isoformat() + "Z"
|
|
|
|
config["groups"][i] = group_data
|
|
group_found = True
|
|
break
|
|
|
|
if not group_found:
|
|
return {"status": "error", "message": "Grupo no encontrado"}
|
|
|
|
with open(self.launcher_config_path, 'w', encoding='utf-8') as f:
|
|
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
|
|
return {"status": "success", "message": "Grupo actualizado exitosamente"}
|
|
|
|
except Exception as e:
|
|
return {"status": "error", "message": f"Error actualizando grupo: {str(e)}"}
|
|
|
|
def delete_launcher_group(self, group_id: str) -> Dict[str, str]:
|
|
"""Eliminar grupo de scripts GUI"""
|
|
try:
|
|
with open(self.launcher_config_path, 'r', encoding='utf-8') as f:
|
|
config = json.load(f)
|
|
|
|
# Filtrar el grupo a eliminar
|
|
original_count = len(config["groups"])
|
|
config["groups"] = [g for g in config["groups"] if g["id"] != group_id]
|
|
|
|
if len(config["groups"]) == original_count:
|
|
return {"status": "error", "message": "Grupo no encontrado"}
|
|
|
|
with open(self.launcher_config_path, 'w', encoding='utf-8') as f:
|
|
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
|
|
# Limpiar favoritos del grupo eliminado
|
|
self._cleanup_favorites_for_group(group_id)
|
|
|
|
return {"status": "success", "message": "Grupo eliminado exitosamente"}
|
|
|
|
except Exception as e:
|
|
return {"status": "error", "message": f"Error eliminando grupo: {str(e)}"}
|
|
|
|
def get_group_scripts(self, group_id: str) -> List[Dict[str, Any]]:
|
|
"""Obtener scripts de un grupo específico"""
|
|
try:
|
|
group = self.get_launcher_group(group_id)
|
|
if not group:
|
|
return []
|
|
|
|
directory = group["directory"]
|
|
if not os.path.isdir(directory):
|
|
return []
|
|
|
|
scripts = []
|
|
for file in os.listdir(directory):
|
|
if file.endswith('.py') and not file.startswith('_'):
|
|
script_path = os.path.join(directory, file)
|
|
if os.path.isfile(script_path):
|
|
scripts.append({
|
|
"name": file,
|
|
"display_name": file[:-3], # Sin extensión .py
|
|
"path": script_path,
|
|
"size": os.path.getsize(script_path),
|
|
"modified": datetime.fromtimestamp(os.path.getmtime(script_path)).isoformat()
|
|
})
|
|
|
|
return sorted(scripts, key=lambda x: x["name"])
|
|
|
|
except Exception as e:
|
|
print(f"Error getting scripts for group {group_id}: {e}")
|
|
return []
|
|
|
|
def execute_gui_script(self, group_id: str, script_name: str, script_args: List[str],
|
|
broadcast_func) -> Dict[str, Any]:
|
|
"""Ejecutar script GUI con argumentos opcionales"""
|
|
try:
|
|
group = self.get_launcher_group(group_id)
|
|
if not group:
|
|
return {"status": "error", "message": "Grupo no encontrado"}
|
|
|
|
script_path = os.path.join(group["directory"], script_name)
|
|
if not os.path.isfile(script_path):
|
|
return {"status": "error", "message": "Script no encontrado"}
|
|
|
|
# Construir comando
|
|
cmd = [sys.executable, script_path] + script_args
|
|
working_dir = group["directory"] # Por defecto directorio del script
|
|
|
|
broadcast_func(f"Ejecutando script GUI: {script_name}")
|
|
broadcast_func(f"Comando: {' '.join(cmd)}")
|
|
broadcast_func(f"Directorio: {working_dir}")
|
|
|
|
# Ejecutar script
|
|
start_time = datetime.now()
|
|
process = subprocess.Popen(
|
|
cmd,
|
|
cwd=working_dir,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True,
|
|
creationflags=subprocess.CREATE_NEW_CONSOLE if sys.platform == "win32" else 0
|
|
)
|
|
|
|
# Registrar en historial
|
|
execution_id = str(uuid.uuid4())[:8]
|
|
self._add_to_history({
|
|
"id": execution_id,
|
|
"group_id": group_id,
|
|
"script_name": script_name,
|
|
"executed_date": start_time.isoformat() + "Z",
|
|
"arguments": script_args,
|
|
"working_directory": working_dir,
|
|
"status": "running",
|
|
"pid": process.pid
|
|
})
|
|
|
|
broadcast_func(f"Script GUI ejecutado con PID: {process.pid}")
|
|
broadcast_func(f"ID de ejecución: {execution_id}")
|
|
|
|
return {
|
|
"status": "success",
|
|
"message": "Script GUI ejecutado exitosamente",
|
|
"execution_id": execution_id,
|
|
"pid": process.pid
|
|
}
|
|
|
|
except Exception as e:
|
|
error_msg = f"Error ejecutando script GUI: {str(e)}"
|
|
broadcast_func(error_msg)
|
|
return {"status": "error", "message": error_msg}
|
|
|
|
def get_favorites(self) -> List[Dict[str, Any]]:
|
|
"""Obtener lista de favoritos"""
|
|
try:
|
|
with open(self.favorites_path, 'r', encoding='utf-8') as f:
|
|
data = json.load(f)
|
|
return data.get("favorites", [])
|
|
except Exception as e:
|
|
print(f"Error loading favorites: {e}")
|
|
return []
|
|
|
|
def toggle_favorite(self, group_id: str, script_name: str) -> Dict[str, str]:
|
|
"""Agregar o quitar script de favoritos"""
|
|
try:
|
|
with open(self.favorites_path, 'r', encoding='utf-8') as f:
|
|
data = json.load(f)
|
|
|
|
favorites = data.get("favorites", [])
|
|
favorite_id = f"{group_id}_{script_name}"
|
|
|
|
# Buscar si ya existe
|
|
existing_favorite = None
|
|
for i, fav in enumerate(favorites):
|
|
if fav["group_id"] == group_id and fav["script_name"] == script_name:
|
|
existing_favorite = i
|
|
break
|
|
|
|
if existing_favorite is not None:
|
|
# Quitar de favoritos
|
|
favorites.pop(existing_favorite)
|
|
action = "removed"
|
|
else:
|
|
# Agregar a favoritos
|
|
favorites.append({
|
|
"id": favorite_id,
|
|
"group_id": group_id,
|
|
"script_name": script_name,
|
|
"added_date": datetime.now().isoformat() + "Z",
|
|
"execution_count": 0,
|
|
"last_executed": None
|
|
})
|
|
action = "added"
|
|
|
|
data["favorites"] = favorites
|
|
with open(self.favorites_path, 'w', encoding='utf-8') as f:
|
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
|
|
return {"status": "success", "action": action}
|
|
|
|
except Exception as e:
|
|
return {"status": "error", "message": f"Error managing favorite: {str(e)}"}
|
|
|
|
def get_history(self) -> List[Dict[str, Any]]:
|
|
"""Obtener historial de ejecución"""
|
|
try:
|
|
with open(self.history_path, 'r', encoding='utf-8') as f:
|
|
data = json.load(f)
|
|
return data.get("history", [])
|
|
except Exception as e:
|
|
print(f"Error loading history: {e}")
|
|
return []
|
|
|
|
def clear_history(self) -> Dict[str, str]:
|
|
"""Limpiar historial de ejecución"""
|
|
try:
|
|
with open(self.history_path, 'r', encoding='utf-8') as f:
|
|
data = json.load(f)
|
|
|
|
data["history"] = []
|
|
|
|
with open(self.history_path, 'w', encoding='utf-8') as f:
|
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
|
|
return {"status": "success", "message": "Historial limpiado exitosamente"}
|
|
|
|
except Exception as e:
|
|
return {"status": "error", "message": f"Error clearing history: {str(e)}"}
|
|
|
|
def get_categories(self) -> Dict[str, Any]:
|
|
"""Obtener categorías disponibles"""
|
|
try:
|
|
with open(self.launcher_config_path, 'r', encoding='utf-8') as f:
|
|
config = json.load(f)
|
|
return config.get("categories", {})
|
|
except Exception as e:
|
|
print(f"Error loading categories: {e}")
|
|
return {}
|
|
|
|
def _add_to_history(self, entry: Dict[str, Any]):
|
|
"""Agregar entrada al historial"""
|
|
try:
|
|
with open(self.history_path, 'r', encoding='utf-8') as f:
|
|
data = json.load(f)
|
|
|
|
history = data.get("history", [])
|
|
history.insert(0, entry) # Agregar al inicio
|
|
|
|
# Limitar tamaño del historial
|
|
max_entries = data.get("settings", {}).get("max_entries", 100)
|
|
if len(history) > max_entries:
|
|
history = history[:max_entries]
|
|
|
|
data["history"] = history
|
|
|
|
with open(self.history_path, 'w', encoding='utf-8') as f:
|
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
|
|
except Exception as e:
|
|
print(f"Error adding to history: {e}")
|
|
|
|
def _cleanup_favorites_for_group(self, group_id: str):
|
|
"""Limpiar favoritos de un grupo eliminado"""
|
|
try:
|
|
with open(self.favorites_path, 'r', encoding='utf-8') as f:
|
|
data = json.load(f)
|
|
|
|
# Filtrar favoritos del grupo eliminado
|
|
data["favorites"] = [f for f in data.get("favorites", []) if f.get("group_id") != group_id]
|
|
|
|
with open(self.favorites_path, 'w', encoding='utf-8') as f:
|
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
|
|
except Exception as e:
|
|
print(f"Error cleaning up favorites for group {group_id}: {e}") |