Files
dtic-DIIAA/adn/tools/cli/db/hito.rb
T

215 lines
8.5 KiB
Ruby

module ADN
module SubcomandoDBHito
# Comando: hito:listar [PROYECTO]
def comando_hito_listar(args)
if args.empty?
puts "#{Color::YELLOW}Uso: adn db hito:listar <CODIGO_PROYECTO> (ej: P2605)#{Color::RESET}"
return
end
codigo = args[0]
puts "#{Color::CYAN}📋 Listando hitos del proyecto #{codigo.upcase}...#{Color::RESET}"
BitacorasDB::BitacoraDB.with_connection do |db|
begin
hitos = db.list_hitos(codigo)
if hitos.empty?
puts "#{Color::YELLOW}⚠ No se encontraron hitos o el proyecto no existe.#{Color::RESET}"
return
end
puts ""
puts "#{Color::BOLD}#{"Hito".ljust(8)} #{"Fase".ljust(6)} #{"Estado".ljust(8)} #{"Hrs P/R".ljust(10)} #{"Título"}#{Color::RESET}"
puts "#{"─" * 8} #{"─" * 6} #{"─" * 8} #{"─" * 10} #{"─" * 40}"
hitos.each do |h|
hrs = "#{h['horas_presencial']}/#{h['horas_remoto']}"
puts "#{h['id_hito'].ljust(8)} #{h['fase_num'].to_s.ljust(6)} #{h['estado'].ljust(8)} #{hrs.ljust(10)} #{h['titulo']}"
end
puts "\nTotal: #{hitos.size} hitos"
rescue => e
puts "#{Color::RED}✗ Error: #{e.message}#{Color::RESET}"
end
end
end
# Comando: hito:ordenar <PROYECTO> [--aplicar FILE]
def comando_hito_ordenar(args)
codigo = args.shift
if !codigo
puts "#{Color::YELLOW}Uso: adn db hito:ordenar <PROYECTO> [--aplicar /ruta/al/archivo.txt]#{Color::RESET}"
return
end
aplicar_path = nil
if args[0] == '--aplicar' && args[1]
aplicar_path = args[1]
end
BitacorasDB::BitacoraDB.with_connection do |db|
begin
hitos = db.list_hitos(codigo)
if hitos.empty?
puts "#{Color::YELLOW}⚠ No se encontraron hitos para #{codigo.upcase}.#{Color::RESET}"
return
end
if aplicar_path
unless File.exist?(aplicar_path)
puts "#{Color::RED}✗ No se encontró el archivo: #{aplicar_path}#{Color::RESET}"
return
end
# Procesar el archivo y aplicarrenombramientos lógicos "Fase.Grupo.Paso"
puts "#{Color::CYAN}📋 Aplicando ordenamiento desde #{aplicar_path}...#{Color::RESET}"
lineas = File.readlines(aplicar_path).map(&:strip).reject { |l| l.empty? || l.start_with?('#') }
# Agrupar por fase según el orden de las líneas
hitos_por_fase = Hash.new { |h, k| h[k] = [] }
lineas.each do |linea|
if linea =~ /^(\d+):\s*\[.+?\]\s*(.+)$/
id_db = $1.to_i
hito = hitos.find { |h| h['id'] == id_db }
hitos_por_fase[hito['fase_num']] << hito if hito
end
end
# Iniciar renombrado secuencial
hitos_por_fase.each do |fase, hitos_fase|
grupo = 1
letra = 65 # 'A'
hitos_fase.each do |h|
nuevo_id = "#{fase}.#{grupo}.#{letra.chr}"
if h['id_hito'] != nuevo_id
db.rename_hito_id(codigo, h['id_hito'], nuevo_id)
puts " #{Color::GREEN}#{h['id_hito'].ljust(8)}#{nuevo_id.ljust(8)}#{Color::RESET} | #{h['titulo']}"
else
puts " #{Color::DIM}#{nuevo_id.ljust(8)} (Sin cambios) | #{h['titulo']}#{Color::RESET}"
end
letra += 1
if letra > 67 # 'C'
letra = 65
grupo += 1
end
end
end
puts "#{Color::GREEN}✅ Ordenamiento aplicado exitosamente a #{codigo.upcase}.#{Color::RESET}"
else
# Generar archivo temporal para reordenamiento (Swarm-Ready)
tmp_file = "/tmp/adn_ordenar_#{codigo.downcase}.txt"
File.open(tmp_file, 'w') do |f|
f.puts "# ==========================================================================="
f.puts "# MODO ORDENAMIENTO DE HITOS - PROYECTO #{codigo.upcase}"
f.puts "# ==========================================================================="
f.puts "# Cambia el orden de las líneas (Cortar y Pegar) dentro de la misma FASE"
f.puts "# para definir la secuencia lógica de ejecución."
f.puts "# NO MODIFIQUES el número inicial ni los corchetes (ej: '42: [F3]')."
f.puts "# Cuando termines de editar, guarda el archivo y ejecuta en la terminal:"
f.puts "# adn db hito:ordenar #{codigo.upcase} --aplicar #{tmp_file}"
f.puts "# ===========================================================================\n\n"
fases = hitos.group_by { |h| h['fase_num'] }.sort_by { |k, v| k }
fases.each do |num_fase, hitos_fase|
f.puts "# --- FASE #{num_fase} ---"
hitos_fase.each do |h|
f.puts "#{h['id']}: [F#{num_fase}] #{h['titulo']}"
end
f.puts "\n"
end
end
puts "#{Color::CYAN}✅ Archivo de configuración generado en: #{tmp_file}#{Color::RESET}"
puts "1. Abre el archivo y reordena las líneas como necesites."
puts "2. Ejecuta: #{Color::BOLD}adn db hito:ordenar #{codigo.upcase} --aplicar #{tmp_file}#{Color::RESET}"
end
rescue => e
puts "#{Color::RED}✗ Error: #{e.message}#{Color::RESET}"
end
end
end
# Comando: hito:renombrar <PROYECTO> <ID_HITO_ACTUAL> <NUEVO_ID_HITO>
def comando_hito_renombrar(args)
if args.length < 3
puts "#{Color::YELLOW}Uso: adn db hito:renombrar <PROYECTO> <ID_HITO_ACTUAL> <NUEVO_ID_HITO>#{Color::RESET}"
return
end
proyecto = args[0]
id_actual = args[1]
id_nuevo = args[2]
puts "#{Color::CYAN}🔄 Renombrando hito #{id_actual}#{id_nuevo} (Proyecto #{proyecto.upcase})...#{Color::RESET}"
BitacorasDB::BitacoraDB.with_connection do |db|
begin
res = db.rename_hito_id(proyecto, id_actual, id_nuevo)
puts "#{Color::GREEN}✅ Hito renombrado exitosamente. Título: #{res['titulo']}#{Color::RESET}"
rescue => e
puts "#{Color::RED}✗ Error: #{e.message}#{Color::RESET}"
end
end
end
# Comando: hito:actualizar <PROYECTO> <ID_HITO> [OPCIONES]
def comando_hito_actualizar(args)
if args.length < 2
puts "#{Color::YELLOW}Uso: adn db hito:actualizar <PROYECTO> <ID_HITO> [OPCIONES]#{Color::RESET}"
puts "Opciones soportadas: --estado=EJ, --horas-p=1.5, --horas-r=0, --desc=\"Texto\""
return
end
proyecto = args.shift
id_hito = args.shift
opts = { estado: nil, horas_presencial: nil, horas_remoto: nil, descripcion: nil }
parser = OptionParser.new do |opt|
opt.banner = "Opciones:"
opt.on("--estado ESTADO", "Nuevo estado (ej: ✅, ⏳, 📋)") { |e| opts[:estado] = e }
opt.on("--horas-p HORAS", "Horas presenciales") { |h| opts[:horas_presencial] = h.to_f }
opt.on("--horas-r HORAS", "Horas remotas") { |h| opts[:horas_remoto] = h.to_f }
opt.on("--desc DESC", "Descripción o detalle para el hito") { |d| opts[:descripcion] = d }
end
begin
parser.parse!(args)
rescue OptionParser::InvalidOption => e
puts "#{Color::RED}✗ Error: #{e.message}#{Color::RESET}"
exit 1
end
if opts.values.all?(&:nil?)
puts "#{Color::YELLOW}⚠ No se especificó nada para actualizar. Use --estado, --horas-p, --horas-r o --desc#{Color::RESET}"
return
end
puts "#{Color::CYAN}🔄 Actualizando hito #{id_hito} del proyecto #{proyecto.upcase}...#{Color::RESET}"
BitacorasDB::BitacoraDB.with_connection do |db|
begin
res = db.update_hito(
proyecto, id_hito,
estado: opts[:estado],
horas_presencial: opts[:horas_presencial],
horas_remoto: opts[:horas_remoto],
descripcion: opts[:descripcion]
)
puts "#{Color::GREEN}✅ Hito actualizado exitosamente.#{Color::RESET}"
hrs = "#{res['horas_presencial'].to_f}P / #{res['horas_remoto'].to_f}R"
puts " Estado actual : #{res['estado']}"
puts " Horas imputadas : #{hrs}"
rescue => e
puts "#{Color::RED}✗ Error: #{e.message}#{Color::RESET}"
end
end
end
end
end