Falta poder guardar los formularios
This commit is contained in:
parent
04e180ae3f
commit
7b369ba199
31
app.py
31
app.py
|
@ -2,6 +2,7 @@ from flask import Flask, render_template, request, jsonify
|
|||
from flask_sock import Sock
|
||||
from config_manager import ConfigurationManager
|
||||
import os
|
||||
import json # Added import
|
||||
|
||||
app = Flask(__name__)
|
||||
sock = Sock(app)
|
||||
|
@ -159,5 +160,35 @@ def handle_logs():
|
|||
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__":
|
||||
app.run(debug=True)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Grupo de Ejemplo",
|
||||
"description": "Scripts de demostración que muestran las funcionalidades básicas del sistema",
|
||||
"version": "1.0",
|
||||
"author": "Admin"
|
||||
}
|
||||
"name": "Grupo de Ejemplo Auto",
|
||||
"description": "Scripts de demostración que muestran las funcionalidades básicas del sistema.",
|
||||
"version": "1.0",
|
||||
"author": "Admin"
|
||||
}
|
|
@ -1,21 +1,30 @@
|
|||
{
|
||||
"type": "object",
|
||||
"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": {
|
||||
"type": "string",
|
||||
"title": "Nombre del Proyecto",
|
||||
"description": "Identificador único del proyecto"
|
||||
},
|
||||
"process_type": {
|
||||
"campo_1739099176331": {
|
||||
"type": "string",
|
||||
"title": "Tipo de Proceso",
|
||||
"enum": ["basic", "advanced", "full"],
|
||||
"description": "Nivel de procesamiento a aplicar"
|
||||
},
|
||||
"debug_mode": {
|
||||
"type": "boolean",
|
||||
"title": "Modo Debug",
|
||||
"description": "Activar logging detallado"
|
||||
"title": "Nuevo Campo",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
28
data/log.txt
28
data/log.txt
|
@ -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.
|
|
@ -157,37 +157,33 @@
|
|||
return value;
|
||||
}
|
||||
|
||||
// Modificar la función generateInputField para quitar el onchange
|
||||
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) {
|
||||
case 'string':
|
||||
if (def.enum) {
|
||||
return `<select class="${baseClasses}"
|
||||
onchange="updateConfig(${level}, '${key}', this.value)">
|
||||
return `<select class="${baseClasses}" data-key="${key}">
|
||||
${def.enum.map(opt => `<option value="${opt}" ${value === opt ? 'selected' : ''}>${opt}</option>`).join('')}
|
||||
</select>`;
|
||||
}
|
||||
return `<input type="text" value="${value || ''}"
|
||||
class="${baseClasses}"
|
||||
onchange="updateConfig(${level}, '${key}', this.value)">`;
|
||||
class="${baseClasses}" data-key="${key}">`;
|
||||
|
||||
case 'number':
|
||||
return `<input type="number" value="${value || 0}"
|
||||
class="${baseClasses}"
|
||||
onchange="updateConfig(${level}, '${key}', Number(this.value))">`;
|
||||
class="${baseClasses}" data-key="${key}">`;
|
||||
|
||||
case 'boolean':
|
||||
return `<div class="flex items-center">
|
||||
<input type="checkbox" ${value ? 'checked' : ''}
|
||||
class="form-checkbox h-5 w-5 bg-green-50"
|
||||
onchange="updateConfig(${level}, '${key}', this.checked)">
|
||||
class="form-checkbox h-5 w-5 bg-green-50" data-key="${key}">
|
||||
</div>`;
|
||||
|
||||
default:
|
||||
return `<input type="text" value="${value || ''}"
|
||||
class="${baseClasses}"
|
||||
onchange="updateConfig(${level}, '${key}', this.value)">`;
|
||||
class="${baseClasses}" data-key="${key}">`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -632,11 +628,17 @@
|
|||
function addLogLine(message) {
|
||||
const logArea = document.getElementById('log-area');
|
||||
const timestamp = getTimestamp();
|
||||
const lines = message.split('\n').map(line =>
|
||||
line.trim() ? `[${timestamp}] ${line}` : line
|
||||
).join('\n');
|
||||
logArea.innerHTML += lines;
|
||||
logArea.scrollTop = logArea.scrollHeight;
|
||||
|
||||
// Filtrar líneas vacías y aplicar timestamp solo a líneas con contenido
|
||||
const lines = message.split('\n')
|
||||
.filter(line => line.trim()) // Eliminar líneas vacías
|
||||
.map(line => `[${timestamp}] ${line}`)
|
||||
.join('\n');
|
||||
|
||||
if (lines) {
|
||||
logArea.innerHTML += lines + '\n';
|
||||
logArea.scrollTop = logArea.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
function updateGroupDescription() {
|
||||
|
@ -659,6 +661,157 @@
|
|||
sidebar.classList.toggle('open');
|
||||
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>
|
||||
<style>
|
||||
.sidebar {
|
||||
|
@ -700,6 +853,73 @@
|
|||
#schema-editor .modal-content {
|
||||
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>
|
||||
</head>
|
||||
<body class="bg-gray-100">
|
||||
|
@ -737,9 +957,14 @@
|
|||
</div>
|
||||
<div id="level1-content" class="hidden">
|
||||
<div id="level1-form"></div>
|
||||
<button class="bg-blue-500 text-white px-4 py-2 rounded mt-4" onclick="modifySchema(1)">
|
||||
Modificar Esquema
|
||||
</button>
|
||||
<div class="flex justify-between mt-4">
|
||||
<button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="modifySchema(1)">
|
||||
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>
|
||||
|
||||
|
@ -754,9 +979,14 @@
|
|||
</div>
|
||||
<div id="level2-content" class="hidden">
|
||||
<div id="level2-form"></div>
|
||||
<button class="bg-blue-500 text-white px-4 py-2 rounded mt-4" onclick="modifySchema(2)">
|
||||
Modificar Esquema
|
||||
</button>
|
||||
<div class="flex justify-between mt-4">
|
||||
<button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="modifySchema(2)">
|
||||
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>
|
||||
|
@ -767,11 +997,18 @@
|
|||
<!-- Script Group Selection -->
|
||||
<div class="mb-8 bg-white p-6 rounded-lg shadow">
|
||||
<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">
|
||||
{% for group in script_groups %}
|
||||
<option value="{{ group.id }}" data-description="{{ group.description }}">{{ group.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class="flex gap-2 items-center">
|
||||
<select id="script-group" class="flex-1 p-2 border rounded mb-2">
|
||||
{% for group in script_groups %}
|
||||
<option value="{{ group.id }}" data-description="{{ group.description }}">{{ group.name }}</option>
|
||||
{% 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>
|
||||
<div class="text-xs text-gray-500 mt-2">
|
||||
<span id="group-version"></span>
|
||||
|
@ -806,9 +1043,14 @@
|
|||
</div>
|
||||
<div id="level3-content" class="hidden">
|
||||
<div id="level3-form"></div>
|
||||
<button class="bg-blue-500 text-white px-4 py-2 rounded mt-4" onclick="modifySchema(3)">
|
||||
Modificar Esquema
|
||||
</button>
|
||||
<div class="flex justify-between mt-4">
|
||||
<button class="bg-blue-500 text-white px-4 py-2 rounded" onclick="modifySchema(3)">
|
||||
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>
|
||||
|
||||
|
@ -833,27 +1075,47 @@
|
|||
|
||||
<!-- Schema Editor Modal -->
|
||||
<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">
|
||||
<h3 class="text-xl font-bold mb-4">Editor de Esquema</h3>
|
||||
<div class="flex mb-4">
|
||||
<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 class="modal-content bg-white rounded-lg shadow-lg max-h-screen overflow-auto">
|
||||
<div class="modal-header">
|
||||
<div class="flex justify-between items-center">
|
||||
<h3 class="text-xl font-bold">Editor de Esquema</h3>
|
||||
<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 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 class="p-4">
|
||||
<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>
|
||||
<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="flex justify-end 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 class="modal-footer">
|
||||
<div class="flex justify-end 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>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue