240 lines
7.9 KiB
Ruby
240 lines
7.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# adn/tools/cli/jornada.rb — Subcomando unificado para gestión de jornada laboral
|
|
# ==================================================================================
|
|
# Uso:
|
|
# ./adn/tools/run jornada iniciar presencial [HH:MM]
|
|
# ./adn/tools/run jornada iniciar remoto [HH:MM]
|
|
# ./adn/tools/run jornada cerrar [presencial|remoto] [HH:MM]
|
|
# ./adn/tools/run jornada estado
|
|
|
|
require 'date'
|
|
require 'time'
|
|
require 'optparse'
|
|
require_relative '../db/core/bitacora_db'
|
|
require_relative '../core/colores'
|
|
|
|
module ADN
|
|
class SubcomandoJornada
|
|
NODO_DEFAULT = 'dtic-DIIAA'
|
|
|
|
def initialize(args, logger)
|
|
@args = args
|
|
@logger = logger
|
|
@fecha = Date.today
|
|
@opciones = { nodo: NODO_DEFAULT, fecha: nil }
|
|
end
|
|
|
|
def ejecutar
|
|
if @args.empty?
|
|
mostrar_ayuda
|
|
return
|
|
end
|
|
|
|
# Parsear opciones globales (--nodo, --fecha) antes del subcomando
|
|
parser_global = OptionParser.new do |opts|
|
|
opts.on('--nodo NOMBRE', "Nodo a registrar (default: #{NODO_DEFAULT})") { |n| @opciones[:nodo] = n }
|
|
opts.on('--fecha YYYY-MM-DD', 'Fecha de la bitácora (default: hoy)') { |f| @opciones[:fecha] = f }
|
|
end
|
|
parser_global.order!(@args)
|
|
|
|
accion = @args.shift
|
|
|
|
case accion
|
|
when 'iniciar', 'inicio', 'abrir'
|
|
ejecutar_inicio
|
|
when 'cerrar', 'cierre', 'finalizar'
|
|
ejecutar_cierre
|
|
when 'estado', 'status'
|
|
ejecutar_estado
|
|
else
|
|
puts "#{Color::RED}✗ Acción desconocida: '#{accion}'#{Color::RESET}"
|
|
mostrar_ayuda
|
|
exit 1
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def mostrar_ayuda
|
|
puts <<~AYUDA
|
|
#{Color::CYAN}📅 Gestión de Jornada Laboral#{Color::RESET}
|
|
|
|
#{Color::YELLOW}Uso:#{Color::RESET} ./adn/tools/run jornada <acción> [modo] [hora] [opciones]
|
|
|
|
#{Color::YELLOW}Acciones:#{Color::RESET}
|
|
iniciar [presencial|remoto] [HH:MM] Registrar inicio de jornada
|
|
cerrar [presencial|remoto] [HH:MM] Registrar cierre de jornada
|
|
estado Ver estado de la jornada actual
|
|
|
|
#{Color::YELLOW}Opciones:#{Color::RESET}
|
|
--nodo NOMBRE Nodo a registrar (default: #{NODO_DEFAULT})
|
|
--fecha YYYY-MM-DD Fecha de la bitácora (default: hoy)
|
|
|
|
#{Color::YELLOW}Ejemplos:#{Color::RESET}
|
|
./adn/tools/run jornada iniciar presencial 08:00
|
|
./adn/tools/run jornada iniciar remoto
|
|
./adn/tools/run jornada cerrar presencial 17:00
|
|
./adn/tools/run jornada cerrar
|
|
./adn/tools/run jornada estado
|
|
./adn/tools/run jornada iniciar presencial 08:00 --nodo srv-ns8
|
|
AYUDA
|
|
end
|
|
|
|
def parsear_modo_y_hora
|
|
modo = 'P' # default presencial
|
|
hora = Time.now.strftime('%H:%M')
|
|
|
|
@args.each do |arg|
|
|
case arg.downcase
|
|
when 'presencial', 'p'
|
|
modo = 'P'
|
|
when 'remoto', 'remota', 'r'
|
|
modo = 'R'
|
|
else
|
|
# Si parece una hora (HH:MM), usarla
|
|
if arg.match?(/^\d{1,2}:\d{2}$/)
|
|
hora = arg
|
|
end
|
|
end
|
|
end
|
|
|
|
[modo, hora]
|
|
end
|
|
|
|
def obtener_fecha
|
|
if @opciones[:fecha]
|
|
Date.parse(@opciones[:fecha])
|
|
else
|
|
@fecha
|
|
end
|
|
end
|
|
|
|
def ejecutar_inicio
|
|
modo, hora = parsear_modo_y_hora
|
|
tipo_str = modo == 'P' ? 'Presencial' : 'Remoto'
|
|
fecha = obtener_fecha
|
|
|
|
@logger.info("Iniciando jornada #{tipo_str} a las #{hora}")
|
|
|
|
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 #{fecha}...")
|
|
bitacora = db.create_bitacora(fecha: fecha)
|
|
end
|
|
|
|
# 2. Buscar nodo
|
|
nodo = db.find_nodo_by_nombre(@opciones[:nodo])
|
|
unless nodo
|
|
@logger.error("Nodo '#{@opciones[:nodo]}' no encontrado en la BD.")
|
|
puts "#{Color::RED}✗ Nodo '#{@opciones[:nodo]}' no existe. Usa 'db nodo:listar' para ver disponibles.#{Color::RESET}"
|
|
return
|
|
end
|
|
|
|
# 3. Crear entrada de inicio
|
|
entrada = db.create_entrada(
|
|
inicio: hora,
|
|
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['id']
|
|
)
|
|
|
|
puts "#{Color::GREEN}✅ Jornada #{tipo_str.downcase} iniciada#{Color::RESET}"
|
|
puts " #{Color::BOLD}ID:#{Color::RESET} #{entrada['id']}"
|
|
puts " #{Color::BOLD}Hora:#{Color::RESET} #{hora}"
|
|
puts " #{Color::BOLD}Nodo:#{Color::RESET} #{@opciones[:nodo]}"
|
|
puts " #{Color::BOLD}Fecha:#{Color::RESET} #{fecha}"
|
|
@logger.exito("Jornada #{tipo_str} iniciada en Base de Datos.")
|
|
end
|
|
end
|
|
|
|
def ejecutar_cierre
|
|
modo, hora = parsear_modo_y_hora
|
|
tipo_str = modo == 'P' ? 'Presencial' : 'Remoto'
|
|
fecha = obtener_fecha
|
|
|
|
@logger.info("Cerrando jornada #{tipo_str} a las #{hora}")
|
|
|
|
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 #{fecha}. ¿Iniciaste la jornada?")
|
|
puts "#{Color::RED}✗ No hay bitácora para #{fecha}#{Color::RESET}"
|
|
return
|
|
end
|
|
|
|
# 2. Buscar nodo
|
|
nodo = db.find_nodo_by_nombre(@opciones[:nodo])
|
|
unless nodo
|
|
@logger.error("Nodo '#{@opciones[:nodo]}' no encontrado.")
|
|
puts "#{Color::RED}✗ Nodo '#{@opciones[:nodo]}' no existe.#{Color::RESET}"
|
|
return
|
|
end
|
|
|
|
# 3. Crear entrada de cierre
|
|
entrada = db.create_entrada(
|
|
inicio: hora,
|
|
fin: hora,
|
|
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['id']
|
|
)
|
|
|
|
puts "#{Color::GREEN}✅ Jornada #{tipo_str.downcase} cerrada#{Color::RESET}"
|
|
puts " #{Color::BOLD}ID:#{Color::RESET} #{entrada['id']}"
|
|
puts " #{Color::BOLD}Hora:#{Color::RESET} #{hora}"
|
|
puts " #{Color::BOLD}Nodo:#{Color::RESET} #{@opciones[:nodo]}"
|
|
puts " #{Color::BOLD}Fecha:#{Color::RESET} #{fecha}"
|
|
@logger.exito("Jornada #{tipo_str} cerrada en Base de Datos.")
|
|
end
|
|
end
|
|
|
|
def ejecutar_estado
|
|
fecha = obtener_fecha
|
|
puts "#{Color::CYAN}📅 Estado de jornada para #{fecha}#{Color::RESET}"
|
|
puts "#{Color::DIM}#{'-' * 50}#{Color::RESET}"
|
|
|
|
BitacorasDB::BitacoraDB.with_connection do |db|
|
|
bitacora = db.find_bitacora_by_fecha(fecha)
|
|
unless bitacora
|
|
puts "#{Color::YELLOW}⚠ No hay bitácora para #{fecha}#{Color::RESET}"
|
|
return
|
|
end
|
|
|
|
entradas = db.list_entradas(bitacora_id: bitacora['id'], limit: 50)
|
|
|
|
if entradas.empty?
|
|
puts "#{Color::YELLOW}⚠ Sin entradas para hoy#{Color::RESET}"
|
|
return
|
|
end
|
|
|
|
abiertas = entradas.select { |e| e['estado'] == '⏳' }
|
|
cerradas = entradas.select { |e| e['estado'] == '✅' }
|
|
|
|
puts " #{Color::GREEN}Total entradas:#{Color::RESET} #{entradas.length}"
|
|
puts " #{Color::GREEN}Completadas:#{Color::RESET} #{cerradas.length}"
|
|
puts " #{Color::YELLOW}Abiertas:#{Color::RESET} #{abiertas.length}"
|
|
|
|
if abiertas.any?
|
|
puts "\n #{Color::YELLOW}⏳ Entradas abiertas:#{Color::RESET}"
|
|
abiertas.each do |e|
|
|
nodo = e['nodo_nombre'] || '(sin nodo)'
|
|
desc = e['descripcion']
|
|
desc = desc[0..50] + '...' if desc.length > 53
|
|
puts " ID #{e['id']} | #{e['inicio'][0..4]} | #{nodo} | #{desc}"
|
|
end
|
|
end
|
|
|
|
puts "#{Color::DIM}#{'-' * 50}#{Color::RESET}"
|
|
end
|
|
end
|
|
end
|
|
end
|