Varias mejoras en el grupo de IO Adapted. Creado boton para abrir vscode y miniconda para instalar nuevas librerias
This commit is contained in:
parent
f6ae6f4f82
commit
7f5d7ff033
197
app.py
197
app.py
|
@ -4,13 +4,14 @@ from lib.config_manager import ConfigurationManager
|
|||
import os
|
||||
import json # Added import
|
||||
from datetime import datetime
|
||||
import time # Added for shutdown delay
|
||||
import time # Added for shutdown delay
|
||||
import subprocess # Add this to the imports at the top
|
||||
|
||||
# --- Imports for System Tray Icon ---
|
||||
import threading
|
||||
import webbrowser
|
||||
import sys
|
||||
import requests # To send shutdown request
|
||||
import requests # To send shutdown request
|
||||
from PIL import Image
|
||||
import pystray
|
||||
|
||||
|
@ -60,7 +61,7 @@ def broadcast_message(message):
|
|||
try:
|
||||
closing_bracket = raw_msg.index("]") + 1
|
||||
if raw_msg[1 : closing_bracket - 1].replace(":", "").isdigit():
|
||||
raw_msg = raw_msg[closing_bracket:].strip() # Update raw_msg itself
|
||||
raw_msg = raw_msg[closing_bracket:].strip() # Update raw_msg itself
|
||||
else:
|
||||
break
|
||||
except:
|
||||
|
@ -76,10 +77,12 @@ def broadcast_message(message):
|
|||
# Enviar a todos los clientes WebSocket
|
||||
for ws in list(websocket_connections):
|
||||
try:
|
||||
if ws.connected: # Check if ws is still connected before sending
|
||||
ws.send(f"{formatted_msg_for_ws}\n") # Use the correct variable name here
|
||||
if ws.connected: # Check if ws is still connected before sending
|
||||
ws.send(
|
||||
f"{formatted_msg_for_ws}\n"
|
||||
) # Use the correct variable name here
|
||||
except Exception:
|
||||
dead_connections.add(ws) # Collect dead connections
|
||||
dead_connections.add(ws) # Collect dead connections
|
||||
|
||||
# Limpiar conexiones muertas
|
||||
websocket_connections.difference_update(dead_connections)
|
||||
|
@ -139,7 +142,17 @@ def get_scripts(group):
|
|||
# list_scripts ahora devuelve detalles y filtra los ocultos
|
||||
scripts = config_manager.list_scripts(group)
|
||||
# El frontend espera 'name' y 'description', mapeamos desde 'display_name' y 'short_description'
|
||||
return jsonify([{"name": s['display_name'], "description": s['short_description'], "filename": s['filename'], "long_description": s['long_description']} for s in scripts])
|
||||
return jsonify(
|
||||
[
|
||||
{
|
||||
"name": s["display_name"],
|
||||
"description": s["short_description"],
|
||||
"filename": s["filename"],
|
||||
"long_description": s["long_description"],
|
||||
}
|
||||
for s in scripts
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@app.route("/api/working-directory", methods=["POST"])
|
||||
|
@ -212,7 +225,7 @@ def handle_group_description(group):
|
|||
try:
|
||||
details = config_manager.get_group_details(group)
|
||||
if "error" in details:
|
||||
return jsonify(details), 404 # Group not found
|
||||
return jsonify(details), 404 # Group not found
|
||||
return jsonify(details)
|
||||
except Exception as e:
|
||||
return jsonify({"status": "error", "message": str(e)}), 500
|
||||
|
@ -234,7 +247,7 @@ def handle_script_details(group, script_filename):
|
|||
except Exception as e:
|
||||
print(f"Error getting script details for {group}/{script_filename}: {e}")
|
||||
return jsonify({"status": "error", "message": str(e)}), 500
|
||||
else: # POST
|
||||
else: # POST
|
||||
try:
|
||||
data = request.json
|
||||
result = config_manager.update_script_details(group, script_filename, data)
|
||||
|
@ -250,15 +263,144 @@ def get_directory_history(group):
|
|||
return jsonify(history)
|
||||
|
||||
|
||||
@app.route("/api/open-vscode/<group>", methods=["POST"])
|
||||
def open_group_in_vscode(group):
|
||||
try:
|
||||
# Get the full path to the script group directory
|
||||
script_group_path = os.path.join(config_manager.script_groups_path, group)
|
||||
|
||||
if not os.path.isdir(script_group_path):
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"status": "error",
|
||||
"message": f"Directorio del grupo '{group}' no encontrado",
|
||||
}
|
||||
),
|
||||
404,
|
||||
)
|
||||
|
||||
# VS Code executable path
|
||||
vscode_path = (
|
||||
r"C:\Users\migue\AppData\Local\Programs\Microsoft VS Code\Code.exe"
|
||||
)
|
||||
|
||||
# Check if the file exists
|
||||
if not os.path.isfile(vscode_path):
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"status": "error",
|
||||
"message": f"VS Code no encontrado en: {vscode_path}",
|
||||
}
|
||||
),
|
||||
404,
|
||||
)
|
||||
|
||||
print(f"Launching VS Code from: {vscode_path}")
|
||||
print(f"Opening directory: {script_group_path}")
|
||||
|
||||
# Try with shell=True which can help with Windows path issues
|
||||
process = subprocess.Popen(f'"{vscode_path}" "{script_group_path}"', shell=True)
|
||||
|
||||
# Log the process ID for debugging
|
||||
print(f"Process started with PID: {process.pid}")
|
||||
|
||||
return jsonify(
|
||||
{"status": "success", "message": f"VS Code abierto en: {script_group_path}"}
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Error opening VS Code for group '{group}': {str(e)}")
|
||||
return (
|
||||
jsonify(
|
||||
{"status": "error", "message": f"Error al abrir VS Code: {str(e)}"}
|
||||
),
|
||||
500,
|
||||
)
|
||||
|
||||
|
||||
@app.route("/api/open-miniconda", methods=["POST"])
|
||||
def open_miniconda_console():
|
||||
try:
|
||||
# Path to the Miniconda installation
|
||||
miniconda_path = r"C:\Users\migue\miniconda3"
|
||||
|
||||
# Check if directory exists
|
||||
if not os.path.isdir(miniconda_path):
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"status": "error",
|
||||
"message": f"Miniconda no encontrado en: {miniconda_path}",
|
||||
}
|
||||
),
|
||||
404,
|
||||
)
|
||||
|
||||
# Path to the activate script
|
||||
activate_path = os.path.join(miniconda_path, "Scripts", "activate.bat")
|
||||
|
||||
if not os.path.isfile(activate_path):
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"status": "error",
|
||||
"message": f"Script de activación no encontrado en: {activate_path}",
|
||||
}
|
||||
),
|
||||
404,
|
||||
)
|
||||
|
||||
print(f"Opening Miniconda Console from: {miniconda_path}")
|
||||
|
||||
# Use subprocess with CREATE_NEW_CONSOLE flag to ensure the window appears
|
||||
# Start the Windows command processor and tell it to run the activate batch file
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
startupinfo.wShowWindow = 1 # SW_SHOWNORMAL
|
||||
|
||||
# Run cmd.exe with specific flags to show a window
|
||||
process = subprocess.Popen(
|
||||
[
|
||||
"cmd.exe",
|
||||
"/K",
|
||||
f"echo Activating Miniconda environment... && "
|
||||
f'"{activate_path}" && '
|
||||
f"echo Miniconda activated successfully. && "
|
||||
f"cd /d {miniconda_path}",
|
||||
],
|
||||
creationflags=subprocess.CREATE_NEW_CONSOLE,
|
||||
startupinfo=startupinfo,
|
||||
)
|
||||
|
||||
print(f"Miniconda Console process started with PID: {process.pid}")
|
||||
|
||||
return jsonify(
|
||||
{"status": "success", "message": "Miniconda Console abierta correctamente"}
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Error opening Miniconda Console: {str(e)}")
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"status": "error",
|
||||
"message": f"Error al abrir Miniconda Console: {str(e)}",
|
||||
}
|
||||
),
|
||||
500,
|
||||
)
|
||||
|
||||
|
||||
# --- System Tray Icon Functions ---
|
||||
|
||||
|
||||
def run_flask():
|
||||
"""Runs the Flask app."""
|
||||
print("Starting Flask server on http://127.0.0.1:5000/")
|
||||
try:
|
||||
# use_reloader=False is important when running in a thread
|
||||
# For production, consider using waitress or gunicorn instead of app.run
|
||||
app.run(host='127.0.0.1', port=5000, debug=True, use_reloader=False)
|
||||
app.run(host="127.0.0.1", port=5000, debug=True, use_reloader=False)
|
||||
except Exception as e:
|
||||
print(f"Error running Flask app: {e}")
|
||||
# Optionally try to stop the tray icon if Flask fails critically
|
||||
|
@ -266,11 +408,13 @@ def run_flask():
|
|||
print("Attempting to stop tray icon due to Flask error.")
|
||||
tray_icon.stop()
|
||||
|
||||
|
||||
def open_app_browser(icon, item):
|
||||
"""Callback function to open the browser."""
|
||||
print("Opening application in browser...")
|
||||
webbrowser.open("http://127.0.0.1:5000/")
|
||||
|
||||
|
||||
def shutdown_flask_server():
|
||||
"""Attempts to gracefully shut down the Werkzeug server."""
|
||||
try:
|
||||
|
@ -281,9 +425,10 @@ def shutdown_flask_server():
|
|||
print(f"Could not send shutdown request to Flask server: {e}")
|
||||
print("Flask server might need to be closed manually.")
|
||||
|
||||
|
||||
def stop_icon_thread():
|
||||
"""Helper function to stop the icon after a delay, allowing HTTP response."""
|
||||
time.sleep(0.1) # Small delay to allow the HTTP response to be sent
|
||||
time.sleep(0.1) # Small delay to allow the HTTP response to be sent
|
||||
if tray_icon:
|
||||
print("Stopping tray icon from shutdown route...")
|
||||
tray_icon.stop()
|
||||
|
@ -293,7 +438,8 @@ def stop_icon_thread():
|
|||
# print("Attempting os._exit(0) as fallback.")
|
||||
# os._exit(0) # Force exit - use with caution
|
||||
|
||||
@app.route('/_shutdown', methods=['POST'])
|
||||
|
||||
@app.route("/_shutdown", methods=["POST"])
|
||||
def shutdown_route():
|
||||
"""Internal route to shut down the application via the tray icon."""
|
||||
print("Shutdown endpoint called.")
|
||||
|
@ -304,43 +450,52 @@ def shutdown_route():
|
|||
print("Shutdown signal sent to tray icon thread.")
|
||||
return jsonify(status="success", message="Application shutdown initiated..."), 200
|
||||
|
||||
|
||||
def exit_application(icon, item):
|
||||
"""Callback function to exit the application."""
|
||||
print("Exit requested via tray menu.")
|
||||
# Just stop the icon. This will end the main thread, and the daemon Flask thread will exit.
|
||||
print("Stopping tray icon...")
|
||||
if icon: # pystray passes the icon object
|
||||
if icon: # pystray passes the icon object
|
||||
icon.stop()
|
||||
elif tray_icon: # Fallback just in case
|
||||
elif tray_icon: # Fallback just in case
|
||||
tray_icon.stop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# --- Start Flask in a background thread ---
|
||||
flask_thread = threading.Thread(target=run_flask, daemon=True)
|
||||
flask_thread.start()
|
||||
|
||||
# --- Setup and run the system tray icon ---
|
||||
icon_path = r"d:\Proyectos\Scripts\ParamManagerScripts\icon.png" # Use absolute path
|
||||
icon_path = (
|
||||
r"d:\Proyectos\Scripts\ParamManagerScripts\icon.png" # Use absolute path
|
||||
)
|
||||
try:
|
||||
image = Image.open(icon_path)
|
||||
menu = pystray.Menu(
|
||||
pystray.MenuItem("Abrir ParamManager", open_app_browser, default=True),
|
||||
pystray.MenuItem("Salir", exit_application)
|
||||
pystray.MenuItem("Salir", exit_application),
|
||||
)
|
||||
tray_icon = pystray.Icon("ParamManager", image, "ParamManager", menu)
|
||||
print("Starting system tray icon...")
|
||||
tray_icon.run() # This blocks the main thread until icon.stop() is called
|
||||
tray_icon.run() # This blocks the main thread until icon.stop() is called
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Icono no encontrado en '{icon_path}'. El icono de notificación no se iniciará.", file=sys.stderr)
|
||||
print("La aplicación Flask seguirá ejecutándose en segundo plano. Presiona Ctrl+C para detenerla si es necesario.")
|
||||
print(
|
||||
f"Error: Icono no encontrado en '{icon_path}'. El icono de notificación no se iniciará.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
print(
|
||||
"La aplicación Flask seguirá ejecutándose en segundo plano. Presiona Ctrl+C para detenerla si es necesario."
|
||||
)
|
||||
# Keep the main thread alive so the Flask thread doesn't exit immediately
|
||||
# This allows Flask to continue running even without the tray icon.
|
||||
try:
|
||||
while flask_thread.is_alive():
|
||||
flask_thread.join(timeout=1.0) # Wait indefinitely
|
||||
flask_thread.join(timeout=1.0) # Wait indefinitely
|
||||
except KeyboardInterrupt:
|
||||
print("\nCtrl+C detectado. Intentando detener Flask...")
|
||||
shutdown_flask_server() # Try to shutdown Flask on Ctrl+C too
|
||||
shutdown_flask_server() # Try to shutdown Flask on Ctrl+C too
|
||||
print("Saliendo.")
|
||||
except Exception as e:
|
||||
print(f"Error al iniciar el icono de notificación: {e}", file=sys.stderr)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"Input_level_1": "Inputs",
|
||||
"Input_level_2": "IO Not in Hardware\\\\InputsMaster"
|
||||
"ObsideanDir": "C:\\Users\\migue\\OneDrive\\Miguel\\Obsidean\\Trabajo\\VM",
|
||||
"ObsideanProjectsBase": "\\04-SIDEL"
|
||||
}
|
|
@ -1,17 +1,18 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Input_level_1": {
|
||||
"ObsideanDir": {
|
||||
"type": "string",
|
||||
"title": "Inputs",
|
||||
"description": "Inputs",
|
||||
"default": "Inputs"
|
||||
"title": "Directorio de Vault de Obsidean",
|
||||
"description": "Directorio de Vault de Obsidean",
|
||||
"format": "directory",
|
||||
"default": "C:\\Users\\migue\\OneDrive\\Miguel\\Obsidean\\Trabajo\\VM"
|
||||
},
|
||||
"Input_level_2": {
|
||||
"ObsideanProjectsBase": {
|
||||
"type": "string",
|
||||
"title": "Inputs Nivel 2",
|
||||
"description": "Inputs Nivel 2",
|
||||
"default": "IO Not in Hardware\\\\InputsMaster"
|
||||
"title": "Subdirectorio",
|
||||
"description": "Subdirectorio de los proyectos actuales en el Vault de Obsidean",
|
||||
"default": "\\04-SIDEL"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
--- Log de Ejecución: x0_documentation.py ---
|
||||
Grupo: IO_adaptation
|
||||
Directorio de Trabajo: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\TAGsIO\v2
|
||||
Inicio: 2025-05-15 13:45:10
|
||||
Fin: 2025-05-15 13:45:10
|
||||
Duración: 0:00:00.048217
|
||||
Estado: SUCCESS (Código de Salida: 0)
|
||||
|
||||
--- SALIDA ESTÁNDAR (STDOUT) ---
|
||||
|
||||
|
||||
--- ERRORES (STDERR) ---
|
||||
Ninguno
|
||||
--- FIN DEL LOG ---
|
|
@ -1,19 +1,20 @@
|
|||
--- Log de Ejecución: x4_prompt_generator.py ---
|
||||
Grupo: IO_adaptation
|
||||
Directorio de Trabajo: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\TAGsIO\v2
|
||||
Inicio: 2025-05-15 12:09:38
|
||||
Fin: 2025-05-15 12:09:38
|
||||
Duración: 0:00:00.084182
|
||||
Estado: ERROR (Código de Salida: 1)
|
||||
Inicio: 2025-05-15 14:05:02
|
||||
Fin: 2025-05-15 14:05:04
|
||||
Duración: 0:00:01.643930
|
||||
Estado: SUCCESS (Código de Salida: 0)
|
||||
|
||||
--- SALIDA ESTÁNDAR (STDOUT) ---
|
||||
|
||||
Generador de prompt para adaptación de IO
|
||||
=========================================
|
||||
Usando directorio de trabajo: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\TAGsIO\v2
|
||||
Usando ruta de Obsidian desde configuración: C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\00 - MASTER\MIXER\IO
|
||||
Usando carpeta de equivalencias en Obsidian: C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\00 - MASTER\MIXER\IO
|
||||
¡Prompt generado y copiado al portapapeles con éxito!
|
||||
Prompt guardado en: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\TAGsIO\v2\IO_Adaptation_Prompt.txt
|
||||
|
||||
--- ERRORES (STDERR) ---
|
||||
Traceback (most recent call last):
|
||||
File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\IO_adaptation\x4_prompt_generator.py", line 11, in <module>
|
||||
import pyperclip # Para copiar al portapapeles
|
||||
^^^^^^^^^^^^^^^^
|
||||
ModuleNotFoundError: No module named 'pyperclip'
|
||||
|
||||
Ninguno
|
||||
--- FIN DEL LOG ---
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
"model": "gpt-3.5-turbo"
|
||||
},
|
||||
"level2": {
|
||||
"Input_level_1": "Inputs",
|
||||
"Input_level_2": "IO Not in Hardware\\\\InputsMaster"
|
||||
"ObsideanDir": "C:\\Users\\migue\\OneDrive\\Miguel\\Obsidean\\Trabajo\\VM",
|
||||
"ObsideanProjectsBase": "\\04-SIDEL"
|
||||
},
|
||||
"level3": {},
|
||||
"working_directory": "C:\\Trabajo\\SIDEL\\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\\Reporte\\TAGsIO\\v2"
|
||||
|
|
|
@ -9,6 +9,7 @@ import sys
|
|||
import tkinter as tk
|
||||
from tkinter import filedialog, messagebox
|
||||
import pyperclip # Para copiar al portapapeles
|
||||
import shutil # Para copiar archivos
|
||||
|
||||
# Determine script_root and add to sys.path for custom module import
|
||||
try:
|
||||
|
@ -32,6 +33,7 @@ except NameError: # __file__ is not defined
|
|||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def verify_path(path, is_file=True):
|
||||
"""Verifica si una ruta existe y es un archivo o directorio según corresponda."""
|
||||
if is_file:
|
||||
|
@ -39,18 +41,19 @@ def verify_path(path, is_file=True):
|
|||
else:
|
||||
return os.path.isdir(path)
|
||||
|
||||
|
||||
def select_obsidian_folder():
|
||||
"""Permite al usuario seleccionar la carpeta base de Obsidian."""
|
||||
root = tk.Tk()
|
||||
root.withdraw() # Ocultar ventana principal
|
||||
|
||||
|
||||
folder_path = filedialog.askdirectory(
|
||||
title="Seleccione la carpeta base de Obsidian",
|
||||
mustexist=True
|
||||
title="Seleccione la carpeta base de Obsidian", mustexist=True
|
||||
)
|
||||
|
||||
|
||||
return folder_path if folder_path else None
|
||||
|
||||
|
||||
def generate_prompt():
|
||||
"""
|
||||
Genera el prompt para la adaptación de IO y lo copia al portapapeles.
|
||||
|
@ -74,51 +77,101 @@ def generate_prompt():
|
|||
|
||||
working_directory_abs = os.path.abspath(working_directory)
|
||||
print(f"Usando directorio de trabajo: {working_directory_abs}")
|
||||
|
||||
# Pedir al usuario que seleccione la carpeta base de Obsidian
|
||||
print("Por favor, seleccione la carpeta base de Obsidian para los archivos de equivalencias...")
|
||||
obsidian_base_folder = select_obsidian_folder()
|
||||
|
||||
if not obsidian_base_folder:
|
||||
|
||||
# Variables para las rutas de Obsidian
|
||||
obsidian_dir = None
|
||||
obsidian_projects_base = None
|
||||
obsidian_io_path = None
|
||||
|
||||
# Intentar obtener la carpeta base de Obsidian desde la configuración
|
||||
try:
|
||||
# Obtener configuración del nivel 2
|
||||
group_config = configs.get("level2", {})
|
||||
obsidian_dir = group_config.get("ObsideanDir")
|
||||
obsidian_projects_base = group_config.get("ObsideanProjectsBase")
|
||||
|
||||
if obsidian_dir and obsidian_projects_base:
|
||||
# Path para la carpeta de equivalencias (00 - MASTER/MIXER/IO)
|
||||
obsidian_mixer_path = os.path.join(
|
||||
obsidian_dir,
|
||||
obsidian_projects_base.lstrip("\\"),
|
||||
"00 - MASTER",
|
||||
"MIXER",
|
||||
"IO",
|
||||
)
|
||||
|
||||
# Path para la carpeta IO donde se copiará el archivo de salida
|
||||
obsidian_io_path = os.path.join(
|
||||
obsidian_dir, obsidian_projects_base.lstrip("\\"), "IO"
|
||||
)
|
||||
|
||||
# Verificar que la ruta de equivalencias exista
|
||||
if os.path.isdir(obsidian_mixer_path):
|
||||
print(
|
||||
f"Usando ruta de Obsidian desde configuración: {obsidian_mixer_path}"
|
||||
)
|
||||
else:
|
||||
print(
|
||||
f"Ruta de Obsidian para equivalencias no válida: {obsidian_mixer_path}"
|
||||
)
|
||||
obsidian_mixer_path = None
|
||||
|
||||
# Crear la carpeta IO si no existe
|
||||
if not os.path.isdir(obsidian_io_path):
|
||||
try:
|
||||
os.makedirs(obsidian_io_path)
|
||||
print(f"Creada carpeta para salida en Obsidian: {obsidian_io_path}")
|
||||
except Exception as e:
|
||||
print(f"No se pudo crear carpeta IO en Obsidian: {e}")
|
||||
obsidian_io_path = None
|
||||
else:
|
||||
print("Configuración incompleta para las rutas de Obsidian")
|
||||
except Exception as e:
|
||||
print(f"Error al leer configuración de Obsidian: {e}")
|
||||
|
||||
# Si no se pudo obtener la carpeta desde la configuración, pedirla al usuario
|
||||
if not obsidian_mixer_path:
|
||||
print(
|
||||
"Carpeta de equivalencias en Obsidian no encontrada en configuración o no válida."
|
||||
)
|
||||
print(
|
||||
"Por favor, seleccione la carpeta base de Obsidian para los archivos de equivalencias..."
|
||||
)
|
||||
obsidian_mixer_path = select_obsidian_folder()
|
||||
|
||||
if not obsidian_mixer_path:
|
||||
print("No se seleccionó ninguna carpeta. Saliendo...")
|
||||
return False
|
||||
|
||||
print(f"Usando carpeta base de Obsidian: {obsidian_base_folder}")
|
||||
|
||||
|
||||
print(f"Usando carpeta de equivalencias en Obsidian: {obsidian_mixer_path}")
|
||||
|
||||
# Definir las rutas a los archivos
|
||||
master_table_path = os.path.join(working_directory_abs, "Master IO Tags.md")
|
||||
hardware_table_path = os.path.join(working_directory_abs, "Hardware.md")
|
||||
adaptation_table_path = os.path.join(working_directory_abs, "IO Adapted.md")
|
||||
|
||||
|
||||
# Rutas a los archivos de datos semánticos
|
||||
# Intentamos encontrar automáticamente la ruta correcta
|
||||
mixer_io_path = os.path.join(obsidian_base_folder, "00 - MASTER", "MIXER", "IO")
|
||||
|
||||
# Si no existe esta ruta, permitimos seleccionar manualmente
|
||||
if not verify_path(mixer_io_path, is_file=False):
|
||||
print("Ruta a la carpeta IO no encontrada. Por favor, seleccione la carpeta IO:")
|
||||
root = tk.Tk()
|
||||
root.withdraw()
|
||||
mixer_io_path = filedialog.askdirectory(
|
||||
title="Seleccione la carpeta IO dentro de la estructura de Obsidian",
|
||||
mustexist=True
|
||||
)
|
||||
|
||||
if not mixer_io_path:
|
||||
print("No se seleccionó ninguna carpeta IO. Saliendo...")
|
||||
return False
|
||||
|
||||
equivalences_data_path = os.path.join(mixer_io_path, "SIDEL - Mixer - Equivalences.md")
|
||||
default_io_data_path = os.path.join(mixer_io_path, "Default IO for Analog.md")
|
||||
|
||||
equivalences_data_path = os.path.join(
|
||||
obsidian_mixer_path, "SIDEL - Mixer - Equivalences.md"
|
||||
)
|
||||
default_io_data_path = os.path.join(obsidian_mixer_path, "Default IO for Analog.md")
|
||||
|
||||
# Verificar que los archivos existan
|
||||
files_to_check = [
|
||||
{"path": master_table_path, "name": "Master IO Tags.md", "required": True},
|
||||
{"path": hardware_table_path, "name": "Hardware.md", "required": True},
|
||||
{"path": equivalences_data_path, "name": "SIDEL - Mixer - Equivalences.md", "required": False},
|
||||
{"path": default_io_data_path, "name": "Default IO for Analog.md", "required": False}
|
||||
{
|
||||
"path": equivalences_data_path,
|
||||
"name": "SIDEL - Mixer - Equivalences.md",
|
||||
"required": False,
|
||||
},
|
||||
{
|
||||
"path": default_io_data_path,
|
||||
"name": "Default IO for Analog.md",
|
||||
"required": False,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
missing_files = []
|
||||
for file_info in files_to_check:
|
||||
if not verify_path(file_info["path"]):
|
||||
|
@ -126,19 +179,21 @@ def generate_prompt():
|
|||
missing_files.append(f"[REQUERIDO] {file_info['name']}")
|
||||
else:
|
||||
missing_files.append(f"[OPCIONAL] {file_info['name']}")
|
||||
|
||||
|
||||
if missing_files:
|
||||
print("Los siguientes archivos no se encontraron:")
|
||||
for missing_file in missing_files:
|
||||
print(f" - {missing_file}")
|
||||
|
||||
|
||||
# Si faltan archivos requeridos, preguntar si se quiere continuar
|
||||
if any(f.startswith("[REQUERIDO]") for f in missing_files):
|
||||
if not messagebox.askyesno("Archivos faltantes",
|
||||
"Faltan archivos requeridos. ¿Desea continuar de todos modos?"):
|
||||
if not messagebox.askyesno(
|
||||
"Archivos faltantes",
|
||||
"Faltan archivos requeridos. ¿Desea continuar de todos modos?",
|
||||
):
|
||||
print("Operación cancelada.")
|
||||
return False
|
||||
|
||||
|
||||
# Generar el texto del prompt
|
||||
prompt_text = f"""
|
||||
Estoy adaptando las entradas y salidas entre un hardware de PLC Siemens Tia Portal y un software master. Para lograr identificar que tags del software master se deben asignar a cada IO del hardware del PLC. Se debe asignar a cada IO del harware un Tag del software master.
|
||||
|
@ -157,7 +212,7 @@ Para acceder a los archivos para leer o escribir puedes usar el MCP filesystem.
|
|||
|
||||
# Definiciones de rutas
|
||||
$Working_Directory = "{working_directory_abs}"
|
||||
$Obsidean_Base_Folder = "{obsidian_base_folder}"
|
||||
$Obsidean_Base_Folder = "{obsidian_mixer_path}"
|
||||
|
||||
# Archivos de entrada
|
||||
$Master_table = $Working_Directory + "/Master IO Tags.md"
|
||||
|
@ -246,31 +301,105 @@ Para entradas con nivel de certeza medio o bajo, añade hasta 3 tags alternativo
|
|||
# EXCEPCIONES
|
||||
Al final del documento, crea una sección titulada "## Excepciones y Problemas" con una tabla que liste las IO sin asignación clara y el problema detectado.
|
||||
"""
|
||||
|
||||
|
||||
# Copiar el texto al portapapeles
|
||||
try:
|
||||
pyperclip.copy(prompt_text)
|
||||
print("¡Prompt generado y copiado al portapapeles con éxito!")
|
||||
|
||||
|
||||
# Guardar el prompt en un archivo para referencia
|
||||
prompt_file_path = os.path.join(working_directory_abs, "IO_Adaptation_Prompt.txt")
|
||||
prompt_file_path = os.path.join(
|
||||
working_directory_abs, "IO_Adaptation_Prompt.txt"
|
||||
)
|
||||
with open(prompt_file_path, "w", encoding="utf-8") as f:
|
||||
f.write(prompt_text)
|
||||
print(f"Prompt guardado en: {prompt_file_path}")
|
||||
|
||||
# Mostrar mensaje de éxito
|
||||
messagebox.showinfo("Éxito",
|
||||
f"Prompt generado y copiado al portapapeles.\n\n"
|
||||
f"También se ha guardado en:\n{prompt_file_path}")
|
||||
|
||||
|
||||
# También copiar el archivo IO Adapted.md a la carpeta de Obsidian cuando se genere
|
||||
if obsidian_io_path:
|
||||
# Crear un mensaje para informar al usuario
|
||||
copy_message = ""
|
||||
|
||||
# Explicar que la copia se realizará cuando se genere el archivo
|
||||
copy_message = (
|
||||
f"Se copiará automáticamente el archivo 'IO Adapted.md' a:\n"
|
||||
f"{obsidian_io_path}\n\n"
|
||||
f"cuando sea generado por la herramienta."
|
||||
)
|
||||
|
||||
# Añadir la información sobre la copia al mensaje de éxito
|
||||
success_message = (
|
||||
f"Prompt generado y copiado al portapapeles.\n\n"
|
||||
f"También se ha guardado en:\n{prompt_file_path}\n\n"
|
||||
f"{copy_message}"
|
||||
)
|
||||
|
||||
messagebox.showinfo("Éxito", success_message)
|
||||
else:
|
||||
# Mostrar mensaje de éxito sin la parte de copia
|
||||
messagebox.showinfo(
|
||||
"Éxito",
|
||||
f"Prompt generado y copiado al portapapeles.\n\n"
|
||||
f"También se ha guardado en:\n{prompt_file_path}",
|
||||
)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error al copiar al portapapeles: {e}")
|
||||
messagebox.showerror("Error",
|
||||
f"Error al copiar al portapapeles: {e}\n\n"
|
||||
f"Por favor, instale pyperclip con 'pip install pyperclip'")
|
||||
messagebox.showerror(
|
||||
"Error",
|
||||
f"Error al copiar al portapapeles: {e}\n\n"
|
||||
f"Por favor, instale pyperclip con 'pip install pyperclip'",
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
# Agregar una función para copiar el archivo adaptado a Obsidian
|
||||
def copy_adapted_file_to_obsidian():
|
||||
"""Copia el archivo IO Adapted.md al directorio de IO en Obsidian si existe"""
|
||||
try:
|
||||
configs = load_configuration()
|
||||
working_directory = configs.get("working_directory")
|
||||
if not working_directory:
|
||||
return False
|
||||
|
||||
# Verificar si el archivo existe en el directorio de trabajo
|
||||
source_file = os.path.join(working_directory, "IO Adapted.md")
|
||||
if not os.path.isfile(source_file):
|
||||
print(f"Archivo '{source_file}' no encontrado.")
|
||||
return False
|
||||
|
||||
# Obtener la ruta de destino en Obsidian
|
||||
group_config = configs.get("level2", {})
|
||||
obsidian_dir = group_config.get("ObsideanDir")
|
||||
obsidian_projects_base = group_config.get("ObsideanProjectsBase")
|
||||
|
||||
if not obsidian_dir or not obsidian_projects_base:
|
||||
print("Configuración de Obsidian incompleta.")
|
||||
return False
|
||||
|
||||
# Construir la ruta de destino
|
||||
obsidian_io_path = os.path.join(
|
||||
obsidian_dir, obsidian_projects_base.lstrip("\\"), "IO"
|
||||
)
|
||||
|
||||
# Crear la carpeta si no existe
|
||||
if not os.path.exists(obsidian_io_path):
|
||||
os.makedirs(obsidian_io_path)
|
||||
|
||||
# Definir el destino
|
||||
dest_file = os.path.join(obsidian_io_path, "IO Adapted.md")
|
||||
|
||||
# Copiar el archivo
|
||||
shutil.copy2(source_file, dest_file)
|
||||
print(f"Archivo copiado exitosamente a: {dest_file}")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error al copiar archivo a Obsidian: {e}")
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Generador de prompt para adaptación de IO")
|
||||
print("=========================================")
|
||||
|
|
29
data/log.txt
29
data/log.txt
|
@ -1,19 +1,10 @@
|
|||
[11:58:03] Iniciando ejecución de x3_excel_to_md.py en C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\TAGsIO\v2...
|
||||
[11:58:04] Usando directorio de trabajo: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\TAGsIO\v2
|
||||
[11:58:04] Configuración de paths cargada desde: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\TAGsIO\v2\io_paths_config.json
|
||||
[11:58:04] Usando archivo Excel predeterminado: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\TAGsIO\v2\PLCTags.xlsx
|
||||
[11:58:04] Procesando archivo Excel: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\TAGsIO\v2\PLCTags.xlsx...
|
||||
[11:58:04] Paths configurados para procesar: ['Inputs', 'Outputs', 'OutputsFesto', 'IO Not in Hardware\\InputsMaster', 'IO Not in Hardware\\OutputsMaster']
|
||||
[11:58:05] ¡Éxito! Archivo Excel convertido a Markdown en: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\TAGsIO\v2\Master IO Tags.md
|
||||
[11:58:05] Ejecución de x3_excel_to_md.py finalizada (success). Duración: 0:00:01.664065.
|
||||
[11:58:05] Log completo guardado en: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\IO_adaptation\log_x3_excel_to_md.txt
|
||||
[12:09:38] Iniciando ejecución de x4_prompt_generator.py en C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\TAGsIO\v2...
|
||||
[12:09:38] --- ERRORES ---
|
||||
[12:09:38] Traceback (most recent call last):
|
||||
[12:09:38] File "D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\IO_adaptation\x4_prompt_generator.py", line 11, in <module>
|
||||
[12:09:38] import pyperclip # Para copiar al portapapeles
|
||||
[12:09:38] ^^^^^^^^^^^^^^^^
|
||||
[12:09:38] ModuleNotFoundError: No module named 'pyperclip'
|
||||
[12:09:38] --- FIN ERRORES ---
|
||||
[12:09:38] Ejecución de x4_prompt_generator.py finalizada (error). Duración: 0:00:00.084182. Se detectaron errores (ver log).
|
||||
[12:09:38] Log completo guardado en: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\IO_adaptation\log_x4_prompt_generator.txt
|
||||
[14:05:02] Iniciando ejecución de x4_prompt_generator.py en C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\TAGsIO\v2...
|
||||
[14:05:03] Generador de prompt para adaptación de IO
|
||||
[14:05:03] =========================================
|
||||
[14:05:03] Usando directorio de trabajo: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\TAGsIO\v2
|
||||
[14:05:03] Usando ruta de Obsidian desde configuración: C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\00 - MASTER\MIXER\IO
|
||||
[14:05:03] Usando carpeta de equivalencias en Obsidian: C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\00 - MASTER\MIXER\IO
|
||||
[14:05:03] ¡Prompt generado y copiado al portapapeles con éxito!
|
||||
[14:05:03] Prompt guardado en: C:\Trabajo\SIDEL\06 - E5.007363 - Modifica O&U - SAE196 (cip integrato)\Reporte\TAGsIO\v2\IO_Adaptation_Prompt.txt
|
||||
[14:05:04] Ejecución de x4_prompt_generator.py finalizada (success). Duración: 0:00:01.643930.
|
||||
[14:05:04] Log completo guardado en: d:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\IO_adaptation\log_x4_prompt_generator.txt
|
||||
|
|
|
@ -1215,8 +1215,6 @@ function showToast(message, type = 'success') {
|
|||
// Llama a fetchLogs al cargar la página si es necesario
|
||||
// document.addEventListener('DOMContentLoaded', fetchLogs);
|
||||
|
||||
|
||||
|
||||
// Agregar función para guardar configuración
|
||||
async function saveConfig(level) {
|
||||
const saveButton = document.getElementById(`save-config-${level}`);
|
||||
|
@ -1270,4 +1268,50 @@ async function saveConfig(level) {
|
|||
}
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
async function openGroupInVsCode() {
|
||||
if (!currentGroup) {
|
||||
alert('Por favor, seleccione un grupo de scripts primero');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/open-vscode/${currentGroup}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.status === 'success') {
|
||||
console.log('VS Code opened successfully');
|
||||
} else {
|
||||
console.error('Error opening VS Code:', result.message);
|
||||
alert(`Error al abrir VS Code: ${result.message}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error calling open-vscode API:', error);
|
||||
alert('Error al intentar abrir VS Code');
|
||||
}
|
||||
}
|
||||
|
||||
function openMinicondaConsole() {
|
||||
fetch('/api/open-miniconda', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 'success') {
|
||||
showNotification('Miniconda Console abierta correctamente', 'success');
|
||||
} else {
|
||||
showNotification(`Error al abrir Miniconda Console: ${data.message}`, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error opening Miniconda Console:', error);
|
||||
showNotification('Error al comunicarse con el servidor', 'error');
|
||||
});
|
||||
}
|
|
@ -71,6 +71,9 @@
|
|||
|
||||
<!-- Botón para detener el servidor -->
|
||||
<div class="mt-8 pt-4 border-t border-gray-300">
|
||||
<button class="w-full bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded shadow mb-2" onclick="openMinicondaConsole()">
|
||||
Abrir Miniconda Console
|
||||
</button>
|
||||
<button class="w-full bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded shadow" onclick="shutdownServer()">
|
||||
Detener Servidor
|
||||
</button>
|
||||
|
@ -94,6 +97,11 @@
|
|||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<button onclick="openGroupInVsCode()" class="bg-blue-500 text-white p-2 rounded mb-2" title="Abrir grupo en VS Code">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<p id="group-description" class="text-gray-600 text-sm italic"></p>
|
||||
<div class="text-xs text-gray-500 mt-2">
|
||||
|
|
Loading…
Reference in New Issue