diff --git a/__pycache__/config_manager.cpython-310.pyc b/__pycache__/config_manager.cpython-310.pyc index 669b292..fe74599 100644 Binary files a/__pycache__/config_manager.cpython-310.pyc and b/__pycache__/config_manager.cpython-310.pyc differ diff --git a/backend/__pycache__/script_utils.cpython-310.pyc b/backend/__pycache__/script_utils.cpython-310.pyc new file mode 100644 index 0000000..951067f Binary files /dev/null and b/backend/__pycache__/script_utils.cpython-310.pyc differ diff --git a/backend/script_groups/CSharpCodeMerger/x1.py b/backend/script_groups/CSharpCodeMerger/x1.py index 00716ac..b718cfb 100644 --- a/backend/script_groups/CSharpCodeMerger/x1.py +++ b/backend/script_groups/CSharpCodeMerger/x1.py @@ -10,6 +10,11 @@ from pathlib import Path from dataclasses import dataclass from typing import List, Dict, Optional, Tuple import difflib +script_root = os.path.dirname( + os.path.dirname(os.path.dirname(os.path.dirname(__file__))) +) +sys.path.append(script_root) +from backend.script_utils import load_configuration # Forzar UTF-8 en la salida estándar sys.stdout.reconfigure(encoding="utf-8") @@ -257,7 +262,8 @@ class CSharpCodeMerger: return ''.join(diff) def main(): - configs = json.loads(os.environ.get("SCRIPT_CONFIGS", "{}")) + # configs = json.loads(os.environ.get("SCRIPT_CONFIGS", "{}")) + configs = load_configuration() working_directory = configs.get("working_directory", ".") work_config = configs.get("level3", {}) diff --git a/backend/script_groups/EmailCrono/x1.py b/backend/script_groups/EmailCrono/x1.py index 0fe3c86..37c847c 100644 --- a/backend/script_groups/EmailCrono/x1.py +++ b/backend/script_groups/EmailCrono/x1.py @@ -9,6 +9,11 @@ from utils.email_parser import procesar_eml from utils.markdown_handler import cargar_cronologia_existente from utils.beautify import BeautifyProcessor import json +script_root = os.path.dirname( + os.path.dirname(os.path.dirname(os.path.dirname(__file__))) +) +sys.path.append(script_root) +from backend.script_utils import load_configuration # Forzar UTF-8 en la salida estándar sys.stdout.reconfigure(encoding="utf-8") @@ -29,7 +34,8 @@ def generar_indice(mensajes): def main(): # Cargar configuraciones del entorno - configs = json.loads(os.environ.get("SCRIPT_CONFIGS", "{}")) + # configs = json.loads(os.environ.get("SCRIPT_CONFIGS", "{}")) + configs = load_configuration() # Obtener working directory working_directory = configs.get("working_directory", ".") diff --git a/backend/script_groups/ImportHTML/x1.py b/backend/script_groups/ImportHTML/x1.py index 8fd67ea..051172d 100644 --- a/backend/script_groups/ImportHTML/x1.py +++ b/backend/script_groups/ImportHTML/x1.py @@ -8,6 +8,11 @@ from pathlib import Path import json from utils.html_parser import procesar_html from utils.markdown_handler import escribir_archivo_markdown +script_root = os.path.dirname( + os.path.dirname(os.path.dirname(os.path.dirname(__file__))) +) +sys.path.append(script_root) +from backend.script_utils import load_configuration # Verificar si la biblioteca mammoth está disponible para procesamiento DOCX try: @@ -31,7 +36,8 @@ ERROR_SYMBOL = "[ERROR]" def main(): # Cargar configuraciones del entorno - configs = json.loads(os.environ.get("SCRIPT_CONFIGS", "{}")) + # configs = json.loads(os.environ.get("SCRIPT_CONFIGS", "{}")) + configs = load_configuration() # Obtener working directory working_directory = configs.get("working_directory", ".") diff --git a/backend/script_groups/example_group/script_config.json b/backend/script_groups/example_group/script_config.json new file mode 100644 index 0000000..8f70403 --- /dev/null +++ b/backend/script_groups/example_group/script_config.json @@ -0,0 +1,15 @@ +{ + "level1": { + "api_key": "your-api-key-here", + "model": "gpt-3.5-turbo" + }, + "level2": { + "input_dir": "D:/Datos/Entrada", + "output_dir": "D:/Datos/Salida", + "batch_size": 50 + }, + "level3": { + "in_dir": "ingesta" + }, + "working_directory": "C:\\Estudio" +} \ No newline at end of file diff --git a/backend/script_groups/example_group/x1.py b/backend/script_groups/example_group/x1.py index 6dfde88..6072b2f 100644 --- a/backend/script_groups/example_group/x1.py +++ b/backend/script_groups/example_group/x1.py @@ -5,24 +5,35 @@ Este script demuestra cómo acceder a las configuraciones de los tres niveles. import json import os +import sys import time + +script_root = os.path.dirname( + os.path.dirname(os.path.dirname(os.path.dirname(__file__))) +) +sys.path.append(script_root) +from backend.script_utils import load_configuration + + def main(): # Cargar configuraciones desde variable de entorno - configs = json.loads(os.environ.get('SCRIPT_CONFIGS', '{}')) - + # configs = json.loads(os.environ.get('SCRIPT_CONFIGS', '{}')) + configs = load_configuration() + print("=== Ejecutando Script de Prueba 1 ===") print("\nConfiguraciones cargadas:") - print("Nivel 1:", json.dumps(configs.get('level1', {}), indent=2)) - print("Nivel 2:", json.dumps(configs.get('level2', {}), indent=2)) - print("Nivel 3:", json.dumps(configs.get('level3', {}), indent=2)) - + print("Nivel 1:", json.dumps(configs.get("level1", {}), indent=2)) + print("Nivel 2:", json.dumps(configs.get("level2", {}), indent=2)) + print("Nivel 3:", json.dumps(configs.get("level3", {}), indent=2)) + print("\nSimulando procesamiento...") for i in range(5): time.sleep(1) print(f"Progreso: {(i+1)*20}%") - + print("\n¡Proceso completado!") -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/backend/script_groups/example_group/x2.py b/backend/script_groups/example_group/x2.py index cd17f26..d01a97d 100644 --- a/backend/script_groups/example_group/x2.py +++ b/backend/script_groups/example_group/x2.py @@ -6,10 +6,17 @@ Demuestra el manejo de errores y logging. import json import os import time +import sys import random +script_root = os.path.dirname( + os.path.dirname(os.path.dirname(os.path.dirname(__file__))) +) +sys.path.append(script_root) +from backend.script_utils import load_configuration def main(): - configs = json.loads(os.environ.get('SCRIPT_CONFIGS', '{}')) + # configs = json.loads(os.environ.get('SCRIPT_CONFIGS', '{}')) + configs = load_configuration() print("=== Ejecutando Script de Prueba 2 ===") print("\nIniciando análisis de datos simulado...") diff --git a/backend/script_utils.py b/backend/script_utils.py new file mode 100644 index 0000000..ad52c41 --- /dev/null +++ b/backend/script_utils.py @@ -0,0 +1,43 @@ +import os +import json +from typing import Dict, Any + + +def load_configuration() -> Dict[str, Any]: + """ + Load configuration from script_config.json in the current script directory. + + Returns: + Dict containing configurations with levels 1, 2, 3 and working_directory + + Example usage in scripts: + from script_utils import load_configuration + + configs = load_configuration() + level1_config = configs.get("level1", {}) + level2_config = configs.get("level2", {}) + level3_config = configs.get("level3", {}) + working_dir = configs.get("working_directory", "") + """ + try: + # Get directory of the calling script + script_dir = os.path.dirname(os.path.abspath(__file__)) + + # Path to the config file + config_file_path = os.path.join(script_dir, "script_config.json") + + # Check if file exists + if not os.path.exists(config_file_path): + print(f"Configuration file not found: {config_file_path}") + return {} + + # Load and parse the JSON file + with open(config_file_path, "r", encoding="utf-8") as f: + return json.load(f) + + except json.JSONDecodeError as e: + print(f"Error parsing configuration file: {str(e)}") + return {} + except Exception as e: + print(f"Error loading configuration: {str(e)}") + return {} diff --git a/config_manager.py b/config_manager.py index aedcc56..4c4a301 100644 --- a/config_manager.py +++ b/config_manager.py @@ -379,6 +379,7 @@ class ConfigurationManager: self.last_execution_time = current_time script_path = os.path.join(self.script_groups_path, group, script_name) + script_dir = os.path.dirname(script_path) if not os.path.exists(script_path): if broadcast_fn: @@ -400,11 +401,22 @@ class ConfigurationManager: "working_directory": working_dir, } + # Save configurations to a JSON file in the script directory + config_file_path = os.path.join(script_dir, "script_config.json") + try: + with open(config_file_path, "w", encoding="utf-8") as f: + json.dump(configs, f, indent=2, ensure_ascii=False) + if broadcast_fn: + broadcast_fn(f"Configuraciones guardadas en {config_file_path}") + except Exception as e: + if broadcast_fn: + broadcast_fn(f"Error guardando configuraciones: {str(e)}") + try: if broadcast_fn: broadcast_fn(f"Iniciando ejecución de {script_name}") - # Execute script with configured environment + # Execute script with configured environment (removed SCRIPT_CONFIGS env var) process = subprocess.Popen( ["python", script_path], cwd=working_dir, @@ -414,7 +426,7 @@ class ConfigurationManager: bufsize=1, env=dict( os.environ, - SCRIPT_CONFIGS=json.dumps(configs), + # SCRIPT_CONFIGS=json.dumps(configs), # Commented out as we now use a file PYTHONIOENCODING="utf-8", ), ) @@ -468,7 +480,7 @@ class ConfigurationManager: """Set working directory path for a script group and update history.""" # Normalizar el path recibido path = os.path.normpath(path) - + if not os.path.exists(path): return {"status": "error", "message": "Directory does not exist"} @@ -493,7 +505,9 @@ class ConfigurationManager: data["history"] = [] # Eliminar la ruta del historial si ya existe (usando path normalizado) - data["history"] = [p for p in data["history"] if os.path.normpath(p) != path] + data["history"] = [ + p for p in data["history"] if os.path.normpath(p) != path + ] # Agregar la ruta al principio del historial data["history"].insert(0, path) diff --git a/data/log.txt b/data/log.txt index 2e9c8ea..b5a0582 100644 --- a/data/log.txt +++ b/data/log.txt @@ -1,18 +1,16 @@ -[13:09:20] Iniciando ejecución de x1.py -[13:09:21] Working directory: C:\Users\migue\Downloads\Nueva carpeta (7) -[13:09:21] Input directory: C:\Users\migue\Downloads\Nueva carpeta (7) -[13:09:21] Output directory: . -[13:09:21] Output file: .\contenidoImportado.md -[13:09:21] Attachments directory: .\adjuntos -[13:09:21] Found 0 HTML files -[13:09:21] Found 1 DOCX files -[13:09:21] Processing DOCX [1/1] C:\Users\migue\Downloads\Nueva carpeta (7)\TIA Portal Ethernet communication Rules V0.1.docx -[13:09:22] Error procesando DOCX C:\Users\migue\Downloads\Nueva carpeta (7)\TIA Portal Ethernet communication Rules V0.1.docx: convert_document_element_to_html() got an unexpected keyword argument 'options' -[13:09:22] [ERROR] Failed: Error al procesar: convert_document_element_to_html() got an unexpected keyword argument 'options' -[13:09:22] Summary: -[13:09:22] - Total files: 1 -[13:09:22] - Successfully processed: 0 -[13:09:22] - Failed: 1 -[13:09:22] Writing 1 pages to .\contenidoImportado.md -[13:09:22] Markdown file created successfully. -[13:09:23] Ejecución completada +[21:32:28] Configuraciones guardadas en d:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\example_group\script_config.json +[21:32:28] Iniciando ejecución de x1.py +[21:32:33] Configuration file not found: d:\Proyectos\Scripts\ParamManagerScripts\backend\script_config.json +[21:32:33] === Ejecutando Script de Prueba 1 === +[21:32:33] Configuraciones cargadas: +[21:32:33] Nivel 1: {} +[21:32:33] Nivel 2: {} +[21:32:33] Nivel 3: {} +[21:32:33] Simulando procesamiento... +[21:32:33] Progreso: 20% +[21:32:33] Progreso: 40% +[21:32:33] Progreso: 60% +[21:32:33] Progreso: 80% +[21:32:33] Progreso: 100% +[21:32:33] ¡Proceso completado! +[21:32:33] Ejecución completada diff --git a/services/llm/openai_api_key.py b/services/llm/openai_api_key.py index 39f5b3a..8f78127 100644 --- a/services/llm/openai_api_key.py +++ b/services/llm/openai_api_key.py @@ -1,3 +1,3 @@ # Configura tu clave API de OpenAI def openai_api_key(): - return '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' \ No newline at end of file + return '' \ No newline at end of file diff --git a/static/js/scripts.js b/static/js/scripts.js index 35c9499..01f5641 100644 --- a/static/js/scripts.js +++ b/static/js/scripts.js @@ -1,18 +1,43 @@ -let socket; let currentGroup; // Initialize WebSocket connection +let socket = null; // Define socket en un alcance accesible (p.ej., globalmente o en el scope del módulo) + function initWebSocket() { - socket = new WebSocket(`ws://${location.host}/ws`); - socket.onmessage = function(event) { - addLogLine(event.data); + // Comprobar si ya existe un socket y está abierto o conectándose + if (socket && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING)) { + console.log("WebSocket ya está abierto o conectándose."); + return; // No crear una nueva conexión + } + + // Determinar URL del WebSocket (ws:// o wss://) + const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + const wsUrl = `${wsProtocol}//${window.location.host}/ws`; + + console.log("Inicializando conexión WebSocket a:", wsUrl); + socket = new WebSocket(wsUrl); + + socket.onopen = () => { + console.log('Conexión WebSocket establecida'); }; - socket.onclose = function() { - console.log('WebSocket cerrado, intentando reconexión...'); - setTimeout(initWebSocket, 1000); + + socket.onmessage = (event) => { + // console.log('Mensaje del servidor:', event.data); // Opcional: para depuración + addLogLine(event.data); // Llama a la función que ya tienes }; - socket.onerror = function(error) { - console.error('Error en WebSocket:', error); + + socket.onerror = (error) => { + console.error('Error WebSocket:', error); + addLogLine('Error de conexión WebSocket.'); // Informar al usuario + }; + + socket.onclose = (event) => { + console.log('Conexión WebSocket cerrada:', event.code, event.reason); + // Opcional: intentar reconectar o informar al usuario + if (!event.wasClean) { + addLogLine('Conexión WebSocket perdida. Intente recargar la página.'); + } + socket = null; // Restablecer la variable socket después de cerrar }; } @@ -80,17 +105,37 @@ async function loadScripts(group) { // Execute a script async function executeScript(scriptName) { - addLogLine(`\nEjecutando script: ${scriptName}...\n`); + // REMOVE this line - let the backend log the start via WebSocket + // addLogLine(`\nEjecutando script: ${scriptName}...\n`); - const response = await fetch('/api/execute_script', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ group: currentGroup, script: scriptName }) - }); + try { + const response = await fetch('/api/execute_script', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ group: currentGroup, script: scriptName }) + }); - const result = await response.json(); - if (result.error) { - addLogLine(`\nError: ${result.error}\n`); + // Check for HTTP errors during the *request* itself + if (!response.ok) { + const errorText = await response.text(); + console.error(`Error initiating script execution request: ${response.status} ${response.statusText}`, errorText); + // Log only the request error, not script execution errors which come via WebSocket + addLogLine(`\nError al iniciar la petición del script: ${response.status} ${errorText}\n`); + return; // Stop if the request failed + } + + // REMOVE logging the result/error here - let the backend log via WebSocket + // const result = await response.json(); // Still potentially need to read body if not done elsewhere + // if (result.error) { + // addLogLine(`\nError: ${result.error}\n`); + // } + + // Script output and final status/errors will arrive via WebSocket messages + // handled by socket.onmessage -> addLogLine + + } catch (error) { + console.error('Error in executeScript fetch:', error); + addLogLine(`\nError de red o JavaScript al intentar ejecutar el script: ${error.message}\n`); } } @@ -755,17 +800,15 @@ function getTimestamp() { // Función para agregar línea al log con timestamp function addLogLine(message) { const logArea = document.getElementById('log-area'); - const timestamp = getTimestamp(); - - // Filtrar líneas vacías y aplicar timestamp solo a líneas con contenido - const lines = message.split('\n') - .filter(line => line.trim()) // Eliminar líneas vacías - .map(line => `[${timestamp}] ${line}`) - .join('\n'); - - if (lines) { - logArea.innerHTML += lines + '\n'; - logArea.scrollTop = logArea.scrollHeight; + + // Message from WebSocket should already have timestamp. + // Trim any extra whitespace just in case. + const cleanMessage = String(message).trim(); + + if (cleanMessage) { + // Append the cleaned message + a newline for display separation. + logArea.innerHTML += cleanMessage + '\n'; + logArea.scrollTop = logArea.scrollHeight; // Ensure scroll to bottom } } diff --git a/utils/config_loader.py b/utils/config_loader.py new file mode 100644 index 0000000..e69de29