Refactor(ADN): Transición absoluta a DB-First, extirpación de MDExporter e inferencia de auto-modo

This commit is contained in:
Ricardo Monla
2026-03-10 16:00:33 -03:00
parent af5f193fab
commit 8aa1532396
4 changed files with 545 additions and 153 deletions
+66
View File
@@ -0,0 +1,66 @@
# frozen_string_literal: true
# tools/adn/comando/cierre.rb — Subcomando para cierre de jornada laboral (DB-First)
# ==============================================================================
require 'date'
require 'time'
require_relative '../../bitacoras-db/core/bitacora_db'
module ADN
class SubcomandoCierre
module Color
RESET = "\e[0m"
BOLD = "\e[1m"
GREEN = "\e[32m"
CYAN = "\e[36m"
YELLOW = "\e[33m"
RED = "\e[31m"
DIM = "\e[2m"
end
def initialize(args, logger)
@args = args
@logger = logger
@fecha = Date.today
end
def ejecutar
if @args.empty? || !(@args.include?('jornada'))
puts "#{Color::YELLOW}Uso: ./tools/adn/run cierre de jornada [presencial|remoto]#{Color::RESET}"
return
end
modo = @args.include?('presencial') ? 'P' : 'R'
tipo_str = modo == 'P' ? 'Presencial' : 'Remoto'
hora_fin = Time.now.strftime('%H:%M')
@logger.info("Iniciando cierre de jornada #{tipo_str} (Modo DB-First)")
BitacorasDB::BitacoraDB.with_connection do |db|
# 1. Buscar la bitácora
bitacora = db.find_bitacora_by_fecha(@fecha)
unless bitacora
@logger.error("No existe bitácora en DB para hoy (#{@fecha}). ¿Iniciaste la jornada?")
return
end
# 2. Obtener nodo srv-ns8
nodo = db.find_nodo_by_nombre('srv-ns8')
# 3. Crear entrada de cierre
db.create_entrada(
inicio: hora_fin,
fin: hora_fin,
descripcion: "Cierre de jornada #{tipo_str.downcase}. Sincronización final y validación de backups.",
estado: '✅',
modo: modo,
bitacora_id: bitacora['id'],
nodo_id: nodo ? nodo['id'] : nil
)
@logger.exito("Cierre de jornada registrado en Base de Datos.")
end
end
end
end
+253 -150
View File
@@ -28,6 +28,7 @@ require 'time'
begin
require_relative '../../bitacoras-db/core/bitacora_db'
require_relative '../../bitacoras-db/importers/md_importer'
require_relative '../../bitacoras-db/exporters/md_exporter'
rescue LoadError => e
# Fallback para desarrollo
puts "⚠ Advertencia: No se pudo cargar BitacoraDB o MDImporter: #{e.message}"
@@ -67,12 +68,20 @@ module ADN
comando_evento_crear(sub_args)
when 'evento:listar'
comando_evento_listar(sub_args)
when 'evento:actualizar'
comando_evento_actualizar(sub_args)
when 'nodo:crear'
comando_nodo_crear(sub_args)
when 'nodo:listar'
comando_nodo_listar(sub_args)
when 'migrar:md'
comando_migrar_md(sub_args)
when 'hito:listar'
comando_hito_listar(sub_args)
when 'hito:actualizar'
comando_hito_actualizar(sub_args)
when 'hito:renombrar'
comando_hito_renombrar(sub_args)
when 'hito:ordenar'
comando_hito_ordenar(sub_args)
when 'estadisticas'
comando_estadisticas
when 'backup'
@@ -266,8 +275,8 @@ module ADN
inicio: nil,
descripcion: nil,
fin: nil,
estado: '⏳',
modo: 'P',
estado: nil,
modo: nil,
es_ia: false,
nodo: nil,
tema: nil
@@ -370,6 +379,59 @@ module ADN
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\", --inicio \"HH:MM\", --estado \"\", --descripcion \"Texto\""
return
end
id_evento = args.shift.to_i
opts = {}
parser = OptionParser.new do |opt|
opt.banner = "Opciones:"
opt.on("--inicio HH:MM", "Nueva hora de inicio") { |i| opts[:inicio] = i }
opt.on("--fin HH:MM", "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("-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 o --estado#{Color::RESET}"
return
end
puts "#{Color::CYAN}🔄 Actualizando evento #{id_evento}...#{Color::RESET}"
BitacorasDB::BitacoraDB.with_connection do |db|
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)'}"
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
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 }
@@ -598,90 +660,216 @@ module ADN
end
end
# Comando: migrar:md [FECHA] - Migrar bitácora .md a base de datos
def comando_migrar_md(args)
opts = { sobrescribir: false, crear_nodos: true, todo: false }
# 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 = "Uso: adn db migrar:md [FECHA] [OPCIONES]"
opt.on("--sobrescribir", "Sobrescribir bitácora si ya existe") do
opts[:sobrescribir] = true
end
opt.on("--no-crear-nodos", "No crear nodos automáticamente") do
opts[:crear_nodos] = false
end
opt.on("--todo", "Importar todas las bitácoras del directorio") do
opts[:todo] = true
end
opt.on("-h", "--help", "Mostrar ayuda") do
puts opt
exit
end
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}"
puts parser
exit 1
end
# Si se especificó --todo, importar todas las bitácoras
if opts[:todo]
importar_todas_bitacoras(opts)
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
# Si no quedan argumentos, mostrar ayuda
if args.empty?
puts "#{Color::YELLOW}⚠ Se requiere fecha o --todo#{Color::RESET}"
puts parser
exit 1
end
fecha_arg = args[0]
begin
fecha = Date.parse(fecha_arg)
rescue Date::Error
puts "#{Color::RED}✗ Formato de fecha inválido: #{fecha_arg}#{Color::RESET}"
puts " Usa formato YYYY-MM-DD (ej: 2026-03-07)"
exit 1
end
archivo_md = File.join(ADN::BITACORAS_DIR, "#{fecha}.md")
unless File.exist?(archivo_md)
puts "#{Color::RED}✗ Archivo de bitácora no encontrado: #{archivo_md}#{Color::RESET}"
puts " Verifica que la bitácora .md existe en #{ADN::BITACORAS_DIR}/"
exit 1
end
puts "#{Color::CYAN}🔄 Migrando bitácora #{fecha} de .md a base de datos...#{Color::RESET}"
puts " #{Color::DIM}Opciones: sobrescribir=#{opts[:sobrescribir]}, crear_nodos=#{opts[:crear_nodos]}#{Color::RESET}"
puts "#{Color::CYAN}🔄 Actualizando hito #{id_hito} del proyecto #{proyecto.upcase}...#{Color::RESET}"
BitacorasDB::BitacoraDB.with_connection do |db|
begin
# Crear instancia del importador
importer = BitacorasDB::Importers::MDImporter.new(db, @logger)
# Importar bitácora
resultado = importer.importar_bitacora(
archivo_md,
sobrescribir: opts[:sobrescribir],
crear_nodos: opts[:crear_nodos]
res = db.update_hito(
proyecto, id_hito,
estado: opts[:estado],
horas_presencial: opts[:horas_presencial],
horas_remoto: opts[:horas_remoto],
descripcion: opts[:descripcion]
)
mostrar_resultado_migracion(resultado, fecha)
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 durante migración: #{e.message}#{Color::RESET}"
puts "#{Color::DIM}#{e.backtrace.first}#{Color::RESET}" if ENV['ADN_DEBUG']
puts "#{Color::RED}✗ Error: #{e.message}#{Color::RESET}"
end
end
end
# Comando: estadisticas - Mostrar estadísticas del sistema
def comando_estadisticas
puts "#{Color::CYAN}📊 Estadísticas del sistema dtic-BITACORAs#{Color::RESET}"
@@ -817,91 +1005,7 @@ module ADN
end
# Importa todas las bitácoras del directorio bitacoras/
def importar_todas_bitacoras(opciones)
puts "#{Color::CYAN}🔄 Migrando todas las bitácoras .md a base de datos...#{Color::RESET}"
puts " #{Color::DIM}Opciones: sobrescribir=#{opciones[:sobrescribir]}, crear_nodos=#{opciones[:crear_nodos]}#{Color::RESET}"
puts ""
BitacorasDB::BitacoraDB.with_connection do |db|
# Crear instancia del importador
importer = BitacorasDB::Importers::MDImporter.new(db, @logger)
# Importar directorio completo
resultado = importer.importar_directorio(
ADN::BITACORAS_DIR,
sobrescribir: opciones[:sobrescribir],
crear_nodos: opciones[:crear_nodos]
)
# Mostrar resultados consolidados
totales = resultado[:totales]
estadisticas = resultado[:estadisticas]
puts ""
puts "#{Color::BOLD}📊 Resultado de migración masiva:#{Color::RESET}"
puts " #{Color::DIM}Archivos procesados:#{Color::RESET} #{totales[:archivos]}"
puts " #{Color::DIM}Migraciones exitosas:#{Color::RESET} #{totales[:exitosos]}"
puts " #{Color::DIM}Omitidos (ya existían):#{Color::RESET} #{totales[:omitidos]}"
puts " #{Color::DIM}Errores:#{Color::RESET} #{totales[:errores]}"
if estadisticas[:errores].any?
puts ""
puts "#{Color::YELLOW}⚠ Errores encontrados:#{Color::RESET}"
estadisticas[:errores].each_with_index do |error, idx|
puts " #{idx + 1}. #{error}"
end
end
puts ""
puts "#{Color::BOLD}📈 Estadísticas acumuladas:#{Color::RESET}"
puts " #{Color::DIM}Bitácoras creadas:#{Color::RESET} #{estadisticas[:bitacoras_creadas]}"
puts " #{Color::DIM}Nodos creados:#{Color::RESET} #{estadisticas[:nodos_creados]}"
puts " #{Color::DIM}Temas creados:#{Color::RESET} #{estadisticas[:temas_creados]}"
puts " #{Color::DIM}Entradas creadas:#{Color::RESET} #{estadisticas[:entradas_creadas]}"
if totales[:errores] > 0
puts ""
puts "#{Color::YELLOW}⚠ Algunas bitácoras no se migraron correctamente#{Color::RESET}"
puts " Revisa los errores arriba y corrige los problemas"
elsif totales[:exitosos] > 0
puts ""
puts "#{Color::GREEN}✅ Migración masiva completada exitosamente#{Color::RESET}"
end
end
rescue => e
puts "#{Color::RED}✗ Error durante migración masiva: #{e.message}#{Color::RESET}"
puts "#{Color::DIM}#{e.backtrace.first}#{Color::RESET}" if ENV['ADN_DEBUG']
end
# Muestra el resultado de una migración individual
def mostrar_resultado_migracion(resultado, fecha)
if resultado[:exitoso]
puts "#{Color::GREEN}✅ Bitácora migrada exitosamente#{Color::RESET}"
puts ""
puts " #{Color::BOLD}Fecha:#{Color::RESET} #{fecha}"
puts " #{Color::BOLD}Bitácora ID:#{Color::RESET} #{resultado[:bitacora_id]}" if resultado[:bitacora_id]
puts " #{Color::BOLD}Estadísticas:#{Color::RESET}"
puts " • Nodos creados: #{resultado.dig(:estadisticas, :nodos_creados) || 0}"
puts " • Temas creados: #{resultado.dig(:estadisticas, :temas_creados) || 0}"
puts " • Entradas creadas: #{resultado.dig(:estadisticas, :entradas_creadas) || 0}"
elsif resultado[:skip]
puts "#{Color::YELLOW}⚠ Bitácora ya existe, omitiendo#{Color::RESET}"
puts " Usa --sobrescribir para forzar migración"
puts " Bitácora ID: #{resultado[:bitacora_id]}" if resultado[:bitacora_id]
else
puts "#{Color::RED}✗ Error migrando bitácora#{Color::RESET}"
if resultado[:error]
puts " #{resultado[:error]}"
end
if resultado[:errores]&.any?
resultado[:errores].each do |error|
puts "#{error}"
end
end
end
end
# ─── MÉTODOS AUXILIARES ──────────────────────────────────────────────
@@ -932,7 +1036,6 @@ module ADN
puts " #{Color::CYAN}evento:listar [OPCIONES]#{Color::RESET} Listar entradas con filtros"
puts " #{Color::CYAN}nodo:crear [OPCIONES]#{Color::RESET} Crear nuevo nodo (servidor/VM/PC)"
puts " #{Color::CYAN}nodo:listar [OPCIONES]#{Color::RESET} Listar nodos del sistema"
puts " #{Color::CYAN}migrar:md [FECHA]#{Color::RESET} Migrar bitácora .md a base de datos"
puts " #{Color::CYAN}estadisticas#{Color::RESET} Mostrar estadísticas del sistema"
puts ""
puts "#{Color::BOLD}Ejemplos:#{Color::RESET}"
+69
View File
@@ -0,0 +1,69 @@
# frozen_string_literal: true
# tools/adn/comando/inicio.rb — Subcomando para inicio de jornada laboral (DB-First)
# ==============================================================================
require 'date'
require 'time'
require_relative '../../bitacoras-db/core/bitacora_db'
module ADN
class SubcomandoInicio
module Color
RESET = "\e[0m"
BOLD = "\e[1m"
GREEN = "\e[32m"
CYAN = "\e[36m"
YELLOW = "\e[33m"
RED = "\e[31m"
DIM = "\e[2m"
end
def initialize(args, logger)
@args = args
@logger = logger
@fecha = Date.today
end
def ejecutar
if @args.empty? || !(@args.include?('jornada'))
puts "#{Color::YELLOW}Uso: ./tools/adn/run inicio de jornada [presencial|remoto]#{Color::RESET}"
return
end
modo = @args.include?('presencial') ? 'P' : 'R'
tipo_str = modo == 'P' ? 'Presencial' : 'Remoto'
hora_ini = Time.now.strftime('%H:%M')
@logger.info("Iniciando jornada #{tipo_str} (Modo DB-First)")
BitacorasDB::BitacoraDB.with_connection do |db|
# 1. Asegurar que existe la bitácora
bitacora = db.find_bitacora_by_fecha(@fecha)
unless bitacora
@logger.info("Creando bitácora en DB para hoy...")
bitacora = db.create_bitacora(fecha: @fecha)
end
# 2. Obtener nodo srv-ns8
nodo = db.find_nodo_by_nombre('srv-ns8')
unless nodo
@logger.warn("Nodo 'srv-ns8' no encontrado, usando nodo general...")
# Podríamos crearlo o buscar uno por defecto
end
# 3. Crear entrada de inicio
db.create_entrada(
inicio: hora_ini,
descripcion: "Inicio de jornada #{tipo_str.downcase}. Sincronización de contexto y revisión de tareas.",
estado: '👁️',
modo: modo,
bitacora_id: bitacora['id'],
nodo_id: nodo ? nodo['id'] : nil
)
@logger.exito("Entrada de inicio creada en Base de Datos.")
end
end
end
end