378 lines
15 KiB
Python
378 lines
15 KiB
Python
"""
|
|
Script de ejemplo para probar el nuevo sistema de proxy interno
|
|
Este script se ejecutará como una aplicación Flask interna
|
|
"""
|
|
|
|
from flask import Flask, render_template_string, request, jsonify, redirect, url_for
|
|
import os
|
|
import sys
|
|
from datetime import datetime
|
|
import pandas as pd
|
|
import matplotlib.pyplot as plt
|
|
import io
|
|
import base64
|
|
|
|
# Variables disponibles del wrapper automático:
|
|
# - WORKSPACE_PATH: directorio de trabajo del script
|
|
# - PARAMETERS: parámetros pasados al script
|
|
# - ENVIRONMENT: variables de entorno
|
|
# - PROJECT_WORKSPACE: alias para WORKSPACE_PATH
|
|
|
|
print(f"Script iniciado en workspace: {WORKSPACE_PATH}")
|
|
print(f"Parámetros recibidos: {PARAMETERS}")
|
|
print(f"Variables de entorno: {ENVIRONMENT}")
|
|
|
|
# Configurar aplicación Flask del usuario
|
|
app.config.update({
|
|
'DEBUG': False,
|
|
'TEMPLATES_AUTO_RELOAD': True
|
|
})
|
|
|
|
# === RUTAS DEL SCRIPT DEL USUARIO ===
|
|
|
|
@app.route('/')
|
|
def index():
|
|
"""Página principal del script"""
|
|
html_template = """
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Script de Ejemplo - {{ script_name }}</title>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<style>
|
|
.script-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; }
|
|
.card-custom { box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); border: none; }
|
|
.status-badge { font-size: 0.8em; }
|
|
</style>
|
|
</head>
|
|
<body class="bg-light">
|
|
<div class="script-header py-4 mb-4">
|
|
<div class="container">
|
|
<h1><i class="fas fa-cog"></i> {{ script_name }}</h1>
|
|
<p class="mb-0">Ejemplo de script Flask ejecutándose a través del proxy interno</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="container">
|
|
<div class="row">
|
|
<!-- Panel de información -->
|
|
<div class="col-md-4">
|
|
<div class="card card-custom mb-4">
|
|
<div class="card-header bg-primary text-white">
|
|
<h5 class="mb-0">Información del Script</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<p><strong>Workspace:</strong><br><code>{{ workspace }}</code></p>
|
|
<p><strong>Usuario:</strong> {{ user_info.get('username', 'N/A') }}</p>
|
|
<p><strong>Proyecto:</strong> {{ project_info.get('name', 'N/A') }}</p>
|
|
<p><strong>Iniciado:</strong> {{ start_time }}</p>
|
|
<span class="badge bg-success status-badge">Ejecutándose</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card card-custom mb-4">
|
|
<div class="card-header bg-info text-white">
|
|
<h5 class="mb-0">Parámetros</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if parameters %}
|
|
{% for key, value in parameters.items() %}
|
|
<p><strong>{{ key }}:</strong> {{ value }}</p>
|
|
{% endfor %}
|
|
{% else %}
|
|
<p class="text-muted">No hay parámetros</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Panel principal -->
|
|
<div class="col-md-8">
|
|
<div class="card card-custom mb-4">
|
|
<div class="card-header bg-secondary text-white">
|
|
<h5 class="mb-0">Herramientas de Datos</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<a href="{{ url_for('generate_data') }}" class="btn btn-primary w-100 mb-2">
|
|
<i class="fas fa-database"></i> Generar Datos
|
|
</a>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<a href="{{ url_for('create_chart') }}" class="btn btn-success w-100 mb-2">
|
|
<i class="fas fa-chart-bar"></i> Crear Gráfico
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<a href="{{ url_for('api_status') }}" class="btn btn-warning w-100 mb-2">
|
|
<i class="fas fa-info-circle"></i> Estado API
|
|
</a>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<a href="{{ url_for('file_browser') }}" class="btn btn-secondary w-100 mb-2">
|
|
<i class="fas fa-folder"></i> Explorar Archivos
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Área de resultados -->
|
|
<div id="results-area">
|
|
<div class="card card-custom">
|
|
<div class="card-header bg-light">
|
|
<h5 class="mb-0">Área de Resultados</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="text-muted">Los resultados aparecerán aquí...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script src="https://kit.fontawesome.com/a076d05399.js"></script>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
return render_template_string(html_template,
|
|
script_name=ENVIRONMENT.get('SCRIPT_GROUP_NAME', 'Script de Ejemplo'),
|
|
workspace=WORKSPACE_PATH,
|
|
user_info={'username': ENVIRONMENT.get('USER_ID', 'demo')},
|
|
project_info={'name': ENVIRONMENT.get('PROJECT_NAME', 'Proyecto Demo')},
|
|
start_time=datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
|
parameters=PARAMETERS
|
|
)
|
|
|
|
@app.route('/generate-data')
|
|
def generate_data():
|
|
"""Genera datos de ejemplo y los guarda en CSV"""
|
|
import numpy as np
|
|
|
|
# Generar datos aleatorios
|
|
np.random.seed(42)
|
|
dates = pd.date_range('2024-01-01', periods=100, freq='D')
|
|
values = np.random.randn(100).cumsum() + 100
|
|
|
|
df = pd.DataFrame({
|
|
'fecha': dates,
|
|
'valor': values,
|
|
'categoria': np.random.choice(['A', 'B', 'C'], 100)
|
|
})
|
|
|
|
# Guardar en workspace
|
|
csv_path = os.path.join(WORKSPACE_PATH, 'data', 'datos_generados.csv')
|
|
df.to_csv(csv_path, index=False)
|
|
|
|
html_result = f"""
|
|
<div class="alert alert-success">
|
|
<h4>Datos Generados Exitosamente</h4>
|
|
<p>Se generaron {len(df)} registros y se guardaron en:</p>
|
|
<code>{csv_path}</code>
|
|
<br><br>
|
|
<strong>Vista previa:</strong>
|
|
<div class="table-responsive mt-3">
|
|
{df.head(10).to_html(classes='table table-striped table-sm', index=False)}
|
|
</div>
|
|
<a href="{url_for('index')}" class="btn btn-primary mt-2">Volver</a>
|
|
</div>
|
|
"""
|
|
|
|
return html_result
|
|
|
|
@app.route('/create-chart')
|
|
def create_chart():
|
|
"""Crea un gráfico con los datos generados"""
|
|
try:
|
|
# Leer datos si existen
|
|
csv_path = os.path.join(WORKSPACE_PATH, 'data', 'datos_generados.csv')
|
|
|
|
if not os.path.exists(csv_path):
|
|
return """
|
|
<div class="alert alert-warning">
|
|
<h4>No hay datos</h4>
|
|
<p>Primero genera algunos datos usando el botón "Generar Datos"</p>
|
|
<a href="{}" class="btn btn-primary">Volver</a>
|
|
</div>
|
|
""".format(url_for('index'))
|
|
|
|
df = pd.read_csv(csv_path)
|
|
df['fecha'] = pd.to_datetime(df['fecha'])
|
|
|
|
# Crear gráfico
|
|
plt.figure(figsize=(10, 6))
|
|
plt.plot(df['fecha'], df['valor'], marker='o', linewidth=2)
|
|
plt.title('Evolución de Valores en el Tiempo', fontsize=16)
|
|
plt.xlabel('Fecha')
|
|
plt.ylabel('Valor')
|
|
plt.grid(True, alpha=0.3)
|
|
plt.xticks(rotation=45)
|
|
plt.tight_layout()
|
|
|
|
# Convertir a base64 para mostrar en HTML
|
|
img_buffer = io.BytesIO()
|
|
plt.savefig(img_buffer, format='png', dpi=150, bbox_inches='tight')
|
|
img_buffer.seek(0)
|
|
img_base64 = base64.b64encode(img_buffer.getvalue()).decode()
|
|
plt.close()
|
|
|
|
# Estadísticas básicas
|
|
stats = df['valor'].describe()
|
|
|
|
html_result = f"""
|
|
<div class="card">
|
|
<div class="card-header bg-success text-white">
|
|
<h4>Gráfico Generado</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="text-center mb-4">
|
|
<img src="data:image/png;base64,{img_base64}" class="img-fluid" style="max-width: 100%;">
|
|
</div>
|
|
|
|
<h5>Estadísticas:</h5>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<table class="table table-sm">
|
|
<tr><td><strong>Media:</strong></td><td>{stats['mean']:.2f}</td></tr>
|
|
<tr><td><strong>Mediana:</strong></td><td>{stats['50%']:.2f}</td></tr>
|
|
<tr><td><strong>Desv. Estándar:</strong></td><td>{stats['std']:.2f}</td></tr>
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<table class="table table-sm">
|
|
<tr><td><strong>Mínimo:</strong></td><td>{stats['min']:.2f}</td></tr>
|
|
<tr><td><strong>Máximo:</strong></td><td>{stats['max']:.2f}</td></tr>
|
|
<tr><td><strong>Rango:</strong></td><td>{stats['max'] - stats['min']:.2f}</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<a href="{url_for('index')}" class="btn btn-primary">Volver</a>
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
return html_result
|
|
|
|
except Exception as e:
|
|
return f"""
|
|
<div class="alert alert-danger">
|
|
<h4>Error</h4>
|
|
<p>Error creando el gráfico: {str(e)}</p>
|
|
<a href="{url_for('index')}" class="btn btn-primary">Volver</a>
|
|
</div>
|
|
"""
|
|
|
|
@app.route('/api/status')
|
|
def api_status():
|
|
"""Endpoint API que devuelve estado en JSON"""
|
|
status_data = {
|
|
'status': 'running',
|
|
'timestamp': datetime.now().isoformat(),
|
|
'workspace': WORKSPACE_PATH,
|
|
'parameters': PARAMETERS,
|
|
'environment': ENVIRONMENT,
|
|
'pid': os.getpid(),
|
|
'python_version': sys.version,
|
|
'files_in_workspace': os.listdir(WORKSPACE_PATH)
|
|
}
|
|
|
|
return jsonify(status_data)
|
|
|
|
@app.route('/files')
|
|
def file_browser():
|
|
"""Explorador simple de archivos del workspace"""
|
|
try:
|
|
files = []
|
|
for root, dirs, filenames in os.walk(WORKSPACE_PATH):
|
|
for filename in filenames:
|
|
full_path = os.path.join(root, filename)
|
|
rel_path = os.path.relpath(full_path, WORKSPACE_PATH)
|
|
stat = os.stat(full_path)
|
|
files.append({
|
|
'path': rel_path,
|
|
'size': stat.st_size,
|
|
'modified': datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
|
|
})
|
|
|
|
html_template = """
|
|
<div class="card">
|
|
<div class="card-header bg-secondary text-white">
|
|
<h4>Explorador de Archivos - Workspace</h4>
|
|
<small>{{ workspace }}</small>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if files %}
|
|
<div class="table-responsive">
|
|
<table class="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Archivo</th>
|
|
<th>Tamaño</th>
|
|
<th>Modificado</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for file in files %}
|
|
<tr>
|
|
<td><code>{{ file.path }}</code></td>
|
|
<td>{{ file.size }} bytes</td>
|
|
<td>{{ file.modified }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<p class="text-muted">No hay archivos en el workspace</p>
|
|
{% endif %}
|
|
|
|
<a href="{{ url_for('index') }}" class="btn btn-primary">Volver</a>
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
return render_template_string(html_template, files=files, workspace=WORKSPACE_PATH)
|
|
|
|
except Exception as e:
|
|
return f"""
|
|
<div class="alert alert-danger">
|
|
<h4>Error</h4>
|
|
<p>Error explorando archivos: {str(e)}</p>
|
|
<a href="{url_for('index')}" class="btn btn-primary">Volver</a>
|
|
</div>
|
|
"""
|
|
|
|
# Endpoint especial para testing
|
|
@app.route('/test-proxy')
|
|
def test_proxy():
|
|
"""Endpoint para probar que el proxy funciona correctamente"""
|
|
return jsonify({
|
|
'message': 'Proxy funcionando correctamente!',
|
|
'timestamp': datetime.now().isoformat(),
|
|
'request_info': {
|
|
'method': request.method,
|
|
'path': request.path,
|
|
'args': dict(request.args),
|
|
'headers': dict(request.headers)
|
|
}
|
|
})
|
|
|
|
print("Script de ejemplo cargado y listo para recibir peticiones")
|
|
print(f"Rutas disponibles:")
|
|
print(" / - Página principal")
|
|
print(" /generate-data - Generar datos de ejemplo")
|
|
print(" /create-chart - Crear gráfico")
|
|
print(" /api/status - Estado del script (JSON)")
|
|
print(" /files - Explorador de archivos")
|
|
print(" /test-proxy - Test del proxy")
|
|
print(" /health - Health check (automático)") |