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
+365
View File
@@ -0,0 +1,365 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
# tools/orquestador/dashboard/actualizar_estados.rb
# ================================================
# Herramienta para actualizar el estado de hitos en el dashboard P2601
# Filosofía: "Menos es más" - herramienta pequeña, específica y reutilizable
require 'fileutils'
class EstadoHitosManager
ESTADOS_VALIDOS = {
'pending' => { nombre: 'Pendiente', clase: 'pending', color: '#eab308' },
'in-progress' => { nombre: 'En Progreso', clase: 'in-progress', color: '#3b82f6' },
'completed' => { nombre: 'Completado', clase: 'completed', color: '#22c55e' },
'blocked' => { nombre: 'Bloqueado', clase: 'blocked', color: '#ef4444' },
'on-hold' => { nombre: 'En Espera', clase: 'on-hold', color: '#8b5cf6' }
}.freeze
def initialize(dashboard_path = nil)
@dashboard_path = dashboard_path || default_dashboard_path
@backup_dir = File.join(File.dirname(@dashboard_path), 'backups')
end
def default_dashboard_path
File.expand_path('../../../../dashboard/index.html', __FILE__)
end
def listar_hitos
puts "📋 LISTADO DE HITOS DISPONIBLES"
puts "=" * 50
unless File.exist?(@dashboard_path)
puts "❌ Error: No se encuentra el dashboard en #{@dashboard_path}"
return false
end
content = File.read(@dashboard_path)
hitos = extraer_hitos(content)
if hitos.empty?
puts "️ No se encontraron hitos en el dashboard"
return true
end
puts "🎯 Total de hitos encontrados: #{hitos.size}"
puts
hitos.each_with_index do |hito, i|
estado_info = ESTADOS_VALIDOS[hito[:estado]] || ESTADOS_VALIDOS['pending']
puts " #{i + 1}. [#{hito[:id]}] #{hito[:titulo]}"
puts " 📊 Estado actual: #{estado_info[:nombre]} (#{hito[:estado]})"
puts " 📅 Fecha: #{hito[:fecha]}"
puts " 🏷️ Tags: #{hito[:tags].join(', ')}" if hito[:tags].any?
puts
end
mostrar_resumen_estados(hitos)
true
end
def actualizar_estado(hito_id, nuevo_estado, fecha = nil)
puts "🔄 ACTUALIZANDO ESTADO DE HITO"
puts "=" * 50
# Validar estado
unless ESTADOS_VALIDOS.key?(nuevo_estado)
puts "❌ Error: Estado '#{nuevo_estado}' no válido"
puts " Estados válidos: #{ESTADOS_VALIDOS.keys.join(', ')}"
return false
end
unless File.exist?(@dashboard_path)
puts "❌ Error: No se encuentra el dashboard"
return false
end
# Crear backup
create_backup
# Leer contenido
puts "📖 Leyendo dashboard..."
content = File.read(@dashboard_path)
# Buscar y actualizar hito
puts "🔍 Buscando hito: #{hito_id}"
nuevo_contenido, hito_encontrado = actualizar_hito_en_contenido(content, hito_id, nuevo_estado, fecha)
unless hito_encontrado
puts "❌ Error: No se encontró el hito con ID '#{hito_id}'"
puts " Use 'listar' para ver los hitos disponibles"
return false
end
# Guardar cambios
puts "💾 Guardando dashboard actualizado..."
File.write(@dashboard_path, nuevo_contenido)
# Mostrar resultados
estado_info = ESTADOS_VALIDOS[nuevo_estado]
puts "\n✅ Estado actualizado exitosamente"
puts " • Hito: #{hito_id}"
puts " • Nuevo estado: #{estado_info[:nombre]}"
puts " • Fecha: #{fecha || 'Actualizada automáticamente'}"
true
end
def actualizar_multiples(hitos_estados)
puts "🔄 ACTUALIZANDO MÚLTIPLES HITOS"
puts "=" * 50
unless File.exist?(@dashboard_path)
puts "❌ Error: No se encuentra el dashboard"
return false
end
# Validar todos los estados primero
hitos_estados.each do |hito_id, estado|
unless ESTADOS_VALIDOS.key?(estado)
puts "❌ Error: Estado '#{estado}' no válido para hito '#{hito_id}'"
return false
end
end
# Crear backup
create_backup
# Leer contenido
puts "📖 Leyendo dashboard..."
content = File.read(@dashboard_path)
nuevo_contenido = content.dup
# Actualizar cada hito
resultados = {}
hitos_estados.each do |hito_id, estado|
puts "🔍 Actualizando hito: #{hito_id} -> #{estado}"
nuevo_contenido, encontrado = actualizar_hito_en_contenido(nuevo_contenido, hito_id, estado)
resultados[hito_id] = encontrado
end
# Guardar cambios
puts "💾 Guardando dashboard actualizado..."
File.write(@dashboard_path, nuevo_contenido)
# Mostrar resultados
puts "\n📊 RESULTADOS DE ACTUALIZACIÓN:"
exitos = resultados.values.count(true)
fallos = resultados.values.count(false)
resultados.each do |hito_id, exitoso|
emoji = exitoso ? '✅' : '❌'
puts " #{emoji} #{hito_id}: #{exitoso ? 'Actualizado' : 'No encontrado'}"
end
puts "\n📈 RESUMEN:"
puts " • Hitós actualizados exitosamente: #{exitos}"
puts " • Hitós no encontrados: #{fallos}"
exitos > 0
end
def mostrar_estados_disponibles
puts "🎨 ESTADOS DISPONIBLES"
puts "=" * 50
ESTADOS_VALIDOS.each do |codigo, info|
puts "#{codigo}: #{info[:nombre]}"
puts " Clase CSS: #{info[:clase]}"
puts " Color: #{info[:color]}"
puts
end
true
end
private
def create_backup
FileUtils.mkdir_p(@backup_dir) unless Dir.exist?(@backup_dir)
timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
backup_file = File.join(@backup_dir, "dashboard_estados_#{timestamp}.html")
puts "💾 Creando backup en: #{backup_file}"
FileUtils.cp(@dashboard_path, backup_file)
puts "✅ Backup creado"
end
def extraer_hitos(content)
hitos = []
# Buscar todos los 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>/)
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',
tags: hito.scan(/<span class="[^"]*tag[^"]*">([^<]+)<\/span>/).flatten,
html_completo: hito_html[0]
}
end
hitos.uniq { |h| h[:id] }
end
def actualizar_hito_en_contenido(content, hito_id, nuevo_estado, fecha = nil)
fecha_actual = fecha || Time.now.strftime('%Y-%m-%d')
estado_info = ESTADOS_VALIDOS[nuevo_estado]
# Patrón para encontrar el hito específico
patron_hito = /(<div class="tl-item">[\s\S]*?<span class="tl-id">#{Regexp.escape(hito_id)}<\/span>[\s\S]*?<\/div>\s*<\/div>\s*<\/div>)/
hito_encontrado = false
nuevo_contenido = content.gsub(patron_hito) do |hito_completo|
hito_encontrado = true
# Actualizar estado (clase del dot)
hito_actualizado = hito_completo.gsub(
/<div class="tl-dot ([^"]+)"><\/div>/,
"<div class=\"tl-dot #{nuevo_estado}\"></div>"
)
# Actualizar fecha
hito_actualizado = hito_actualizado.gsub(
/<span class="tl-date">([^<]+)<\/span>/,
"<span class=\"tl-date\">#{estado_info[:nombre]} - #{fecha_actual}</span>"
)
hito_actualizado
end
[nuevo_contenido, hito_encontrado]
end
def mostrar_resumen_estados(hitos)
return if hitos.empty?
puts "📊 RESUMEN DE ESTADOS"
puts "=" * 50
conteo_estados = Hash.new(0)
hitos.each { |h| conteo_estados[h[:estado]] += 1 }
total = hitos.size
conteo_estados.each do |estado, cantidad|
porcentaje = (cantidad * 100.0 / total).round(1)
estado_info = ESTADOS_VALIDOS[estado] || ESTADOS_VALIDOS['pending']
puts "#{estado_info[:nombre]}: #{cantidad} (#{porcentaje}%)"
end
puts "\n💡 RECOMENDACIONES:"
if conteo_estados['pending'] && conteo_estados['pending'] > total * 0.7
puts " • Muchos hitos pendientes (#{conteo_estados['pending']}/#{total})"
puts " • Considerar avanzar algunos a 'en-progreso'"
end
if conteo_estados['completed'] && conteo_estados['completed'] < total * 0.2
puts " • Pocos hitos completados (#{conteo_estados['completed'] || 0}/#{total})"
puts " • Revisar progreso real del proyecto"
end
puts
end
end
# Interfaz de línea de comandos
if __FILE__ == $0
begin
manager = EstadoHitosManager.new
# Parsear argumentos
command = ARGV[0] || 'listar'
case command
when 'listar', 'list', 'ls'
success = manager.listar_hitos
when 'actualizar', 'update'
if ARGV.length < 3
puts "❌ Error: Se requieren ID del hito y nuevo estado"
puts " Uso: #{$0} actualizar ID_HITO NUEVO_ESTADO [FECHA]"
puts " Ejemplo: #{$0} actualizar BKP02 in-progress 2024-03-06"
success = false
else
hito_id = ARGV[1]
nuevo_estado = ARGV[2]
fecha = ARGV[3] if ARGV.length >= 4
success = manager.actualizar_estado(hito_id, nuevo_estado, fecha)
end
when 'multiples', 'batch'
if ARGV.length < 2 || ARGV[1..-1].length.odd?
puts "❌ Error: Se requieren pares ID-ESTADO"
puts " Uso: #{$0} multiples ID1 ESTADO1 ID2 ESTADO2 ..."
puts " Ejemplo: #{$0} multiples BKP02 in-progress DB02 completed"
success = false
else
hitos_estados = {}
args = ARGV[1..-1]
args.each_slice(2) do |hito_id, estado|
hitos_estados[hito_id] = estado
end
success = manager.actualizar_multiples(hitos_estados)
end
when 'estados', 'states'
success = manager.mostrar_estados_disponibles
when 'help', '--help', '-h'
puts "📖 USO:"
puts " #{$0} [comando] [argumentos...]"
puts
puts "COMANDOS:"
puts " listar, list - Listar hitos disponibles con sus estados (predeterminado)"
puts " actualizar, update ID ESTADO [FECHA] - Actualizar estado de un hito"
puts " multiples, batch ID1 ESTADO1 ID2 ESTADO2 ... - Actualizar múltiples hitos"
puts " estados, states - Mostrar estados disponibles"
puts " help - Mostrar esta ayuda"
puts
puts "ESTADOS DISPONIBLES:"
puts " pending - Pendiente (amarillo)"
puts " in-progress - En Progreso (azul)"
puts " completed - Completado (verde)"
puts " blocked - Bloqueado (rojo)"
puts " on-hold - En Espera (púrpura)"
puts
puts "EJEMPLOS:"
puts " #{$0} listar"
puts " #{$0} actualizar BKP02 in-progress"
puts " #{$0} actualizar DB02 completed 2024-03-06"
puts " #{$0} multiples BKP02 in-progress MON01 completed"
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. Revisar dashboard actualizado"
puts " 2. Verificar que los estados se muestren correctamente"
puts " 3. Actualizar bitácora del proyecto con los cambios"
puts "\n📅 #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
puts "=" * 50
else
exit 1
end
rescue => e
puts "❌ Error: #{e.message}"
puts e.backtrace if ENV['DEBUG']
exit 1
end
end