Reestructuración modular integral de servicios, reorganización de gestión en docs/ y limpieza profunda del repositorio

This commit is contained in:
Ricardo Monla
2026-03-06 18:03:07 -03:00
parent 15725e0b3c
commit ac9bdf516b
286 changed files with 36187 additions and 112775 deletions
+620
View File
@@ -0,0 +1,620 @@
#!/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("bitacoras-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