#!/usr/bin/env ruby # frozen_string_literal: true # tools/orquestador/dashboard/verificar.rb # ======================================== # Herramienta para verificar el estado y estructura del dashboard P2601 # Filosofía: "Menos es más" - herramienta pequeña, específica y reutilizable require 'fileutils' class DashboardVerifier def initialize(dashboard_path = nil) @dashboard_path = dashboard_path || default_dashboard_path end def default_dashboard_path File.expand_path('../../../../dashboard/index.html', __FILE__) end def verificar_estructura puts "🔍 VERIFICANDO ESTRUCTURA DEL DASHBOARD P2601" puts "=" * 60 unless File.exist?(@dashboard_path) puts "❌ Error: No se encuentra el dashboard en #{@dashboard_path}" return false end # Leer contenido puts "📖 Analizando dashboard..." content = File.read(@dashboard_path) # Realizar verificaciones resultados = { archivo: verificar_archivo(content), estructura: verificar_estructura_basica(content), hitos: verificar_hitos(content), timeline: verificar_timeline(content), estadisticas: verificar_estadisticas(content) } # Mostrar resumen mostrar_resumen(resultados) # Retornar éxito si todas las verificaciones críticas pasan resultados[:archivo][:exito] && resultados[:estructura][:exito] end def analizar_hitos puts "📋 ANALIZANDO HITOS DEL DASHBOARD" puts "=" * 60 unless File.exist?(@dashboard_path) puts "❌ Error: No se encuentra el dashboard" return false end content = File.read(@dashboard_path) # Extraer información de hitos hitos_info = extraer_info_hitos(content) # Mostrar análisis mostrar_analisis_hitos(hitos_info) true end private def verificar_archivo(content) puts "📄 Verificando archivo..." { exito: true, detalles: { tamaño_bytes: content.length, lineas: content.lines.count, encoding: content.encoding.name, valido_html: content.include?('') && content.include?('') } } end def verificar_estructura_basica(content) puts "🏗️ Verificando estructura básica..." elementos_requeridos = { 'DOCTYPE html' => content.include?(''), 'head' => content.include?('') && content.include?(''), 'body' => content.include?('') && content.include?(''), 'title P2601' => content.include?('P2601'), 'container' => content.include?('class="container"'), 'header' => content.include?('class="header"'), 'footer' => content.include?('class="footer"') } exito = elementos_requeridos.values.all? { exito: exito, detalles: elementos_requeridos } end def verificar_hitos(content) puts "🎯 Verificando hitos..." # Buscar hitos hitos_count = content.scan(/
/).size hitos_duplicados = buscar_hitos_duplicados(content) # Verificar estructura de hitos hitos_validos = content.scan(/
[\s\S]*?<\/div>\s*<\/div>\s*<\/div>/m).size { exito: hitos_count > 0 && hitos_count == hitos_validos, detalles: { total_hitos: hitos_count, hitos_validos: hitos_validos, hitos_duplicados: hitos_duplicados.size, porcentaje_validos: hitos_count > 0 ? (hitos_validos * 100 / hitos_count).round(1) : 0 } } end def verificar_timeline(content) puts "📅 Verificando timeline..." timeline_presente = content.include?('class="timeline"') if timeline_presente # Extraer sección timeline timeline_match = content.match(/
([\s\S]*?)<\/div>\s*<\/div>\s*<\/div>/m) timeline_content = timeline_match ? timeline_match[1] : '' hitos_en_timeline = timeline_content.scan(/
/).size else hitos_en_timeline = 0 end { exito: timeline_presente, detalles: { presente: timeline_presente, hitos_en_timeline: hitos_en_timeline } } end def verificar_estadisticas(content) puts "📊 Verificando estadísticas..." # Buscar métricas comunes metricas = { progreso: content.include?('Progreso General') || content.include?('progressPct'), hitos_completados: content.include?('Hitos Completados'), hitos_pendientes: content.include?('Hitos Pendientes'), esfuerzo_total: content.include?('Esfuerzo Total') } { exito: metricas.values.any?, detalles: metricas } end def buscar_hitos_duplicados(content) # Extraer todos los hitos hitos = content.scan(/
([\s\S]*?)<\/div>\s*<\/div>\s*<\/div>/m) # Buscar duplicados por ID ids_encontrados = {} duplicados = [] hitos.each do |hito_html| # Buscar ID en el hito id_match = hito_html[0].match(/([^<]+)<\/span>/) next unless id_match id = id_match[1] if ids_encontrados[id] duplicados << id else ids_encontrados[id] = true end end duplicados end def extraer_info_hitos(content) # Extraer todos los hitos hitos_matches = content.scan(/
([\s\S]*?)<\/div>\s*<\/div>\s*<\/div>/m) hitos_info = [] hitos_matches.each do |hito_html| hito = hito_html[0] # Extraer información id_match = hito.match(/([^<]+)<\/span>/) titulo_match = hito.match(/
([^<]+)<\/div>/) estado_match = hito.match(/
<\/div>/) fecha_match = hito.match(/([^<]+)<\/span>/) hitos_info << { id: id_match ? id_match[1] : 'Desconocido', titulo: titulo_match ? titulo_match[1] : 'Sin título', estado: estado_match ? estado_match[1] : 'unknown', fecha: fecha_match ? fecha_match[1] : 'Sin fecha', tags: hito.scan(/([^<]+)<\/span>/).flatten } end hitos_info.uniq { |h| h[:id] } # Eliminar duplicados por ID end def mostrar_resumen(resultados) puts "\n📋 RESUMEN DE VERIFICACIÓN" puts "=" * 60 resultados.each do |nombre, resultado| emoji = resultado[:exito] ? '✅' : '❌' puts "#{emoji} #{nombre.to_s.capitalize}: #{resultado[:exito] ? 'OK' : 'PROBLEMA'}" # Mostrar detalles importantes if resultado[:detalles].is_a?(Hash) resultado[:detalles].each do |detalle, valor| puts " • #{detalle}: #{valor}" end end puts end # Resumen general verificaciones_exitosas = resultados.values.count { |r| r[:exito] } total_verificaciones = resultados.size puts "📊 RESUMEN GENERAL:" puts " • Verificaciones exitosas: #{verificaciones_exitosas}/#{total_verificaciones}" puts " • Porcentaje de éxito: #{(verificaciones_exitosas * 100 / total_verificaciones).round(1)}%" if verificaciones_exitosas == total_verificaciones puts "\n🎉 ¡Todas las verificaciones pasaron exitosamente!" else puts "\n⚠️ Se encontraron problemas que requieren atención" end end def mostrar_analisis_hitos(hitos_info) puts "📈 ANÁLISIS DE HITOS" puts "=" * 60 if hitos_info.empty? puts "❌ No se encontraron hitos en el dashboard" return end # Estadísticas generales puts "📊 ESTADÍSTICAS:" puts " • Total de hitos únicos: #{hitos_info.size}" # Agrupar por estado estados = Hash.new(0) hitos_info.each { |h| estados[h[:estado]] += 1 } puts " • Distribución por estado:" estados.each do |estado, count| porcentaje = (count * 100.0 / hitos_info.size).round(1) puts " - #{estado}: #{count} (#{porcentaje}%)" end # Tags más comunes todos_tags = hitos_info.flat_map { |h| h[:tags] } tags_frecuentes = Hash.new(0) todos_tags.each { |tag| tags_frecuentes[tag] += 1 } if tags_frecuentes.any? puts " • Tags más comunes:" tags_frecuentes.sort_by { |_, count| -count }.first(5).each do |tag, count| puts " - #{tag}: #{count} hitos" end end # Lista de hitos puts "\n📋 LISTA DE HITOS:" hitos_info.each_with_index do |hito, i| puts " #{i + 1}. [#{hito[:id]}] #{hito[:titulo]}" puts " Estado: #{hito[:estado]} | Fecha: #{hito[:fecha]}" puts " Tags: #{hito[:tags].join(', ')}" if hito[:tags].any? puts end # Recomendaciones puts "💡 RECOMENDACIONES:" if estados['pending'] && estados['pending'] > hitos_info.size * 0.5 puts " • Muchos hitos pendientes (#{estados['pending']}/#{hitos_info.size})" puts " • Considerar priorizar y actualizar estados" end if hitos_info.size < 5 puts " • Pocos hitos definidos (#{hitos_info.size})" puts " • Considerar agregar más hitos para mejor seguimiento" end puts "\n✅ Análisis completado" end end # Interfaz de línea de comandos if __FILE__ == $0 begin # Parsear argumentos dashboard_path = nil command = 'verificar' ARGV.each_with_index do |arg, i| if arg == '--dashboard' && ARGV[i + 1] dashboard_path = ARGV[i + 1] elsif i == 0 && !arg.start_with?('--') command = arg end end verifier = DashboardVerifier.new(dashboard_path) case command when 'verificar', 'check' success = verifier.verificar_estructura when 'analizar', 'analyze' success = verifier.analizar_hitos when 'help', '--help', '-h' puts "📖 USO:" puts " #{$0} [comando] [--dashboard RUTA]" puts puts "COMANDOS:" puts " verificar, check - Verificar estructura del dashboard (predeterminado)" puts " analizar, analyze - Analizar hitos en detalle" puts " help - Mostrar esta ayuda" puts puts "OPCIONES:" puts " --dashboard RUTA - Ruta personalizada al archivo dashboard" puts success = true else puts "❌ Comando '#{command}' no reconocido" puts " Use '#{$0} help' para ver la ayuda" success = false end if success puts "\n🚀 PRÓXIMOS PASOS SUGERIDOS:" puts " 1. Usar 'limpiar.rb' si hay hitos duplicados" puts " 2. Usar 'agregar_hitos.rb' para agregar hitos de fase 5" puts " 3. Revisar dashboard en: https://ns8.frlr.utn.edu.ar/bitacoras/p2601" puts "\n📅 #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" puts "=" * 60 else exit 1 end rescue => e puts "❌ Error: #{e.message}" puts e.backtrace if ENV['DEBUG'] exit 1 end end