Files
dtic-DIIAA/tools/analizar_dashboard_p2601.rb
T

621 lines
22 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env ruby
# frozen_string_literal: true
# tools/analizar_dashboard_p2601.rb — Analizador del dashboard P2601 para proponer hitos adicionales
# ================================================================================
# Este script analiza el estado actual del dashboard P2601 y propone hitos
# intermedios adicionales para la fase de implementación final, utilizando
# herramientas ADN existentes y mejorándolas según sea necesario.
#
# Características:
# • Análisis del dashboard actual (HTML estático)
# • Integración con base de datos dtic-BITACORAs
# • Uso de herramientas ADN existentes
# • Propuesta de hitos basados en mejores prácticas
# • Generación de reporte estructurado
#
# Uso:
# ruby tools/analizar_dashboard_p2601.rb [OPCIONES]
#
# Opciones:
# --analizar-html Analizar dashboard/index.html actual
# --consultar-bd Consultar estado en base de datos
# --proponer-hitos Proponer hitos adicionales
# --generar-reporte Generar reporte completo
# --integrar-adn Integrar con herramientas ADN existentes
# --todo Ejecutar todas las opciones
require 'pg'
require 'yaml'
require 'json'
require 'nokogiri'
require 'optparse'
require 'date'
class AnalizadorDashboardP2601
def initialize(opciones = {})
@opciones = opciones
@config = cargar_configuracion_bd
@conn = nil
@dashboard_html = nil
@hitos_actuales = []
@hitos_propuestos = []
@estadisticas = {}
end
def ejecutar
puts "🔍 ANALIZADOR DE DASHBOARD P2601 - PROYECTO DASUTEN"
puts "=" * 70
puts "Fecha: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
puts ""
if @opciones[:todo] || @opciones[:analizar_html]
analizar_dashboard_html
end
if @opciones[:todo] || @opciones[:consultar_bd]
consultar_base_datos
end
if @opciones[:todo] || @opciones[:proponer_hitos]
proponer_hitos_adicionales
end
if @opciones[:todo] || @opciones[:generar_reporte]
generar_reporte_completo
end
if @opciones[:todo] || @opciones[:integrar_adn]
integrar_con_herramientas_adn
end
mostrar_resumen
rescue => e
puts "❌ Error durante el análisis: #{e.message}"
puts e.backtrace if @opciones[:debug]
exit 1
ensure
desconectar_bd if @conn
end
private
def cargar_configuracion_bd
config_path = File.join(__dir__, 'config', 'database.yml')
unless File.exist?(config_path)
puts "⚠️ Archivo de configuración no encontrado: #{config_path}"
return {}
end
config = YAML.load_file(config_path)
config['development'] || config['production'] || config['test']
end
def conectar_bd
return unless @config && @config['host']
puts "🔌 Conectando a PostgreSQL: #{@config['host']}:#{@config['port']}/#{@config['database']}"
@conn = PG.connect(
host: @config['host'],
port: @config['port'],
dbname: @config['database'],
user: @config['username'],
password: @config['password']
)
puts "✅ Conexión establecida\n"
rescue PG::Error => e
puts "⚠️ Error de conexión a BD: #{e.message}"
puts " Continuando sin conexión a base de datos\n"
end
def desconectar_bd
@conn&.close
puts "\n🔌 Conexión cerrada" if @conn
end
def analizar_dashboard_html
puts "📋 ANALIZANDO DASHBOARD HTML ACTUAL"
puts "-" * 50
dashboard_path = File.join(__dir__, '..', '..', 'dashboard', 'index.html')
unless File.exist?(dashboard_path)
puts "❌ Archivo dashboard no encontrado: #{dashboard_path}"
return
end
@dashboard_html = File.read(dashboard_path)
doc = Nokogiri::HTML(@dashboard_html)
# Extraer hitos del timeline
@hitos_actuales = []
doc.css('.tl-item').each do |item|
hito = {
id: item.css('.tl-id').text.strip,
fecha: item.css('.tl-date').text.strip,
titulo: item.css('.tl-title').text.strip,
descripcion: item.css('.tl-desc').text.strip.gsub(/\s+/, ' '),
estado: determinar_estado_hito(item),
tags: item.css('.tag').map(&:text).map(&:strip)
}
@hitos_actuales << hito
end
# Extraer nodos
nodos = []
doc.css('.node-card').each do |node|
nodos << {
nombre: node.css('.node-name').text.strip,
estado: node.css('.status-dot').attr('title')&.value || 'desconocido',
detalles: node.css('.node-detail').text.strip.gsub(/\s+/, ' ')
}
end
# Extraer métricas
metricas = {}
doc.css('.metric').each do |metric|
label = metric.css('.label').text.strip
value = metric.css('.value').text.strip
metricas[label] = value if label && value
end
@estadisticas[:hitos_total] = @hitos_actuales.size
@estadisticas[:hitos_completados] = @hitos_actuales.count { |h| h[:estado] == 'completado' }
@estadisticas[:hitos_activos] = @hitos_actuales.count { |h| h[:estado] == 'activo' }
@estadisticas[:hitos_pendientes] = @hitos_actuales.count { |h| h[:estado] == 'pendiente' }
@estadisticas[:nodos_total] = nodos.size
@estadisticas[:nodos_online] = nodos.count { |n| n[:estado] == 'Online' }
puts "📊 Estadísticas extraídas:"
puts " • Hitos totales: #{@estadisticas[:hitos_total]}"
puts " • Hitos completados: #{@estadisticas[:hitos_completados]}"
puts " • Hitos activos: #{@estadisticas[:hitos_activos]}"
puts " • Hitos pendientes: #{@estadisticas[:hitos_pendientes]}"
puts " • Nodos totales: #{@estadisticas[:nodos_total]}"
puts " • Nodos online: #{@estadisticas[:nodos_online]}"
# Mostrar últimos hitos
puts "\n📅 ÚLTIMOS HITOS REGISTRADOS:"
@hitos_actuales.last(5).each do |hito|
estado_emoji = case hito[:estado]
when 'completado' then '✅'
when 'activo' then '🔵'
when 'pendiente' then '⏳'
else '❓'
end
puts " #{estado_emoji} #{hito[:id]}: #{hito[:titulo][0..60]}..."
end
puts ""
end
def determinar_estado_hito(item)
dot_class = item.css('.tl-dot').attr('class').to_s
if dot_class.include?('done')
'completado'
elsif dot_class.include?('active')
'activo'
elsif dot_class.include?('pending')
'pendiente'
else
'desconocido'
end
end
def consultar_base_datos
puts "📋 CONSULTANDO BASE DE DATOS dtic-BITACORAs"
puts "-" * 50
conectar_bd
return unless @conn
begin
# Consultar si existen tablas de proyectos
tablas = @conn.exec("SELECT table_name FROM information_schema.tables WHERE table_schema = 'bitacoras'")
tablas_proyecto = tablas.select { |t| t['table_name'] =~ /proyectos|fases|hitos/ }
if tablas_proyecto.any?
puts "✅ Tablas de proyectos encontradas: #{tablas_proyecto.map { |t| t['table_name'] }.join(', ')}"
# Consultar proyecto P2601
proyecto = @conn.exec_params(
"SELECT * FROM bitacoras.proyectos WHERE codigo = $1",
['P2601']
)
if proyecto.ntuples > 0
puts "✅ Proyecto P2601 encontrado en base de datos"
proyecto_info = proyecto[0]
puts " • Nombre: #{proyecto_info['nombre']}"
puts " • Estado: #{proyecto_info['estado']}"
puts " • Descripción: #{proyecto_info['descripcion'][0..100]}..." if proyecto_info['descripcion']
# Consultar fases del proyecto
fases = @conn.exec_params(
"SELECT * FROM bitacoras.fases WHERE proyecto_id = $1 ORDER BY numero",
[proyecto_info['id']]
)
puts " • Fases: #{fases.ntuples} fases encontradas"
fases.each do |fase|
puts " - Fase #{fase['numero']}: #{fase['nombre']} (#{fase['estado']})"
end
else
puts "⚠️ Proyecto P2601 no encontrado en base de datos"
end
else
puts "️ Tablas de proyectos no encontradas en el esquema 'bitacoras'"
puts " Considerar crear estructura: proyectos, fases, hitos"
end
# Consultar estadísticas de entradas relacionadas con DASUTEN
entradas_dasuten = @conn.exec_params(
"SELECT COUNT(*) as total FROM bitacoras.entradas WHERE descripcion ILIKE $1",
['%dasuten%']
)
if entradas_dasuten.ntuples > 0
total = entradas_dasuten[0]['total'].to_i
puts "📝 Entradas relacionadas con DASUTEN: #{total}"
@estadisticas[:entradas_dasuten] = total
end
rescue PG::Error => e
puts "⚠️ Error consultando base de datos: #{e.message}"
end
puts ""
end
def proponer_hitos_adicionales
puts "🎯 PROPONIENDO HITOS ADICIONALES PARA FASE DE IMPLEMENTACIÓN"
puts "-" * 50
# Basado en el análisis del dashboard y mejores prácticas
hitos_propuestos = [
{
id: "BKP02",
titulo: "Backup Automático de VMs en Proxmox",
descripcion: "Configurar backups automáticos VZDump programados para DC y SQL Server. Establecer retención de 7 días, almacenamiento en NAS/SMB.",
categoria: "Infraestructura",
prioridad: "alta",
estimacion_horas: 2,
prerequisitos: ["FÍSICO"],
herramientas: ["Proxmox VE", "VZDump", "Scripts Bash"]
},
{
id: "MON01",
titulo: "Monitoreo de Recursos del Hipervisor",
descripcion: "Implementar sistema de monitoreo básico para CPU, RAM, almacenamiento y red de srv-dasu. Alertas por email/Telegram.",
categoria: "Monitoreo",
prioridad: "media",
estimacion_horas: 3,
prerequisitos: ["FÍSICO"],
herramientas: ["Prometheus Node Exporter", "Grafana", "Alertmanager"]
},
{
id: "DB02",
titulo: "Backup Automático de Base de Datos sysdasuten",
descripcion: "Configurar jobs de SQL Server Agent para backup diario completo y log cada 15 minutos. Verificar restauración periódica.",
categoria: "Base de Datos",
prioridad: "alta",
estimacion_horas: 2,
prerequisitos: ["ADDOM"],
herramientas: ["SQL Server Management Studio", "SQL Agent", "PowerShell"]
},
{
id: "SEC01",
titulo: "Auditoría de Seguridad del Dominio",
descripcion: "Revisar y configurar políticas de auditoría en DC. Logs de acceso, cambios en AD, intentos fallidos. Centralización de logs.",
categoria: "Seguridad",
prioridad: "media",
estimacion_horas: 4,
prerequisitos: ["GPO"],
herramientas: ["Windows Event Viewer", "Audit Policy", "SIEM básico"]
},
{
id: "DOC01",
titulo: "Documentación Operativa del Sistema",
descripcion: "Crear manual de operaciones: procedimientos de backup/restore, acceso remoto, resolución de problemas comunes, contactos de soporte.",
categoria: "Documentación",
prioridad: "media",
estimacion_horas: 6,
prerequisitos: ["UAT"],
herramientas: ["Markdown", "Git", "Herramientas ADN"]
},
{
id: "RED01",
titulo: "Optimización de Red y Firewall",
descripcion: "Revisar reglas de firewall en todas las VMs. Optimizar rutas Tailscale. Configurar QoS si es necesario.",
categoria: "Red",
prioridad: "baja",
estimacion_horas: 3,
prerequisitos: ["NET01"],
herramientas: ["Windows Firewall", "Tailscale", "PowerShell"]
},
{
id: "PERF01",
titulo: "Pruebas de Rendimiento del Sistema",
descripcion: "Realizar pruebas de carga simulada en sistema DASUTEN. Medir tiempos de respuesta, consumo de recursos, identificar cuellos de botella.",
categoria: "Performance",
prioridad: "media",
estimacion_horas: 4,
prerequisitos: ["CLI"],
herramientas: ["SQL Profiler", "Performance Monitor", "Scripts de prueba"]
},
{
id: "MIG02",
titulo: "Plan de Migración de Datos Final",
descripcion: "Desarrollar script/procedimiento para migración incremental de datos desde sistema antiguo. Validar integridad de datos migrados.",
categoria: "Migración",
prioridad: "alta",
estimacion_horas: 8,
prerequisitos: ["P02.APP"],
herramientas: ["SQL Server Integration Services", "PowerShell", "Validación de datos"]
}
]
@hitos_propuestos = hitos_propuestos
puts "💡 HITOS PROPUESTOS (#{hitos_propuestos.size} nuevos):"
puts ""
hitos_propuestos.each do |hito|
prioridad_emoji = case hito[:prioridad]
when 'alta' then '🔴'
when 'media' then '🟡'
when 'baja' then '🟢'
else '⚪'
end
puts " #{prioridad_emoji} #{hito[:id]}: #{hito[:titulo]}"
puts " 📍 Categoría: #{hito[:categoria]}"
puts " ⏱️ Estimación: #{hito[:estimacion_horas]} horas"
puts " 📋 Prerequisitos: #{hito[:prerequisitos].join(', ')}" if hito[:prerequisitos].any?
puts " 🛠️ Herramientas: #{hito[:herramientas].join(', ')}"
puts ""
end
# Estadísticas de propuestas
categorias = hitos_propuestos.group_by { |h| h[:categoria] }
puts "📈 DISTRIBUCIÓN POR CATEGORÍA:"
categorias.each do |categoria, hitos|
puts " • #{categoria}: #{hitos.size} hitos"
end
puts ""
end
def generar_reporte_completo
puts "📊 GENERANDO REPORTE COMPLETO"
puts "-" * 50
reporte = {
metadata: {
generado_en: Time.now.iso8601,
proyecto: "P2601 - DASUTEN",
version_analizador: "1.0.0"
},
estado_actual: @estadisticas,
hitos_actuales: @hitos_actuales,
hitos_propuestos: @hitos_propuestos,
recomendaciones: generar_recomendaciones,
acciones_sugeridas: generar_acciones_sugeridas
}
# Guardar reporte JSON
reporte_path = File.join(__dir__, '..', '..', 'tmp', 'reporte_p2601_analisis.json')
FileUtils.mkdir_p(File.dirname(reporte_path))
File.write(reporte_path, JSON.pretty_generate(reporte))
puts "✅ Reporte guardado en: #{reporte_path}"
puts " • Tamaño: #{File.size(reporte_path)} bytes"
puts " • Elementos: #{reporte[:hitos_actuales].size} hitos actuales, #{reporte[:hitos_propuestos].size} hitos propuestos"
puts ""
end
def generar_recomendaciones
recomendaciones = []
# Recomendaciones basadas en análisis
if @estadisticas[:hitos_pendientes] && @estadisticas[:hitos_pendientes] > 3
recomendaciones << "Priorizar hitos pendientes críticos (FÍSICO, ADDOM) antes de agregar nuevos"
end
if @estadisticas[:entradas_dasuten] && @estadisticas[:entradas_dasuten] < 20
recomendaciones << "Aumentar registro de actividades específicas de DASUTEN en bitácoras"
end
recomendaciones << "Integrar hitos propuestos en timeline del dashboard de forma gradual"
recomendaciones << "Utilizar herramientas ADN para automatizar seguimiento de hitos"
recomendaciones << "Considerar implementar sistema de notificaciones para hitos críticos"
recomendaciones << "Documentar cada hito completado con lecciones aprendidas"
recomendaciones
end
def generar_acciones_sugeridas
acciones = [
{
accion: "Actualizar dashboard HTML",
descripcion: "Agregar hitos propuestos al timeline",
prioridad: "media",
herramientas: ["HTML/CSS", "Herramientas ADN"]
},
{
accion: "Crear estructura en base de datos",
descripcion: "Implementar tablas proyectos/fases/hitos si no existen",
prioridad: "alta",
herramientas: ["PostgreSQL", "SQL migrations"]
},
{
accion: "Integrar con sistema de búsqueda",
descripcion: "Conectar hitos con sistema de búsqueda ADN BITACORAs",
prioridad: "media",
herramientas: ["BuscadorBDSimple", "API REST"]
},
{
accion: "Automatizar actualizaciones",
descripcion: "Crear script para actualizar dashboard desde base de datos",
prioridad: "baja",
herramientas: ["Ruby", "Cron jobs", "Herramientas ADN"]
},
{
accion: "Implementar métricas avanzadas",
descripcion: "Agregar KPIs de progreso, desviaciones, pronósticos",
prioridad: "baja",
herramientas: ["JavaScript", "Chart.js", "API"]
}
]
acciones
end
def integrar_con_herramientas_adn
puts "🔄 INTEGRANDO CON HERRAMIENTAS ADN EXISTENTES"
puts "-" * 50
puts "🔍 Herramientas ADN disponibles:"
# Verificar herramientas existentes
herramientas_adn = [
{ nombre: "Sistema de búsqueda", archivo: "buscador_bd_simple.rb", estado: verificar_herramienta("adn/db/core/buscador_bd_simple.rb") },
{ nombre: "Seguimiento de plan", archivo: "subcomando_plan.rb", estado: verificar_herramienta("adn/subcomando_plan.rb") },
{ nombre: "Operaciones BD", archivo: "subcomando_db.rb", estado: verificar_herramienta("adn/subcomando_db.rb") },
{ nombre: "Sistema de triggers", archivo: "subcomando_triggers.rb", estado: verificar_herramienta("adn/subcomando_triggers.rb") },
{ nombre: "Logger ADN", archivo: "logger.rb", estado: verificar_herramienta("adn/logger.rb") }
]
herramientas_adn.each do |herramienta|
estado_emoji = herramienta[:estado] ? "✅" : "❌"
puts " #{estado_emoji} #{herramienta[:nombre]}: #{herramienta[:archivo]}"
end
puts "\n💡 OPORTUNIDADES DE INTEGRACIÓN:"
puts " 1. Usar BuscadorBDSimple para buscar hitos relacionados con DASUTEN"
puts " 2. Integrar hitos del dashboard con el sistema de seguimiento de plan ADN"
puts " 3. Usar subcomando_db para registrar hitos completados en base de datos"
puts " 4. Implementar triggers para notificar cambios en estado de hitos"
puts " 5. Usar logger ADN para registrar actividades de seguimiento"
puts "\n🚀 ACCIONES RECOMENDADAS:"
puts " • Crear comando: adn proyecto:hitos --proyecto P2601"
puts " • Integrar dashboard con API de proyectos en dtic-BITACORAs"
puts " • Automatizar actualización de métricas del dashboard"
puts " • Implementar sistema de notificaciones para hitos críticos"
puts ""
end
def verificar_herramienta(ruta_relativa)
File.exist?(File.join(__dir__, '..', ruta_relativa))
end
def mostrar_resumen
puts "=" * 70
puts "📊 RESUMEN DEL ANÁLISIS DASHBOARD P2601"
puts "=" * 70
puts "\n🎯 ESTADO ACTUAL:"
puts " • Hitos totales: #{@estadisticas[:hitos_total] || 'N/A'}"
puts " • Completados: #{@estadisticas[:hitos_completados] || 'N/A'}"
puts " • Activos: #{@estadisticas[:hitos_activos] || 'N/A'}"
puts " • Pendientes: #{@estadisticas[:hitos_pendientes] || 'N/A'}"
puts " • Nodos online: #{@estadisticas[:nodos_online] || 'N/A'}/#{@estadisticas[:nodos_total] || 'N/A'}"
puts "\n💡 HITOS PROPUESTOS: #{@hitos_propuestos.size}"
categorias = @hitos_propuestos.group_by { |h| h[:categoria] }
categorias.each do |categoria, hitos|
puts " • #{categoria}: #{hitos.size} hitos"
end
puts "\n🔄 INTEGRACIÓN ADN:"
puts " • Herramientas disponibles: 5 verificadas"
puts " • Oportunidades identificadas: 5 áreas"
puts "\n📈 RECOMENDACIONES PRINCIPALES:"
puts " 1. Priorizar hitos FÍSICO y ADDOM antes de agregar nuevos"
puts " 2. Integrar hitos propuestos gradualmente en el dashboard"
puts " 3. Usar herramientas ADN para automatizar seguimiento"
puts " 4. Considerar implementar sistema de notificaciones"
puts " 5. Documentar lecciones aprendidas de cada hito"
puts "\n🚀 PRÓXIMOS PASOS SUGERIDOS:"
puts " 1. Ejecutar: ruby tools/analizar_dashboard_p2601.rb --proponer-hitos"
puts " 2. Revisar reporte en tmp/reporte_p2601_analisis.json"
puts " 3. Actualizar dashboard/index.html con hitos adicionales"
puts " 4. Integrar con sistema de búsqueda ADN BITACORAs"
puts " 5. Automatizar actualizaciones del dashboard"
puts "\n📅 " + Time.now.strftime('%Y-%m-%d %H:%M:%S')
puts "=" * 70
end
end
# Parsear opciones de línea de comandos
if __FILE__ == $0
opciones = {
analizar_html: false,
consultar_bd: false,
proponer_hitos: false,
generar_reporte: false,
integrar_adn: false,
debug: false
}
OptionParser.new do |opts|
opts.banner = "Uso: ruby tools/analizar_dashboard_p2601.rb [OPCIONES]"
opts.on("--analizar-html", "Analizar dashboard/index.html actual") do
opciones[:analizar_html] = true
end
opts.on("--consultar-bd", "Consultar estado en base de datos") do
opciones[:consultar_bd] = true
end
opts.on("--proponer-hitos", "Proponer hitos adicionales") do
opciones[:proponer_hitos] = true
end
opts.on("--generar-reporte", "Generar reporte completo") do
opciones[:generar_reporte] = true
end
opts.on("--integrar-adn", "Integrar con herramientas ADN existentes") do
opciones[:integrar_adn] = true
end
opts.on("--todo", "Ejecutar todas las opciones") do
opciones.each_key { |k| opciones[k] = true }
end
opts.on("--debug", "Mostrar información de depuración") do
opciones[:debug] = true
end
opts.on("-h", "--help", "Mostrar ayuda") do
puts opts
exit
end
end.parse!
# Si no se especificó ninguna opción, mostrar ayuda
if opciones.values.none? { |v| v == true }
puts "Ejecutar con --help para ver opciones disponibles"
puts "Ejemplo: ruby tools/analizar_dashboard_p2601.rb --todo"
exit
end
# Ejecutar analizador
begin
analizador = AnalizadorDashboardP2601.new(opciones)
analizador.ejecutar
rescue => e
puts "❌ Error fatal: #{e.message}"
puts e.backtrace if opciones[:debug]
exit 1
end
end