514 lines
19 KiB
Ruby
514 lines
19 KiB
Ruby
module ADN
|
||
module SubcomandoDBEvento
|
||
# Comando: evento:crear [OPCIONES] - Crear nueva entrada cronológica
|
||
def comando_evento_crear(args)
|
||
opts = {
|
||
inicio: nil,
|
||
descripcion: nil,
|
||
fin: nil,
|
||
estado: nil,
|
||
modo: nil,
|
||
es_ia: false,
|
||
nodo: nil,
|
||
tema: nil,
|
||
plantilla: nil,
|
||
fecha: nil,
|
||
ambito: nil,
|
||
doc_path: nil
|
||
}
|
||
|
||
parser = OptionParser.new do |opt|
|
||
opt.banner = "Uso: adn db evento:crear [OPCIONES]"
|
||
opt.on("--inicio HH:MM[:SS]", "Hora de inicio (requerido)") do |i|
|
||
opts[:inicio] = i
|
||
end
|
||
opt.on("--descripcion TEXTO", "Descripción de la actividad (requerido)") do |d|
|
||
opts[:descripcion] = d
|
||
end
|
||
opt.on("--fin HH:MM[:SS]", "Hora de fin (opcional)") do |f|
|
||
opts[:fin] = f
|
||
end
|
||
opt.on("--estado ESTADO", "Estado (⏳, ✅, ❌, ⚠️, ⏸️)") do |e|
|
||
opts[:estado] = e
|
||
end
|
||
opt.on("--modo MODO", "Modo (P=Presencial, R=Remoto)") do |m|
|
||
opts[:modo] = m
|
||
end
|
||
opt.on("--nodo NOMBRE", "Nombre del nodo (servidor/VM)") do |n|
|
||
opts[:nodo] = n
|
||
end
|
||
opt.on("--fecha YYYY-MM-DD", "Fecha de la bitácora (default: hoy)") do |f|
|
||
opts[:fecha] = f
|
||
end
|
||
opt.on("--ambito NOMBRE_O_ID", "Ámbito del evento") do |a|
|
||
opts[:ambito] = a
|
||
end
|
||
opt.on("--doc RUTA", "Ruta al plan .md (ej: ambito/dtic-DASUTEN/P2601.09_DASUTEN-sin-DC.md)") do |d|
|
||
opts[:doc_path] = d
|
||
end
|
||
opt.on("--ia", "Marcar como generado por IA") do
|
||
opts[:es_ia] = true
|
||
end
|
||
opt.on("--plantilla TIPO", "Usar plantilla (implementacion, investigacion, resolucion, documentacion, test, debug, planificacion)") do |p|
|
||
opts[:plantilla] = p
|
||
end
|
||
opt.on("-h", "--help", "Mostrar ayuda") do
|
||
puts opt
|
||
exit
|
||
end
|
||
end
|
||
|
||
begin
|
||
parser.parse!(args)
|
||
rescue OptionParser::InvalidOption => e
|
||
puts "#{Color::RED}✗ Error: #{e.message}#{Color::RESET}"
|
||
puts parser
|
||
exit 1
|
||
end
|
||
|
||
# Validaciones básicas
|
||
unless opts[:descripcion]
|
||
puts "#{Color::RED}✗ Error: --descripcion es requerido#{Color::RESET}"
|
||
puts parser
|
||
exit 1
|
||
end
|
||
|
||
# Si no se especifica --inicio, usar hora actual
|
||
opts[:inicio] ||= Time.now.strftime('%H:%M:%S')
|
||
puts "#{Color::DIM}ℹ Hora de inicio: #{opts[:inicio]}#{Color::RESET}"
|
||
|
||
# Si se specify plantilla, mostrar y prependear al texto
|
||
if opts[:plantilla]
|
||
require_relative '../../templates/plantillas_eventos'
|
||
if ADN::PlantillasEventos.existe?(opts[:plantilla])
|
||
plantilla = ADN::PlantillasEventos.obtener(opts[:plantilla])
|
||
puts "#{Color::CYAN}📋 Plantilla: #{opts[:plantilla]}#{Color::RESET}"
|
||
puts plantilla
|
||
puts ""
|
||
puts "#{Color::YELLOW}💡 Completa la plantilla y usa --descripcion con el resultado#{Color::RESET}"
|
||
else
|
||
puts "#{Color::RED}✗ Tipo de plantilla desconocido: #{opts[:plantilla]}#{Color::RESET}"
|
||
puts "#{Color::YELLOW}Disponibles: #{ADN::PlantillasEventos.lista_tipos.join(', ')}#{Color::RESET}"
|
||
exit 1
|
||
end
|
||
end
|
||
|
||
# Obtener modo desde jornada actual si no se especifica
|
||
modo_resuelto = opts[:modo]
|
||
if modo_resuelto.nil?
|
||
puts "#{Color::DIM}ℹ Modo no especificado, buscando jornada actual...#{Color::RESET}"
|
||
end
|
||
|
||
puts "#{Color::CYAN}📝 Creando nueva entrada...#{Color::RESET}"
|
||
|
||
BitacorasDB::BitacoraDB.with_connection do |db|
|
||
# Buscar nodo por nombre si se especificó
|
||
nodo_id = nil
|
||
if opts[:nodo]
|
||
nodo = db.find_nodo_by_nombre(opts[:nodo])
|
||
if nodo
|
||
nodo_id = nodo['id']
|
||
puts " #{Color::DIM}Nodo encontrado: #{opts[:nodo]} (ID: #{nodo_id})#{Color::RESET}"
|
||
else
|
||
puts "#{Color::YELLOW}⚠ Nodo '#{opts[:nodo]}' no encontrado#{Color::RESET}"
|
||
puts " Creando entrada sin nodo asociado"
|
||
end
|
||
end
|
||
|
||
# Resolver ámbito si se especificó
|
||
ambito_id = nil
|
||
if opts[:ambito]
|
||
ambito = db.get_ambito(opts[:ambito])
|
||
if ambito
|
||
ambito_id = ambito['id']
|
||
puts " #{Color::DIM}Ámbito encontrado: #{opts[:ambito]} (ID: #{ambito_id})#{Color::RESET}"
|
||
else
|
||
puts "#{Color::YELLOW}⚠ Ámbito '#{opts[:ambito]}' no encontrado#{Color::RESET}"
|
||
end
|
||
end
|
||
|
||
# Determinar fecha: --fecha o hoy
|
||
fecha_target = opts[:fecha] ? opts[:fecha] : Date.today.to_s
|
||
if opts[:fecha]
|
||
begin
|
||
Date.parse(opts[:fecha])
|
||
puts " #{Color::DIM}ℹ Fecha especificada: #{fecha_target}#{Color::RESET}"
|
||
rescue Date::Error
|
||
puts "#{Color::RED}✗ Formato de fecha inválido: #{opts[:fecha]}. Usar YYYY-MM-DD#{Color::RESET}"
|
||
exit 1
|
||
end
|
||
end
|
||
|
||
bitacora = db.find_bitacora_by_fecha(fecha_target)
|
||
|
||
if bitacora.nil?
|
||
puts " #{Color::DIM}Creando bitácora para #{fecha_target}...#{Color::RESET}"
|
||
bitacora = db.create_bitacora(fecha: fecha_target)
|
||
end
|
||
|
||
bitacora_id = bitacora['id']
|
||
|
||
# Obtener modo desde jornada actual si no se especificó
|
||
if modo_resuelto.nil?
|
||
if bitacora && bitacora['modo']
|
||
modo_resuelto = bitacora['modo']
|
||
puts " #{Color::DIM}ℹ Modo resuelto desde jornada: #{modo_resuelto == 'P' ? 'Presencial' : (modo_resuelto == 'R' ? 'Remoto' : 'Servicio')}#{Color::RESET}"
|
||
else
|
||
modo_resuelto = 'P' # default
|
||
puts " #{Color::DIM}ℹ Sin jornada activa, usando default: Presencial#{Color::RESET}"
|
||
end
|
||
end
|
||
|
||
# Calcular estado automáticamente desde hora_fin
|
||
estado_resuelto = opts[:estado]
|
||
if estado_resuelto.nil?
|
||
if opts[:fin]
|
||
estado_resuelto = '✅'
|
||
puts " #{Color::DIM}ℹ Estado calculado: ✅ (tiene hora de fin)#{Color::RESET}"
|
||
else
|
||
estado_resuelto = '⏳'
|
||
puts " #{Color::DIM}ℹ Estado calculado: ⏳ (sin hora de fin)#{Color::RESET}"
|
||
end
|
||
end
|
||
|
||
# Crear la entrada
|
||
entrada = db.create_entrada(
|
||
inicio: opts[:inicio],
|
||
descripcion: opts[:descripcion],
|
||
fin: opts[:fin],
|
||
estado: estado_resuelto,
|
||
modo: modo_resuelto,
|
||
es_ia: opts[:es_ia],
|
||
bitacora_id: bitacora_id,
|
||
nodo_id: nodo_id,
|
||
tema_id: nil # Por ahora sin tema
|
||
)
|
||
|
||
# Asignar doc_path si se especificó
|
||
if opts[:doc_path] && entrada
|
||
db.update_entrada(entrada['id'], { doc_path: opts[:doc_path] })
|
||
puts " #{Color::DIM}ℹ Doc vinculado: #{opts[:doc_path]}#{Color::RESET}"
|
||
end
|
||
|
||
# Asignar ámbito si se especificó
|
||
if ambito_id && entrada
|
||
db.update_entrada(entrada['id'], { ambito_id: ambito_id })
|
||
puts " #{Color::DIM}ℹ Ámbito asignado (ID: #{ambito_id})#{Color::RESET}"
|
||
end
|
||
|
||
puts "#{Color::GREEN}✅ Entrada creada exitosamente#{Color::RESET}"
|
||
puts " #{Color::BOLD}Fecha:#{Color::RESET} #{fecha_target}"
|
||
puts " #{Color::BOLD}Inicio:#{Color::RESET} #{entrada['inicio']}"
|
||
puts " #{Color::BOLD}Fin:#{Color::RESET} #{entrada['fin'] || '(sin fin)'}"
|
||
puts " 📝 #{Color::BOLD}Descripción:#{Color::RESET} #{entrada['descripcion']}"
|
||
|
||
mostrar_pendientes(db)
|
||
end
|
||
end
|
||
|
||
# Comando: evento:actualizar <ID> [OPCIONES] - Actualizar una entrada existente
|
||
def comando_evento_actualizar(args)
|
||
if args.length < 1 || args[0].start_with?('-')
|
||
puts "#{Color::YELLOW}Uso: adn db evento:actualizar <ID> [OPCIONES]#{Color::RESET}"
|
||
puts "Opciones soportadas: --fin \"HH:MM[:SS]\", --inicio \"HH:MM[:SS]\", --estado \"✅\", --descripcion \"Texto\", --nodo \"Nombre\""
|
||
return
|
||
end
|
||
|
||
id_evento = args.shift.to_i
|
||
opts = {}
|
||
|
||
parser = OptionParser.new do |opt|
|
||
opt.banner = "Opciones:"
|
||
opt.on("--inicio HH:MM[:SS]", "Nueva hora de inicio") { |i| opts[:inicio] = i }
|
||
opt.on("--fin HH:MM[:SS]", "Nueva hora de fin") { |f| opts[:fin] = f }
|
||
opt.on("--descripcion TEXTO", "Nueva descripción") { |d| opts[:descripcion] = d }
|
||
opt.on("--estado ESTADO", "Nuevo estado (⏳, ✅, ❌, ⚠️, ⏸️)") { |e| opts[:estado] = e }
|
||
opt.on("--modo MODO", "Modo (P=Presencial, R=Remoto)") { |m| opts[:modo] = m }
|
||
opt.on("--nodo NOMBRE", "Nuevo nodo") { |n| opts[:nodo_raw] = n }
|
||
opt.on("--ambito NOMBRE_O_ID", "Nuevo ámbito") { |a| opts[:ambito_raw] = a }
|
||
opt.on("--doc RUTA", "Ruta al plan .md vinculado") { |d| opts[:doc_path] = d }
|
||
opt.on("-h", "--help", "Mostrar ayuda") do
|
||
puts opt
|
||
exit
|
||
end
|
||
end
|
||
|
||
begin
|
||
parser.parse!(args)
|
||
rescue OptionParser::InvalidOption => e
|
||
puts "#{Color::RED}✗ Error: #{e.message}#{Color::RESET}"
|
||
exit 1
|
||
end
|
||
|
||
if opts.empty?
|
||
puts "#{Color::YELLOW}⚠ No se especificó nada para actualizar. Use --fin, --inicio, --descripcion, --estado, --modo o --nodo#{Color::RESET}"
|
||
return
|
||
end
|
||
|
||
puts "#{Color::CYAN}🔄 Actualizando evento #{id_evento}...#{Color::RESET}"
|
||
|
||
BitacorasDB::BitacoraDB.with_connection do |db|
|
||
# Calcular estado automáticamente si se proporciona --fin (sin verificar entrada actual)
|
||
if opts.key?(:fin) && !opts.key?(:estado)
|
||
if opts[:fin] && !opts[:fin].empty?
|
||
opts[:estado] = '✅'
|
||
puts " #{Color::DIM}ℹ Estado calculado automáticamente: ✅ (hora de fin agregada)#{Color::RESET}"
|
||
end
|
||
end
|
||
|
||
if opts.key?(:nodo_raw)
|
||
if opts[:nodo_raw].nil? || opts[:nodo_raw].empty?
|
||
opts[:nodo_id] = nil
|
||
else
|
||
nodo = db.find_nodo_by_nombre(opts[:nodo_raw])
|
||
if nodo
|
||
opts[:nodo_id] = nodo['id']
|
||
else
|
||
puts "#{Color::YELLOW}⚠ Nodo '#{opts[:nodo_raw]}' no encontrado#{Color::RESET}"
|
||
end
|
||
end
|
||
opts.delete(:nodo_raw)
|
||
end
|
||
|
||
if opts.key?(:ambito_raw)
|
||
if opts[:ambito_raw].nil? || opts[:ambito_raw].empty?
|
||
opts[:ambito_id] = nil
|
||
else
|
||
ambito = db.get_ambito(opts[:ambito_raw])
|
||
if ambito
|
||
opts[:ambito_id] = ambito['id']
|
||
else
|
||
puts "#{Color::YELLOW}⚠ Ámbito '#{opts[:ambito_raw]}' no encontrado#{Color::RESET}"
|
||
end
|
||
end
|
||
opts.delete(:ambito_raw)
|
||
end
|
||
|
||
begin
|
||
res = db.update_entrada(id_evento, opts)
|
||
if res
|
||
puts "#{Color::GREEN}✅ Evento #{id_evento} actualizado exitosamente.#{Color::RESET}"
|
||
puts " #{Color::BOLD}Inicio:#{Color::RESET} #{res['inicio']}"
|
||
puts " #{Color::BOLD}Fin:#{Color::RESET} #{res['fin'] || '(sin fin)'}"
|
||
puts " 📝 #{Color::BOLD}Descripción:#{Color::RESET} #{res['descripcion']}"
|
||
else
|
||
puts "#{Color::YELLOW}⚠ No se pudo actualizar el evento. ¿Existe el ID #{id_evento}?#{Color::RESET}"
|
||
end
|
||
rescue => e
|
||
puts "#{Color::RED}✗ Error: #{e.message}#{Color::RESET}"
|
||
end
|
||
|
||
mostrar_pendientes(db)
|
||
end
|
||
end
|
||
|
||
# Comando: evento:listar [OPCIONES] - Listar entradas con filtros
|
||
def comando_evento_listar(args)
|
||
opts = { limit: 20, offset: 0, nodo: nil, estado: nil, buscar: nil, desde: nil, hasta: nil, detalle: false }
|
||
|
||
parser = OptionParser.new do |opt|
|
||
opt.banner = "Uso: adn db evento:listar [OPCIONES]"
|
||
opt.on("--limit N", Integer, "Número máximo de resultados (default: 20)") do |n|
|
||
opts[:limit] = n
|
||
end
|
||
opt.on("--offset N", Integer, "Desplazamiento para paginación (default: 0)") do |n|
|
||
opts[:offset] = n
|
||
end
|
||
opt.on("--nodo NOMBRE", "Filtrar por nombre de nodo") do |n|
|
||
opts[:nodo] = n
|
||
end
|
||
opt.on("--ambito NOMBRE_O_ID", "Filtrar por ámbito") do |a|
|
||
opts[:ambito] = a
|
||
end
|
||
opt.on("--estado ESTADO", "Filtrar por estado (⏳, ✅, ❌, ⚠️, ⏸️)") do |e|
|
||
opts[:estado] = e
|
||
end
|
||
opt.on("--buscar TEXTO", "Buscar texto en descripción") do |b|
|
||
opts[:buscar] = b
|
||
end
|
||
opt.on("--desde FECHA", "Filtrar desde fecha (YYYY-MM-DD)") do |d|
|
||
opts[:desde] = d
|
||
end
|
||
opt.on("--hasta FECHA", "Filtrar hasta fecha (YYYY-MM-DD)") do |h|
|
||
opts[:hasta] = h
|
||
end
|
||
opt.on("--detalle", "Mostrar descripciones completas (formato vertical)") do
|
||
opts[:detalle] = true
|
||
end
|
||
opt.on("-h", "--help", "Mostrar ayuda") do
|
||
puts opt
|
||
exit
|
||
end
|
||
end
|
||
|
||
begin
|
||
parser.parse!(args)
|
||
rescue OptionParser::InvalidOption => e
|
||
puts "#{Color::RED}✗ Error: #{e.message}#{Color::RESET}"
|
||
puts parser
|
||
exit 1
|
||
end
|
||
|
||
puts "#{Color::CYAN}📋 Listando entradas...#{Color::RESET}"
|
||
|
||
BitacorasDB::BitacoraDB.with_connection do |db|
|
||
# Obtener ID de nodo si se especificó nombre
|
||
nodo_id = nil
|
||
if opts[:nodo]
|
||
nodo = db.find_nodo_by_nombre(opts[:nodo])
|
||
if nodo
|
||
nodo_id = nodo['id']
|
||
puts " #{Color::DIM}Filtrando por nodo: #{opts[:nodo]} (ID: #{nodo_id})#{Color::RESET}"
|
||
else
|
||
puts "#{Color::YELLOW}⚠ Nodo '#{opts[:nodo]}' no encontrado#{Color::RESET}"
|
||
end
|
||
end
|
||
|
||
# Obtener ID de ámbito si se especificó nombre
|
||
ambito_id = nil
|
||
if opts[:ambito]
|
||
ambito = db.get_ambito(opts[:ambito])
|
||
if ambito
|
||
ambito_id = ambito['id']
|
||
puts " #{Color::DIM}Filtrando por ámbito: #{opts[:ambito]} (ID: #{ambito_id})#{Color::RESET}"
|
||
else
|
||
puts "#{Color::YELLOW}⚠ Ámbito '#{opts[:ambito]}' no encontrado#{Color::RESET}"
|
||
end
|
||
end
|
||
|
||
entradas = db.list_entradas(
|
||
ambito_id: ambito_id,
|
||
nodo_id: nodo_id,
|
||
estado: opts[:estado],
|
||
buscar: opts[:buscar],
|
||
desde: opts[:desde],
|
||
hasta: opts[:hasta],
|
||
limit: opts[:limit],
|
||
offset: opts[:offset]
|
||
)
|
||
|
||
if entradas.empty?
|
||
puts "#{Color::YELLOW}⚠ No se encontraron entradas#{Color::RESET}"
|
||
return
|
||
end
|
||
|
||
if opts[:detalle]
|
||
# Formato vertical con descripciones completas
|
||
entradas.each_with_index do |e, i|
|
||
puts "#{Color::DIM}#{"─" * 60}#{Color::RESET}" if i > 0
|
||
id = e['id'].to_s
|
||
inicio = e['inicio'] ? e['inicio'][0..7] : '--:--:--'
|
||
fin = e['fin'] ? e['fin'][0..7] : '--:--:--'
|
||
nodo = "#{e['ambito_nombre'] || '-'} / #{e['nodo_nombre'] || '-'}"
|
||
estado = e['estado']
|
||
desc = e['descripcion']
|
||
|
||
puts "#{estado} #{Color::BOLD}##{id}#{Color::RESET} | #{inicio} → #{fin} | #{Color::CYAN}#{nodo}#{Color::RESET}"
|
||
puts " #{desc}"
|
||
end
|
||
else
|
||
# Formato tabla compacta (original ajustado)
|
||
puts ""
|
||
puts "#{Color::BOLD}#{"ID".ljust(5)} #{"Inicio".ljust(9)} #{"Fin".ljust(9)} #{"Ámbito/Nodo".ljust(25)} #{"Est".ljust(3)} #{"Descripción".ljust(35)}#{Color::RESET}"
|
||
puts "#{"─" * 5} #{"─" * 9} #{"─" * 9} #{"─" * 25} #{"─" * 3} #{"─" * 35}"
|
||
|
||
entradas.each do |e|
|
||
id = e['id'].to_s
|
||
inicio = e['inicio'] ? e['inicio'][0..7] : '--:--'
|
||
fin = e['fin'] ? e['fin'][0..7] : '--:--'
|
||
nodo = "#{e['ambito_nombre'] || '-'} / #{e['nodo_nombre'] || '-'}"
|
||
nodo = nodo[0..23] + '...' if nodo.length > 25
|
||
estado = e['estado']
|
||
desc = e['descripcion'] || ''
|
||
desc = desc.split("\n").first || '' # Tomar solo la primera línea
|
||
desc = desc[0..32] + '...' if desc.length > 35
|
||
|
||
puts "#{id.ljust(5)} #{inicio.ljust(9)} #{fin.ljust(9)} #{nodo.ljust(25)} #{estado.ljust(3)} #{desc.ljust(35)}"
|
||
end
|
||
end
|
||
|
||
puts ""
|
||
puts "Total: #{entradas.length} entradas"
|
||
end
|
||
end
|
||
|
||
# Comando: evento:eliminar <ID> - Eliminar una entrada existente
|
||
def comando_evento_eliminar(args)
|
||
if args.empty? || args[0].start_with?('-')
|
||
puts "#{Color::YELLOW}Uso: adn db evento:eliminar <ID>#{Color::RESET}"
|
||
return
|
||
end
|
||
|
||
id_evento = args.shift.to_i
|
||
puts "#{Color::CYAN}🗑️ Eliminando evento #{id_evento}...#{Color::RESET}"
|
||
|
||
BitacorasDB::BitacoraDB.with_connection do |db|
|
||
begin
|
||
res = db.delete_entrada(id_evento)
|
||
if res
|
||
puts "#{Color::GREEN}✅ Evento #{id_evento} eliminado exitosamente.#{Color::RESET}"
|
||
else
|
||
puts "#{Color::YELLOW}⚠ No se pudo eliminar el evento. ¿Existe el ID #{id_evento}?#{Color::RESET}"
|
||
end
|
||
rescue => e
|
||
puts "#{Color::RED}✗ Error al eliminar: #{e.message}#{Color::RESET}"
|
||
end
|
||
|
||
mostrar_pendientes(db)
|
||
end
|
||
end
|
||
|
||
private
|
||
|
||
def mostrar_pendientes(db)
|
||
puts ""
|
||
puts "#{Color::CYAN}⏳ Eventos pendientes de cierre:#{Color::RESET}"
|
||
|
||
# Filtrar por bitácora de hoy y estado '⏳' o '⏸️'
|
||
fecha_hoy = Date.today.to_s
|
||
bitacora = db.find_bitacora_by_fecha(fecha_hoy)
|
||
|
||
unless bitacora
|
||
puts " #{Color::DIM}No hay bitácora para hoy.#{Color::RESET}"
|
||
return
|
||
end
|
||
|
||
# Buscar todos los pendientes de hoy
|
||
entradas_pendientes = db.list_entradas(
|
||
bitacora_id: bitacora['id'],
|
||
estado: '⏳',
|
||
limit: 20
|
||
)
|
||
|
||
# Sumamos pausados si los hay
|
||
entradas_pausadas = db.list_entradas(
|
||
bitacora_id: bitacora['id'],
|
||
estado: '⏸️',
|
||
limit: 20
|
||
)
|
||
|
||
entradas = entradas_pendientes + entradas_pausadas
|
||
|
||
if entradas.empty?
|
||
puts " #{Color::GREEN}¡Excelente! No hay eventos pendientes de cierre.#{Color::RESET}"
|
||
return
|
||
end
|
||
|
||
# Ordenar por hora de inicio (más antiguos primero)
|
||
entradas = entradas.sort_by { |e| e['inicio'] }
|
||
|
||
entradas.each do |e|
|
||
id = e['id'].to_s
|
||
inicio = e['inicio'] ? e['inicio'][0..7] : '--:--:--' # HH:MM:SS
|
||
nodo = (e['nodo_nombre'] || '(sin nodo)')[0..14]
|
||
desc = e['descripcion']
|
||
desc = desc[0..47] + '...' if desc.length > 50
|
||
estado_icon = e['estado']
|
||
|
||
puts " #{estado_icon} #{Color::YELLOW}ID #{id.ljust(5)}#{Color::RESET} #{inicio.ljust(11)} #{nodo.ljust(15)} #{desc}"
|
||
end
|
||
end
|
||
|
||
end
|
||
end
|