Files
dtic-DIIAA/tools/orquestador/dashboard/vista_previa.rb
T

535 lines
16 KiB
Ruby
Executable File
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/dashboard/vista_previa.rb
# ===========================================
# Herramienta para vista previa rápida del dashboard P2601
# Filosofía: "Menos es más" - herramienta pequeña, específica y reutilizable
require 'fileutils'
class DashboardPreview
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 mostrar_vista_previa(modo = 'resumen')
puts "👁️ VISTA PREVIA DEL DASHBOARD P2601"
puts "=" * 60
unless File.exist?(@dashboard_path)
puts "❌ Error: No se encuentra el dashboard en #{@dashboard_path}"
return false
end
puts "📖 Analizando dashboard..."
content = File.read(@dashboard_path)
case modo
when 'resumen'
mostrar_resumen(content)
when 'hitos'
mostrar_hitos_detallados(content)
when 'estructura'
mostrar_estructura(content)
when 'estadisticas'
mostrar_estadisticas(content)
else
puts "❌ Modo '#{modo}' no válido"
puts " Modos disponibles: resumen, hitos, estructura, estadisticas"
return false
end
true
end
def mostrar_resumen_rapido
puts "⚡ RESUMEN RÁPIDO DASHBOARD"
puts "=" * 40
unless File.exist?(@dashboard_path)
puts "❌ Dashboard no encontrado"
return false
end
content = File.read(@dashboard_path)
# Información básica
puts "📄 Archivo: #{File.basename(@dashboard_path)}"
puts " • Tamaño: #{content.bytesize / 1024} KB"
puts " • Líneas: #{content.lines.count}"
puts " • Última modificación: #{File.mtime(@dashboard_path).strftime('%Y-%m-%d %H:%M')}"
# Hitos
hitos_count = content.scan(/<div class="tl-item">/).size
hitos_unicos = extraer_hitos_unicos(content).size
puts "🎯 Hitos: #{hitos_count} totales, #{hitos_unicos} únicos"
# Estados
estados = contar_estados_hitos(content)
if estados.any?
puts "📊 Estados:"
estados.each do |estado, count|
puts " • #{estado}: #{count}"
end
end
# Timeline
timeline_presente = content.include?('class="timeline"')
puts "📅 Timeline: #{timeline_presente ? '✅ Presente' : '❌ Ausente'}"
# Métricas clave
metricas = extraer_metricas_clave(content)
if metricas.any?
puts "📈 Métricas clave:"
metricas.each do |nombre, valor|
puts " • #{nombre}: #{valor}"
end
end
puts "\n🔗 URL: https://ns8.frlr.utn.edu.ar/bitacoras/p2601"
puts "📅 #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
puts "=" * 40
true
end
private
def mostrar_resumen(content)
puts "📋 RESUMEN COMPLETO"
puts "=" * 60
# Información del archivo
puts "📄 INFORMACIÓN DEL ARCHIVO:"
puts " • Ruta: #{@dashboard_path}"
puts " • Tamaño: #{content.bytesize} bytes (#{content.bytesize / 1024} KB)"
puts " • Líneas: #{content.lines.count}"
puts " • Encoding: #{content.encoding.name}"
# Estructura HTML
puts "\n🏗️ ESTRUCTURA HTML:"
elementos = {
'DOCTYPE html' => content.include?('<!DOCTYPE html>'),
'Elemento head' => content.include?('<head>') && content.include?('</head>'),
'Elemento body' => content.include?('<body>') && content.include?('</body>'),
'Título P2601' => content.include?('P2601'),
'Container principal' => content.include?('class="container"'),
'Header' => content.include?('class="header"'),
'Footer' => content.include?('class="footer"')
}
elementos.each do |elemento, presente|
puts " • #{elemento}: #{presente ? '✅' : '❌'}"
end
# Hitos
puts "\n🎯 INFORMACIÓN DE HITOS:"
hitos_count = content.scan(/<div class="tl-item">/).size
hitos_unicos = extraer_hitos_unicos(content)
puts " • Total de hitos: #{hitos_count}"
puts " • Hitos únicos: #{hitos_unicos.size}"
if hitos_unicos.any?
puts " • Distribución por estado:"
estados = Hash.new(0)
hitos_unicos.each { |h| estados[h[:estado]] += 1 }
estados.each do |estado, count|
porcentaje = (count * 100.0 / hitos_unicos.size).round(1)
puts " - #{estado}: #{count} (#{porcentaje}%)"
end
end
# Timeline
puts "\n📅 TIMELINE:"
timeline_presente = content.include?('class="timeline"')
puts " • Presente: #{timeline_presente ? '✅ Sí' : '❌ No'}"
if timeline_presente
timeline_match = content.match(/<div class="timeline">([\s\S]*?)<\/div>\s*<\/div>\s*<\/div>/m)
if timeline_match
hitos_en_timeline = timeline_match[1].scan(/<div class="tl-item">/).size
puts " • Hitos en timeline: #{hitos_en_timeline}"
end
end
# Métricas
puts "\n📈 MÉTRICAS Y ESTADÍSTICAS:"
metricas = {
'Progreso General' => content.include?('Progreso General'),
'Hitos Completados' => content.include?('Hitos Completados'),
'Hitos Pendientes' => content.include?('Hitos Pendientes'),
'Esfuerzo Total' => content.include?('Esfuerzo Total')
}
metricas.each do |metrica, presente|
puts " • #{metrica}: #{presente ? '✅ Presente' : '❌ Ausente'}"
end
# Extraer valores de métricas si existen
valores_metricas = extraer_valores_metricas(content)
if valores_metricas.any?
puts " • Valores encontrados:"
valores_metricas.each do |nombre, valor|
puts " - #{nombre}: #{valor}"
end
end
puts "\n💡 RECOMENDACIONES:"
if hitos_count > hitos_unicos.size * 2
puts " • Posibles hitos duplicados detectados"
puts " • Considerar usar 'limpiar.rb'"
end
if hitos_unicos.size < 3
puts " • Pocos hitos definidos"
puts " • Considerar agregar más hitos con 'agregar_hitos.rb'"
end
puts "\n✅ Resumen completado"
end
def mostrar_hitos_detallados(content)
puts "📋 HITOS DETALLADOS"
puts "=" * 60
hitos = extraer_hitos_unicos(content)
if hitos.empty?
puts "️ No se encontraron hitos en el dashboard"
return
end
puts "🎯 Total de hitos únicos: #{hitos.size}"
puts
hitos.each_with_index do |hito, i|
puts " #{i + 1}. [#{hito[:id]}] #{hito[:titulo]}"
puts " 📊 Estado: #{hito[:estado]}"
puts " 📅 Fecha: #{hito[:fecha]}"
puts " 🏷️ Tags: #{hito[:tags].join(', ')}" if hito[:tags].any?
# Mostrar descripción truncada
if hito[:descripcion] && hito[:descripcion].length > 100
puts " 📝 Descripción: #{hito[:descripcion][0..100]}..."
elsif hito[:descripcion]
puts " 📝 Descripción: #{hito[:descripcion]}"
end
puts
end
# Resumen de estados
puts "📊 RESUMEN DE ESTADOS:"
estados = Hash.new(0)
hitos.each { |h| estados[h[:estado]] += 1 }
estados.each do |estado, count|
porcentaje = (count * 100.0 / hitos.size).round(1)
puts " • #{estado}: #{count} hitos (#{porcentaje}%)"
end
end
def mostrar_estructura(content)
puts "🏗️ ESTRUCTURA DEL DASHBOARD"
puts "=" * 60
# Mostrar secciones principales
puts "📂 SECCIONES PRINCIPALES:"
secciones = [
{ nombre: 'Head', inicio: '<head>', fin: '</head>' },
{ nombre: 'Body', inicio: '<body>', fin: '</body>' },
{ nombre: 'Header', inicio: 'class="header"', fin: nil },
{ nombre: 'Timeline', inicio: 'class="timeline"', fin: '</div><!-- Timeline de Hitos -->' },
{ nombre: 'Footer', inicio: 'class="footer"', fin: '</footer>' }
]
secciones.each do |seccion|
presente = content.include?(seccion[:inicio])
puts " • #{seccion[:nombre]}: #{presente ? '✅ Presente' : '❌ Ausente'}"
if presente && seccion[:fin] && content.include?(seccion[:fin])
# Calcular tamaño aproximado de la sección
inicio_idx = content.index(seccion[:inicio])
fin_idx = content.index(seccion[:fin], inicio_idx)
if inicio_idx && fin_idx
tamaño = fin_idx - inicio_idx + seccion[:fin].length
puts " Tamaño: ~#{tamaño} bytes"
end
end
end
# Contar elementos HTML
puts "\n🔢 CONTEO DE ELEMENTOS HTML:"
elementos = {
'Divs' => content.scan(/<div/).size,
'Spans' => content.scan(/<span/).size,
'Headers (h1-h6)' => content.scan(/<h[1-6]/).size,
'Párrafos (p)' => content.scan(/<p>/).size,
'Enlaces (a)' => content.scan(/<a /).size
}
elementos.each do |elemento, count|
puts " • #{elemento}: #{count}"
end
# Clases CSS más comunes
puts "\n🎨 CLASES CSS MÁS COMUNES:"
clases = content.scan(/class="([^"]+)"/).flatten
clases_frecuentes = Hash.new(0)
clases.each do |clase_lista|
clase_lista.split(/\s+/).each do |clase|
clases_frecuentes[clase] += 1
end
end
# Mostrar las 10 clases más comunes
clases_frecuentes.sort_by { |_, count| -count }.first(10).each do |clase, count|
puts " • .#{clase}: #{count} ocurrencias"
end
end
def mostrar_estadisticas(content)
puts "📈 ESTADÍSTICAS DETALLADAS"
puts "=" * 60
# Métricas extraídas del contenido
metricas = extraer_valores_metricas(content)
if metricas.any?
puts "📊 MÉTRICAS ENCONTRADAS:"
metricas.each do |nombre, valor|
puts " • #{nombre}: #{valor}"
end
else
puts "️ No se encontraron métricas específicas en el contenido"
end
# Estadísticas de hitos
puts "\n🎯 ESTADÍSTICAS DE HITOS:"
hitos = extraer_hitos_unicos(content)
if hitos.any?
puts " • Total de hitos únicos: #{hitos.size}"
# Por estado
estados = Hash.new(0)
hitos.each { |h| estados[h[:estado]] += 1 }
if estados.any?
puts " • Distribución por estado:"
estados.each do |estado, count|
porcentaje = (count * 100.0 / hitos.size).round(1)
puts " - #{estado}: #{count} (#{porcentaje}%)"
end
end
# Por tags
todos_tags = hitos.flat_map { |h| h[:tags] }
if todos_tags.any?
tags_frecuentes = Hash.new(0)
todos_tags.each { |tag| tags_frecuentes[tag] += 1 }
puts " • Tags más comunes:"
tags_frecuentes.sort_by { |_, count| -count }.first(5).each do |tag, count|
puts " - #{tag}: #{count} hitos"
end
end
else
puts " • No se encontraron hitos"
end
# Estadísticas de texto
puts "\n📝 ESTADÍSTICAS DE TEXTO:"
texto_sin_html = content.gsub(/<[^>]*>/, ' ').gsub(/\s+/, ' ')
puts " • Caracteres totales: #{content.length}"
puts " • Caracteres sin HTML: #{texto_sin_html.length}"
puts " • Palabras aproximadas: #{texto_sin_html.split.size}"
puts " • Líneas de código: #{content.lines.count}"
# Densidad de información
if hitos.any?
palabras_por_hito = texto_sin_html.split.size / hitos.size.to_f
puts " • Palabras por hito: #{palabras_por_hito.round(1)}"
end
end
def extraer_hitos_unicos(content)
hitos = []
content.scan(/<div class="tl-item">([\s\S]*?)<\/div>\s*<\/div>\s*<\/div>/m) 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>/)
descripcion_match = hito.match(/<div class="tl-desc">([^<]+)<\/div>/)
next unless id_match && titulo_match
hitos << {
id: id_match[1],
titulo: titulo_match[1],
estado: estado_match ? estado_match[1] : 'pending',
fecha: fecha_match ? fecha_match[1] : 'Sin fecha',
descripcion: descripcion_match ? descripcion_match[1] : nil,
tags: hito.scan(/<span class="[^"]*tag[^"]*">([^<]+)<\/span>/).flatten
}
end
# Eliminar duplicados por ID
hitos.uniq { |h| h[:id] }
end
def contar_estados_hitos(content)
estados = Hash.new(0)
content.scan(/<div class="tl-dot ([^"]+)"><\/div>/) do |estado|
estados[estado[0]] += 1
end
estados
end
def extraer_metricas_clave(content)
metricas = {}
# Buscar porcentaje de progreso
if match = content.match(/id="progressPct">([^<]+)</)
metricas['Progreso'] = match[1]
end
# Buscar hitos completados
if match = content.match(/Hitos Completados[^<]*<[^>]*>([^<]+)</)
metricas['Hitos Completados'] = match[1]
end
# Buscar hitos pendientes
if match = content.match(/Hitos Pendientes[^<]*<[^>]*>([^<]+)</)
metricas['Hitos Pendientes'] = match[1]
end
metricas
end
def extraer_valores_metricas(content)
metricas = {}
# Buscar porcentaje de progreso
if match = content.match(/id="progressPct">([^<]+)</)
metricas['Progreso'] = match[1]
end
# Buscar hitos completados
if match = content.match(/Hitos Completados[^<]*<[^>]*>([^<]+)</)
metricas['Hitos Completados'] = match[1]
end
# Buscar hitos pendientes
if match = content.match(/Hitos Pendientes[^<]*<[^>]*>([^<]+)</)
metricas['Hitos Pendientes'] = match[1]
end
# Buscar esfuerzo total
if match = content.match(/Esfuerzo Total[^<]*<[^>]*>([^<]+)</)
metricas['Esfuerzo Total'] = match[1]
end
# Buscar horas físicas
if match = content.match(/Horas Físicas[^<]*<[^>]*>([^<]+)</)
metricas['Horas Físicas'] = match[1]
end
metricas
end
end
# Interfaz de línea de comandos
if __FILE__ == $0
begin
preview = DashboardPreview.new
# Parsear argumentos
modo = 'resumen'
dashboard_path = nil
ARGV.each_with_index do |arg, i|
if arg == '--dashboard' && ARGV[i + 1]
dashboard_path = ARGV[i + 1]
elsif i == 0 && !arg.start_with?('--')
modo = arg
end
end
preview = DashboardPreview.new(dashboard_path)
case modo
when 'resumen', 'summary'
success = preview.mostrar_vista_previa('resumen')
when 'hitos', 'milestones'
success = preview.mostrar_vista_previa('hitos')
when 'estructura', 'structure'
success = preview.mostrar_vista_previa('estructura')
when 'estadisticas', 'stats', 'statistics'
success = preview.mostrar_vista_previa('estadisticas')
when 'rapido', 'quick', 'fast'
success = preview.mostrar_resumen_rapido
when 'help', '--help', '-h'
puts "📖 USO:"
puts " #{$0} [modo] [--dashboard RUTA]"
puts
puts "MODOS:"
puts " resumen, summary - Resumen completo (predeterminado)"
puts " hitos, milestones - Hitos detallados"
puts " estructura, structure - Estructura del dashboard"
puts " estadisticas, stats - Estadísticas detalladas"
puts " rapido, quick - Resumen rápido"
puts " help - Mostrar esta ayuda"
puts
puts "OPCIONES:"
puts " --dashboard RUTA - Ruta personalizada al archivo dashboard"
puts
puts "EJEMPLOS:"
puts " #{$0} resumen"
puts " #{$0} hitos"
puts " #{$0} rapido"
puts " #{$0} --dashboard /ruta/al/dashboard.html estructura"
puts
success = true
else
puts "❌ Modo '#{modo}' no reconocido"
puts " Use '#{$0} help' para ver la ayuda"
success = false
end
if success
puts "\n🚀 HERRAMIENTAS RELACIONADAS:"
puts " 1. limpiar.rb - Limpiar hitos duplicados"
puts " 2. agregar_hitos.rb - Agregar nuevos hitos"
puts " 3. actualizar_estados.rb - Actualizar estados de hitos"
puts " 4. verificar.rb - Verificar estructura del dashboard"
puts
puts "🔗 Dashboard: https://ns8.frlr.utn.edu.ar/bitacoras/p2601"
puts "📅 #{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