Files
dtic-DIIAA/tools/orquestador/lib/verificacion/manager.rb
T

583 lines
19 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/orquestador/lib/verificacion/manager.rb
# ============================================
# Clase para verificación centralizada del dashboard P2601
# Filosofía: "Menos es más" - código reutilizable y modular
require_relative '../dashboard_core'
module DashboardCore
class VerificacionManager < Base
# Reglas de verificación
REGLAS_VERIFICACION = {
estructura: {
nivel: 'crítico',
descripcion: 'Estructura HTML básica del dashboard',
verificar: ->(contenido) {
elementos = {
'DOCTYPE html' => contenido.include?('<!DOCTYPE html>'),
'head' => contenido.include?('<head>') && contenido.include?('</head>'),
'body' => contenido.include?('<body>') && contenido.include?('</body>'),
'title P2601' => contenido.include?('P2601'),
'container' => contenido.include?('class="container"'),
'header' => contenido.include?('class="header"'),
'footer' => contenido.include?('class="footer"'),
'timeline' => contenido.include?('class="timeline"')
}
elementos
}
},
hitos: {
nivel: 'alto',
descripcion: 'Integridad y formato de hitos',
verificar: ->(contenido) {
hitos = extraer_hitos(contenido)
{
total_hitos: hitos.size,
hitos_validos: hitos.count { |h| h[:id] && h[:titulo] },
ids_validos: hitos.count { |h| HitoIdGenerator.es_valido?(h[:id]) },
duplicados: buscar_hitos_duplicados(contenido).size
}
}
},
estados: {
nivel: 'medio',
descripcion: 'Estados válidos de hitos',
verificar: ->(contenido) {
hitos = extraer_hitos(contenido)
estados_validos = hitos.count { |h| Validator.validar_estado(h[:estado]) }
estados_invalidos = hitos.reject { |h| Validator.validar_estado(h[:estado]) }
{
estados_validos: estados_validos,
estados_invalidos: estados_invalidos.size,
estados_presentes: hitos.map { |h| h[:estado] }.uniq
}
}
},
timeline: {
nivel: 'alto',
descripcion: 'Estructura de la timeline',
verificar: ->(contenido) {
timeline_presente = contenido.include?('class="timeline"')
if timeline_presente
timeline_match = contenido.match(/<div class="timeline">([\s\S]*?)<\/div>\s*<\/div>\s*<\/div>/m)
timeline_content = timeline_match ? timeline_match[1] : ''
hitos_en_timeline = timeline_content.scan(/<div class="tl-item">/).size
else
hitos_en_timeline = 0
end
{
presente: timeline_presente,
hitos_en_timeline: hitos_en_timeline,
estructura_correcta: timeline_presente && hitos_en_timeline > 0
}
}
},
metricas: {
nivel: 'bajo',
descripcion: 'Métricas y estadísticas',
verificar: ->(contenido) {
metricas_encontradas = {
progreso: contenido.include?('Progreso General') || contenido.include?('progressPct'),
hitos_completados: contenido.include?('Hitos Completados'),
hitos_pendientes: contenido.include?('Hitos Pendientes'),
esfuerzo_total: contenido.include?('Esfuerzo Total')
}
valores = extraer_valores_metricas(contenido)
{
metricas_presentes: metricas_encontradas.values.count(true),
valores_encontrados: valores.size,
valores: valores
}
}
}
}.freeze
def initialize(dashboard_path = nil)
super(dashboard_path)
@resultados_cache = nil
end
# Métodos principales de verificación
def verificar_completo
puts "🔍 VERIFICACIÓN COMPLETA DEL DASHBOARD P2601"
puts "=" * 60
contenido = leer_dashboard
resultados = {}
REGLAS_VERIFICACION.each do |nombre, regla|
puts "📋 Verificando: #{regla[:descripcion]}"
resultados[nombre] = ejecutar_verificacion(contenido, regla)
end
@resultados_cache = resultados
mostrar_resumen_verificacion(resultados)
resultados
end
def verificar_estructura
puts "🏗️ VERIFICANDO ESTRUCTURA DEL DASHBOARD"
puts "=" * 60
contenido = leer_dashboard
regla = REGLAS_VERIFICACION[:estructura]
resultado = ejecutar_verificacion(contenido, regla)
mostrar_detalle_estructura(resultado)
resultado
end
def verificar_hitos
puts "🎯 VERIFICANDO HITOS DEL DASHBOARD"
puts "=" * 60
contenido = leer_dashboard
regla = REGLAS_VERIFICACION[:hitos]
resultado = ejecutar_verificacion(contenido, regla)
mostrar_detalle_hitos(resultado, contenido)
resultado
end
def verificar_estados
puts "📊 VERIFICANDO ESTADOS DE HITOS"
puts "=" * 60
contenido = leer_dashboard
regla = REGLAS_VERIFICACION[:estados]
resultado = ejecutar_verificacion(contenido, regla)
mostrar_detalle_estados(resultado, contenido)
resultado
end
def analizar_hitos_detallado
puts "📋 ANÁLISIS DETALLADO DE HITOS"
puts "=" * 60
contenido = leer_dashboard
hitos = extraer_hitos(contenido)
if hitos.empty?
puts "️ No se encontraron hitos en el dashboard"
return nil
end
analisis = {
total: hitos.size,
por_estado: analizar_por_estado(hitos),
por_fase: analizar_por_fase(hitos),
por_tag: analizar_por_tag(hitos),
duplicados: buscar_hitos_duplicados(contenido),
ids_invalidos: hitos.reject { |h| HitoIdGenerator.es_valido?(h[:id]) }
}
mostrar_analisis_detallado(analisis, hitos)
analisis
end
def generar_reporte_verificacion
resultados = @resultados_cache || verificar_completo
reporte = "📋 REPORTE DE VERIFICACIÓN - DASHBOARD P2601\n"
reporte += "=" * 70 + "\n"
reporte += "Fecha: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}\n"
reporte += "Dashboard: #{CONFIG[:production_url]}\n\n"
# Resumen ejecutivo
reporte += "📊 RESUMEN EJECUTIVO\n"
reporte += "-" * 40 + "\n"
total_reglas = resultados.size
reglas_exitosas = resultados.values.count { |r| r[:exito] }
porcentaje_exito = (reglas_exitosas * 100.0 / total_reglas).round(1)
reporte += "Reglas verificadas: #{total_reglas}\n"
reporte += "Reglas exitosas: #{reglas_exitosas}\n"
reporte += "Porcentaje de éxito: #{porcentaje_exito}%\n\n"
# Detalle por regla
reporte += "🔍 DETALLE POR REGLA DE VERIFICACIÓN\n"
reporte += "-" * 40 + "\n"
resultados.each do |nombre, resultado|
regla = REGLAS_VERIFICACION[nombre]
emoji = resultado[:exito] ? '✅' : '❌'
reporte += "#{emoji} #{regla[:descripcion]} (#{regla[:nivel].upcase})\n"
if resultado[:detalles].is_a?(Hash)
resultado[:detalles].each do |detalle, valor|
reporte += " • #{detalle}: #{valor}\n"
end
end
unless resultado[:exito]
reporte += " ⚠️ Problemas detectados:\n"
resultado[:problemas].each do |problema|
reporte += " - #{problema}\n"
end
end
reporte += "\n"
end
# Recomendaciones
reporte += "💡 RECOMENDACIONES\n"
reporte += "-" * 40 + "\n"
recomendaciones = generar_recomendaciones(resultados)
if recomendaciones.any?
recomendaciones.each do |recomendacion|
reporte += "• #{recomendacion}\n"
end
else
reporte += "✅ Todas las verificaciones pasaron exitosamente.\n"
end
reporte += "\n🔗 Dashboard: #{CONFIG[:production_url]}\n"
reporte += "📅 Reporte generado: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}\n"
reporte
end
private
def ejecutar_verificacion(contenido, regla)
begin
detalles = regla[:verificar].call(contenido)
problemas = identificar_problemas(detalles, regla[:nivel])
{
exito: problemas.empty?,
detalles: detalles,
problemas: problemas,
nivel: regla[:nivel],
descripcion: regla[:descripcion]
}
rescue => e
{
exito: false,
detalles: { error: e.message },
problemas: ["Error durante verificación: #{e.message}"],
nivel: regla[:nivel],
descripcion: regla[:descripcion]
}
end
end
def identificar_problemas(detalles, nivel)
problemas = []
case nivel
when 'crítico'
# Para estructura, todos los elementos deben estar presentes
if detalles.is_a?(Hash) && detalles.key?('DOCTYPE html')
detalles.each do |elemento, presente|
problemas << "Falta elemento: #{elemento}" unless presente
end
end
when 'alto'
# Para hitos, no debe haber duplicados y todos deben ser válidos
if detalles.is_a?(Hash)
if detalles[:duplicados] && detalles[:duplicados] > 0
problemas << "Se encontraron #{detalles[:duplicados]} hitos duplicados"
end
if detalles[:total_hitos] && detalles[:hitos_validos]
if detalles[:total_hitos] > 0 && detalles[:hitos_validos] < detalles[:total_hitos]
problemas << "#{detalles[:total_hitos] - detalles[:hitos_validos]} hitos no válidos"
end
end
end
when 'medio'
# Para estados, todos deben ser válidos
if detalles.is_a?(Hash) && detalles[:estados_invalidos] && detalles[:estados_invalidos] > 0
problemas << "Se encontraron #{detalles[:estados_invalidos]} estados inválidos"
end
end
problemas
end
def extraer_valores_metricas(contenido)
valores = {}
# Buscar porcentaje de progreso
if match = contenido.match(/id="progressPct">([^<]+)</)
valores['Progreso'] = match[1]
end
# Buscar hitos completados
if match = contenido.match(/Hitos Completados[^<]*<[^>]*>([^<]+)</)
valores['Hitos Completados'] = match[1]
end
# Buscar hitos pendientes
if match = contenido.match(/Hitos Pendientes[^<]*<[^>]*>([^<]+)</)
valores['Hitos Pendientes'] = match[1]
end
# Buscar esfuerzo total
if match = contenido.match(/Esfuerzo Total[^<]*<[^>]*>([^<]+)</)
valores['Esfuerzo Total'] = match[1]
end
valores
end
def analizar_por_estado(hitos)
conteo = Hash.new(0)
hitos.each { |h| conteo[h[:estado]] += 1 }
conteo
end
def analizar_por_fase(hitos)
conteo = Hash.new(0)
hitos.each do |hito|
if info = HitoIdGenerator.parsear_id(hito[:id])
conteo[info[:fase]] += 1
else
conteo['sin_fase'] += 1
end
end
conteo
end
def analizar_por_tag(hitos)
conteo = Hash.new(0)
hitos.each do |hito|
hito[:tags].each { |tag| conteo[tag] += 1 }
end
conteo
end
def mostrar_resumen_verificacion(resultados)
puts "\n📊 RESUMEN DE VERIFICACIÓN"
puts "=" * 60
total_reglas = resultados.size
reglas_exitosas = resultados.values.count { |r| r[:exito] }
reglas_fallidas = total_reglas - reglas_exitosas
puts "Reglas verificadas: #{total_reglas}"
puts "Reglas exitosas: #{reglas_exitosas}"
puts "Reglas fallidas: #{reglas_fallidas}"
puts "Porcentaje de éxito: #{(reglas_exitosas * 100.0 / total_reglas).round(1)}%"
# Mostrar resumen por nivel
puts "\n📈 DISTRIBUCIÓN POR NIVEL DE IMPORTANCIA:"
['crítico', 'alto', 'medio', 'bajo'].each do |nivel|
reglas_nivel = resultados.select { |_, r| r[:nivel] == nivel }
exitos_nivel = reglas_nivel.count { |_, r| r[:exito] }
total_nivel = reglas_nivel.size
if total_nivel > 0
puts " • #{nivel.capitalize}: #{exitos_nivel}/#{total_nivel} exitosas"
end
end
# Mostrar problemas críticos
problemas_criticos = resultados.select { |_, r| !r[:exito] && r[:nivel] == 'crítico' }
if problemas_criticos.any?
puts "\n⚠️ PROBLEMAS CRÍTICOS DETECTADOS:"
problemas_criticos.each do |nombre, resultado|
puts " • #{resultado[:descripcion]}"
resultado[:problemas].each do |problema|
puts " - #{problema}"
end
end
end
if reglas_exitosas == total_reglas
puts "\n🎉 ¡Todas las verificaciones pasaron exitosamente!"
else
puts "\n⚠️ Se encontraron problemas que requieren atención"
end
end
def mostrar_detalle_estructura(resultado)
puts "\n🏗️ DETALLE DE ESTRUCTURA:"
if resultado[:exito]
puts "✅ Estructura HTML básica correcta"
else
puts "❌ Problemas en la estructura:"
resultado[:problemas].each do |problema|
puts " • #{problema}"
end
end
puts "\n📋 ELEMENTOS VERIFICADOS:"
resultado[:detalles].each do |elemento, presente|
puts " • #{elemento}: #{presente ? '✅ Presente' : '❌ Ausente'}"
end
end
def mostrar_detalle_hitos(resultado, contenido)
hitos = extraer_hitos(contenido)
puts "\n🎯 DETALLE DE HITOS:"
puts " • Total de hitos: #{resultado[:detalles][:total_hitos]}"
puts " • Hitos válidos: #{resultado[:detalles][:hitos_validos]}"
puts " • IDs válidos: #{resultado[:detalles][:ids_validos]}"
puts " • Hitos duplicados: #{resultado[:detalles][:duplicados]}"
if resultado[:detalles][:duplicados] > 0
duplicados = buscar_hitos_duplicados(contenido)
puts " • IDs duplicados: #{duplicados.join(', ')}"
end
unless resultado[:exito]
puts "\n⚠️ PROBLEMAS DETECTADOS:"
resultado[:problemas].each do |problema|
puts " • #{problema}"
end
end
end
def mostrar_detalle_estados(resultado, contenido)
hitos = extraer_hitos(contenido)
puts "\n📊 DETALLE DE ESTADOS:"
puts " • Estados válidos: #{resultado[:detalles][:estados_validos]}"
puts " • Estados inválidos: #{resultado[:detalles][:estados_invalidos]}"
puts " • Estados presentes: #{resultado[:detalles][:estados_presentes].join(', ')}"
unless resultado[:exito]
puts "\n⚠️ PROBLEMAS DETECTADOS:"
resultado[:problemas].each do |problema|
puts " • #{problema}"
end
end
if resultado[:detalles][:estados_invalidos] > 0
puts "\n🔍 ESTADOS INVÁLIDOS ENCONTRADOS:"
hitos.each do |hito|
unless Validator.validar_estado(hito[:estado])
puts " • #{hito[:id]}: '#{hito[:estado]}'"
end
end
end
end
def mostrar_analisis_detallado(analisis, hitos)
puts "\n📈 ANÁLISIS DETALLADO DE HITOS"
puts "=" * 60
puts "📊 ESTADÍSTICAS GENERALES:"
puts " • Total de hitos: #{analisis[:total]}"
if analisis[:por_estado].any?
puts "\n📊 DISTRIBUCIÓN POR ESTADO:"
analisis[:por_estado].each do |estado, cantidad|
porcentaje = (cantidad * 100.0 / analisis[:total]).round(1)
estado_info = ESTADOS_VALIDOS[estado] || ESTADOS_VALIDOS['pending']
puts " • #{estado_info[:nombre]}: #{cantidad} (#{porcentaje}%)"
end
end
if analisis[:por_fase].any?
puts "\n📁 DISTRIBUCIÓN POR FASE:"
analisis[:por_fase].each do |fase, cantidad|
puts " • Fase #{fase}: #{cantidad} hitos"
end
end
if analisis[:por_tag].any?
puts "\n🏷️ DISTRIBUCIÓN POR TAG:"
analisis[:por_tag].sort_by { |_, count| -count }.first(10).each do |tag, cantidad|
puts " • #{tag}: #{cantidad} hitos"
end
end
if analisis[:duplicados].any?
puts "\n⚠️ HITOS DUPLICADOS:"
puts " • Total: #{analisis[:duplicados].size}"
puts " • IDs: #{analisis[:duplicados].join(', ')}"
end
if analisis[:ids_invalidos].any?
puts "\n❌ IDs DE HITOS INVÁLIDOS:"
analisis[:ids_invalidos].each do |hito|
puts " • #{hito[:id]}: #{hito[:titulo]}"
end
end
# Mostrar hitos por fase y estado
puts "\n🎯 HITOS POR FASE Y ESTADO:"
hitos.group_by do |hito|
info = HitoIdGenerator.parsear_id(hito[:id])
info ? info[:fase] : 'sin_fase'
end.each do |fase, hitos_fase|
puts " 📁 Fase #{fase}:"
hitos_fase.group_by { |h| h[:estado] }.each do |estado, hitos_estado|
estado_info = ESTADOS_VALIDOS[estado] || ESTADOS_VALIDOS['pending']
puts " • #{estado_info[:nombre]}: #{hitos_estado.size}"
end
end
end
def generar_recomendaciones(resultados)
recomendaciones = []
# Recomendaciones basadas en problemas encontrados
resultados.each do |nombre, resultado|
next if resultado[:exito]
case nombre
when :estructura
recomendaciones << "Corregir estructura HTML básica del dashboard"
when :hitos
if resultado[:detalles][:duplicados] > 0
recomendaciones << "Eliminar hitos duplicados usando la herramienta 'limpiar'"
end
if resultado[:detalles][:total_hitos] > 0 &&
resultado[:detalles][:hitos_validos] < resultado[:detalles][:total_hitos]
recomendaciones << "Corregir hitos con estructura inválida"
end
when :estados
if resultado[:detalles][:estados_invalidos] > 0
recomendaciones << "Corregir estados inválidos de hitos"
end
when :timeline
unless resultado[:detalles][:presente]
recomendaciones << "Agregar sección timeline al dashboard"
end
end
end
# Recomendaciones generales
contenido = leer_dashboard
hitos = extraer_hitos(contenido)
if hitos.empty?
recomendaciones << "Agregar hitos al dashboard usando la herramienta 'agregar_hitos'"
elsif hitos.size < 5
recomendaciones << "Considerar agregar más hitos para mejor seguimiento del proyecto"
end
estados = hitos.map { |h| h[:estado] }
if estados.count('pending') > hitos.size * 0.7
recomendaciones << "Iniciar más hitos pendientes para avanzar en el proyecto"
end
if estados.count('blocked') > 0
recomendaciones << "Revisar hitos bloqueados y resolver dependencias"
end
recomendaciones.uniq
end
end
end