36 KiB
Guía de Desarrollo: Launcher para Scripts GUI
La aplicación original @app.py busca y ejecuta scripts Python sin GUI. Deseo agregar una nueva funcionalidad para scripts con GUI que no necesiten el sistema de datos grupal o directorio de trabajo con eschema de datos. La página principal @index.html se dividirá en dos pestañas: una sin cambios y otra para un launcher de scripts Python con GUI. Esta nueva pagina de launcher usará una lista manual de directorios almacenada en un archivo launcher_scripts.json. Necesitamos la opcion de poder agregar, eliminar o modificar estos directorios de grupos de scripts. La lista se mostrará en un combo box, similar a la aplicación actual, describiendo cada grupo de scripts. Al seleccionar un grupo, se buscarán los scripts en el directorio root correspondiente. El sistema de configuración no se usará, solo el directorio de trabajo para indicar desde donde se ejecuta el script, por defecto el directorio root del script. El sistema de logging se mantendrá. Además, si hay un archivo icon.ico en un directorio de scripts, se mostrará como referencia gráfica del grupo en la página. De ser posible quisiera que el icono del grupo se muestre en la combo de seleccion de grupos.
Resumen del Proyecto
Esta guía detalla la implementación de un nuevo launcher para scripts Python con interfaz gráfica (GUI), que coexistirá con el sistema actual de scripts de configuración. El nuevo sistema incluirá funcionalidades avanzadas como categorización, favoritos, historial y argumentos simples.
Índice
- Arquitectura General
- Estructura de Archivos
- Fase 1: Estructura Básica
- Fase 2: Funcionalidad Core
- Fase 3: Mejoras Avanzadas
- Fase 4: Sistema de Iconos
- APIs Requeridas
- Estructura de Datos
- Componentes UI
- Testing y Validación
Arquitectura General
Principios de Diseño
- Coexistencia: El nuevo launcher coexistirá con el sistema actual sin interferir
- Reutilización: Aprovechar componentes existentes (logging, UI base, etc.)
- Modularidad: Código separado y mantenible
- Flexibilidad: Configuración manual de directorios y categorías
- Mecanismo para seleccionar el entorno miniconda a utlizar para cada grupo de scripts.
Diferencias con Sistema Actual
Aspecto | Sistema Actual | Nuevo Launcher GUI |
---|---|---|
Fuente de grupos | Directorios automáticos | launcher_scripts.json manual |
Configuración | 3 niveles de schemas | Sin configuración compleja |
Directorio trabajo | Configurable avanzado | Por defecto directorio del script |
Metadatos | Archivos separados | Centralizados en JSON |
Categorización | No disponible | ✅ Por categorías |
Favoritos | No disponible | ✅ Sistema de favoritos |
Historial | No disponible | ✅ Historial de ejecución |
Argumentos | Vía configuración | ✅ Argumentos simples |
Estructura de Archivos
Archivos Nuevos a Crear
ParamManagerScripts/ ├── data/ │ ├── launcher_scripts.json # Configuración principal del launcher │ ├── launcher_favorites.json # Scripts marcados como favoritos │ └── launcher_history.json # Historial de ejecución ├── lib/ │ └── launcher_manager.py # Gestor principal del launcher ├── static/ │ ├── css/ │ │ └── launcher.css # Estilos específicos │ └── js/ │ └── launcher.js # Lógica JavaScript ├── templates/ │ └── components/ │ ├── launcher_tab.html # Tab del launcher │ ├── launcher_editor.html # Editor de grupos │ ├── script_args_modal.html # Modal para argumentos │ └── favorites_panel.html # Panel de favoritos └── adicion_launcher4GUI.md # Esta guía
Archivos a Modificar
ParamManagerScripts/ ├── app.py # Agregar rutas del launcher ├── templates/ │ └── index.html # Implementar sistema de tabs └── static/ ├── css/ │ └── styles.css # Estilos para tabs y iconos └── js/ └── scripts.js # Lógica de tabs
Fase 1: Estructura Básica
1.1 Crear launcher_manager.py
# lib/launcher_manager.py
import os
import json
from typing import Dict, Any, List, Optional
from datetime import datetime
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"""
# Implementar inicialización
pass
def get_launcher_groups(self) -> List[Dict[str, Any]]:
"""Obtener todos los grupos de scripts GUI"""
pass
def add_launcher_group(self, group_data: Dict[str, Any]) -> Dict[str, str]:
"""Agregar nuevo grupo de scripts GUI"""
pass
def update_launcher_group(self, group_id: str, group_data: Dict[str, Any]) -> Dict[str, str]:
"""Actualizar grupo existente"""
pass
def delete_launcher_group(self, group_id: str) -> Dict[str, str]:
"""Eliminar grupo de scripts GUI"""
pass
def get_group_scripts(self, group_id: str) -> List[Dict[str, Any]]:
"""Obtener scripts de un grupo específico"""
pass
1.2 Implementar Sistema de Tabs en index.html
<!-- Agregar al inicio del main content en index.html -->
<div class="container mx-auto px-4 py-8">
<!-- Tab Navigation -->
<div class="mb-8">
<div class="border-b border-gray-200">
<nav class="-mb-px flex space-x-8">
<button id="config-tab" onclick="switchTab('config')"
class="tab-button active py-2 px-1 border-b-2 font-medium text-sm">
<span class="flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
</svg>
Scripts de Configuración
</span>
</button>
<button id="launcher-tab" onclick="switchTab('launcher')"
class="tab-button py-2 px-1 border-b-2 font-medium text-sm">
<span class="flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
</svg>
Launcher GUI
</span>
</button>
</nav>
</div>
</div>
<!-- Tab Content -->
<div id="config-content" class="tab-content">
<!-- Contenido actual del sistema -->
</div>
<div id="launcher-content" class="tab-content hidden">
<!-- Contenido del nuevo launcher -->
</div>
</div>
1.3 CSS para Tabs
/* static/css/styles.css - Agregar al final */
/* Tab Styles */
.tab-button {
color: #6B7280;
border-color: transparent;
transition: all 0.2s ease;
}
.tab-button:hover {
color: #374151;
border-color: #D1D5DB;
}
.tab-button.active {
color: #3B82F6;
border-color: #3B82F6;
}
.tab-content {
display: block;
}
.tab-content.hidden {
display: none;
}
/* Icon Styles */
.group-icon {
width: 48px;
height: 48px;
border-radius: 8px;
object-fit: cover;
border: 2px solid #E5E7EB;
}
.group-icon.default {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 20px;
}
/* Category Badge */
.category-badge {
display: inline-block;
padding: 0.25rem 0.5rem;
background: #EFF6FF;
color: #1D4ED8;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 500;
}
/* Favorite Star */
.favorite-star {
cursor: pointer;
transition: all 0.2s ease;
}
.favorite-star:hover {
transform: scale(1.1);
}
.favorite-star.active {
color: #F59E0B;
}
Fase 2: Funcionalidad Core
2.1 APIs del Launcher
# Agregar a app.py
from lib.launcher_manager import LauncherManager
# Inicializar launcher manager
launcher_manager = LauncherManager(os.path.join(config_manager.data_path))
@app.route("/api/launcher-groups", methods=["GET", "POST"])
def handle_launcher_groups():
"""Gestionar grupos de launcher (GET: obtener, POST: crear)"""
if request.method == "GET":
try:
groups = launcher_manager.get_launcher_groups()
return jsonify(groups)
except Exception as e:
return jsonify({"error": str(e)}), 500
else: # POST
try:
data = request.json
result = launcher_manager.add_launcher_group(data)
return jsonify(result)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route("/api/launcher-groups/<group_id>", methods=["GET", "PUT", "DELETE"])
def handle_launcher_group(group_id):
"""Gestionar grupo específico (GET: obtener, PUT: actualizar, DELETE: eliminar)"""
if request.method == "GET":
try:
group = launcher_manager.get_launcher_group(group_id)
return jsonify(group)
except Exception as e:
return jsonify({"error": str(e)}), 500
elif request.method == "PUT":
try:
data = request.json
result = launcher_manager.update_launcher_group(group_id, data)
return jsonify(result)
except Exception as e:
return jsonify({"error": str(e)}), 500
else: # DELETE
try:
result = launcher_manager.delete_launcher_group(group_id)
return jsonify(result)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route("/api/launcher-scripts/<group_id>")
def get_launcher_scripts(group_id):
"""Obtener scripts de un grupo del launcher"""
try:
scripts = launcher_manager.get_group_scripts(group_id)
return jsonify(scripts)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route("/api/execute-gui-script", methods=["POST"])
def execute_gui_script():
"""Ejecutar script GUI con argumentos opcionales"""
try:
data = request.json
group_id = data["group_id"]
script_name = data["script_name"]
script_args = data.get("args", [])
result = launcher_manager.execute_gui_script(
group_id, script_name, script_args, broadcast_message
)
return jsonify(result)
except Exception as e:
error_msg = f"Error ejecutando script GUI: {str(e)}"
broadcast_message(error_msg)
return jsonify({"error": error_msg}), 500
2.2 JavaScript del Launcher
// static/js/launcher.js
class LauncherManager {
constructor() {
this.currentGroup = null;
this.groups = [];
this.scripts = [];
this.favorites = new Set();
this.history = [];
this.categories = [
'Herramientas', 'Análisis', 'Utilidades',
'Desarrollo', 'Visualización', 'Otros'
];
}
async init() {
await this.loadGroups();
await this.loadFavorites();
await this.loadHistory();
this.setupEventListeners();
this.renderInterface();
}
async loadGroups() {
try {
const response = await fetch('/api/launcher-groups');
this.groups = await response.json();
} catch (error) {
console.error('Error loading launcher groups:', error);
}
}
async loadFavorites() {
try {
const response = await fetch('/api/launcher-favorites');
const data = await response.json();
this.favorites = new Set(data.favorites || []);
} catch (error) {
console.error('Error loading favorites:', error);
}
}
async loadHistory() {
try {
const response = await fetch('/api/launcher-history');
const data = await response.json();
this.history = data.history || [];
} catch (error) {
console.error('Error loading history:', error);
}
}
setupEventListeners() {
// Event listeners para la interfaz
}
renderInterface() {
this.renderGroupSelector();
this.renderCategoryFilter();
this.renderFavoritesPanel();
this.renderHistoryPanel();
}
// Métodos adicionales...
}
// Inicialización global
window.launcherManager = new LauncherManager();
Fase 3: Mejoras Avanzadas
3.1 Sistema de Categorías
Estructura de Datos
{
"groups": [
{
"id": "data_tools",
"name": "Herramientas de Datos",
"description": "Scripts para análisis y manipulación de datos",
"directory": "D:/Scripts/DataTools",
"category": "Análisis",
"subcategory": "Datos",
"version": "1.0",
"author": "DataTeam",
"icon": "icon.ico",
"tags": ["excel", "csv", "análisis"]
}
],
"categories": {
"Herramientas": {
"color": "#3B82F6",
"icon": "🔧",
"subcategories": ["Generales", "Desarrollo", "Sistema"]
},
"Análisis": {
"color": "#10B981",
"icon": "📊",
"subcategories": ["Datos", "Estadísticas", "Visualización"]
}
}
}
Componente de Filtro por Categorías
<!-- templates/components/category_filter.html -->
<div class="category-filter mb-6">
<h3 class="text-lg font-semibold mb-3">Filtrar por Categoría</h3>
<div class="flex flex-wrap gap-2">
<button class="category-btn active" data-category="all">
Todas las Categorías
</button>
<button class="category-btn" data-category="Herramientas">
🔧 Herramientas
</button>
<button class="category-btn" data-category="Análisis">
📊 Análisis
</button>
<button class="category-btn" data-category="Utilidades">
⚙️ Utilidades
</button>
</div>
</div>
3.2 Sistema de Favoritos
Estructura de Datos
{
"favorites": [
{
"group_id": "data_tools",
"script_name": "excel_analyzer.py",
"added_date": "2024-01-15T10:30:00Z",
"execution_count": 15,
"last_executed": "2024-01-20T14:45:00Z"
}
]
}
Panel de Favoritos
<!-- templates/components/favorites_panel.html -->
<div class="favorites-panel bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6">
<div class="flex items-center justify-between mb-3">
<h3 class="text-lg font-semibold text-yellow-800">
⭐ Scripts Favoritos
</h3>
<span class="text-sm text-yellow-600" id="favorites-count">
0 favoritos
</span>
</div>
<div id="favorites-list" class="space-y-2">
<!-- Lista dinámica de favoritos -->
</div>
</div>
3.3 Historial de Ejecución
Estructura de Datos
{
"history": [
{
"group_id": "data_tools",
"script_name": "excel_analyzer.py",
"executed_date": "2024-01-20T14:45:00Z",
"execution_time": 2.5,
"arguments": ["--input", "data.xlsx"],
"status": "success",
"output_summary": "Procesados 1,500 registros exitosamente"
}
],
"settings": {
"max_history_entries": 100,
"auto_cleanup_days": 30
}
}
Panel de Historial
<!-- templates/components/history_panel.html -->
<div class="history-panel">
<h3 class="text-lg font-semibold mb-3">📝 Historial Reciente</h3>
<div class="space-y-2 max-h-64 overflow-y-auto">
<div class="history-item bg-gray-50 rounded p-3">
<div class="flex justify-between items-start">
<div>
<span class="font-medium">excel_analyzer.py</span>
<span class="text-sm text-gray-500 ml-2">Herramientas de Datos</span>
</div>
<span class="text-xs text-gray-400">hace 2 horas</span>
</div>
<div class="text-sm text-gray-600 mt-1">
✅ Éxito - 2.5s - Procesados 1,500 registros
</div>
</div>
</div>
</div>
3.4 Sistema de Argumentos Simples
Modal de Argumentos
<!-- templates/components/script_args_modal.html -->
<div id="script-args-modal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 z-50">
<div class="flex items-center justify-center min-h-screen p-4">
<div class="bg-white rounded-lg shadow-xl max-w-md w-full">
<div class="p-6">
<h3 class="text-lg font-semibold mb-4">Argumentos del Script</h3>
<div id="script-info" class="mb-4 p-3 bg-gray-50 rounded">
<div class="font-medium" id="script-display-name"></div>
<div class="text-sm text-gray-600" id="script-description"></div>
</div>
<div class="space-y-3">
<div>
<label class="block text-sm font-medium mb-1">
Argumentos de Línea de Comandos
</label>
<textarea id="script-args-input"
class="w-full p-2 border rounded h-20"
placeholder="--input data.xlsx --output results.csv"></textarea>
<p class="text-xs text-gray-500 mt-1">
Separar argumentos con espacios. Usar comillas para valores con espacios.
</p>
</div>
<div>
<label class="block text-sm font-medium mb-1">
Directorio de Ejecución
</label>
<input type="text" id="execution-directory"
class="w-full p-2 border rounded"
placeholder="Por defecto: directorio del script">
</div>
</div>
<div class="flex justify-end gap-3 mt-6">
<button onclick="closeArgsModal()"
class="px-4 py-2 text-gray-600 hover:text-gray-800">
Cancelar
</button>
<button onclick="executeWithArgs()"
class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
Ejecutar Script
</button>
</div>
</div>
</div>
</div>
</div>
Fase 4: Sistema de Iconos
4.1 API para Iconos
# Agregar a app.py
@app.route("/api/group-icon/<launcher_type>/<group_id>")
def get_group_icon(launcher_type, group_id):
"""Obtener icono de un grupo (config o launcher)"""
try:
if launcher_type == "launcher":
group = launcher_manager.get_launcher_group(group_id)
if not group:
return jsonify({"error": "Group not found"}), 404
icon_path = os.path.join(group["directory"], "icon.ico")
if os.path.exists(icon_path):
return send_file(icon_path, mimetype='image/x-icon')
elif launcher_type == "config":
group_path = os.path.join(config_manager.script_groups_path, group_id)
icon_path = os.path.join(group_path, "icon.ico")
if os.path.exists(icon_path):
return send_file(icon_path, mimetype='image/x-icon')
# Icono por defecto
default_icon_path = os.path.join(app.static_folder, 'icons', 'default-group.png')
if os.path.exists(default_icon_path):
return send_file(default_icon_path)
else:
# SVG por defecto como fallback
return jsonify({"type": "default", "icon": "📁"})
except Exception as e:
return jsonify({"error": str(e)}), 500
4.2 Componente de Icono
// static/js/launcher.js - Agregar método
class LauncherManager {
renderGroupIcon(group) {
const iconContainer = document.createElement('div');
iconContainer.className = 'group-icon-container relative';
// Intentar cargar icono personalizado
const img = document.createElement('img');
img.className = 'group-icon';
img.src = `/api/group-icon/launcher/${group.id}`;
img.alt = group.name;
img.onerror = () => {
// Fallback a icono por defecto
iconContainer.innerHTML = `
<div class="group-icon default">
${this.getDefaultIconForCategory(group.category)}
</div>
`;
};
iconContainer.appendChild(img);
return iconContainer;
}
getDefaultIconForCategory(category) {
const icons = {
'Herramientas': '🔧',
'Análisis': '📊',
'Utilidades': '⚙️',
'Desarrollo': '💻',
'Visualización': '📈',
'Otros': '📁'
};
return icons[category] || '📁';
}
}
APIs Requeridas
Resumen de Endpoints
Método | Endpoint | Descripción |
---|---|---|
GET | /api/launcher-groups |
Obtener todos los grupos |
POST | /api/launcher-groups |
Crear nuevo grupo |
GET | /api/launcher-groups/<id> |
Obtener grupo específico |
PUT | /api/launcher-groups/<id> |
Actualizar grupo |
DELETE | /api/launcher-groups/<id> |
Eliminar grupo |
GET | /api/launcher-scripts/<group_id> |
Obtener scripts de grupo |
POST | /api/execute-gui-script |
Ejecutar script GUI |
GET | /api/launcher-favorites |
Obtener favoritos |
POST | /api/launcher-favorites |
Agregar/quitar favorito |
GET | /api/launcher-history |
Obtener historial |
DELETE | /api/launcher-history |
Limpiar historial |
GET | /api/group-icon/<type>/<id> |
Obtener icono de grupo |
Estructura de Datos
launcher_scripts.json
{
"version": "1.0",
"groups": [
{
"id": "data_analysis",
"name": "Análisis de Datos",
"description": "Scripts para análisis y visualización de datos",
"directory": "D:/Scripts/DataAnalysis",
"category": "Análisis",
"subcategory": "Datos",
"version": "2.1",
"author": "DataTeam",
"icon": "icon.ico",
"tags": ["excel", "csv", "análisis", "visualización"],
"created_date": "2024-01-01T00:00:00Z",
"updated_date": "2024-01-15T10:30:00Z"
}
],
"categories": {
"Análisis": {
"color": "#10B981",
"icon": "📊",
"subcategories": ["Datos", "Estadísticas", "Visualización"]
},
"Herramientas": {
"color": "#3B82F6",
"icon": "🔧",
"subcategories": ["Generales", "Desarrollo", "Sistema"]
}
},
"settings": {
"default_execution_directory": "script_directory",
"enable_argument_validation": true,
"max_history_entries": 100,
"auto_cleanup_days": 30
}
}
launcher_favorites.json
{
"favorites": [
{
"id": "data_analysis_excel_analyzer",
"group_id": "data_analysis",
"script_name": "excel_analyzer.py",
"added_date": "2024-01-15T10:30:00Z",
"execution_count": 15,
"last_executed": "2024-01-20T14:45:00Z",
"custom_name": "Analizador Excel Principal"
}
]
}
launcher_history.json
{
"history": [
{
"id": "exec_20240120_144500",
"group_id": "data_analysis",
"script_name": "excel_analyzer.py",
"executed_date": "2024-01-20T14:45:00Z",
"execution_time": 2.5,
"arguments": ["--input", "data.xlsx", "--output", "results.csv"],
"working_directory": "D:/Projects/DataAnalysis",
"status": "success",
"exit_code": 0,
"output_summary": "Procesados 1,500 registros exitosamente",
"error_message": null
}
],
"settings": {
"max_entries": 100,
"auto_cleanup_days": 30,
"track_execution_time": true,
"track_arguments": true
}
}
Componentes UI
Selector de Grupos con Iconos
<div class="group-selector mb-6">
<label class="block text-sm font-medium mb-2">Seleccionar Grupo de Scripts</label>
<div class="relative">
<select id="launcher-group-select" class="w-full p-3 border rounded-lg pl-12">
<option value="">-- Seleccionar Grupo --</option>
</select>
<div class="absolute left-3 top-1/2 transform -translate-y-1/2">
<div id="selected-group-icon" class="w-6 h-6 bg-gray-200 rounded"></div>
</div>
</div>
</div>
Lista de Scripts con Favoritos
<div class="scripts-grid grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="script-card bg-white border rounded-lg p-4 hover:shadow-md transition-shadow">
<div class="flex justify-between items-start mb-2">
<h4 class="font-medium">excel_analyzer.py</h4>
<button class="favorite-star text-gray-300 hover:text-yellow-500">⭐</button>
</div>
<p class="text-sm text-gray-600 mb-3">Analiza archivos Excel y genera reportes</p>
<div class="flex justify-between items-center">
<span class="category-badge">Análisis</span>
<div class="space-x-2">
<button class="text-blue-500 hover:underline text-sm" onclick="showArgsModal()">
Con Argumentos
</button>
<button class="bg-blue-500 text-white px-3 py-1 rounded text-sm hover:bg-blue-600">
Ejecutar
</button>
</div>
</div>
</div>
</div>
Editor de Grupos
<div id="group-editor-modal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 z-50">
<div class="flex items-center justify-center min-h-screen p-4">
<div class="bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-screen overflow-y-auto">
<div class="p-6">
<h3 class="text-xl font-semibold mb-6">Gestionar Grupos de Scripts GUI</h3>
<!-- Lista de grupos existentes -->
<div class="mb-6">
<h4 class="font-medium mb-3">Grupos Existentes</h4>
<div id="existing-groups-list" class="space-y-2 max-h-40 overflow-y-auto">
<!-- Lista dinámica -->
</div>
</div>
<!-- Formulario de edición -->
<form id="group-form" class="space-y-4">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium mb-1">ID del Grupo</label>
<input type="text" id="group-id" class="w-full p-2 border rounded" required>
</div>
<div>
<label class="block text-sm font-medium mb-1">Nombre</label>
<input type="text" id="group-name" class="w-full p-2 border rounded" required>
</div>
</div>
<div>
<label class="block text-sm font-medium mb-1">Descripción</label>
<textarea id="group-description" class="w-full p-2 border rounded h-20"></textarea>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium mb-1">Categoría</label>
<select id="group-category" class="w-full p-2 border rounded">
<option value="Herramientas">🔧 Herramientas</option>
<option value="Análisis">📊 Análisis</option>
<option value="Utilidades">⚙️ Utilidades</option>
<option value="Desarrollo">💻 Desarrollo</option>
<option value="Visualización">📈 Visualización</option>
<option value="Otros">📁 Otros</option>
</select>
</div>
<div>
<label class="block text-sm font-medium mb-1">Versión</label>
<input type="text" id="group-version" class="w-full p-2 border rounded" value="1.0">
</div>
</div>
<div>
<label class="block text-sm font-medium mb-1">Directorio</label>
<div class="flex gap-2">
<input type="text" id="group-directory" class="flex-1 p-2 border rounded" required>
<button type="button" onclick="browseDirectory()" class="bg-gray-500 text-white px-4 py-2 rounded">
Explorar
</button>
</div>
</div>
<div class="flex justify-end gap-3 pt-4">
<button type="button" onclick="closeGroupEditor()" class="px-4 py-2 text-gray-600 hover:text-gray-800">
Cancelar
</button>
<button type="button" onclick="deleteGroup()" class="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600">
Eliminar
</button>
<button type="submit" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
Guardar
</button>
</div>
</form>
</div>
</div>
</div>
</div>
Testing y Validación
Plan de Testing
1. Testing Unitario
- LauncherManager: Todas las operaciones CRUD
- Validación: Rutas de directorios, estructura JSON
- Ejecución: Scripts con diferentes argumentos
2. Testing de Integración
- APIs: Todos los endpoints del launcher
- Persistencia: Guardado y carga de configuraciones
- Logging: Integración con sistema de logs existente
3. Testing de UI
- Navegación: Cambio entre tabs
- Responsividad: Funcionalidad en diferentes tamaños de pantalla
- Interactividad: Favoritos, filtros, argumentos
4. Testing de Sistema
- Coexistencia: No interferencia con sistema actual
- Rendimiento: Carga de grupos grandes
- Seguridad: Validación de rutas y argumentos
Casos de Prueba
Funcionalidad Básica
// Ejemplo de test para favoritos
describe('Favorites System', () => {
test('should add script to favorites', async () => {
const result = await launcherManager.addToFavorites('group1', 'script1.py');
expect(result.status).toBe('success');
expect(launcherManager.favorites.has('group1_script1.py')).toBe(true);
});
test('should remove script from favorites', async () => {
await launcherManager.removeFromFavorites('group1', 'script1.py');
expect(launcherManager.favorites.has('group1_script1.py')).toBe(false);
});
});
Validación de Datos
# Ejemplo de validación de grupo
def test_group_validation():
invalid_group = {
"name": "", # Nombre vacío
"directory": "/path/that/does/not/exist" # Directorio inexistente
}
result = launcher_manager.add_launcher_group(invalid_group)
assert result["status"] == "error"
assert "validation" in result["message"]
Cronograma de Desarrollo
Semana 1: Estructura Básica
- Crear
launcher_manager.py
con funciones básicas - Implementar sistema de tabs en
index.html
- Configurar rutas básicas en
app.py
- CSS base para tabs y componentes
Semana 2: Funcionalidad Core
- APIs completas para gestión de grupos
- Editor de grupos funcional
- Carga y listado de scripts
- Ejecución básica de scripts
Semana 3: Mejoras Avanzadas
- Sistema de categorías
- Funcionalidad de favoritos
- Historial de ejecución
- Modal de argumentos
Semana 4: Sistema de Iconos y Pulimiento
- API de iconos
- Iconos por defecto
- Refinamiento de UI
- Testing y correcciones
Semana 5: Testing y Documentación
- Testing completo
- Documentación de usuario
- Optimizaciones de rendimiento
- Deployment y validación final
Notas de Implementación
Consideraciones de Seguridad
- Validación de Rutas: Verificar que los directorios especificados existen y son accesibles
- Sanitización de Argumentos: Limpiar argumentos de línea de comandos para prevenir inyección
- Permisos: Verificar permisos de ejecución en scripts
- Logging de Seguridad: Registrar intentos de acceso a rutas no autorizadas
Optimizaciones de Rendimiento
- Caché de Scripts: Cachear lista de scripts por directorio
- Lazy Loading: Cargar scripts solo cuando se selecciona un grupo
- Debouncing: Evitar búsquedas/filtros excesivos
- Paginación: Para directorios con muchos scripts
Compatibilidad
- Paths Multiplataforma: Usar
os.path
para compatibilidad Windows/Linux - Encoding: UTF-8 para todos los archivos JSON
- Fallbacks: Manejar casos donde faltan dependencias o archivos
Extensibilidad Futura
- Plugins: Sistema para agregar nuevos tipos de launcher
- Temas: Personalización visual
- Integración: APIs para herramientas externas
- Sincronización: Posible sincronización entre múltiples instancias
Recursos de Referencia
Documentación Relevante
Archivos de Referencia del Proyecto
lib/config_manager.py
- Patrón de gestión existentestatic/js/scripts.js
- Implementación JavaScript actualtemplates/index.html
- Estructura HTML base
Autor: Sistema de Desarrollo ParamManagerScripts
Fecha: Enero 2024
Versión: 1.0