369 lines
10 KiB
Ruby
Executable File
369 lines
10 KiB
Ruby
Executable File
#!/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?('<!DOCTYPE html>') && content.include?('</html>')
|
|
}
|
|
}
|
|
end
|
|
|
|
def verificar_estructura_basica(content)
|
|
puts "🏗️ Verificando estructura básica..."
|
|
|
|
elementos_requeridos = {
|
|
'DOCTYPE html' => content.include?('<!DOCTYPE html>'),
|
|
'head' => content.include?('<head>') && content.include?('</head>'),
|
|
'body' => content.include?('<body>') && content.include?('</body>'),
|
|
'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(/<div class="tl-item">/).size
|
|
hitos_duplicados = buscar_hitos_duplicados(content)
|
|
|
|
# Verificar estructura de hitos
|
|
hitos_validos = content.scan(/<div class="tl-item">[\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(/<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
|
|
|
|
{
|
|
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(/<div class="tl-item">([\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 class="tl-id">([^<]+)<\/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(/<div class="tl-item">([\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 class="tl-id">([^<]+)<\/span>/)
|
|
titulo_match = hito.match(/<div class="tl-title">([^<]+)<\/div>/)
|
|
estado_match = hito.match(/<div class="tl-dot ([^"]+)"><\/div>/)
|
|
fecha_match = hito.match(/<span class="tl-date">([^<]+)<\/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 class="[^"]*tag[^"]*">([^<]+)<\/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
|