Reestructuración modular integral de servicios, reorganización de gestión en docs/ y limpieza profunda del repositorio
This commit is contained in:
Executable
+368
@@ -0,0 +1,368 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user