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 } parser = OptionParser.new do |opt| opt.banner = "Uso: adn db evento:crear [OPCIONES]" opt.on("--inicio HH:MM", "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", "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("--ia", "Marcar como generado por IA") do opts[:es_ia] = 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 # Validaciones básicas unless opts[:inicio] && opts[:descripcion] puts "#{Color::RED}✗ Error: --inicio y --descripcion son requeridos#{Color::RESET}" puts parser exit 1 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 # Buscar bitácora para hoy (o crearla) fecha_hoy = Date.today.to_s bitacora = db.find_bitacora_by_fecha(fecha_hoy) if bitacora.nil? puts " #{Color::DIM}Creando bitácora para hoy...#{Color::RESET}" bitacora = db.create_bitacora(fecha: fecha_hoy) end bitacora_id = bitacora['id'] # Crear la entrada entrada = db.create_entrada( inicio: opts[:inicio], descripcion: opts[:descripcion], fin: opts[:fin], estado: opts[:estado], modo: opts[:modo], es_ia: opts[:es_ia], bitacora_id: bitacora_id, nodo_id: nodo_id, tema_id: nil # Por ahora sin tema ) puts "#{Color::GREEN}✅ Entrada creada exitosamente#{Color::RESET}" puts "" puts " #{Color::BOLD}ID:#{Color::RESET} #{entrada['id']}" 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']}" puts " #{Color::BOLD}Estado:#{Color::RESET} #{entrada['estado']}" puts " #{Color::BOLD}Modo:#{Color::RESET} #{entrada['modo'] == 'P' ? 'Presencial' : 'Remoto'}" puts " #{Color::BOLD}IA:#{Color::RESET} #{entrada['es_ia'] ? 'Sí' : 'No'}" puts " #{Color::BOLD}Bitácora:#{Color::RESET} #{fecha_hoy} (ID: #{bitacora_id})" end end # Comando: evento:actualizar [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 [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 } 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("--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("-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 entradas = db.list_entradas( 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 puts "" puts "#{Color::BOLD}#{"ID".ljust(5)} #{"Inicio".ljust(8)} #{"Fin".ljust(8)} #{"Nodo".ljust(15)} #{"Estado".ljust(3)} #{"Descripción".ljust(40)}#{Color::RESET}" puts "#{"─" * 5} #{"─" * 8} #{"─" * 8} #{"─" * 15} #{"─" * 3} #{"─" * 40}" entradas.each do |e| id = e['id'].to_s inicio = e['inicio'][0..7] # HH:MM:SS -> HH:MM fin = e['fin'] ? e['fin'][0..7] : '--:--' nodo = e['nodo_nombre'] || '(sin nodo)' nodo = nodo[0..13] + '...' if nodo.length > 15 estado = e['estado'] desc = e['descripcion'] desc = desc[0..37] + '...' if desc.length > 40 puts "#{id.ljust(5)} #{inicio.ljust(8)} #{fin.ljust(8)} #{nodo.ljust(15)} #{estado.ljust(3)} #{desc.ljust(40)}" end puts "" puts "Total: #{entradas.length} entradas" end end # Comando: evento:eliminar - Eliminar una entrada existente def comando_evento_eliminar(args) if args.empty? || args[0].start_with?('-') puts "#{Color::YELLOW}Uso: adn db evento:eliminar #{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 end end end end