Falta poder guardar los formularios

This commit is contained in:
Miguel 2025-02-09 12:20:11 +01:00
parent 04e180ae3f
commit 7b369ba199
5 changed files with 366 additions and 92 deletions

31
app.py
View File

@ -2,6 +2,7 @@ from flask import Flask, render_template, request, jsonify
from flask_sock import Sock from flask_sock import Sock
from config_manager import ConfigurationManager from config_manager import ConfigurationManager
import os import os
import json # Added import
app = Flask(__name__) app = Flask(__name__)
sock = Sock(app) sock = Sock(app)
@ -159,5 +160,35 @@ def handle_logs():
return jsonify({"status": "success" if success else "error"}) return jsonify({"status": "success" if success else "error"})
@app.route("/api/group-description/<group>", methods=["GET", "POST"])
def handle_group_description(group):
description_path = os.path.join(
config_manager.script_groups_path, group, "description.json"
)
if request.method == "GET":
try:
with open(description_path, "r", encoding="utf-8") as f:
return jsonify(json.load(f))
except FileNotFoundError:
return jsonify(
{
"name": group,
"description": "Sin descripción",
"version": "1.0",
"author": "Unknown",
}
)
else: # POST
try:
data = request.json
os.makedirs(os.path.dirname(description_path), exist_ok=True)
with open(description_path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
return jsonify({"status": "success"})
except Exception as e:
return jsonify({"status": "error", "message": str(e)}), 500
if __name__ == "__main__": if __name__ == "__main__":
app.run(debug=True) app.run(debug=True)

View File

@ -1,6 +1,6 @@
{ {
"name": "Grupo de Ejemplo", "name": "Grupo de Ejemplo Auto",
"description": "Scripts de demostración que muestran las funcionalidades básicas del sistema", "description": "Scripts de demostración que muestran las funcionalidades básicas del sistema.",
"version": "1.0", "version": "1.0",
"author": "Admin" "author": "Admin"
} }

View File

@ -1,21 +1,30 @@
{ {
"type": "object", "type": "object",
"properties": { "properties": {
"debug_mode": {
"type": "boolean",
"title": "Modo Debug",
"description": "Activar logging detallado"
},
"process_type": {
"type": "string",
"title": "Tipo de Proceso",
"description": "Nivel de procesamiento a aplicar",
"enum": [
"basic",
"advanced",
"full"
]
},
"project_name": { "project_name": {
"type": "string", "type": "string",
"title": "Nombre del Proyecto", "title": "Nombre del Proyecto",
"description": "Identificador único del proyecto" "description": "Identificador único del proyecto"
}, },
"process_type": { "campo_1739099176331": {
"type": "string", "type": "string",
"title": "Tipo de Proceso", "title": "Nuevo Campo",
"enum": ["basic", "advanced", "full"], "description": ""
"description": "Nivel de procesamiento a aplicar"
},
"debug_mode": {
"type": "boolean",
"title": "Modo Debug",
"description": "Activar logging detallado"
} }
} }
} }

View File

@ -1,28 +0,0 @@
Iniciando ejecución de x1.py...
=== Ejecutando Script de Prueba 1 ===
Configuraciones cargadas:
Nivel 1: {
"api_key": "your-api-key-here",
"model": "gpt-3.5-turbo"
}
Nivel 2: {
"input_dir": "D:/Datos/Entrada",
"output_dir": "D:/Datos/Salida",
"batch_size": 50
}
Nivel 3: {
"project_name": "Test"
}
Simulando procesamiento...
Progreso: 20%
Progreso: 40%
Progreso: 60%
Progreso: 80%
Progreso: 100%
¡Proceso completado!
Ejecución completada.

View File

@ -157,37 +157,33 @@
return value; return value;
} }
// Modificar la función generateInputField para quitar el onchange
function generateInputField(def, key, value, level) { function generateInputField(def, key, value, level) {
const baseClasses = "w-full p-2 border rounded bg-green-50"; // Agregado bg-green-50 para fondo verde claro const baseClasses = "w-full p-2 border rounded bg-green-50";
switch (def.type) { switch (def.type) {
case 'string': case 'string':
if (def.enum) { if (def.enum) {
return `<select class="${baseClasses}" return `<select class="${baseClasses}" data-key="${key}">
onchange="updateConfig(${level}, '${key}', this.value)">
${def.enum.map(opt => `<option value="${opt}" ${value === opt ? 'selected' : ''}>${opt}</option>`).join('')} ${def.enum.map(opt => `<option value="${opt}" ${value === opt ? 'selected' : ''}>${opt}</option>`).join('')}
</select>`; </select>`;
} }
return `<input type="text" value="${value || ''}" return `<input type="text" value="${value || ''}"
class="${baseClasses}" class="${baseClasses}" data-key="${key}">`;
onchange="updateConfig(${level}, '${key}', this.value)">`;
case 'number': case 'number':
return `<input type="number" value="${value || 0}" return `<input type="number" value="${value || 0}"
class="${baseClasses}" class="${baseClasses}" data-key="${key}">`;
onchange="updateConfig(${level}, '${key}', Number(this.value))">`;
case 'boolean': case 'boolean':
return `<div class="flex items-center"> return `<div class="flex items-center">
<input type="checkbox" ${value ? 'checked' : ''} <input type="checkbox" ${value ? 'checked' : ''}
class="form-checkbox h-5 w-5 bg-green-50" class="form-checkbox h-5 w-5 bg-green-50" data-key="${key}">
onchange="updateConfig(${level}, '${key}', this.checked)">
</div>`; </div>`;
default: default:
return `<input type="text" value="${value || ''}" return `<input type="text" value="${value || ''}"
class="${baseClasses}" class="${baseClasses}" data-key="${key}">`;
onchange="updateConfig(${level}, '${key}', this.value)">`;
} }
} }
@ -632,11 +628,17 @@
function addLogLine(message) { function addLogLine(message) {
const logArea = document.getElementById('log-area'); const logArea = document.getElementById('log-area');
const timestamp = getTimestamp(); const timestamp = getTimestamp();
const lines = message.split('\n').map(line =>
line.trim() ? `[${timestamp}] ${line}` : line // Filtrar líneas vacías y aplicar timestamp solo a líneas con contenido
).join('\n'); const lines = message.split('\n')
logArea.innerHTML += lines; .filter(line => line.trim()) // Eliminar líneas vacías
logArea.scrollTop = logArea.scrollHeight; .map(line => `[${timestamp}] ${line}`)
.join('\n');
if (lines) {
logArea.innerHTML += lines + '\n';
logArea.scrollTop = logArea.scrollHeight;
}
} }
function updateGroupDescription() { function updateGroupDescription() {
@ -659,6 +661,157 @@
sidebar.classList.toggle('open'); sidebar.classList.toggle('open');
overlay.classList.toggle('show'); overlay.classList.toggle('show');
} }
async function editGroupDescription() {
if (!currentGroup) {
alert('Por favor, seleccione un grupo de scripts primero');
return;
}
try {
const response = await fetch(`/api/group-description/${currentGroup}`);
if (!response.ok) throw new Error('Error cargando descripción del grupo');
const description = await response.json();
// Show schema editor modal with description data
const modal = document.getElementById('schema-editor');
const modalTitle = modal.querySelector('h3');
const visualEditor = document.getElementById('visual-editor');
const jsonEditor = document.getElementById('json-editor');
const tabs = document.getElementById('editor-tabs');
// Configurar modal para edición de descripción
modalTitle.textContent = 'Editar Descripción del Grupo';
tabs.classList.add('hidden');
// Crear el formulario en el visualEditor
visualEditor.innerHTML = `
<form id="group-description-form" class="grid gap-4">
<div>
<label class="block text-sm font-bold mb-2">Nombre del Grupo</label>
<input type="text" name="name" class="w-full p-2 border rounded"
value="${description.name || ''}" placeholder="Nombre del grupo">
</div>
<div>
<label class="block text-sm font-bold mb-2">Descripción</label>
<textarea name="description" class="w-full p-2 border rounded" rows="3"
placeholder="Descripción detallada del grupo">${description.description || ''}</textarea>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-bold mb-2">Versión</label>
<input type="text" name="version" class="w-full p-2 border rounded"
value="${description.version || '1.0'}" placeholder="1.0">
</div>
<div>
<label class="block text-sm font-bold mb-2">Autor</label>
<input type="text" name="author" class="w-full p-2 border rounded"
value="${description.author || ''}" placeholder="Nombre del autor">
</div>
</div>
</form>
`;
visualEditor.classList.remove('hidden');
jsonEditor.classList.add('hidden');
modal.classList.remove('hidden');
// Cambiar comportamiento de todos los botones de guardar
const saveButtons = modal.querySelectorAll('button[onclick="saveSchema()"]');
saveButtons.forEach(btn => {
btn.onclick = async () => {
try {
const form = document.getElementById('group-description-form');
const formData = new FormData(form);
const updatedDescription = {
name: formData.get('name') || '',
description: formData.get('description') || '',
version: formData.get('version') || '1.0',
author: formData.get('author') || ''
};
const saveResponse = await fetch(`/api/group-description/${currentGroup}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updatedDescription)
});
if (!saveResponse.ok) throw new Error('Error guardando descripción');
// Restaurar modal a su estado original
modalTitle.textContent = 'Editor de Esquema';
tabs.classList.remove('hidden');
saveButtons.forEach(btn => btn.onclick = saveSchema);
modal.classList.add('hidden');
// Recargar la página para actualizar la descripción
location.reload();
} catch (e) {
alert('Error guardando descripción: ' + e.message);
}
};
});
} catch (e) {
alert('Error: ' + e.message);
}
}
// Agregar función para recolectar datos del formulario
function collectFormData(level) {
const formContainer = document.getElementById(`level${level}-form`);
const data = {};
formContainer.querySelectorAll('input, select').forEach(input => {
const key = input.getAttribute('data-key');
if (!key) return;
let value;
if (input.type === 'checkbox') {
value = input.checked;
} else if (input.type === 'number') {
value = Number(input.value);
} else {
value = input.value;
}
// Manejar claves anidadas (por ejemplo: "parent.child")
const keys = key.split('.');
let current = data;
for (let i = 0; i < keys.length - 1; i++) {
current[keys[i]] = current[keys[i]] || {};
current = current[keys[i]];
}
current[keys[keys.length - 1]] = value;
});
return data;
}
// Agregar función para guardar configuración
async function saveConfig(level) {
try {
const data = collectFormData(level);
const response = await fetch(`/api/config/${level}?group=${currentGroup}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (!response.ok) throw new Error('Error al guardar la configuración');
// Mostrar mensaje de éxito
alert('Configuración guardada correctamente');
// Recargar el formulario para mostrar los datos actualizados
const configResponse = await fetch(`/api/config/${level}?group=${currentGroup}`);
const updatedData = await configResponse.json();
await renderForm(`level${level}-form`, updatedData);
} catch (error) {
alert('Error: ' + error.message);
}
}
</script> </script>
<style> <style>
.sidebar { .sidebar {
@ -700,6 +853,73 @@
#schema-editor .modal-content { #schema-editor .modal-content {
z-index: 51; z-index: 51;
} }
/* Reducción general de padding */
.container {
padding-left: 1.5rem !important; /* 30% menos que 4rem */
padding-right: 1.5rem !important;
}
.p-6 {
padding: 1.1rem !important; /* 30% menos que 1.5rem */
}
.px-4 {
padding-left: 0.7rem !important; /* 30% menos que 1rem */
padding-right: 0.7rem !important;
}
.py-8 {
padding-top: 1.5rem !important; /* 30% menos que 2rem */
padding-bottom: 1.5rem !important;
}
/* Ajustes para el log */
#log-area {
font-size: 0.7rem !important;
line-height: 1.2 !important;
padding: 0.7rem !important;
}
/* Ajustes para el modal */
#schema-editor .modal-content {
width: 50% !important;
max-width: 800px;
}
.mb-4 {
margin-bottom: 0.7rem !important; /* 30% menos que 1rem */
}
.mb-6 {
margin-bottom: 1.1rem !important; /* 30% menos que 1.5rem */
}
.mb-8 {
margin-bottom: 1.5rem !important; /* 30% menos que 2rem */
}
.mt-4 {
margin-top: 0.7rem !important;
}
.modal-header {
position: sticky;
top: 0;
background: white;
z-index: 52;
padding: 1rem;
border-bottom: 1px solid #e5e7eb;
}
.modal-footer {
position: sticky;
bottom: 0;
background: white;
z-index: 52;
padding: 1rem;
border-top: 1px solid #e5e7eb;
}
</style> </style>
</head> </head>
<body class="bg-gray-100"> <body class="bg-gray-100">
@ -737,9 +957,14 @@
</div> </div>
<div id="level1-content" class="hidden"> <div id="level1-content" class="hidden">
<div id="level1-form"></div> <div id="level1-form"></div>
<button class="bg-blue-500 text-white px-4 py-2 rounded mt-4" onclick="modifySchema(1)"> <div class="flex justify-between mt-4">
Modificar Esquema <button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="modifySchema(1)">
</button> Modificar Esquema
</button>
<button class="bg-green-500 text-white px-4 py-2 rounded" onclick="saveConfig(1)">
Guardar Configuración
</button>
</div>
</div> </div>
</div> </div>
@ -754,9 +979,14 @@
</div> </div>
<div id="level2-content" class="hidden"> <div id="level2-content" class="hidden">
<div id="level2-form"></div> <div id="level2-form"></div>
<button class="bg-blue-500 text-white px-4 py-2 rounded mt-4" onclick="modifySchema(2)"> <div class="flex justify-between mt-4">
Modificar Esquema <button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="modifySchema(2)">
</button> Modificar Esquema
</button>
<button class="bg-green-500 text-white px-4 py-2 rounded" onclick="saveConfig(2)">
Guardar Configuración
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -767,11 +997,18 @@
<!-- Script Group Selection --> <!-- Script Group Selection -->
<div class="mb-8 bg-white p-6 rounded-lg shadow"> <div class="mb-8 bg-white p-6 rounded-lg shadow">
<h2 class="text-xl font-bold mb-4">Grupo de Scripts</h2> <h2 class="text-xl font-bold mb-4">Grupo de Scripts</h2>
<select id="script-group" class="w-full p-2 border rounded mb-2"> <div class="flex gap-2 items-center">
{% for group in script_groups %} <select id="script-group" class="flex-1 p-2 border rounded mb-2">
<option value="{{ group.id }}" data-description="{{ group.description }}">{{ group.name }}</option> {% for group in script_groups %}
{% endfor %} <option value="{{ group.id }}" data-description="{{ group.description }}">{{ group.name }}</option>
</select> {% endfor %}
</select>
<button onclick="editGroupDescription()" class="bg-blue-500 text-white p-2 rounded mb-2">
<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="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>
</div>
<p id="group-description" class="text-gray-600 text-sm italic"></p> <p id="group-description" class="text-gray-600 text-sm italic"></p>
<div class="text-xs text-gray-500 mt-2"> <div class="text-xs text-gray-500 mt-2">
<span id="group-version"></span> <span id="group-version"></span>
@ -806,9 +1043,14 @@
</div> </div>
<div id="level3-content" class="hidden"> <div id="level3-content" class="hidden">
<div id="level3-form"></div> <div id="level3-form"></div>
<button class="bg-blue-500 text-white px-4 py-2 rounded mt-4" onclick="modifySchema(3)"> <div class="flex justify-between mt-4">
Modificar Esquema <button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="modifySchema(3)">
</button> Modificar Esquema
</button>
<button class="bg-green-500 text-white px-4 py-2 rounded" onclick="saveConfig(3)">
Guardar Configuración
</button>
</div>
</div> </div>
</div> </div>
@ -833,27 +1075,47 @@
<!-- Schema Editor Modal --> <!-- Schema Editor Modal -->
<div id="schema-editor" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center"> <div id="schema-editor" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center">
<div class="modal-content bg-white p-6 rounded-lg shadow-lg w-3/4 max-h-screen overflow-auto"> <div class="modal-content bg-white rounded-lg shadow-lg max-h-screen overflow-auto">
<h3 class="text-xl font-bold mb-4">Editor de Esquema</h3> <div class="modal-header">
<div class="flex mb-4"> <div class="flex justify-between items-center">
<button id="visual-tab" class="px-4 py-2 border-b-2" onclick="switchEditorMode('visual')">Visual</button> <h3 class="text-xl font-bold">Editor de Esquema</h3>
<button id="json-tab" class="px-4 py-2 border-b-2" onclick="switchEditorMode('json')">JSON</button> <div class="flex gap-4">
<button onclick="document.getElementById('schema-editor').classList.add('hidden')"
class="bg-gray-500 text-white px-4 py-2 rounded">
Cancelar
</button>
<button onclick="saveSchema()"
class="bg-blue-500 text-white px-4 py-2 rounded">
Guardar
</button>
</div>
</div>
<div class="flex mt-4" id="editor-tabs">
<button id="visual-tab" class="px-4 py-2 border-b-2" onclick="switchEditorMode('visual')">Visual</button>
<button id="json-tab" class="px-4 py-2 border-b-2" onclick="switchEditorMode('json')">JSON</button>
</div>
</div> </div>
<div id="visual-editor" class="hidden">
<div id="schema-fields"></div> <div class="p-4">
<button onclick="addSchemaField()" class="mt-4 bg-green-500 text-white px-4 py-2 rounded">Agregar Campo</button> <div id="visual-editor" class="hidden">
<div id="schema-fields"></div>
<button onclick="addSchemaField()" class="mt-4 bg-green-500 text-white px-4 py-2 rounded">Agregar Campo</button>
</div>
<textarea id="json-editor" class="w-full h-96 font-mono p-2 border rounded"></textarea>
<input type="hidden" id="schema-level">
</div> </div>
<textarea id="json-editor" class="w-full h-96 font-mono p-2 border rounded mb-4"></textarea>
<input type="hidden" id="schema-level"> <div class="modal-footer">
<div class="flex justify-end gap-4"> <div class="flex justify-end gap-4">
<button onclick="document.getElementById('schema-editor').classList.add('hidden')" <button onclick="document.getElementById('schema-editor').classList.add('hidden')"
class="bg-gray-500 text-white px-4 py-2 rounded"> class="bg-gray-500 text-white px-4 py-2 rounded">
Cancelar Cancelar
</button> </button>
<button onclick="saveSchema()" <button onclick="saveSchema()"
class="bg-blue-500 text-white px-4 py-2 rounded"> class="bg-blue-500 text-white px-4 py-2 rounded">
Guardar Guardar
</button> </button>
</div>
</div> </div>
</div> </div>
</div> </div>