#!/usr/bin/env ruby
# frozen_string_literal: true
# tools/orquestador/migrar_ids_hitos.rb
# ======================================
# Script de migración para actualizar IDs de hitos al nuevo formato fNNNNN
# Migra IDs antiguos como BKP02, DB02, etc. a f05001, f05002, etc.
require 'fileutils'
class MigradorIDsHitos
# Mapeo de IDs antiguos a nuevos (Fase 5)
MAPEO_IDS = {
# Fase 5
'BKP02' => 'f05001',
'DB02' => 'f05002',
'MON01' => 'f05003',
'DOC01' => 'f05004',
'PERF01' => 'f05005',
# IDs genéricos para migración automática
'BKP' => 'f05', # Patrón para backups
'DB' => 'f05', # Patrón para bases de datos
'MON' => 'f05', # Patrón para monitoreo
'DOC' => 'f05', # Patrón para documentación
'PERF' => 'f05', # Patrón para performance
}.freeze
def initialize(dashboard_path = nil)
@dashboard_path = dashboard_path || default_dashboard_path
@backup_dir = File.join(File.dirname(@dashboard_path), 'backups')
@contador_migrados = 0
@contador_errores = 0
@ids_migrados = []
end
def default_dashboard_path
File.expand_path('../../../dashboard/index.html', __FILE__)
end
def migrar_ids
puts "🔄 MIGRANDO IDs DE HITOS AL NUEVO FORMATO"
puts "=" * 60
unless File.exist?(@dashboard_path)
puts "❌ Error: No se encuentra el dashboard en #{@dashboard_path}"
return false
end
# Crear backup
crear_backup_migracion
# Leer contenido
puts "📖 Leyendo dashboard..."
contenido = File.read(@dashboard_path)
contenido_original = contenido.dup
# Extraer todos los hitos para análisis
hitos = extraer_hitos_con_ids(contenido)
puts "🔍 Encontrados #{hitos.size} hitos para analizar"
# Mostrar IDs actuales
mostrar_ids_actuales(hitos)
# Preguntar confirmación
unless confirmar_migracion(hitos.size)
puts "❌ Migración cancelada por el usuario"
return false
end
# Realizar migración
puts "\n🔄 Realizando migración..."
contenido_migrado = migrar_contenido(contenido)
# Verificar cambios
if contenido_migrado == contenido_original
puts "ℹ️ No se realizaron cambios (posiblemente ya están migrados)"
return true
end
# Guardar cambios
puts "💾 Guardando dashboard migrado..."
File.write(@dashboard_path, contenido_migrado)
# Mostrar resultados
mostrar_resultados_migracion
# Verificar integridad
verificar_integridad_migracion(contenido_migrado)
true
end
def simular_migracion
puts "🔍 SIMULACIÓN DE MIGRACIÓN (MODO DRY RUN)"
puts "=" * 60
unless File.exist?(@dashboard_path)
puts "❌ Error: No se encuentra el dashboard"
return false
end
contenido = File.read(@dashboard_path)
hitos = extraer_hitos_con_ids(contenido)
puts "📊 ANÁLISIS DE MIGRACIÓN:"
puts " • Total de hitos encontrados: #{hitos.size}"
puts " • IDs que requieren migración: #{hitos.count { |h| requiere_migracion?(h[:id]) }}"
puts " • IDs ya en formato nuevo: #{hitos.count { |h| !requiere_migracion?(h[:id]) }}"
puts "\n📋 DETALLE DE MIGRACIÓN:"
hitos.each do |hito|
if requiere_migracion?(hito[:id])
nuevo_id = generar_nuevo_id(hito[:id])
puts " • #{hito[:id]} → #{nuevo_id} (#{hito[:titulo][0..50]}...)"
else
puts " • #{hito[:id]} (ya migrado)"
end
end
puts "\n💡 RECOMENDACIONES:"
if hitos.any? { |h| requiere_migracion?(h[:id]) }
puts " • Ejecutar migración completa: ./migrar_ids_hitos.rb migrar"
puts " • Revisar mapeo de IDs antes de proceder"
else
puts " • Todos los IDs ya están en formato nuevo"
puts " • No se requiere migración"
end
true
end
def verificar_estado_migracion
puts "🔍 VERIFICANDO ESTADO DE MIGRACIÓN"
puts "=" * 60
unless File.exist?(@dashboard_path)
puts "❌ Error: No se encuentra el dashboard"
return false
end
contenido = File.read(@dashboard_path)
hitos = extraer_hitos_con_ids(contenido)
puts "📊 ESTADO ACTUAL:"
# Contar por tipo de ID
ids_antiguos = hitos.select { |h| requiere_migracion?(h[:id]) }
ids_nuevos = hitos.select { |h| !requiere_migracion?(h[:id]) }
puts " • IDs en formato antiguo: #{ids_antiguos.size}"
puts " • IDs en formato nuevo: #{ids_nuevos.size}"
puts " • Total de hitos: #{hitos.size}"
if ids_antiguos.any?
puts "\n⚠️ IDs QUE REQUIEREN MIGRACIÓN:"
ids_antiguos.each do |hito|
nuevo_id = generar_nuevo_id(hito[:id])
puts " • #{hito[:id]} → #{nuevo_id}"
end
end
if ids_nuevos.any?
puts "\n✅ IDs YA MIGRADOS:"
ids_nuevos.each do |hito|
puts " • #{hito[:id]}"
end
end
# Verificar formato de IDs nuevos
puts "\n🔍 VALIDACIÓN DE FORMATOS:"
ids_nuevos.each do |hito|
if formato_valido_nuevo?(hito[:id])
puts " • #{hito[:id]}: ✅ Formato válido"
else
puts " • #{hito[:id]}: ❌ Formato inválido"
end
end
puts "\n💡 RECOMENDACIONES:"
if ids_antiguos.any?
puts " • Ejecutar migración para estandarizar IDs"
puts " • Usar: ./migrar_ids_hitos.rb migrar"
else
puts " • Todos los IDs están en formato nuevo"
puts " • Sistema listo para usar módulos optimizados"
end
true
end
private
def crear_backup_migracion
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_pre_migracion_#{timestamp}.html")
puts "💾 Creando backup pre-migración: #{backup_file}"
FileUtils.cp(@dashboard_path, backup_file)
puts "✅ Backup creado"
end
def extraer_hitos_con_ids(contenido)
hitos = []
contenido.scan(/
([\s\S]*?)<\/div>\s*<\/div>\s*<\/div>/m) do |hito_html|
hito = hito_html[0]
# Extraer ID
id_match = hito.match(/
([^<]+)<\/span>/)
titulo_match = hito.match(/([^<]+)<\/div>/)
if id_match && titulo_match
hitos << {
id: id_match[1],
titulo: titulo_match[1],
html_completo: hito_html[0]
}
end
end
hitos.uniq { |h| h[:id] }
end
def mostrar_ids_actuales(hitos)
puts "\n📋 IDs ACTUALES ENCONTRADOS:"
hitos.each_with_index do |hito, i|
requiere = requiere_migracion?(hito[:id]) ? "⚠️ Requiere migración" : "✅ Ya migrado"
puts " #{i + 1}. #{hito[:id]}: #{hito[:titulo][0..40]}... (#{requiere})"
end
end
def confirmar_migracion(total_hitos)
puts "\n⚠️ ADVERTENCIA: Esta operación modificará el dashboard"
puts " • Total de hitos a analizar: #{total_hitos}"
puts " • Se creará un backup automático"
puts " • Los cambios no se pueden deshacer automáticamente"
# Para ejecución automática, siempre confirmar
puts "\n✅ Confirmación automática para migración"
true
end
def requiere_migracion?(id)
# Un ID requiere migración si no sigue el formato fNNNNN
!id.match(/^f\d{5}$/)
end
def formato_valido_nuevo?(id)
id.match(/^f\d{5}$/)
end
def generar_nuevo_id(id_antiguo)
# Buscar mapeo directo
return MAPEO_IDS[id_antiguo] if MAPEO_IDS.key?(id_antiguo)
# Buscar por prefijo
MAPEO_IDS.each do |prefijo, nuevo_base|
if id_antiguo.start_with?(prefijo)
# Extraer número del ID antiguo
numero_match = id_antiguo.match(/\d+/)
numero = numero_match ? numero_match[0].to_i : 1
# Generar nuevo ID con secuencia
return "#{nuevo_base}#{numero.to_s.rjust(3, '0')}"
end
end
# ID genérico para fase 5
"f05#{(@contador_migrados + 1).to_s.rjust(3, '0')}"
end
def migrar_contenido(contenido)
nuevo_contenido = contenido.dup
# Extraer todos los hitos
hitos = extraer_hitos_con_ids(contenido)
hitos.each do |hito|
if requiere_migracion?(hito[:id])
nuevo_id = generar_nuevo_id(hito[:id])
# Reemplazar en el contenido
nuevo_contenido = nuevo_contenido.gsub(
/#{Regexp.escape(hito[:id])}<\/span>/,
"#{nuevo_id}"
)
@contador_migrados += 1
@ids_migrados << { antiguo: hito[:id], nuevo: nuevo_id }
puts " • #{hito[:id]} → #{nuevo_id}"
end
end
nuevo_contenido
end
def mostrar_resultados_migracion
puts "\n📊 RESULTADOS DE LA MIGRACIÓN:"
puts "=" * 60
puts "✅ Migración completada exitosamente"
puts " • IDs migrados: #{@contador_migrados}"
puts " • Errores: #{@contador_errores}"
if @ids_migrados.any?
puts "\n📋 DETALLE DE MIGRACIONES:"
@ids_migrados.each do |migracion|
puts " • #{migracion[:antiguo]} → #{migracion[:nuevo]}"
end
end
puts "\n💡 RECOMENDACIONES POST-MIGRACIÓN:"
puts " 1. Verificar dashboard en producción"
puts " 2. Actualizar referencias en documentación"
puts " 3. Usar nuevos módulos optimizados"
puts " 4. Backup disponible en: #{@backup_dir}"
end
def verificar_integridad_migracion(contenido_migrado)
puts "\n🔍 VERIFICANDO INTEGRIDAD POST-MIGRACIÓN"
hitos = extraer_hitos_con_ids(contenido_migrado)
ids_invalidos = hitos.select { |h| requiere_migracion?(h[:id]) }
if ids_invalidos.empty?
puts "✅ Todos los IDs están en formato nuevo"
else
puts "⚠️ Se encontraron IDs aún en formato antiguo:"
ids_invalidos.each do |hito|
puts " • #{hito[:id]}"
end
end
# Verificar duplicados
ids = hitos.map { |h| h[:id] }
duplicados = ids.select { |id| ids.count(id) > 1 }.uniq
if duplicados.empty?
puts "✅ No hay IDs duplicados"
else
puts "❌ IDs duplicados encontrados:"
duplicados.each do |id|
puts " • #{id}"
end
end
end
end
# Interfaz de línea de comandos
if __FILE__ == $0
begin
migrador = MigradorIDsHitos.new
# Parsear argumentos
command = ARGV[0] || 'verificar'
case command
when 'migrar', 'migrate'
success = migrador.migrar_ids
when 'simular', 'simulate', 'dry-run'
success = migrador.simular_migracion
when 'verificar', 'check', 'status'
success = migrador.verificar_estado_migracion
when 'help', '--help', '-h'
puts "📖 MIGRADOR DE IDs DE HITOS - Dashboard P2601"
puts "=" * 60
puts
puts "DESCRIPCIÓN:"
puts " Migra IDs de hitos del formato antiguo (BKP02, DB02, etc.)"
puts " al nuevo formato estandarizado (f05001, f05002, etc.)"
puts
puts "USO:"
puts " #{$0} [comando]"
puts
puts "COMANDOS:"
puts " migrar, migrate - Realizar migración completa (con backup)"
puts " simular, simulate - Simular migración (modo dry-run)"
puts " verificar, check - Verificar estado actual de migración"
puts " help - Mostrar esta ayuda"
puts
puts "MAFEO DE IDs (Fase 5):"
puts " • BKP02 → f05001 (Backup Automático de VMs)"
puts " • DB02 → f05002 (Backup Automático de Base de Datos)"
puts " • MON01 → f05003 (Monitoreo de Recursos)"
puts " • DOC01 → f05004 (Documentación Operativa)"
puts " • PERF01 → f05005 (Pruebas de Rendimiento)"
puts
puts "CARACTERÍSTICAS:"
puts " • Backup automático pre-migración"
puts " • Confirmación interactiva"
puts " • Verificación de integridad post-migración"
puts " • Modo dry-run para pruebas"
puts
puts "EJEMPLOS:"
puts " #{$0} verificar # Verificar estado actual"
puts " #{$0} simular # Simular migración"
puts " #{$0} migrar # Realizar migración completa"
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:"
puts " 1. Usar nuevos módulos optimizados en tools/orquestador/lib/"
puts " 2. Consultar README_OPTIMIZADO.md para documentación"
puts " 3. Verificar dashboard: https://ns8.frlr.utn.edu.ar/bitacoras/p2601"
puts
puts "📅 #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
puts "=" * 60
else
exit 1
end
rescue => e
puts "❌ Error: #{e.message}"
puts e.backtrace if ENV['DEBUG']
exit 1
end
end