Reestructuración modular integral de servicios, reorganización de gestión en docs/ y limpieza profunda del repositorio

This commit is contained in:
Ricardo Monla
2026-03-06 18:03:07 -03:00
parent 15725e0b3c
commit ac9bdf516b
286 changed files with 36187 additions and 112775 deletions
+440
View File
@@ -0,0 +1,440 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
# tools/orquestador/lib/estados/manager.rb
# ========================================
# Clase para manejo centralizado de estados de hitos del dashboard P2601
# Filosofía: "Menos es más" - código reutilizable y modular
require_relative '../dashboard_core'
module DashboardCore
class EstadosManager < Base
attr_reader :estados_cache
def initialize(dashboard_path = nil)
super(dashboard_path)
@estados_cache = nil
end
# Métodos principales para gestión de estados
def cargar_estados(force_reload = false)
return @estados_cache if @estados_cache && !force_reload
hitos = cargar_hitos
@estados_cache = analizar_estados(hitos)
@estados_cache
end
def listar_estados_disponibles
ESTADOS_VALIDOS.map do |codigo, info|
{
codigo: codigo,
nombre: info[:nombre],
clase: info[:clase],
color: info[:color],
descripcion: descripcion_estado(codigo)
}
end
end
def actualizar_estado_hito(id_hito, nuevo_estado, fecha = nil)
# Validar estado
unless Validator.validar_estado(nuevo_estado)
raise "Estado '#{nuevo_estado}' no válido. Estados disponibles: #{ESTADOS_VALIDOS.keys.join(', ')}"
end
# Verificar que el hito exista
hito = buscar_hito_por_id(id_hito)
unless hito
raise "Hito #{id_hito} no encontrado"
end
# No hacer nada si el estado ya es el mismo
if hito[:estado] == nuevo_estado
puts "️ El hito #{id_hito} ya tiene estado '#{nuevo_estado}'"
return true
end
# Crear backup
crear_backup('actualizar_estado')
# Leer contenido actual
contenido = leer_dashboard
# Actualizar estado en el contenido
fecha_actual = fecha || Time.now.strftime('%Y-%m-%d')
estado_info = ESTADOS_VALIDOS[nuevo_estado]
nuevo_contenido, encontrado = actualizar_hito_en_contenido(
contenido, id_hito, nuevo_estado, fecha_actual
)
unless encontrado
raise "No se pudo encontrar el hito #{id_hito} para actualizar estado"
end
# Guardar cambios
guardar_dashboard(nuevo_contenido)
# Actualizar cache
@estados_cache = nil
cargar_estados(true)
# Registrar cambio
registrar_cambio_estado(id_hito, hito[:estado], nuevo_estado, fecha_actual)
puts "✅ Estado del hito #{id_hito} actualizado: #{hito[:estado]}#{nuevo_estado}"
true
end
def actualizar_estados_multiples(cambios)
# Validar todos los cambios primero
cambios.each do |id_hito, nuevo_estado|
unless Validator.validar_estado(nuevo_estado)
raise "Estado '#{nuevo_estado}' no válido para hito #{id_hito}"
end
end
# Crear backup
crear_backup('actualizar_estados_multiples')
# Leer contenido actual
contenido = leer_dashboard
nuevo_contenido = contenido.dup
# Aplicar cada cambio
resultados = {}
cambios.each do |id_hito, nuevo_estado|
puts "🔍 Actualizando hito: #{id_hito}#{nuevo_estado}"
# Verificar que el hito exista
hito = buscar_hito_por_id(id_hito)
if hito
# Actualizar en el contenido
fecha_actual = Time.now.strftime('%Y-%m-%d')
nuevo_contenido, encontrado = actualizar_hito_en_contenido(
nuevo_contenido, id_hito, nuevo_estado, fecha_actual
)
if encontrado && hito[:estado] != nuevo_estado
resultados[id_hito] = { exito: true, anterior: hito[:estado], nuevo: nuevo_estado }
registrar_cambio_estado(id_hito, hito[:estado], nuevo_estado, fecha_actual)
else
resultados[id_hito] = { exito: false, error: 'No encontrado o estado igual' }
end
else
resultados[id_hito] = { exito: false, error: 'Hito no encontrado' }
end
end
# Guardar cambios si hubo actualizaciones exitosas
exitos = resultados.values.count { |r| r[:exito] }
if exitos > 0
guardar_dashboard(nuevo_contenido)
@estados_cache = nil
cargar_estados(true)
end
# Mostrar resultados
mostrar_resultados_actualizacion(resultados)
exitos > 0
end
def avanzar_estado_hito(id_hito)
# Obtener hito actual
hito = buscar_hito_por_id(id_hito)
unless hito
raise "Hito #{id_hito} no encontrado"
end
# Determinar siguiente estado según flujo de trabajo
siguiente_estado = siguiente_estado_en_flujo(hito[:estado])
if siguiente_estado
actualizar_estado_hito(id_hito, siguiente_estado)
else
puts "️ El hito #{id_hito} ya está en el estado final del flujo"
false
end
end
def retroceder_estado_hito(id_hito)
# Obtener hito actual
hito = buscar_hito_por_id(id_hito)
unless hito
raise "Hito #{id_hito} no encontrado"
end
# Determinar estado anterior según flujo de trabajo
estado_anterior = estado_anterior_en_flujo(hito[:estado])
if estado_anterior
actualizar_estado_hito(id_hito, estado_anterior)
else
puts "️ El hito #{id_hito} ya está en el estado inicial del flujo"
false
end
end
def reiniciar_estado_hito(id_hito)
actualizar_estado_hito(id_hito, 'pending')
end
def completar_hito(id_hito, fecha = nil)
actualizar_estado_hito(id_hito, 'completed', fecha)
end
def bloquear_hito(id_hito, fecha = nil)
actualizar_estado_hito(id_hito, 'blocked', fecha)
end
def poner_en_espera_hito(id_hito, fecha = nil)
actualizar_estado_hito(id_hito, 'on-hold', fecha)
end
def iniciar_hito(id_hito, fecha = nil)
actualizar_estado_hito(id_hito, 'in-progress', fecha)
end
# Métodos de consulta y análisis
def estadisticas_estados
estados = cargar_estados
{
total_hitos: estados[:total_hitos],
distribucion: estados[:distribucion],
porcentajes: estados[:porcentajes],
tendencia: calcular_tendencia(estados),
ultima_actualizacion: Time.now.strftime('%Y-%m-%d %H:%M:%S')
}
end
def reporte_estados
estados = cargar_estados
reporte = "📊 REPORTE DE ESTADOS DE HITOS\n"
reporte += "=" * 60 + "\n"
reporte += "Total de hitos: #{estados[:total_hitos]}\n\n"
reporte += "📈 DISTRIBUCIÓN POR ESTADO:\n"
estados[:distribucion].each do |estado, cantidad|
porcentaje = estados[:porcentajes][estado]
estado_info = ESTADOS_VALIDOS[estado] || ESTADOS_VALIDOS['pending']
reporte += "#{estado_info[:nombre]}: #{cantidad} (#{porcentaje}%)\n"
end
# Análisis de progreso
completados = estados[:distribucion]['completed'] || 0
en_progreso = estados[:distribucion]['in-progress'] || 0
total = estados[:total_hitos]
if total > 0
progreso_total = ((completados + (en_progreso * 0.5)) / total * 100).round(1)
reporte += "\n🎯 PROGRESO GENERAL DEL PROYECTO:\n"
reporte += " • Progreso estimado: #{progreso_total}%\n"
reporte += " • Hitos completados: #{completados}/#{total}\n"
reporte += " • Hitos en progreso: #{en_progreso}/#{total}\n"
end
# Recomendaciones
reporte += "\n💡 RECOMENDACIONES:\n"
if (estados[:distribucion]['pending'] || 0) > total * 0.7
reporte += " • Muchos hitos pendientes (#{estados[:distribucion]['pending']}/#{total})\n"
reporte += " • Considerar iniciar más hitos\n"
end
if (estados[:distribucion]['blocked'] || 0) > 0
reporte += " • Hay hitos bloqueados (#{estados[:distribucion]['blocked']})\n"
reporte += " • Revisar dependencias y obstáculos\n"
end
if (estados[:distribucion]['on-hold'] || 0) > 0
reporte += " • Hay hitos en espera (#{estados[:distribucion]['on-hold']})\n"
reporte += " • Evaluar prioridades y recursos\n"
end
reporte
end
def hitos_por_estado(estado)
hitos = cargar_hitos
hitos.select { |h| h[:estado] == estado }
end
def hitos_pendientes
hitos_por_estado('pending')
end
def hitos_en_progreso
hitos_por_estado('in-progress')
end
def hitos_completados
hitos_por_estado('completed')
end
def hitos_bloqueados
hitos_por_estado('blocked')
end
def hitos_en_espera
hitos_por_estado('on-hold')
end
def progreso_por_fase
hitos = cargar_hitos
progreso_por_fase = {}
hitos.group_by do |hito|
info = HitoIdGenerator.parsear_id(hito[:id])
info ? info[:fase] : 'sin_fase'
end.each do |fase, hitos_fase|
total = hitos_fase.size
completados = hitos_fase.count { |h| h[:estado] == 'completed' }
en_progreso = hitos_fase.count { |h| h[:estado] == 'in-progress' }
progreso = total > 0 ? ((completados + (en_progreso * 0.5)) / total * 100).round(1) : 0
progreso_por_fase[fase] = {
total: total,
completados: completados,
en_progreso: en_progreso,
progreso: progreso
}
end
progreso_por_fase
end
private
def cargar_hitos
contenido = leer_dashboard
extraer_hitos(contenido)
end
def buscar_hito_por_id(id_hito)
hitos = cargar_hitos
hitos.find { |h| h[:id] == id_hito }
end
def analizar_estados(hitos)
total_hitos = hitos.size
distribucion = Hash.new(0)
hitos.each { |h| distribucion[h[:estado]] += 1 }
porcentajes = {}
distribucion.each do |estado, cantidad|
porcentajes[estado] = total_hitos > 0 ? (cantidad * 100.0 / total_hitos).round(1) : 0
end
{
total_hitos: total_hitos,
distribucion: distribucion,
porcentajes: porcentajes,
hitos: hitos
}
end
def descripcion_estado(codigo_estado)
case codigo_estado
when 'pending'
"Hito pendiente de iniciar. Color amarillo."
when 'in-progress'
"Hito en ejecución activa. Color azul."
when 'completed'
"Hito finalizado exitosamente. Color verde."
when 'blocked'
"Hito bloqueado por dependencias u obstáculos. Color rojo."
when 'on-hold'
"Hito en espera por priorización o recursos. Color púrpura."
else
"Estado desconocido."
end
end
def siguiente_estado_en_flujo(estado_actual)
flujo = {
'pending' => 'in-progress',
'in-progress' => 'completed',
'completed' => nil, # Estado final
'blocked' => 'in-progress', # Desbloqueado
'on-hold' => 'in-progress' # Reanudado
}
flujo[estado_actual]
end
def estado_anterior_en_flujo(estado_actual)
flujo_inverso = {
'in-progress' => 'pending',
'completed' => 'in-progress',
'blocked' => 'in-progress',
'on-hold' => 'in-progress'
}
flujo_inverso[estado_actual]
end
def registrar_cambio_estado(id_hito, estado_anterior, estado_nuevo, fecha)
# Podría guardarse en un log o base de datos
log_entry = {
id_hito: id_hito,
estado_anterior: estado_anterior,
estado_nuevo: estado_nuevo,
fecha: fecha,
timestamp: Time.now.to_i
}
# Por ahora solo imprimimos para debug
debug("Cambio de estado registrado: #{log_entry}")
end
def mostrar_resultados_actualizacion(resultados)
puts "\n📊 RESULTADOS DE ACTUALIZACIÓN DE ESTADOS"
puts "=" * 60
exitos = resultados.values.count { |r| r[:exito] }
fallos = resultados.values.count { |r| !r[:exito] }
resultados.each do |id_hito, resultado|
if resultado[:exito]
puts "#{id_hito}: #{resultado[:anterior]}#{resultado[:nuevo]}"
else
puts "#{id_hito}: #{resultado[:error]}"
end
end
puts "\n📈 RESUMEN:"
puts " • Hitós actualizados exitosamente: #{exitos}"
puts " • Hitós con errores: #{fallos}"
end
def calcular_tendencia(estados)
# Simple análisis de tendencia (podría mejorarse con histórico)
completados = estados[:distribucion]['completed'] || 0
en_progreso = estados[:distribucion]['in-progress'] || 0
total = estados[:total_hitos]
if total == 0
return 'sin_datos'
end
porcentaje_completados = (completados * 100.0 / total).round(1)
if porcentaje_completados > 70
'avanzado'
elsif porcentaje_completados > 30
'en_progreso'
elsif en_progreso > 0
'iniciando'
else
'pendiente'
end
end
end
end