SIDEL_ScriptsManager/data/script_groups/examples/example_flask_app.py

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)")