Reestructuración modular integral de servicios, reorganización de gestión en docs/ y limpieza profunda del repositorio
This commit is contained in:
@@ -0,0 +1,653 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
# tools/orquestador/lib/vista_previa/manager.rb
|
||||
# ============================================
|
||||
# Clase para vista previa centralizada del dashboard P2601
|
||||
# Filosofía: "Menos es más" - código reutilizable y modular
|
||||
|
||||
require_relative '../dashboard_core'
|
||||
|
||||
module DashboardCore
|
||||
class VistaPreviaManager < Base
|
||||
# Modos de vista previa disponibles
|
||||
MODOS_VISTA_PREVIA = {
|
||||
'resumen' => {
|
||||
descripcion: 'Resumen completo del dashboard',
|
||||
metodo: :mostrar_resumen_completo
|
||||
},
|
||||
'hitos' => {
|
||||
descripcion: 'Hitos detallados',
|
||||
metodo: :mostrar_hitos_detallados
|
||||
},
|
||||
'estructura' => {
|
||||
descripcion: 'Estructura del dashboard',
|
||||
metodo: :mostrar_estructura
|
||||
},
|
||||
'estadisticas' => {
|
||||
descripcion: 'Estadísticas detalladas',
|
||||
metodo: :mostrar_estadisticas_detalladas
|
||||
},
|
||||
'rapido' => {
|
||||
descripcion: 'Resumen rápido',
|
||||
metodo: :mostrar_resumen_rapido
|
||||
},
|
||||
'metricas' => {
|
||||
descripcion: 'Métricas clave',
|
||||
metodo: :mostrar_metricas_clave
|
||||
},
|
||||
'estados' => {
|
||||
descripcion: 'Análisis de estados',
|
||||
metodo: :mostrar_analisis_estados
|
||||
},
|
||||
'fases' => {
|
||||
descripcion: 'Análisis por fases',
|
||||
metodo: :mostrar_analisis_fases
|
||||
}
|
||||
}.freeze
|
||||
|
||||
def initialize(dashboard_path = nil)
|
||||
super(dashboard_path)
|
||||
@contenido_cache = nil
|
||||
@hitos_cache = nil
|
||||
end
|
||||
|
||||
# Métodos principales de vista previa
|
||||
|
||||
def mostrar_vista_previa(modo = 'resumen')
|
||||
unless MODOS_VISTA_PREVIA.key?(modo)
|
||||
raise "Modo '#{modo}' no válido. Modos disponibles: #{MODOS_VISTA_PREVIA.keys.join(', ')}"
|
||||
end
|
||||
|
||||
modo_info = MODOS_VISTA_PREVIA[modo]
|
||||
puts "👁️ VISTA PREVIA - #{modo_info[:descripcion].upcase}"
|
||||
puts "=" * 60
|
||||
|
||||
send(modo_info[:metodo])
|
||||
true
|
||||
end
|
||||
|
||||
def mostrar_resumen_completo
|
||||
contenido = leer_contenido
|
||||
hitos = cargar_hitos
|
||||
|
||||
# Información del archivo
|
||||
puts "📄 INFORMACIÓN DEL ARCHIVO:"
|
||||
puts " • Ruta: #{@dashboard_path}"
|
||||
puts " • Tamaño: #{contenido.bytesize} bytes (#{contenido.bytesize / 1024} KB)"
|
||||
puts " • Líneas: #{contenido.lines.count}"
|
||||
puts " • Última modificación: #{File.mtime(@dashboard_path).strftime('%Y-%m-%d %H:%M')}"
|
||||
|
||||
# Estructura básica
|
||||
puts "\n🏗️ ESTRUCTURA BÁSICA:"
|
||||
estructura = verificar_estructura_basica(contenido)
|
||||
estructura[:detalles].each do |elemento, presente|
|
||||
puts " • #{elemento}: #{presente ? '✅ Presente' : '❌ Ausente'}"
|
||||
end
|
||||
|
||||
# Hitos
|
||||
puts "\n🎯 INFORMACIÓN DE HITOS:"
|
||||
puts " • Total de hitos: #{hitos.size}"
|
||||
|
||||
if hitos.any?
|
||||
# Distribución por estado
|
||||
estados = Hash.new(0)
|
||||
hitos.each { |h| estados[h[:estado]] += 1 }
|
||||
|
||||
puts " • Distribución por estado:"
|
||||
estados.each do |estado, cantidad|
|
||||
porcentaje = (cantidad * 100.0 / hitos.size).round(1)
|
||||
estado_info = ESTADOS_VALIDOS[estado] || ESTADOS_VALIDOS['pending']
|
||||
puts " - #{estado_info[:nombre]}: #{cantidad} (#{porcentaje}%)"
|
||||
end
|
||||
|
||||
# Distribución por fase
|
||||
fases = Hash.new(0)
|
||||
hitos.each do |hito|
|
||||
if info = HitoIdGenerator.parsear_id(hito[:id])
|
||||
fases[info[:fase]] += 1
|
||||
end
|
||||
end
|
||||
|
||||
if fases.any?
|
||||
puts " • Distribución por fase:"
|
||||
fases.sort.each do |fase, cantidad|
|
||||
puts " - Fase #{fase}: #{cantidad} hitos"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Timeline
|
||||
puts "\n📅 TIMELINE:"
|
||||
timeline_presente = contenido.include?('class="timeline"')
|
||||
puts " • Presente: #{timeline_presente ? '✅ Sí' : '❌ No'}"
|
||||
|
||||
if timeline_presente
|
||||
timeline_match = contenido.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:"
|
||||
metricas = extraer_metricas_clave(contenido)
|
||||
if metricas.any?
|
||||
metricas.each do |nombre, valor|
|
||||
puts " • #{nombre}: #{valor}"
|
||||
end
|
||||
else
|
||||
puts " • No se encontraron métricas específicas"
|
||||
end
|
||||
|
||||
# Recomendaciones rápidas
|
||||
puts "\n💡 RECOMENDACIONES RÁPIDAS:"
|
||||
generar_recomendaciones_rapidas(contenido, hitos).each do |recomendacion|
|
||||
puts " • #{recomendacion}"
|
||||
end
|
||||
|
||||
mostrar_pie
|
||||
end
|
||||
|
||||
def mostrar_hitos_detallados
|
||||
hitos = cargar_hitos
|
||||
|
||||
if hitos.empty?
|
||||
puts "ℹ️ No se encontraron hitos en el dashboard"
|
||||
return
|
||||
end
|
||||
|
||||
puts "📋 HITOS DETALLADOS (#{hitos.size} total)"
|
||||
puts "=" * 60
|
||||
|
||||
hitos.each_with_index do |hito, i|
|
||||
puts "\n#{i + 1}. [#{hito[:id]}] #{hito[:titulo]}"
|
||||
puts " 📊 Estado: #{hito[:estado]}"
|
||||
puts " 📅 Fecha: #{hito[:fecha]}"
|
||||
|
||||
if hito[:tags].any?
|
||||
puts " 🏷️ Tags: #{hito[:tags].join(', ')}"
|
||||
end
|
||||
|
||||
if hito[:descripcion]
|
||||
if hito[:descripcion].length > 100
|
||||
puts " 📝 Descripción: #{hito[:descripcion][0..100]}..."
|
||||
else
|
||||
puts " 📝 Descripción: #{hito[:descripcion]}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Resumen de estados
|
||||
puts "\n📊 RESUMEN DE ESTADOS:"
|
||||
estados = Hash.new(0)
|
||||
hitos.each { |h| estados[h[:estado]] += 1 }
|
||||
|
||||
estados.each do |estado, cantidad|
|
||||
porcentaje = (cantidad * 100.0 / hitos.size).round(1)
|
||||
estado_info = ESTADOS_VALIDOS[estado] || ESTADOS_VALIDOS['pending']
|
||||
puts " • #{estado_info[:nombre]}: #{cantidad} (#{porcentaje}%)"
|
||||
end
|
||||
|
||||
mostrar_pie
|
||||
end
|
||||
|
||||
def mostrar_estructura
|
||||
contenido = leer_contenido
|
||||
|
||||
puts "🏗️ ESTRUCTURA DEL DASHBOARD"
|
||||
puts "=" * 60
|
||||
|
||||
# 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 = contenido.include?(seccion[:inicio])
|
||||
puts " • #{seccion[:nombre]}: #{presente ? '✅ Presente' : '❌ Ausente'}"
|
||||
|
||||
if presente && seccion[:fin] && contenido.include?(seccion[:fin])
|
||||
inicio_idx = contenido.index(seccion[:inicio])
|
||||
fin_idx = contenido.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
|
||||
|
||||
# Elementos HTML
|
||||
puts "\n🔢 CONTEO DE ELEMENTOS HTML:"
|
||||
elementos = {
|
||||
'Divs' => contenido.scan(/<div/).size,
|
||||
'Spans' => contenido.scan(/<span/).size,
|
||||
'Headers (h1-h6)' => contenido.scan(/<h[1-6]/).size,
|
||||
'Párrafos (p)' => contenido.scan(/<p>/).size,
|
||||
'Enlaces (a)' => contenido.scan(/<a /).size
|
||||
}
|
||||
|
||||
elementos.each do |elemento, count|
|
||||
puts " • #{elemento}: #{count}"
|
||||
end
|
||||
|
||||
# Clases CSS
|
||||
puts "\n🎨 CLASES CSS MÁS COMUNES:"
|
||||
clases = contenido.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
|
||||
|
||||
mostrar_pie
|
||||
end
|
||||
|
||||
def mostrar_estadisticas_detalladas
|
||||
contenido = leer_contenido
|
||||
hitos = cargar_hitos
|
||||
|
||||
puts "📈 ESTADÍSTICAS DETALLADAS"
|
||||
puts "=" * 60
|
||||
|
||||
# Estadísticas del archivo
|
||||
puts "📄 ESTADÍSTICAS DEL ARCHIVO:"
|
||||
puts " • Caracteres totales: #{contenido.length}"
|
||||
|
||||
texto_sin_html = contenido.gsub(/<[^>]*>/, ' ').gsub(/\s+/, ' ')
|
||||
puts " • Caracteres sin HTML: #{texto_sin_html.length}"
|
||||
puts " • Palabras aproximadas: #{texto_sin_html.split.size}"
|
||||
puts " • Líneas de código: #{contenido.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
|
||||
|
||||
# Estadísticas de hitos
|
||||
puts "\n🎯 ESTADÍSTICAS DE HITOS:"
|
||||
puts " • Total de hitos: #{hitos.size}"
|
||||
|
||||
if hitos.any?
|
||||
# Por estado
|
||||
estados = Hash.new(0)
|
||||
hitos.each { |h| estados[h[:estado]] += 1 }
|
||||
|
||||
puts " • Distribución por estado:"
|
||||
estados.each do |estado, cantidad|
|
||||
porcentaje = (cantidad * 100.0 / hitos.size).round(1)
|
||||
estado_info = ESTADOS_VALIDOS[estado] || ESTADOS_VALIDOS['pending']
|
||||
puts " - #{estado_info[:nombre]}: #{cantidad} (#{porcentaje}%)"
|
||||
end
|
||||
|
||||
# Por fase
|
||||
fases = Hash.new(0)
|
||||
hitos.each do |hito|
|
||||
if info = HitoIdGenerator.parsear_id(hito[:id])
|
||||
fases[info[:fase]] += 1
|
||||
end
|
||||
end
|
||||
|
||||
if fases.any?
|
||||
puts " • Distribución por fase:"
|
||||
fases.sort.each do |fase, cantidad|
|
||||
porcentaje = (cantidad * 100.0 / hitos.size).round(1)
|
||||
puts " - Fase #{fase}: #{cantidad} (#{porcentaje}%)"
|
||||
end
|
||||
end
|
||||
|
||||
# Por tag
|
||||
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|
|
||||
porcentaje = (count * 100.0 / hitos.size).round(1)
|
||||
puts " - #{tag}: #{count} hitos (#{porcentaje}%)"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Métricas extraídas
|
||||
puts "\n📊 MÉTRICAS EXTRAÍDAS:"
|
||||
metricas = extraer_todas_metricas(contenido)
|
||||
if metricas.any?
|
||||
metricas.each do |nombre, valor|
|
||||
puts " • #{nombre}: #{valor}"
|
||||
end
|
||||
else
|
||||
puts " • No se encontraron métricas específicas"
|
||||
end
|
||||
|
||||
mostrar_pie
|
||||
end
|
||||
|
||||
def mostrar_resumen_rapido
|
||||
contenido = leer_contenido
|
||||
hitos = cargar_hitos
|
||||
|
||||
puts "⚡ RESUMEN RÁPIDO DASHBOARD"
|
||||
puts "=" * 40
|
||||
|
||||
# Información básica
|
||||
puts "📄 Archivo: #{File.basename(@dashboard_path)}"
|
||||
puts " • Tamaño: #{contenido.bytesize / 1024} KB"
|
||||
puts " • Líneas: #{contenido.lines.count}"
|
||||
puts " • Modificado: #{File.mtime(@dashboard_path).strftime('%Y-%m-%d %H:%M')}"
|
||||
|
||||
# Hitos
|
||||
puts "🎯 Hitos: #{hitos.size} totales"
|
||||
|
||||
# Estados
|
||||
if hitos.any?
|
||||
estados = Hash.new(0)
|
||||
hitos.each { |h| estados[h[:estado]] += 1 }
|
||||
|
||||
puts "📊 Estados:"
|
||||
estados.each do |estado, count|
|
||||
estado_info = ESTADOS_VALIDOS[estado] || ESTADOS_VALIDOS['pending']
|
||||
puts " • #{estado_info[:nombre]}: #{count}"
|
||||
end
|
||||
end
|
||||
|
||||
# Timeline
|
||||
timeline_presente = contenido.include?('class="timeline"')
|
||||
puts "📅 Timeline: #{timeline_presente ? '✅ Presente' : '❌ Ausente'}"
|
||||
|
||||
# Métricas clave
|
||||
metricas = extraer_metricas_clave(contenido)
|
||||
if metricas.any?
|
||||
puts "📈 Métricas clave:"
|
||||
metricas.each do |nombre, valor|
|
||||
puts " • #{nombre}: #{valor}"
|
||||
end
|
||||
end
|
||||
|
||||
puts "\n🔗 URL: #{CONFIG[:production_url]}"
|
||||
puts "📅 #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
puts "=" * 40
|
||||
end
|
||||
|
||||
def mostrar_metricas_clave
|
||||
contenido = leer_contenido
|
||||
|
||||
puts "📊 MÉTRICAS CLAVE DEL DASHBOARD"
|
||||
puts "=" * 60
|
||||
|
||||
metricas = extraer_todas_metricas(contenido)
|
||||
|
||||
if metricas.empty?
|
||||
puts "ℹ️ No se encontraron métricas específicas en el dashboard"
|
||||
return
|
||||
end
|
||||
|
||||
puts "📈 MÉTRICAS ENCONTRADAS:"
|
||||
metricas.each do |nombre, valor|
|
||||
puts " • #{nombre}: #{valor}"
|
||||
end
|
||||
|
||||
# Análisis de progreso si hay métricas relevantes
|
||||
if metricas['Progreso']
|
||||
progreso = metricas['Progreso'].to_i rescue 0
|
||||
puts "\n🎯 ANÁLISIS DE PROGRESO:"
|
||||
puts " • Progreso general: #{progreso}%"
|
||||
|
||||
if progreso >= 80
|
||||
puts " • Estado: 🎉 Avanzado"
|
||||
elsif progreso >= 50
|
||||
puts " • Estado: ⚡ En progreso"
|
||||
elsif progreso >= 20
|
||||
puts " • Estado: 🚀 Iniciando"
|
||||
else
|
||||
puts " • Estado: 📋 Pendiente"
|
||||
end
|
||||
end
|
||||
|
||||
mostrar_pie
|
||||
end
|
||||
|
||||
def mostrar_analisis_estados
|
||||
hitos = cargar_hitos
|
||||
|
||||
puts "📊 ANÁLISIS DE ESTADOS DE HITOS"
|
||||
puts "=" * 60
|
||||
|
||||
if hitos.empty?
|
||||
puts "ℹ️ No se encontraron hitos para analizar"
|
||||
return
|
||||
end
|
||||
|
||||
# Distribución por estado
|
||||
estados = Hash.new(0)
|
||||
hitos.each { |h| estados[h[:estado]] += 1 }
|
||||
|
||||
puts "📈 DISTRIBUCIÓN POR ESTADO:"
|
||||
estados.each do |estado, cantidad|
|
||||
porcentaje = (cantidad * 100.0 / hitos.size).round(1)
|
||||
estado_info = ESTADOS_VALIDOS[estado] || ESTADOS_VALIDOS['pending']
|
||||
puts " • #{estado_info[:nombre]}: #{cantidad} (#{porcentaje}%)"
|
||||
end
|
||||
|
||||
# Análisis de progreso
|
||||
completados = estados['completed'] || 0
|
||||
en_progreso = estados['in-progress'] || 0
|
||||
total = hitos.size
|
||||
|
||||
if total > 0
|
||||
progreso_total = ((completados + (en_progreso * 0.5)) / total * 100).round(1)
|
||||
puts "\n🎯 ANÁLISIS DE PROGRESO:"
|
||||
puts " • Progreso estimado: #{progreso_total}%"
|
||||
puts " • Hitos completados: #{completados}/#{total}"
|
||||
puts " • Hitos en progreso: #{en_progreso}/#{total}"
|
||||
puts " • Hitos pendientes: #{estados['pending'] || 0}/#{total}"
|
||||
end
|
||||
|
||||
# Recomendaciones basadas en estados
|
||||
puts "\n💡 RECOMENDACIONES:"
|
||||
if (estados['pending'] || 0) > total * 0.7
|
||||
puts " • Muchos hitos pendientes (#{estados['pending']}/#{total})"
|
||||
puts " • Considerar iniciar más hitos"
|
||||
end
|
||||
|
||||
if (estados['blocked'] || 0) > 0
|
||||
puts " • Hay hitos bloqueados (#{estados['blocked']})"
|
||||
puts " • Revisar dependencias y obstáculos"
|
||||
end
|
||||
|
||||
if (estados['on-hold'] || 0) > 0
|
||||
puts " • Hay hitos en espera (#{estados['on-hold']})"
|
||||
puts " • Evaluar prioridades y recursos"
|
||||
end
|
||||
|
||||
mostrar_pie
|
||||
end
|
||||
|
||||
def mostrar_analisis_fases
|
||||
hitos = cargar_hitos
|
||||
|
||||
puts "📁 ANÁLISIS POR FASES"
|
||||
puts "=" * 60
|
||||
|
||||
if hitos.empty?
|
||||
puts "ℹ️ No se encontraron hitos para analizar"
|
||||
return
|
||||
end
|
||||
|
||||
# Agrupar hitos por fase
|
||||
hitos_por_fase = Hash.new { |h, k| h[k] = [] }
|
||||
hitos.each do |hito|
|
||||
if info = HitoIdGenerator.parsear_id(hito[:id])
|
||||
hitos_por_fase[info[:fase]] << hito
|
||||
else
|
||||
hitos_por_fase['sin_fase'] << hito
|
||||
end
|
||||
end
|
||||
|
||||
puts "📊 DISTRIBUCIÓN POR FASE:"
|
||||
hitos_por_fase.sort.each do |fase, hitos_fase|
|
||||
puts " • Fase #{fase}: #{hitos_fase.size} hitos"
|
||||
end
|
||||
|
||||
# Análisis detallado por fase
|
||||
puts "\n📈 ANÁLISIS DETALLADO POR FASE:"
|
||||
hitos_por_fase.sort.each do |fase, hitos_fase|
|
||||
next if hitos_fase.empty?
|
||||
|
||||
puts "\n 📁 FASE #{fase}:"
|
||||
puts " • Total hitos: #{hitos_fase.size}"
|
||||
|
||||
# Estados en esta fase
|
||||
estados_fase = Hash.new(0)
|
||||
hitos_fase.each { |h| estados_fase[h[:estado]] += 1 }
|
||||
|
||||
if estados_fase.any?
|
||||
puts " • Distribución por estado:"
|
||||
estados_fase.each do |estado, cantidad|
|
||||
porcentaje = (cantidad * 100.0 / hitos_fase.size).round(1)
|
||||
estado_info = ESTADOS_VALIDOS[estado] || ESTADOS_VALIDOS['pending']
|
||||
puts " - #{estado_info[:nombre]}: #{cantidad} (#{porcentaje}%)"
|
||||
end
|
||||
end
|
||||
|
||||
# Progreso de la fase
|
||||
completados = estados_fase['completed'] || 0
|
||||
en_progreso = estados_fase['in-progress'] || 0
|
||||
progreso_fase = hitos_fase.size > 0 ? ((completados + (en_progreso * 0.5)) / hitos_fase.size * 100).round(1) : 0
|
||||
|
||||
puts " • Progreso de la fase: #{progreso_fase}%"
|
||||
end
|
||||
|
||||
# Hitos sin fase
|
||||
if hitos_por_fase['sin_fase'] && !hitos_por_fase['sin_fase'].empty?
|
||||
puts "\n⚠️ HITOS SIN FASE ASIGNADA:"
|
||||
hitos_por_fase['sin_fase'].each do |hito|
|
||||
puts " • #{hito[:id]}: #{hito[:titulo]}"
|
||||
end
|
||||
end
|
||||
|
||||
mostrar_pie
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def leer_contenido
|
||||
@contenido_cache ||= leer_dashboard
|
||||
end
|
||||
|
||||
def cargar_hitos
|
||||
@hitos_cache ||= begin
|
||||
contenido = leer_contenido
|
||||
extraer_hitos(contenido)
|
||||
end
|
||||
end
|
||||
|
||||
def extraer_metricas_clave(contenido)
|
||||
metricas = {}
|
||||
|
||||
# Buscar porcentaje de progreso
|
||||
if match = contenido.match(/id="progressPct">([^<]+)</)
|
||||
metricas['Progreso'] = match[1]
|
||||
end
|
||||
|
||||
# Buscar hitos completados
|
||||
if match = contenido.match(/Hitos Completados[^<]*<[^>]*>([^<]+)</)
|
||||
metricas['Hitos Completados'] = match[1]
|
||||
end
|
||||
|
||||
# Buscar hitos pendientes
|
||||
if match = contenido.match(/Hitos Pendientes[^<]*<[^>]*>([^<]+)</)
|
||||
metricas['Hitos Pendientes'] = match[1]
|
||||
end
|
||||
|
||||
metricas
|
||||
end
|
||||
|
||||
def extraer_todas_metricas(contenido)
|
||||
metricas = {}
|
||||
|
||||
# Buscar porcentaje de progreso
|
||||
if match = contenido.match(/id="progressPct">([^<]+)</)
|
||||
metricas['Progreso'] = match[1]
|
||||
end
|
||||
|
||||
# Buscar hitos completados
|
||||
if match = contenido.match(/Hitos Completados[^<]*<[^>]*>([^<]+)</)
|
||||
metricas['Hitos Completados'] = match[1]
|
||||
end
|
||||
|
||||
# Buscar hitos pendientes
|
||||
if match = contenido.match(/Hitos Pendientes[^<]*<[^>]*>([^<]+)</)
|
||||
metricas['Hitos Pendientes'] = match[1]
|
||||
end
|
||||
|
||||
# Buscar esfuerzo total
|
||||
if match = contenido.match(/Esfuerzo Total[^<]*<[^>]*>([^<]+)</)
|
||||
metricas['Esfuerzo Total'] = match[1]
|
||||
end
|
||||
|
||||
# Buscar horas físicas
|
||||
if match = contenido.match(/Horas Físicas[^<]*<[^>]*>([^<]+)</)
|
||||
metricas['Horas Físicas'] = match[1]
|
||||
end
|
||||
|
||||
metricas
|
||||
end
|
||||
|
||||
def generar_recomendaciones_rapidas(contenido, hitos)
|
||||
recomendaciones = []
|
||||
|
||||
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
|
||||
|
||||
# Verificar duplicados
|
||||
duplicados = buscar_hitos_duplicados(contenido)
|
||||
if duplicados.any?
|
||||
recomendaciones << "Eliminar hitos duplicados usando la herramienta 'limpiar'"
|
||||
end
|
||||
|
||||
# Verificar estructura
|
||||
estructura = verificar_estructura_basica(contenido)
|
||||
unless estructura[:exito]
|
||||
recomendaciones << "Corregir estructura HTML básica del dashboard"
|
||||
end
|
||||
|
||||
# Verificar timeline
|
||||
unless contenido.include?('class="timeline"')
|
||||
recomendaciones << "Agregar sección timeline al dashboard"
|
||||
end
|
||||
|
||||
# Verificar estados
|
||||
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
|
||||
Reference in New Issue
Block a user