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,582 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
# tools/orquestador/lib/verificacion/manager.rb
|
||||
# ============================================
|
||||
# Clase para verificación centralizada del dashboard P2601
|
||||
# Filosofía: "Menos es más" - código reutilizable y modular
|
||||
|
||||
require_relative '../dashboard_core'
|
||||
|
||||
module DashboardCore
|
||||
class VerificacionManager < Base
|
||||
# Reglas de verificación
|
||||
REGLAS_VERIFICACION = {
|
||||
estructura: {
|
||||
nivel: 'crítico',
|
||||
descripcion: 'Estructura HTML básica del dashboard',
|
||||
verificar: ->(contenido) {
|
||||
elementos = {
|
||||
'DOCTYPE html' => contenido.include?('<!DOCTYPE html>'),
|
||||
'head' => contenido.include?('<head>') && contenido.include?('</head>'),
|
||||
'body' => contenido.include?('<body>') && contenido.include?('</body>'),
|
||||
'title P2601' => contenido.include?('P2601'),
|
||||
'container' => contenido.include?('class="container"'),
|
||||
'header' => contenido.include?('class="header"'),
|
||||
'footer' => contenido.include?('class="footer"'),
|
||||
'timeline' => contenido.include?('class="timeline"')
|
||||
}
|
||||
elementos
|
||||
}
|
||||
},
|
||||
hitos: {
|
||||
nivel: 'alto',
|
||||
descripcion: 'Integridad y formato de hitos',
|
||||
verificar: ->(contenido) {
|
||||
hitos = extraer_hitos(contenido)
|
||||
{
|
||||
total_hitos: hitos.size,
|
||||
hitos_validos: hitos.count { |h| h[:id] && h[:titulo] },
|
||||
ids_validos: hitos.count { |h| HitoIdGenerator.es_valido?(h[:id]) },
|
||||
duplicados: buscar_hitos_duplicados(contenido).size
|
||||
}
|
||||
}
|
||||
},
|
||||
estados: {
|
||||
nivel: 'medio',
|
||||
descripcion: 'Estados válidos de hitos',
|
||||
verificar: ->(contenido) {
|
||||
hitos = extraer_hitos(contenido)
|
||||
estados_validos = hitos.count { |h| Validator.validar_estado(h[:estado]) }
|
||||
estados_invalidos = hitos.reject { |h| Validator.validar_estado(h[:estado]) }
|
||||
|
||||
{
|
||||
estados_validos: estados_validos,
|
||||
estados_invalidos: estados_invalidos.size,
|
||||
estados_presentes: hitos.map { |h| h[:estado] }.uniq
|
||||
}
|
||||
}
|
||||
},
|
||||
timeline: {
|
||||
nivel: 'alto',
|
||||
descripcion: 'Estructura de la timeline',
|
||||
verificar: ->(contenido) {
|
||||
timeline_presente = contenido.include?('class="timeline"')
|
||||
|
||||
if timeline_presente
|
||||
timeline_match = contenido.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
|
||||
|
||||
{
|
||||
presente: timeline_presente,
|
||||
hitos_en_timeline: hitos_en_timeline,
|
||||
estructura_correcta: timeline_presente && hitos_en_timeline > 0
|
||||
}
|
||||
}
|
||||
},
|
||||
metricas: {
|
||||
nivel: 'bajo',
|
||||
descripcion: 'Métricas y estadísticas',
|
||||
verificar: ->(contenido) {
|
||||
metricas_encontradas = {
|
||||
progreso: contenido.include?('Progreso General') || contenido.include?('progressPct'),
|
||||
hitos_completados: contenido.include?('Hitos Completados'),
|
||||
hitos_pendientes: contenido.include?('Hitos Pendientes'),
|
||||
esfuerzo_total: contenido.include?('Esfuerzo Total')
|
||||
}
|
||||
|
||||
valores = extraer_valores_metricas(contenido)
|
||||
|
||||
{
|
||||
metricas_presentes: metricas_encontradas.values.count(true),
|
||||
valores_encontrados: valores.size,
|
||||
valores: valores
|
||||
}
|
||||
}
|
||||
}
|
||||
}.freeze
|
||||
|
||||
def initialize(dashboard_path = nil)
|
||||
super(dashboard_path)
|
||||
@resultados_cache = nil
|
||||
end
|
||||
|
||||
# Métodos principales de verificación
|
||||
|
||||
def verificar_completo
|
||||
puts "🔍 VERIFICACIÓN COMPLETA DEL DASHBOARD P2601"
|
||||
puts "=" * 60
|
||||
|
||||
contenido = leer_dashboard
|
||||
resultados = {}
|
||||
|
||||
REGLAS_VERIFICACION.each do |nombre, regla|
|
||||
puts "📋 Verificando: #{regla[:descripcion]}"
|
||||
resultados[nombre] = ejecutar_verificacion(contenido, regla)
|
||||
end
|
||||
|
||||
@resultados_cache = resultados
|
||||
mostrar_resumen_verificacion(resultados)
|
||||
|
||||
resultados
|
||||
end
|
||||
|
||||
def verificar_estructura
|
||||
puts "🏗️ VERIFICANDO ESTRUCTURA DEL DASHBOARD"
|
||||
puts "=" * 60
|
||||
|
||||
contenido = leer_dashboard
|
||||
regla = REGLAS_VERIFICACION[:estructura]
|
||||
resultado = ejecutar_verificacion(contenido, regla)
|
||||
|
||||
mostrar_detalle_estructura(resultado)
|
||||
resultado
|
||||
end
|
||||
|
||||
def verificar_hitos
|
||||
puts "🎯 VERIFICANDO HITOS DEL DASHBOARD"
|
||||
puts "=" * 60
|
||||
|
||||
contenido = leer_dashboard
|
||||
regla = REGLAS_VERIFICACION[:hitos]
|
||||
resultado = ejecutar_verificacion(contenido, regla)
|
||||
|
||||
mostrar_detalle_hitos(resultado, contenido)
|
||||
resultado
|
||||
end
|
||||
|
||||
def verificar_estados
|
||||
puts "📊 VERIFICANDO ESTADOS DE HITOS"
|
||||
puts "=" * 60
|
||||
|
||||
contenido = leer_dashboard
|
||||
regla = REGLAS_VERIFICACION[:estados]
|
||||
resultado = ejecutar_verificacion(contenido, regla)
|
||||
|
||||
mostrar_detalle_estados(resultado, contenido)
|
||||
resultado
|
||||
end
|
||||
|
||||
def analizar_hitos_detallado
|
||||
puts "📋 ANÁLISIS DETALLADO DE HITOS"
|
||||
puts "=" * 60
|
||||
|
||||
contenido = leer_dashboard
|
||||
hitos = extraer_hitos(contenido)
|
||||
|
||||
if hitos.empty?
|
||||
puts "ℹ️ No se encontraron hitos en el dashboard"
|
||||
return nil
|
||||
end
|
||||
|
||||
analisis = {
|
||||
total: hitos.size,
|
||||
por_estado: analizar_por_estado(hitos),
|
||||
por_fase: analizar_por_fase(hitos),
|
||||
por_tag: analizar_por_tag(hitos),
|
||||
duplicados: buscar_hitos_duplicados(contenido),
|
||||
ids_invalidos: hitos.reject { |h| HitoIdGenerator.es_valido?(h[:id]) }
|
||||
}
|
||||
|
||||
mostrar_analisis_detallado(analisis, hitos)
|
||||
analisis
|
||||
end
|
||||
|
||||
def generar_reporte_verificacion
|
||||
resultados = @resultados_cache || verificar_completo
|
||||
|
||||
reporte = "📋 REPORTE DE VERIFICACIÓN - DASHBOARD P2601\n"
|
||||
reporte += "=" * 70 + "\n"
|
||||
reporte += "Fecha: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}\n"
|
||||
reporte += "Dashboard: #{CONFIG[:production_url]}\n\n"
|
||||
|
||||
# Resumen ejecutivo
|
||||
reporte += "📊 RESUMEN EJECUTIVO\n"
|
||||
reporte += "-" * 40 + "\n"
|
||||
|
||||
total_reglas = resultados.size
|
||||
reglas_exitosas = resultados.values.count { |r| r[:exito] }
|
||||
porcentaje_exito = (reglas_exitosas * 100.0 / total_reglas).round(1)
|
||||
|
||||
reporte += "Reglas verificadas: #{total_reglas}\n"
|
||||
reporte += "Reglas exitosas: #{reglas_exitosas}\n"
|
||||
reporte += "Porcentaje de éxito: #{porcentaje_exito}%\n\n"
|
||||
|
||||
# Detalle por regla
|
||||
reporte += "🔍 DETALLE POR REGLA DE VERIFICACIÓN\n"
|
||||
reporte += "-" * 40 + "\n"
|
||||
|
||||
resultados.each do |nombre, resultado|
|
||||
regla = REGLAS_VERIFICACION[nombre]
|
||||
emoji = resultado[:exito] ? '✅' : '❌'
|
||||
|
||||
reporte += "#{emoji} #{regla[:descripcion]} (#{regla[:nivel].upcase})\n"
|
||||
|
||||
if resultado[:detalles].is_a?(Hash)
|
||||
resultado[:detalles].each do |detalle, valor|
|
||||
reporte += " • #{detalle}: #{valor}\n"
|
||||
end
|
||||
end
|
||||
|
||||
unless resultado[:exito]
|
||||
reporte += " ⚠️ Problemas detectados:\n"
|
||||
resultado[:problemas].each do |problema|
|
||||
reporte += " - #{problema}\n"
|
||||
end
|
||||
end
|
||||
|
||||
reporte += "\n"
|
||||
end
|
||||
|
||||
# Recomendaciones
|
||||
reporte += "💡 RECOMENDACIONES\n"
|
||||
reporte += "-" * 40 + "\n"
|
||||
|
||||
recomendaciones = generar_recomendaciones(resultados)
|
||||
if recomendaciones.any?
|
||||
recomendaciones.each do |recomendacion|
|
||||
reporte += "• #{recomendacion}\n"
|
||||
end
|
||||
else
|
||||
reporte += "✅ Todas las verificaciones pasaron exitosamente.\n"
|
||||
end
|
||||
|
||||
reporte += "\n🔗 Dashboard: #{CONFIG[:production_url]}\n"
|
||||
reporte += "📅 Reporte generado: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}\n"
|
||||
|
||||
reporte
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ejecutar_verificacion(contenido, regla)
|
||||
begin
|
||||
detalles = regla[:verificar].call(contenido)
|
||||
problemas = identificar_problemas(detalles, regla[:nivel])
|
||||
|
||||
{
|
||||
exito: problemas.empty?,
|
||||
detalles: detalles,
|
||||
problemas: problemas,
|
||||
nivel: regla[:nivel],
|
||||
descripcion: regla[:descripcion]
|
||||
}
|
||||
rescue => e
|
||||
{
|
||||
exito: false,
|
||||
detalles: { error: e.message },
|
||||
problemas: ["Error durante verificación: #{e.message}"],
|
||||
nivel: regla[:nivel],
|
||||
descripcion: regla[:descripcion]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def identificar_problemas(detalles, nivel)
|
||||
problemas = []
|
||||
|
||||
case nivel
|
||||
when 'crítico'
|
||||
# Para estructura, todos los elementos deben estar presentes
|
||||
if detalles.is_a?(Hash) && detalles.key?('DOCTYPE html')
|
||||
detalles.each do |elemento, presente|
|
||||
problemas << "Falta elemento: #{elemento}" unless presente
|
||||
end
|
||||
end
|
||||
when 'alto'
|
||||
# Para hitos, no debe haber duplicados y todos deben ser válidos
|
||||
if detalles.is_a?(Hash)
|
||||
if detalles[:duplicados] && detalles[:duplicados] > 0
|
||||
problemas << "Se encontraron #{detalles[:duplicados]} hitos duplicados"
|
||||
end
|
||||
|
||||
if detalles[:total_hitos] && detalles[:hitos_validos]
|
||||
if detalles[:total_hitos] > 0 && detalles[:hitos_validos] < detalles[:total_hitos]
|
||||
problemas << "#{detalles[:total_hitos] - detalles[:hitos_validos]} hitos no válidos"
|
||||
end
|
||||
end
|
||||
end
|
||||
when 'medio'
|
||||
# Para estados, todos deben ser válidos
|
||||
if detalles.is_a?(Hash) && detalles[:estados_invalidos] && detalles[:estados_invalidos] > 0
|
||||
problemas << "Se encontraron #{detalles[:estados_invalidos]} estados inválidos"
|
||||
end
|
||||
end
|
||||
|
||||
problemas
|
||||
end
|
||||
|
||||
def extraer_valores_metricas(contenido)
|
||||
valores = {}
|
||||
|
||||
# Buscar porcentaje de progreso
|
||||
if match = contenido.match(/id="progressPct">([^<]+)</)
|
||||
valores['Progreso'] = match[1]
|
||||
end
|
||||
|
||||
# Buscar hitos completados
|
||||
if match = contenido.match(/Hitos Completados[^<]*<[^>]*>([^<]+)</)
|
||||
valores['Hitos Completados'] = match[1]
|
||||
end
|
||||
|
||||
# Buscar hitos pendientes
|
||||
if match = contenido.match(/Hitos Pendientes[^<]*<[^>]*>([^<]+)</)
|
||||
valores['Hitos Pendientes'] = match[1]
|
||||
end
|
||||
|
||||
# Buscar esfuerzo total
|
||||
if match = contenido.match(/Esfuerzo Total[^<]*<[^>]*>([^<]+)</)
|
||||
valores['Esfuerzo Total'] = match[1]
|
||||
end
|
||||
|
||||
valores
|
||||
end
|
||||
|
||||
def analizar_por_estado(hitos)
|
||||
conteo = Hash.new(0)
|
||||
hitos.each { |h| conteo[h[:estado]] += 1 }
|
||||
conteo
|
||||
end
|
||||
|
||||
def analizar_por_fase(hitos)
|
||||
conteo = Hash.new(0)
|
||||
hitos.each do |hito|
|
||||
if info = HitoIdGenerator.parsear_id(hito[:id])
|
||||
conteo[info[:fase]] += 1
|
||||
else
|
||||
conteo['sin_fase'] += 1
|
||||
end
|
||||
end
|
||||
conteo
|
||||
end
|
||||
|
||||
def analizar_por_tag(hitos)
|
||||
conteo = Hash.new(0)
|
||||
hitos.each do |hito|
|
||||
hito[:tags].each { |tag| conteo[tag] += 1 }
|
||||
end
|
||||
conteo
|
||||
end
|
||||
|
||||
def mostrar_resumen_verificacion(resultados)
|
||||
puts "\n📊 RESUMEN DE VERIFICACIÓN"
|
||||
puts "=" * 60
|
||||
|
||||
total_reglas = resultados.size
|
||||
reglas_exitosas = resultados.values.count { |r| r[:exito] }
|
||||
reglas_fallidas = total_reglas - reglas_exitosas
|
||||
|
||||
puts "Reglas verificadas: #{total_reglas}"
|
||||
puts "Reglas exitosas: #{reglas_exitosas}"
|
||||
puts "Reglas fallidas: #{reglas_fallidas}"
|
||||
puts "Porcentaje de éxito: #{(reglas_exitosas * 100.0 / total_reglas).round(1)}%"
|
||||
|
||||
# Mostrar resumen por nivel
|
||||
puts "\n📈 DISTRIBUCIÓN POR NIVEL DE IMPORTANCIA:"
|
||||
['crítico', 'alto', 'medio', 'bajo'].each do |nivel|
|
||||
reglas_nivel = resultados.select { |_, r| r[:nivel] == nivel }
|
||||
exitos_nivel = reglas_nivel.count { |_, r| r[:exito] }
|
||||
total_nivel = reglas_nivel.size
|
||||
|
||||
if total_nivel > 0
|
||||
puts " • #{nivel.capitalize}: #{exitos_nivel}/#{total_nivel} exitosas"
|
||||
end
|
||||
end
|
||||
|
||||
# Mostrar problemas críticos
|
||||
problemas_criticos = resultados.select { |_, r| !r[:exito] && r[:nivel] == 'crítico' }
|
||||
if problemas_criticos.any?
|
||||
puts "\n⚠️ PROBLEMAS CRÍTICOS DETECTADOS:"
|
||||
problemas_criticos.each do |nombre, resultado|
|
||||
puts " • #{resultado[:descripcion]}"
|
||||
resultado[:problemas].each do |problema|
|
||||
puts " - #{problema}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if reglas_exitosas == total_reglas
|
||||
puts "\n🎉 ¡Todas las verificaciones pasaron exitosamente!"
|
||||
else
|
||||
puts "\n⚠️ Se encontraron problemas que requieren atención"
|
||||
end
|
||||
end
|
||||
|
||||
def mostrar_detalle_estructura(resultado)
|
||||
puts "\n🏗️ DETALLE DE ESTRUCTURA:"
|
||||
|
||||
if resultado[:exito]
|
||||
puts "✅ Estructura HTML básica correcta"
|
||||
else
|
||||
puts "❌ Problemas en la estructura:"
|
||||
resultado[:problemas].each do |problema|
|
||||
puts " • #{problema}"
|
||||
end
|
||||
end
|
||||
|
||||
puts "\n📋 ELEMENTOS VERIFICADOS:"
|
||||
resultado[:detalles].each do |elemento, presente|
|
||||
puts " • #{elemento}: #{presente ? '✅ Presente' : '❌ Ausente'}"
|
||||
end
|
||||
end
|
||||
|
||||
def mostrar_detalle_hitos(resultado, contenido)
|
||||
hitos = extraer_hitos(contenido)
|
||||
|
||||
puts "\n🎯 DETALLE DE HITOS:"
|
||||
puts " • Total de hitos: #{resultado[:detalles][:total_hitos]}"
|
||||
puts " • Hitos válidos: #{resultado[:detalles][:hitos_validos]}"
|
||||
puts " • IDs válidos: #{resultado[:detalles][:ids_validos]}"
|
||||
puts " • Hitos duplicados: #{resultado[:detalles][:duplicados]}"
|
||||
|
||||
if resultado[:detalles][:duplicados] > 0
|
||||
duplicados = buscar_hitos_duplicados(contenido)
|
||||
puts " • IDs duplicados: #{duplicados.join(', ')}"
|
||||
end
|
||||
|
||||
unless resultado[:exito]
|
||||
puts "\n⚠️ PROBLEMAS DETECTADOS:"
|
||||
resultado[:problemas].each do |problema|
|
||||
puts " • #{problema}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def mostrar_detalle_estados(resultado, contenido)
|
||||
hitos = extraer_hitos(contenido)
|
||||
|
||||
puts "\n📊 DETALLE DE ESTADOS:"
|
||||
puts " • Estados válidos: #{resultado[:detalles][:estados_validos]}"
|
||||
puts " • Estados inválidos: #{resultado[:detalles][:estados_invalidos]}"
|
||||
puts " • Estados presentes: #{resultado[:detalles][:estados_presentes].join(', ')}"
|
||||
|
||||
unless resultado[:exito]
|
||||
puts "\n⚠️ PROBLEMAS DETECTADOS:"
|
||||
resultado[:problemas].each do |problema|
|
||||
puts " • #{problema}"
|
||||
end
|
||||
end
|
||||
|
||||
if resultado[:detalles][:estados_invalidos] > 0
|
||||
puts "\n🔍 ESTADOS INVÁLIDOS ENCONTRADOS:"
|
||||
hitos.each do |hito|
|
||||
unless Validator.validar_estado(hito[:estado])
|
||||
puts " • #{hito[:id]}: '#{hito[:estado]}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def mostrar_analisis_detallado(analisis, hitos)
|
||||
puts "\n📈 ANÁLISIS DETALLADO DE HITOS"
|
||||
puts "=" * 60
|
||||
|
||||
puts "📊 ESTADÍSTICAS GENERALES:"
|
||||
puts " • Total de hitos: #{analisis[:total]}"
|
||||
|
||||
if analisis[:por_estado].any?
|
||||
puts "\n📊 DISTRIBUCIÓN POR ESTADO:"
|
||||
analisis[:por_estado].each do |estado, cantidad|
|
||||
porcentaje = (cantidad * 100.0 / analisis[:total]).round(1)
|
||||
estado_info = ESTADOS_VALIDOS[estado] || ESTADOS_VALIDOS['pending']
|
||||
puts " • #{estado_info[:nombre]}: #{cantidad} (#{porcentaje}%)"
|
||||
end
|
||||
end
|
||||
|
||||
if analisis[:por_fase].any?
|
||||
puts "\n📁 DISTRIBUCIÓN POR FASE:"
|
||||
analisis[:por_fase].each do |fase, cantidad|
|
||||
puts " • Fase #{fase}: #{cantidad} hitos"
|
||||
end
|
||||
end
|
||||
|
||||
if analisis[:por_tag].any?
|
||||
puts "\n🏷️ DISTRIBUCIÓN POR TAG:"
|
||||
analisis[:por_tag].sort_by { |_, count| -count }.first(10).each do |tag, cantidad|
|
||||
puts " • #{tag}: #{cantidad} hitos"
|
||||
end
|
||||
end
|
||||
|
||||
if analisis[:duplicados].any?
|
||||
puts "\n⚠️ HITOS DUPLICADOS:"
|
||||
puts " • Total: #{analisis[:duplicados].size}"
|
||||
puts " • IDs: #{analisis[:duplicados].join(', ')}"
|
||||
end
|
||||
|
||||
if analisis[:ids_invalidos].any?
|
||||
puts "\n❌ IDs DE HITOS INVÁLIDOS:"
|
||||
analisis[:ids_invalidos].each do |hito|
|
||||
puts " • #{hito[:id]}: #{hito[:titulo]}"
|
||||
end
|
||||
end
|
||||
|
||||
# Mostrar hitos por fase y estado
|
||||
puts "\n🎯 HITOS POR FASE Y ESTADO:"
|
||||
hitos.group_by do |hito|
|
||||
info = HitoIdGenerator.parsear_id(hito[:id])
|
||||
info ? info[:fase] : 'sin_fase'
|
||||
end.each do |fase, hitos_fase|
|
||||
puts " 📁 Fase #{fase}:"
|
||||
hitos_fase.group_by { |h| h[:estado] }.each do |estado, hitos_estado|
|
||||
estado_info = ESTADOS_VALIDOS[estado] || ESTADOS_VALIDOS['pending']
|
||||
puts " • #{estado_info[:nombre]}: #{hitos_estado.size}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def generar_recomendaciones(resultados)
|
||||
recomendaciones = []
|
||||
|
||||
# Recomendaciones basadas en problemas encontrados
|
||||
resultados.each do |nombre, resultado|
|
||||
next if resultado[:exito]
|
||||
|
||||
case nombre
|
||||
when :estructura
|
||||
recomendaciones << "Corregir estructura HTML básica del dashboard"
|
||||
when :hitos
|
||||
if resultado[:detalles][:duplicados] > 0
|
||||
recomendaciones << "Eliminar hitos duplicados usando la herramienta 'limpiar'"
|
||||
end
|
||||
if resultado[:detalles][:total_hitos] > 0 &&
|
||||
resultado[:detalles][:hitos_validos] < resultado[:detalles][:total_hitos]
|
||||
recomendaciones << "Corregir hitos con estructura inválida"
|
||||
end
|
||||
when :estados
|
||||
if resultado[:detalles][:estados_invalidos] > 0
|
||||
recomendaciones << "Corregir estados inválidos de hitos"
|
||||
end
|
||||
when :timeline
|
||||
unless resultado[:detalles][:presente]
|
||||
recomendaciones << "Agregar sección timeline al dashboard"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Recomendaciones generales
|
||||
contenido = leer_dashboard
|
||||
hitos = extraer_hitos(contenido)
|
||||
|
||||
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
|
||||
|
||||
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