Reestructuración modular integral de servicios, reorganización de gestión en docs/ y limpieza profunda del repositorio
This commit is contained in:
+365
@@ -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
|
||||
Reference in New Issue
Block a user