From 3b3cfd7062e1b1750bdaaf6b165d763782fe995c Mon Sep 17 00:00:00 2001 From: Miguel Date: Mon, 14 Jul 2025 17:33:37 +0200 Subject: [PATCH] =?UTF-8?q?Se=20implement=C3=B3=20la=20funcionalidad=20par?= =?UTF-8?q?a=20abrir=20archivos=20seleccionados=20desde=20el=20editor=20de?= =?UTF-8?q?=20esquemas,=20a=C3=B1adiendo=20un=20bot=C3=B3n=20"Abrir"=20que?= =?UTF-8?q?=20permite=20al=20usuario=20abrir=20archivos=20con=20la=20aplic?= =?UTF-8?q?aci=C3=B3n=20predeterminada=20del=20sistema.=20Se=20desarroll?= =?UTF-8?q?=C3=B3=20una=20nueva=20ruta=20API=20`/api/open-file`=20para=20m?= =?UTF-8?q?anejar=20la=20apertura=20de=20archivos,=20con=20soporte=20multi?= =?UTF-8?q?plataforma=20para=20Windows,=20macOS=20y=20Linux.=20Adem=C3=A1s?= =?UTF-8?q?,=20se=20realizaron=20mejoras=20en=20la=20interfaz=20de=20usuar?= =?UTF-8?q?io=20para=20habilitar=20el=20bot=C3=B3n=20"Abrir"=20solo=20cuan?= =?UTF-8?q?do=20se=20selecciona=20un=20archivo.=20Se=20actualiz=C3=B3=20la?= =?UTF-8?q?=20documentaci=C3=B3n=20en=20`MemoriaDeEvolucion.md`=20para=20r?= =?UTF-8?q?eflejar=20estos=20cambios.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .doc/MemoriaDeEvolucion.md | 10 + app.py | 878 +++++++++++++++++++++++++------------ data/log.txt | 38 +- static/js/scripts.js | 42 ++ 4 files changed, 680 insertions(+), 288 deletions(-) diff --git a/.doc/MemoriaDeEvolucion.md b/.doc/MemoriaDeEvolucion.md index 0549f76..80f90ba 100644 --- a/.doc/MemoriaDeEvolucion.md +++ b/.doc/MemoriaDeEvolucion.md @@ -58,3 +58,13 @@ El sistema está diseñado para trabajar con directorios específicos donde los - Documentación inicial del sistema ParamManagerScripts - Descripción de la arquitectura y funcionalidades principales - Establecimiento de la estructura de memoria de evolución + +### [2024-12-19] - Implementación de Campos de Tipo Archivo +- **Nueva funcionalidad**: Agregado soporte para campos de tipo "Archivo" en el editor de esquemas +- **Selección de archivos**: Implementado diálogo de selección de archivos con `tkinter.filedialog.askopenfilename` +- **Botón "Abrir"**: Agregado botón adicional para abrir archivos seleccionados con la aplicación predeterminada del sistema +- **Compatibilidad multiplataforma**: Soporte para Windows (`os.startfile`), macOS (`open`) y Linux (`xdg-open`) +- **Validaciones**: Verificación de existencia y tipo de archivo antes de intentar abrirlo +- **Integración UI**: Botón "Abrir" se habilita automáticamente cuando se selecciona un archivo +- **Endpoints API**: Nuevas rutas `/api/browse-files` y `/api/open-file` para manejo de archivos +- **Casos de uso**: Ideal para archivos Excel, Markdown, documentos y cualquier tipo de archivo que requiera apertura rápida diff --git a/app.py b/app.py index 4d2d8e9..d6ec9b0 100644 --- a/app.py +++ b/app.py @@ -8,9 +8,9 @@ import os import json # Added import from datetime import datetime import time # Added for shutdown delay -import sys # Added for platform detection +import sys # Added for platform detection import subprocess # Add this to the imports at the top -import shutil # For shutil.which +import shutil # For shutil.which import glob # For finding workspace files # --- Imports for System Tray Icon --- @@ -47,6 +47,7 @@ BATCH_FLUSH_INTERVAL = 0.5 # segundos broadcast_buffer = [] # Almacena líneas formateadas pendientes de envío buffer_lock = threading.Lock() # Sincroniza acceso al buffer + def _broadcast_flush_loop(): """Hilo que vacía el buffer de logs cada BATCH_FLUSH_INTERVAL segundos.""" while True: @@ -58,6 +59,7 @@ def _broadcast_flush_loop(): broadcast_buffer.clear() _send_batch_to_clients(batch) + def _send_batch_to_clients(batch_message: str): """Envía un bloque de texto a todas las conexiones WebSocket activas.""" dead_connections = set() @@ -69,6 +71,7 @@ def _send_batch_to_clients(batch_message: str): dead_connections.add(ws) websocket_connections.difference_update(dead_connections) + # Iniciar hilo de vaciado en segundo plano (ahora que las dependencias están definidas) flusher_thread = threading.Thread(target=_broadcast_flush_loop, daemon=True) flusher_thread.start() @@ -103,7 +106,7 @@ def broadcast_message(message): while raw_msg.startswith("[") and "]" in raw_msg: try: closing_bracket = raw_msg.index("]") + 1 - if raw_msg[1:closing_bracket - 1].replace(":", "").isdigit(): + if raw_msg[1 : closing_bracket - 1].replace(":", "").isdigit(): raw_msg = raw_msg[closing_bracket:].strip() else: break @@ -142,7 +145,7 @@ def index(): script_groups = config_manager.get_script_groups() # Para ordenar una lista de diccionarios, necesitamos especificar una clave. # Asumimos que cada diccionario tiene una clave 'name' por la cual ordenar. - sorted_script_groups = sorted(script_groups, key=lambda group: group['name']) + sorted_script_groups = sorted(script_groups, key=lambda group: group["name"]) return render_template("index.html", script_groups=sorted_script_groups) @@ -278,6 +281,41 @@ def browse_files(): return jsonify({"status": "cancelled"}) +@app.route("/api/open-file", methods=["POST"]) +def open_file(): + data = request.json + if not data: + return jsonify({"status": "error", "message": "No data provided"}) + + file_path = data.get("path") + if not file_path: + return jsonify({"status": "error", "message": "No file path provided"}) + + if not os.path.exists(file_path): + return jsonify( + {"status": "error", "message": f"File does not exist: {file_path}"} + ) + + if not os.path.isfile(file_path): + return jsonify( + {"status": "error", "message": f"Path is not a file: {file_path}"} + ) + + try: + if sys.platform == "win32": + os.startfile(file_path) + elif sys.platform == "darwin": # macOS + subprocess.Popen(["open", file_path]) + else: # linux variants + subprocess.Popen(["xdg-open", file_path]) + + return jsonify({"status": "success", "message": f"Opening file: {file_path}"}) + except Exception as e: + error_msg = f"Error opening file '{file_path}': {str(e)}" + print(error_msg) + return jsonify({"status": "error", "message": error_msg}), 500 + + @app.route("/api/logs", methods=["GET", "DELETE"]) def handle_logs(): if request.method == "GET": @@ -473,32 +511,65 @@ def open_explorer_route(): # Obtener el directorio de trabajo configurado y normalizado para el grupo configured_work_dir_raw = config_manager.get_work_dir(group) if not configured_work_dir_raw: - return jsonify({"status": "error", "message": f"No hay directorio de trabajo configurado para el grupo '{group}'."}), 404 - + return ( + jsonify( + { + "status": "error", + "message": f"No hay directorio de trabajo configurado para el grupo '{group}'.", + } + ), + 404, + ) + configured_work_dir_abs = os.path.abspath(configured_work_dir_raw) frontend_path_abs = os.path.abspath(frontend_path) # Validar que la ruta del frontend coincide con la ruta configurada if configured_work_dir_abs != frontend_path_abs: - print(f"Intento de acceso no válido: Grupo '{group}', Frontend Path '{frontend_path_abs}', Configured Path '{configured_work_dir_abs}'") - return jsonify({"status": "error", "message": "La ruta proporcionada no coincide con el directorio de trabajo seguro para este grupo."}), 403 + print( + f"Intento de acceso no válido: Grupo '{group}', Frontend Path '{frontend_path_abs}', Configured Path '{configured_work_dir_abs}'" + ) + return ( + jsonify( + { + "status": "error", + "message": "La ruta proporcionada no coincide con el directorio de trabajo seguro para este grupo.", + } + ), + 403, + ) # Validar que la ruta (ahora sabemos que es la configurada) realmente existe if not os.path.isdir(configured_work_dir_abs): - return jsonify({"status": "error", "message": f"El directorio de trabajo configurado '{configured_work_dir_abs}' no es un directorio válido o no existe."}), 400 + return ( + jsonify( + { + "status": "error", + "message": f"El directorio de trabajo configurado '{configured_work_dir_abs}' no es un directorio válido o no existe.", + } + ), + 400, + ) try: if sys.platform == "win32": os.startfile(configured_work_dir_abs) - elif sys.platform == "darwin": # macOS + elif sys.platform == "darwin": # macOS subprocess.Popen(["open", configured_work_dir_abs]) - else: # linux variants + else: # linux variants subprocess.Popen(["xdg-open", configured_work_dir_abs]) - - return jsonify({"status": "success", "message": f"Abriendo '{configured_work_dir_abs}' en el explorador."}) + + return jsonify( + { + "status": "success", + "message": f"Abriendo '{configured_work_dir_abs}' en el explorador.", + } + ) except Exception as e: - error_msg = f"Error al abrir el explorador en '{configured_work_dir_abs}': {str(e)}" - print(error_msg) + error_msg = ( + f"Error al abrir el explorador en '{configured_work_dir_abs}': {str(e)}" + ) + print(error_msg) return jsonify({"status": "error", "message": error_msg}), 500 @@ -575,6 +646,7 @@ def exit_application(icon, item): # === LAUNCHER GUI APIs === + @app.route("/api/launcher-groups", methods=["GET", "POST"]) def handle_launcher_groups(): """Gestionar grupos de launcher (GET: obtener, POST: crear)""" @@ -592,6 +664,7 @@ def handle_launcher_groups(): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/launcher-groups/", methods=["GET", "PUT", "DELETE"]) def handle_launcher_group(group_id): """Gestionar grupo específico (GET: obtener, PUT: actualizar, DELETE: eliminar)""" @@ -617,6 +690,7 @@ def handle_launcher_group(group_id): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/launcher-scripts/") def get_launcher_scripts(group_id): """Obtener scripts de un grupo del launcher""" @@ -626,6 +700,7 @@ def get_launcher_scripts(group_id): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/launcher-scripts-all/") def get_all_launcher_scripts(group_id): """Obtener TODOS los scripts de un grupo (incluyendo ocultos) para gestión""" @@ -635,7 +710,10 @@ def get_all_launcher_scripts(group_id): except Exception as e: return jsonify({"error": str(e)}), 500 -@app.route("/api/launcher-script-metadata//", methods=["GET", "POST"]) + +@app.route( + "/api/launcher-script-metadata//", methods=["GET", "POST"] +) def handle_launcher_script_metadata(group_id, script_name): """Gestionar metadatos de un script específico""" if request.method == "GET": @@ -647,11 +725,14 @@ def handle_launcher_script_metadata(group_id, script_name): else: # POST try: data = request.json - result = launcher_manager.update_script_metadata(group_id, script_name, data) + result = launcher_manager.update_script_metadata( + group_id, script_name, data + ) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/python-environments") def get_python_environments(): """Obtener entornos de Python disponibles""" @@ -661,6 +742,7 @@ def get_python_environments(): 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""" @@ -670,10 +752,17 @@ def execute_gui_script(): script_name = data["script_name"] script_args = data.get("args", []) working_dir = data.get("working_dir", None) - use_pythonw = data.get("use_pythonw", False) # Por defecto python.exe para logging - + use_pythonw = data.get( + "use_pythonw", False + ) # Por defecto python.exe para logging + result = launcher_manager.execute_gui_script( - group_id, script_name, script_args, broadcast_message, working_dir, use_pythonw + group_id, + script_name, + script_args, + broadcast_message, + working_dir, + use_pythonw, ) return jsonify(result) except Exception as e: @@ -681,6 +770,7 @@ def execute_gui_script(): broadcast_message(error_msg) return jsonify({"error": error_msg}), 500 + @app.route("/api/launcher-favorites", methods=["GET", "POST"]) def handle_launcher_favorites(): """Gestionar favoritos del launcher""" @@ -700,6 +790,7 @@ def handle_launcher_favorites(): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/launcher-history", methods=["GET", "DELETE"]) def handle_launcher_history(): """Gestionar historial del launcher""" @@ -716,6 +807,7 @@ def handle_launcher_history(): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/launcher-categories") def get_launcher_categories(): """Obtener categorías disponibles del launcher""" @@ -725,6 +817,7 @@ def get_launcher_categories(): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/group-icon//") def get_group_icon(launcher_type, group_id): """Obtener icono de un grupo (config o launcher)""" @@ -733,27 +826,31 @@ def get_group_icon(launcher_type, group_id): 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): from flask import send_file - return send_file(icon_path, mimetype='image/x-icon') - + + 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): from flask import send_file - return send_file(icon_path, mimetype='image/x-icon') - + + return send_file(icon_path, mimetype="image/x-icon") + # Icono por defecto - devolver datos para que el frontend genere el icono return jsonify({"type": "default", "icon": "📁"}) - + except Exception as e: return jsonify({"error": str(e)}), 500 + # Nuevas APIs para gestión de procesos y Markdown + @app.route("/api/launcher-process-focus/", methods=["POST"]) def focus_launcher_process(pid): """Activar foco de un proceso""" @@ -763,6 +860,7 @@ def focus_launcher_process(pid): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/launcher-process-terminate/", methods=["POST"]) def terminate_launcher_process(pid): """Cerrar un proceso""" @@ -772,6 +870,7 @@ def terminate_launcher_process(pid): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/launcher-running-processes") def get_launcher_running_processes(): """Obtener procesos en ejecución""" @@ -781,6 +880,7 @@ def get_launcher_running_processes(): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/launcher-open-vscode/", methods=["POST"]) def open_launcher_group_in_vscode(group_id): """Abrir grupo del launcher en VS Code""" @@ -788,41 +888,55 @@ def open_launcher_group_in_vscode(group_id): group = launcher_manager.get_launcher_group(group_id) if not group: return jsonify({"status": "error", "message": "Grupo no encontrado"}), 404 - + script_group_path = group["directory"] - + if not os.path.isdir(script_group_path): - return jsonify({ - "status": "error", - "message": f"Directorio del grupo '{group['name']}' no encontrado" - }), 404 - + return ( + jsonify( + { + "status": "error", + "message": f"Directorio del grupo '{group['name']}' no encontrado", + } + ), + 404, + ) + # VS Code executable path - vscode_path = r"C:\Users\migue\AppData\Local\Programs\Microsoft VS Code\Code.exe" - + vscode_path = ( + r"C:\Users\migue\AppData\Local\Programs\Microsoft VS Code\Code.exe" + ) + if not os.path.isfile(vscode_path): - return jsonify({ - "status": "error", - "message": f"VS Code no encontrado en: {vscode_path}" - }), 404 - + return ( + jsonify( + { + "status": "error", + "message": f"VS Code no encontrado en: {vscode_path}", + } + ), + 404, + ) + print(f"Launching VS Code for launcher group: {group['name']}") print(f"Opening directory: {script_group_path}") - + process = subprocess.Popen(f'"{vscode_path}" "{script_group_path}"', shell=True) print(f"Process started with PID: {process.pid}") - - return jsonify({ - "status": "success", - "message": f"VS Code abierto en: {script_group_path}" - }) - + + return jsonify( + {"status": "success", "message": f"VS Code abierto en: {script_group_path}"} + ) + except Exception as e: print(f"Error opening VS Code for launcher group '{group_id}': {str(e)}") - return jsonify({ - "status": "error", - "message": f"Error al abrir VS Code: {str(e)}" - }), 500 + return ( + jsonify( + {"status": "error", "message": f"Error al abrir VS Code: {str(e)}"} + ), + 500, + ) + @app.route("/api/launcher-markdown/") def get_launcher_markdown_files(group_id): @@ -835,6 +949,7 @@ def get_launcher_markdown_files(group_id): # Devolver lista vacía en lugar de error para no interferir con scripts return jsonify({"files": []}) + @app.route("/api/launcher-markdown-content//") def get_launcher_markdown_content(group_id, relative_path): """Obtener contenido de un archivo Markdown""" @@ -844,20 +959,25 @@ def get_launcher_markdown_content(group_id, relative_path): except Exception as e: return jsonify({"error": str(e)}), 500 + # --- Global Error Handler (for debugging unhandled exceptions) --- @app.errorhandler(Exception) def handle_unhandled_exception(e): # Log the error with traceback - app.logger.error('Unhandled Exception: %s', e, exc_info=True) + app.logger.error("Unhandled Exception: %s", e, exc_info=True) # Return a JSON response for API calls, or HTML for others - if request.path.startswith('/api/'): - return jsonify({"status": "error", "message": f"Internal Server Error: {str(e)}"}), 500 + if request.path.startswith("/api/"): + return ( + jsonify({"status": "error", "message": f"Internal Server Error: {str(e)}"}), + 500, + ) else: return "

Internal Server Error

An unhandled error occurred.

", 500 # === C# LAUNCHER APIs === + @app.route("/api/csharp-projects", methods=["GET", "POST"]) def handle_csharp_projects(): """Gestionar proyectos C# (GET: obtener, POST: crear)""" @@ -875,6 +995,7 @@ def handle_csharp_projects(): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/csharp-projects/", methods=["GET", "PUT", "DELETE"]) def handle_csharp_project(project_id): """Gestionar proyecto C# específico (GET: obtener, PUT: actualizar, DELETE: eliminar)""" @@ -900,6 +1021,7 @@ def handle_csharp_project(project_id): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/csharp-executables/") def get_csharp_executables(project_id): """Obtener ejecutables de un proyecto C#""" @@ -909,6 +1031,7 @@ def get_csharp_executables(project_id): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/execute-csharp-executable", methods=["POST"]) def execute_csharp_executable(): """Ejecutar ejecutable C# con argumentos opcionales""" @@ -918,7 +1041,7 @@ def execute_csharp_executable(): exe_name = data["exe_name"] exe_args = data.get("args", []) working_dir = data.get("working_dir", None) - + result = csharp_launcher_manager.execute_csharp_executable( project_id, exe_name, exe_args, broadcast_message, working_dir ) @@ -928,6 +1051,7 @@ def execute_csharp_executable(): broadcast_message(error_msg) return jsonify({"error": error_msg}), 500 + @app.route("/api/csharp-favorites", methods=["GET", "POST"]) def handle_csharp_favorites(): """Gestionar favoritos del launcher C#""" @@ -947,6 +1071,7 @@ def handle_csharp_favorites(): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/csharp-categories") def get_csharp_categories(): """Obtener categorías disponibles del launcher C#""" @@ -956,6 +1081,7 @@ def get_csharp_categories(): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/csharp-running-processes") def get_csharp_running_processes(): """Obtener procesos C# en ejecución""" @@ -965,6 +1091,7 @@ def get_csharp_running_processes(): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/csharp-process-terminate/", methods=["POST"]) def terminate_csharp_process(pid): """Cerrar un proceso C#""" @@ -974,6 +1101,7 @@ def terminate_csharp_process(pid): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/csharp-process-focus/", methods=["POST"]) def focus_csharp_process(pid): """Activar foco de un proceso C#""" @@ -983,29 +1111,41 @@ def focus_csharp_process(pid): except Exception as e: return jsonify({"error": str(e)}), 500 -@app.route("/api/csharp-executable-metadata//", methods=["GET", "PUT"]) + +@app.route( + "/api/csharp-executable-metadata//", methods=["GET", "PUT"] +) def handle_csharp_executable_metadata(project_id, exe_name): """Gestionar metadatos de ejecutables C# (GET: obtener, PUT: actualizar)""" if request.method == "GET": try: - metadata = csharp_launcher_manager.get_executable_metadata(project_id, exe_name) + metadata = csharp_launcher_manager.get_executable_metadata( + project_id, exe_name + ) return jsonify(metadata) except Exception as e: return jsonify({"error": str(e)}), 500 else: # PUT try: data = request.json - result = csharp_launcher_manager.update_executable_metadata(project_id, exe_name, data) + result = csharp_launcher_manager.update_executable_metadata( + project_id, exe_name, data + ) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 -@app.route("/api/csharp-executable-arguments//", methods=["GET", "PUT"]) + +@app.route( + "/api/csharp-executable-arguments//", methods=["GET", "PUT"] +) def handle_csharp_executable_arguments(project_id, exe_name): """Gestionar argumentos predefinidos de ejecutables C#""" if request.method == "GET": try: - arguments = csharp_launcher_manager.get_executable_arguments(project_id, exe_name) + arguments = csharp_launcher_manager.get_executable_arguments( + project_id, exe_name + ) return jsonify({"arguments": arguments}) except Exception as e: return jsonify({"error": str(e)}), 500 @@ -1019,6 +1159,7 @@ def handle_csharp_executable_arguments(project_id, exe_name): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/csharp-all-executables/") def get_all_csharp_executables(project_id): """Obtener todos los ejecutables de un proyecto C# (incluyendo ocultos)""" @@ -1028,34 +1169,38 @@ def get_all_csharp_executables(project_id): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/csharp-solution-file/", methods=["GET"]) def get_csharp_solution_file(project_id): """Obtener archivo de solución (.sln) del proyecto C#""" try: solution_file = csharp_launcher_manager.find_solution_file(project_id) if solution_file: - return jsonify({ - "status": "success", - "solution_file": solution_file, - "exists": True - }) + return jsonify( + {"status": "success", "solution_file": solution_file, "exists": True} + ) else: - return jsonify({ - "status": "success", - "solution_file": None, - "exists": False - }) + return jsonify( + {"status": "success", "solution_file": None, "exists": False} + ) except Exception as e: - return jsonify({ - "status": "error", - "message": f"Error buscando archivo de solución: {str(e)}" - }), 500 + return ( + jsonify( + { + "status": "error", + "message": f"Error buscando archivo de solución: {str(e)}", + } + ), + 500, + ) + # === FIN C# LAUNCHER APIs === # === PYTHON LAUNCHER APIs === + @app.route("/api/python-projects", methods=["GET", "POST"]) def handle_python_projects(): """Gestionar proyectos Python (GET: obtener, POST: crear)""" @@ -1073,6 +1218,7 @@ def handle_python_projects(): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/python-projects/", methods=["GET", "PUT", "DELETE"]) def handle_python_project(project_id): """Gestionar proyecto Python específico (GET: obtener, PUT: actualizar, DELETE: eliminar)""" @@ -1098,6 +1244,7 @@ def handle_python_project(project_id): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/python-scripts/") def get_python_scripts(project_id): """Obtener scripts de un proyecto Python""" @@ -1107,6 +1254,7 @@ def get_python_scripts(project_id): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/python-scripts-all/") def get_all_python_scripts(project_id): """Obtener TODOS los scripts de un proyecto Python (incluyendo ocultos) para gestión""" @@ -1116,23 +1264,31 @@ def get_all_python_scripts(project_id): except Exception as e: return jsonify({"error": str(e)}), 500 -@app.route("/api/python-script-metadata//", methods=["GET", "POST"]) + +@app.route( + "/api/python-script-metadata//", methods=["GET", "POST"] +) def handle_python_script_metadata(project_id, script_name): """Gestionar metadatos de un script Python específico""" if request.method == "GET": try: - metadata = python_launcher_manager.get_script_metadata(project_id, script_name) + metadata = python_launcher_manager.get_script_metadata( + project_id, script_name + ) return jsonify(metadata) except Exception as e: return jsonify({"error": str(e)}), 500 else: # POST try: data = request.json - result = python_launcher_manager.update_script_metadata(project_id, script_name, data) + result = python_launcher_manager.update_script_metadata( + project_id, script_name, data + ) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/execute-python-script", methods=["POST"]) def execute_python_script(): """Ejecutar script Python con argumentos opcionales""" @@ -1142,10 +1298,17 @@ def execute_python_script(): script_name = data["script_name"] script_args = data.get("args", []) working_dir = data.get("working_dir", None) - run_in_background = data.get("run_in_background", False) # Para servidores MCP, Flask, etc. - + run_in_background = data.get( + "run_in_background", False + ) # Para servidores MCP, Flask, etc. + result = python_launcher_manager.execute_python_script( - project_id, script_name, script_args, broadcast_message, working_dir, run_in_background + project_id, + script_name, + script_args, + broadcast_message, + working_dir, + run_in_background, ) return jsonify(result) except Exception as e: @@ -1153,6 +1316,7 @@ def execute_python_script(): broadcast_message(error_msg) return jsonify({"error": error_msg}), 500 + @app.route("/api/python-favorites", methods=["GET", "POST"]) def handle_python_favorites(): """Gestionar favoritos del launcher Python""" @@ -1172,6 +1336,7 @@ def handle_python_favorites(): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/python-history", methods=["GET", "DELETE"]) def handle_python_history(): """Gestionar historial del launcher Python""" @@ -1188,6 +1353,7 @@ def handle_python_history(): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/python-categories") def get_python_categories(): """Obtener categorías disponibles del launcher Python""" @@ -1197,6 +1363,7 @@ def get_python_categories(): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/python-running-processes") def get_python_running_processes(): """Obtener procesos Python en ejecución""" @@ -1206,6 +1373,7 @@ def get_python_running_processes(): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/python-process-terminate/", methods=["POST"]) def terminate_python_process(pid): """Cerrar un proceso Python""" @@ -1215,6 +1383,7 @@ def terminate_python_process(pid): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/python-process-focus/", methods=["POST"]) def focus_python_process(pid): """Activar foco de un proceso Python""" @@ -1224,6 +1393,7 @@ def focus_python_process(pid): except Exception as e: return jsonify({"error": str(e)}), 500 + @app.route("/api/python-markdown/") def get_python_markdown_files(project_id): """Obtener archivos Markdown de un proyecto Python""" @@ -1235,6 +1405,7 @@ def get_python_markdown_files(project_id): # Devolver lista vacía en lugar de error para no interferir con scripts return jsonify({"files": []}) + @app.route("/api/python-markdown-content//") def get_python_markdown_content(project_id, relative_path): """Obtener contenido de un archivo Markdown de un proyecto Python""" @@ -1244,45 +1415,48 @@ def get_python_markdown_content(project_id, relative_path): except Exception as e: return jsonify({"error": str(e)}), 500 + # === FIN PYTHON LAUNCHER APIs === + # --- Helper functions --- def find_workspace_file(directory, editor): """ Busca archivos de workspace específicos en un directorio. - + Args: directory (str): Directorio donde buscar editor (str): Editor ('vscode' o 'cursor') - + Returns: str: Ruta del archivo de workspace encontrado, o None si no hay """ if not os.path.isdir(directory): return None - + # Definir extensiones de archivo según el editor - if editor == 'vscode': - extensions = ['.code-workspace'] - elif editor == 'cursor': + if editor == "vscode": + extensions = [".code-workspace"] + elif editor == "cursor": # Cursor puede usar tanto .cursor-workspace como .code-workspace - extensions = ['.cursor-workspace', '.code-workspace'] + extensions = [".cursor-workspace", ".code-workspace"] else: return None - + # Buscar archivos con las extensiones apropiadas for ext in extensions: pattern = os.path.join(directory, f"*{ext}") workspace_files = glob.glob(pattern) - + if workspace_files: # Si hay múltiples, tomar el primero (o se podría implementar lógica más sofisticada) workspace_file = workspace_files[0] print(f"Found {editor} workspace: {workspace_file}") return workspace_file - + return None + def find_vscode_executable(): """Intenta encontrar el ejecutable de VS Code en ubicaciones comunes y en el PATH.""" # Comprobar la variable de entorno VSCODE_PATH primero (si la defines) @@ -1291,153 +1465,222 @@ def find_vscode_executable(): return vscode_env_path common_paths = [] - local_app_data = os.getenv('LOCALAPPDATA') + local_app_data = os.getenv("LOCALAPPDATA") if local_app_data: - common_paths.append(os.path.join(local_app_data, r"Programs\Microsoft VS Code\Code.exe")) - - common_paths.extend([ - r"C:\Program Files\Microsoft VS Code\Code.exe", - r"C:\Program Files (x86)\Microsoft VS Code\Code.exe", - ]) + common_paths.append( + os.path.join(local_app_data, r"Programs\Microsoft VS Code\Code.exe") + ) + + common_paths.extend( + [ + r"C:\Program Files\Microsoft VS Code\Code.exe", + r"C:\Program Files (x86)\Microsoft VS Code\Code.exe", + ] + ) for path in common_paths: if os.path.isfile(path): return path - return shutil.which("code") # Busca 'code' en el PATH + return shutil.which("code") # Busca 'code' en el PATH + @app.route("/api/open-editor///", methods=["POST"]) def open_group_in_editor(editor, group_system, group_id): """Ruta unificada para abrir grupos en diferentes editores""" try: # Validar editor - if editor not in ['vscode', 'cursor', 'vs2022']: - return jsonify({ - "status": "error", - "message": f"Editor '{editor}' no soportado. Usar 'vscode', 'cursor' o 'vs2022'" - }), 400 + if editor not in ["vscode", "cursor", "vs2022"]: + return ( + jsonify( + { + "status": "error", + "message": f"Editor '{editor}' no soportado. Usar 'vscode', 'cursor' o 'vs2022'", + } + ), + 400, + ) # Determinar directorio según el sistema - if group_system == 'config': - script_group_path = os.path.join(config_manager.script_groups_path, group_id) + if group_system == "config": + script_group_path = os.path.join( + config_manager.script_groups_path, group_id + ) if not os.path.isdir(script_group_path): - return jsonify({ - "status": "error", - "message": f"Directorio del grupo config '{group_id}' no encontrado" - }), 404 - elif group_system == 'launcher': + return ( + jsonify( + { + "status": "error", + "message": f"Directorio del grupo config '{group_id}' no encontrado", + } + ), + 404, + ) + elif group_system == "launcher": group = launcher_manager.get_launcher_group(group_id) if not group: - return jsonify({ - "status": "error", - "message": f"Grupo launcher '{group_id}' no encontrado" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"Grupo launcher '{group_id}' no encontrado", + } + ), + 404, + ) script_group_path = group["directory"] if not os.path.isdir(script_group_path): - return jsonify({ - "status": "error", - "message": f"Directorio del grupo launcher '{group['name']}' no encontrado" - }), 404 - elif group_system == 'csharp': + return ( + jsonify( + { + "status": "error", + "message": f"Directorio del grupo launcher '{group['name']}' no encontrado", + } + ), + 404, + ) + elif group_system == "csharp": project = csharp_launcher_manager.get_csharp_project(group_id) if not project: - return jsonify({ - "status": "error", - "message": f"Proyecto C# '{group_id}' no encontrado" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"Proyecto C# '{group_id}' no encontrado", + } + ), + 404, + ) script_group_path = project["directory"] if not os.path.isdir(script_group_path): - return jsonify({ - "status": "error", - "message": f"Directorio del proyecto C# '{project['name']}' no encontrado" - }), 404 - elif group_system == 'python': + return ( + jsonify( + { + "status": "error", + "message": f"Directorio del proyecto C# '{project['name']}' no encontrado", + } + ), + 404, + ) + elif group_system == "python": project = python_launcher_manager.get_python_project(group_id) if not project: - return jsonify({ - "status": "error", - "message": f"Proyecto Python '{group_id}' no encontrado" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"Proyecto Python '{group_id}' no encontrado", + } + ), + 404, + ) script_group_path = project["directory"] if not os.path.isdir(script_group_path): - return jsonify({ - "status": "error", - "message": f"Directorio del proyecto Python '{project['name']}' no encontrado" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"Directorio del proyecto Python '{project['name']}' no encontrado", + } + ), + 404, + ) else: - return jsonify({ - "status": "error", - "message": f"Sistema de grupo '{group_system}' no válido. Usar 'config', 'launcher', 'csharp' o 'python'" - }), 400 + return ( + jsonify( + { + "status": "error", + "message": f"Sistema de grupo '{group_system}' no válido. Usar 'config', 'launcher', 'csharp' o 'python'", + } + ), + 400, + ) # Definir rutas de ejecutables - if editor == 'vscode': - editor_path = r"C:\Users\migue\AppData\Local\Programs\Microsoft VS Code\Code.exe" + if editor == "vscode": + editor_path = ( + r"C:\Users\migue\AppData\Local\Programs\Microsoft VS Code\Code.exe" + ) editor_name = "VS Code" - elif editor == 'cursor': + elif editor == "cursor": # Rutas comunes donde se instala Cursor possible_cursor_paths = [ r"C:\Users\migue\AppData\Local\Programs\cursor\Cursor.exe", r"C:\Program Files\Cursor\Cursor.exe", - r"C:\Program Files (x86)\Cursor\Cursor.exe" + r"C:\Program Files (x86)\Cursor\Cursor.exe", ] editor_path = None for path in possible_cursor_paths: if os.path.isfile(path): editor_path = path break - + if not editor_path: # Intentar buscar en PATH editor_path = shutil.which("cursor") - + if not editor_path: - return jsonify({ - "status": "error", - "message": f"Cursor no encontrado. Intenté en: {', '.join(possible_cursor_paths)}" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"Cursor no encontrado. Intenté en: {', '.join(possible_cursor_paths)}", + } + ), + 404, + ) editor_name = "Cursor" - elif editor == 'vs2022': + elif editor == "vs2022": # Rutas comunes para Visual Studio 2022 possible_vs_paths = [ r"C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\devenv.exe", r"C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\devenv.exe", r"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\devenv.exe", - r"C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\Common7\IDE\devenv.exe" + r"C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\Common7\IDE\devenv.exe", ] editor_path = None for path in possible_vs_paths: if os.path.isfile(path): editor_path = path break - + if not editor_path: - return jsonify({ - "status": "error", - "message": f"Visual Studio 2022 no encontrado. Intenté en: {', '.join(possible_vs_paths)}" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"Visual Studio 2022 no encontrado. Intenté en: {', '.join(possible_vs_paths)}", + } + ), + 404, + ) editor_name = "Visual Studio 2022" # Verificar que el ejecutable existe if not os.path.isfile(editor_path): - return jsonify({ - "status": "error", - "message": f"{editor_name} no encontrado en: {editor_path}" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"{editor_name} no encontrado en: {editor_path}", + } + ), + 404, + ) print(f"Launching {editor_name} from: {editor_path}") print(f"Opening directory: {script_group_path}") # Buscar archivos de workspace específicos o archivos de solución target_to_open = script_group_path - + # Para VSCode y Cursor, buscar archivos de workspace - if editor in ['vscode', 'cursor']: + if editor in ["vscode", "cursor"]: workspace_file = find_workspace_file(script_group_path, editor) if workspace_file: target_to_open = workspace_file print(f"Found {editor} workspace file: {workspace_file}") - + # Para Visual Studio 2022 y proyectos C#, intentar abrir archivo .sln específico - elif editor == 'vs2022' and group_system == 'csharp': + elif editor == "vs2022" and group_system == "csharp": solution_file = csharp_launcher_manager.find_solution_file(group_id) if solution_file: target_to_open = solution_file @@ -1447,74 +1690,121 @@ def open_group_in_editor(editor, group_system, group_id): process = subprocess.Popen(f'"{editor_path}" "{target_to_open}"', shell=True) print(f"{editor_name} process started with PID: {process.pid}") - return jsonify({ - "status": "success", - "message": f"{editor_name} abierto en: {target_to_open}" - }) + return jsonify( + { + "status": "success", + "message": f"{editor_name} abierto en: {target_to_open}", + } + ) except Exception as e: print(f"Error opening {editor} for {group_system} group '{group_id}': {str(e)}") - return jsonify({ - "status": "error", - "message": f"Error al abrir {editor}: {str(e)}" - }), 500 + return ( + jsonify( + {"status": "error", "message": f"Error al abrir {editor}: {str(e)}"} + ), + 500, + ) + @app.route("/api/open-group-folder//", methods=["POST"]) def open_group_folder(group_system, group_id): """Abrir carpeta de un grupo en el explorador de archivos""" try: # Determinar directorio según el sistema - if group_system == 'config': - script_group_path = os.path.join(config_manager.script_groups_path, group_id) + if group_system == "config": + script_group_path = os.path.join( + config_manager.script_groups_path, group_id + ) if not os.path.isdir(script_group_path): - return jsonify({ - "status": "error", - "message": f"Directorio del grupo config '{group_id}' no encontrado" - }), 404 - elif group_system == 'launcher': + return ( + jsonify( + { + "status": "error", + "message": f"Directorio del grupo config '{group_id}' no encontrado", + } + ), + 404, + ) + elif group_system == "launcher": group = launcher_manager.get_launcher_group(group_id) if not group: - return jsonify({ - "status": "error", - "message": f"Grupo launcher '{group_id}' no encontrado" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"Grupo launcher '{group_id}' no encontrado", + } + ), + 404, + ) script_group_path = group["directory"] if not os.path.isdir(script_group_path): - return jsonify({ - "status": "error", - "message": f"Directorio del grupo launcher '{group['name']}' no encontrado" - }), 404 - elif group_system == 'csharp': + return ( + jsonify( + { + "status": "error", + "message": f"Directorio del grupo launcher '{group['name']}' no encontrado", + } + ), + 404, + ) + elif group_system == "csharp": project = csharp_launcher_manager.get_csharp_project(group_id) if not project: - return jsonify({ - "status": "error", - "message": f"Proyecto C# '{group_id}' no encontrado" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"Proyecto C# '{group_id}' no encontrado", + } + ), + 404, + ) script_group_path = project["directory"] if not os.path.isdir(script_group_path): - return jsonify({ - "status": "error", - "message": f"Directorio del proyecto C# '{project['name']}' no encontrado" - }), 404 - elif group_system == 'python': + return ( + jsonify( + { + "status": "error", + "message": f"Directorio del proyecto C# '{project['name']}' no encontrado", + } + ), + 404, + ) + elif group_system == "python": project = python_launcher_manager.get_python_project(group_id) if not project: - return jsonify({ - "status": "error", - "message": f"Proyecto Python '{group_id}' no encontrado" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"Proyecto Python '{group_id}' no encontrado", + } + ), + 404, + ) script_group_path = project["directory"] if not os.path.isdir(script_group_path): - return jsonify({ - "status": "error", - "message": f"Directorio del proyecto Python '{project['name']}' no encontrado" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"Directorio del proyecto Python '{project['name']}' no encontrado", + } + ), + 404, + ) else: - return jsonify({ - "status": "error", - "message": f"Sistema de grupo '{group_system}' no válido. Usar 'config', 'launcher', 'csharp' o 'python'" - }), 400 + return ( + jsonify( + { + "status": "error", + "message": f"Sistema de grupo '{group_system}' no válido. Usar 'config', 'launcher', 'csharp' o 'python'", + } + ), + 400, + ) # Abrir en el explorador según el sistema operativo try: @@ -1524,93 +1814,143 @@ def open_group_folder(group_system, group_id): subprocess.Popen(["open", script_group_path]) else: # linux variants subprocess.Popen(["xdg-open", script_group_path]) - - return jsonify({ - "status": "success", - "message": f"Abriendo '{script_group_path}' en el explorador", - "path": script_group_path - }) + + return jsonify( + { + "status": "success", + "message": f"Abriendo '{script_group_path}' en el explorador", + "path": script_group_path, + } + ) except Exception as e: - return jsonify({ - "status": "error", - "message": f"Error al abrir el explorador en '{script_group_path}': {str(e)}" - }), 500 + return ( + jsonify( + { + "status": "error", + "message": f"Error al abrir el explorador en '{script_group_path}': {str(e)}", + } + ), + 500, + ) except Exception as e: print(f"Error opening folder for {group_system} group '{group_id}': {str(e)}") - return jsonify({ - "status": "error", - "message": f"Error al abrir carpeta: {str(e)}" - }), 500 + return ( + jsonify( + {"status": "error", "message": f"Error al abrir carpeta: {str(e)}"} + ), + 500, + ) + @app.route("/api/get-group-path//", methods=["GET"]) def get_group_path(group_system, group_id): """Obtener el path completo de un grupo de scripts""" try: # Determinar directorio según el sistema - if group_system == 'config': - script_group_path = os.path.join(config_manager.script_groups_path, group_id) + if group_system == "config": + script_group_path = os.path.join( + config_manager.script_groups_path, group_id + ) if not os.path.isdir(script_group_path): - return jsonify({ - "status": "error", - "message": f"Directorio del grupo config '{group_id}' no encontrado" - }), 404 - elif group_system == 'launcher': + return ( + jsonify( + { + "status": "error", + "message": f"Directorio del grupo config '{group_id}' no encontrado", + } + ), + 404, + ) + elif group_system == "launcher": group = launcher_manager.get_launcher_group(group_id) if not group: - return jsonify({ - "status": "error", - "message": f"Grupo launcher '{group_id}' no encontrado" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"Grupo launcher '{group_id}' no encontrado", + } + ), + 404, + ) script_group_path = group["directory"] if not os.path.isdir(script_group_path): - return jsonify({ - "status": "error", - "message": f"Directorio del grupo launcher '{group['name']}' no encontrado" - }), 404 - elif group_system == 'csharp': + return ( + jsonify( + { + "status": "error", + "message": f"Directorio del grupo launcher '{group['name']}' no encontrado", + } + ), + 404, + ) + elif group_system == "csharp": project = csharp_launcher_manager.get_csharp_project(group_id) if not project: - return jsonify({ - "status": "error", - "message": f"Proyecto C# '{group_id}' no encontrado" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"Proyecto C# '{group_id}' no encontrado", + } + ), + 404, + ) script_group_path = project["directory"] if not os.path.isdir(script_group_path): - return jsonify({ - "status": "error", - "message": f"Directorio del proyecto C# '{project['name']}' no encontrado" - }), 404 - elif group_system == 'python': + return ( + jsonify( + { + "status": "error", + "message": f"Directorio del proyecto C# '{project['name']}' no encontrado", + } + ), + 404, + ) + elif group_system == "python": project = python_launcher_manager.get_python_project(group_id) if not project: - return jsonify({ - "status": "error", - "message": f"Proyecto Python '{group_id}' no encontrado" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"Proyecto Python '{group_id}' no encontrado", + } + ), + 404, + ) script_group_path = project["directory"] if not os.path.isdir(script_group_path): - return jsonify({ - "status": "error", - "message": f"Directorio del proyecto Python '{project['name']}' no encontrado" - }), 404 + return ( + jsonify( + { + "status": "error", + "message": f"Directorio del proyecto Python '{project['name']}' no encontrado", + } + ), + 404, + ) else: - return jsonify({ - "status": "error", - "message": f"Sistema de grupo '{group_system}' no válido. Usar 'config', 'launcher', 'csharp' o 'python'" - }), 400 + return ( + jsonify( + { + "status": "error", + "message": f"Sistema de grupo '{group_system}' no válido. Usar 'config', 'launcher', 'csharp' o 'python'", + } + ), + 400, + ) - return jsonify({ - "status": "success", - "path": script_group_path - }) + return jsonify({"status": "success", "path": script_group_path}) except Exception as e: print(f"Error getting path for {group_system} group '{group_id}': {str(e)}") - return jsonify({ - "status": "error", - "message": f"Error al obtener path: {str(e)}" - }), 500 + return ( + jsonify({"status": "error", "message": f"Error al obtener path: {str(e)}"}), + 500, + ) + if __name__ == "__main__": # --- Start Flask in a background thread --- diff --git a/data/log.txt b/data/log.txt index a3eb0e5..52eb263 100644 --- a/data/log.txt +++ b/data/log.txt @@ -1,19 +1,19 @@ -[16:52:51] Iniciando ejecución de x3_excel_to_md.py en C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\Analisis\Siemens... -[16:52:52] === Conversión de archivos Excel a Markdown === -[16:52:52] 1. Convirtiendo Excel de tags de TIA Portal... -[16:52:52] Usando directorio de trabajo: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\Analisis\Siemens -[16:52:52] Configuración de paths cargada desde: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\Analisis\Siemens\io_paths_config.json -[16:52:52] Buscando archivos Excel en: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\IOTags -[16:52:52] Archivo Excel encontrado automáticamente: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\IOTags\All_PLCTags.xlsx -[16:52:52] Procesando archivo Excel: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\IOTags\All_PLCTags.xlsx... -[16:52:52] Paths configurados para procesar: ['Inputs', 'Outputs', 'OutputsFesto', 'IO Not in Hardware\\InputsMaster', 'IO Not in Hardware\\OutputsMaster'] -[16:52:53] ¡Éxito! Archivo Markdown generado en: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\Analisis\Siemens\Resultados\Master IO Tags.md -[16:52:53] ================================================== -[16:52:53] 2. Convirtiendo Excel de IO desde esquema eléctrico... -[16:52:53] Usando directorio de trabajo: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\Analisis\Siemens -[16:52:53] Procesando archivo Excel de IO: C:/Trabajo/SIDEL/13 - E5.007560 - Modifica O&U - SAE235/Reporte/IO.xlsx... -[16:52:53] Columnas encontradas en el Excel: ['Unnamed: 0', 'Unnamed: 1', 'Unnamed: 2', 'Unnamed: 3', 'Unnamed: 4', 'Unnamed: 5'] -[16:52:53] ¡Éxito! Archivo Markdown de IO generado en: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\Analisis\Siemens\Resultados\Hardware_ED.md -[16:52:53] === Proceso completado === -[16:52:53] Ejecución de x3_excel_to_md.py finalizada (success). Duración: 0:00:01.794236. -[16:52:53] Log completo guardado en: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\IO_adaptation\.log\log_x3_excel_to_md.txt +[17:10:45] Iniciando ejecución de x3_excel_to_md.py en C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\Analisis\Siemens... +[17:10:46] === Conversión de archivos Excel a Markdown === +[17:10:46] 1. Convirtiendo Excel de tags de TIA Portal... +[17:10:46] Usando directorio de trabajo: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\Analisis\Siemens +[17:10:46] Configuración de paths cargada desde: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\Analisis\Siemens\io_paths_config.json +[17:10:46] Buscando archivos Excel en: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\IOTags +[17:10:46] Archivo Excel encontrado automáticamente: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\IOTags\All_PLCTags.xlsx +[17:10:46] Procesando archivo Excel: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\IOTags\All_PLCTags.xlsx... +[17:10:46] Paths configurados para procesar: ['Inputs', 'Outputs', 'OutputsFesto', 'IO Not in Hardware\\InputsMaster', 'IO Not in Hardware\\OutputsMaster'] +[17:10:47] ¡Éxito! Archivo Markdown generado en: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\Analisis\Siemens\Resultados\Master IO Tags.md +[17:10:47] ================================================== +[17:10:47] 2. Convirtiendo Excel de IO desde esquema eléctrico... +[17:10:47] Usando directorio de trabajo: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\Analisis\Siemens +[17:10:47] Procesando archivo Excel de IO: C:/Trabajo/SIDEL/13 - E5.007560 - Modifica O&U - SAE235/Reporte/IO.xlsx... +[17:10:47] Columnas encontradas en el Excel: ['Master TAG', 'IO', 'Sensor', 'Descripcion', 'Descripcion.1'] +[17:10:47] ¡Éxito! Archivo Markdown de IO generado en: C:\Trabajo\SIDEL\13 - E5.007560 - Modifica O&U - SAE235\Reporte\Analisis\Siemens\Resultados\Hardware_ED.md +[17:10:47] === Proceso completado === +[17:10:47] Ejecución de x3_excel_to_md.py finalizada (success). Duración: 0:00:01.379917. +[17:10:47] Log completo guardado en: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\IO_adaptation\.log\log_x3_excel_to_md.txt diff --git a/static/js/scripts.js b/static/js/scripts.js index 4d5bac4..6b9519d 100644 --- a/static/js/scripts.js +++ b/static/js/scripts.js @@ -406,6 +406,13 @@ function generateInputField(def, key, value, level) { data-key="${key}"> Buscar... + `; } if (def.enum) { @@ -467,6 +474,12 @@ async function browseFieldFile(button) { bubbles: true }); input.dispatchEvent(event); + + // Habilitar el botón "Abrir" después de seleccionar un archivo + const openButton = button.parentElement.querySelector('button[onclick="openFieldFile(this)"]'); + if (openButton) { + openButton.disabled = false; + } } } catch (error) { console.error('Error browsing file:', error); @@ -474,6 +487,35 @@ async function browseFieldFile(button) { } } +// Agregar función para abrir el archivo seleccionado +async function openFieldFile(button) { + const input = button.parentElement.querySelector('input'); + const filePath = input.value; + + if (!filePath || filePath.trim() === '') { + alert('No hay archivo seleccionado para abrir'); + return; + } + + try { + const response = await fetch('/api/open-file', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ path: filePath }) + }); + + const result = await response.json(); + if (result.status === 'success') { + console.log('Archivo abierto correctamente'); + } else { + alert(`Error al abrir el archivo: ${result.message}`); + } + } catch (error) { + console.error('Error opening file:', error); + alert('Error al abrir el archivo'); + } +} + async function modifySchema(level) { try { console.log('Loading schema for level:', level); // Debug line