131 lines
4.4 KiB
Ruby
131 lines
4.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'date'
|
|
|
|
module BitacorasDB
|
|
module Exporters
|
|
class MDExporter
|
|
def initialize(db, logger = nil)
|
|
@db = db
|
|
@logger = logger || default_logger
|
|
end
|
|
|
|
def exportar_bitacora(fecha, ruta_destino)
|
|
@logger.info("Exportando bitácora #{fecha} a #{ruta_destino}...")
|
|
|
|
bitacora = @db.find_bitacora_by_fecha(fecha)
|
|
unless bitacora
|
|
@logger.error("No se encontró la bitácora para la fecha: #{fecha}")
|
|
return false
|
|
end
|
|
|
|
bitacora_id = bitacora['id']
|
|
|
|
# Obtener datos estructurados (incluyendo lo nuevo)
|
|
entradas = @db.execute("
|
|
SELECT e.*, n.nombre as nodo_nombre
|
|
FROM bitacoras.entradas e
|
|
LEFT JOIN bitacoras.nodos n ON e.nodo_id = n.id
|
|
WHERE e.bitacora_id = $1
|
|
ORDER BY n.nombre, e.inicio ASC
|
|
", [bitacora_id])
|
|
|
|
gestion = @db.execute("
|
|
SELECT g.*, n.nombre as nodo_nombre
|
|
FROM bitacoras.gestion g
|
|
LEFT JOIN bitacoras.nodos n ON g.nodo_id = n.id
|
|
WHERE g.bitacora_id = $1 OR g.activo = true
|
|
ORDER BY g.tipo, g.orden
|
|
", [bitacora_id]) rescue [] # Por si la tabla no existe aún en otros entornos
|
|
|
|
resumenes = @db.execute("
|
|
SELECT r.*, n.nombre as nodo_nombre
|
|
FROM bitacoras.resumen_nodos r
|
|
JOIN bitacoras.nodos n ON r.nodo_id = n.id
|
|
WHERE r.bitacora_id = $1
|
|
", [bitacora_id]) rescue []
|
|
|
|
# Generar contenido Markdown
|
|
contenido = generar_markdown(fecha, bitacora['resumen'], gestion, resumenes, entradas)
|
|
|
|
File.write(ruta_destino, contenido)
|
|
@logger.info("✅ Exportación completada con éxito.")
|
|
true
|
|
rescue => e
|
|
@logger.error("Error exportando bitácora: #{e.message}")
|
|
false
|
|
end
|
|
|
|
private
|
|
|
|
def generar_markdown(fecha, resumen_gen, gestion, resumenes, entradas)
|
|
y, m, d = fecha.to_s.split('-')
|
|
md = String.new("<!-- GENERADO AUTOMÁTICAMENTE por adn db exportar:md — No editar manualmente -->\n")
|
|
md << "# Bitácora de Operaciones - #{d}/#{m}/#{y}\n\n"
|
|
|
|
if gestion && gestion.ntuples > 0
|
|
md << "## 📋 Control de Gestión\n\n"
|
|
|
|
pendientes = gestion.select { |g| g['tipo'] == 'pendiente' }
|
|
if pendientes.any?
|
|
md << "### 📝 Resumen de Actividades\n" # Formato ADN
|
|
md << "| NODO | PENDIENTES |\n| :--- | :--- |\n"
|
|
pendientes.each do |p|
|
|
md << "| #{p['nodo_nombre'] || '—'} | #{p['detalle']} |\n"
|
|
end
|
|
md << "\n"
|
|
end
|
|
|
|
en_proceso = gestion.select { |g| g['tipo'] == 'proceso' }
|
|
if en_proceso.any?
|
|
md << "### ⏳ En Proceso\n"
|
|
md << "| NODO | ACTIVIDAD |\n| :--- | :--- |\n"
|
|
en_proceso.each do |p|
|
|
md << "| #{p['nodo_nombre'] || '—'} | #{p['detalle']} |\n"
|
|
end
|
|
md << "\n"
|
|
end
|
|
end
|
|
|
|
if resumenes && resumenes.ntuples > 0
|
|
md << "### 📝 Resumen Integral\n"
|
|
md << "| NODO | RESUMEN INTEGRAL |\n| :--- | :--- |\n"
|
|
resumenes.each do |r|
|
|
md << "| #{r['nodo_nombre']} | #{r['resumen']} |\n"
|
|
end
|
|
md << "\n"
|
|
end
|
|
|
|
md << "## 📂 Actividades Detalladas\n\n"
|
|
|
|
por_nodo = entradas.group_by { |e| e['nodo_nombre'] || 'sin-nodo' }
|
|
por_nodo.each do |nodo, entries|
|
|
md << "### #{nodo}\n\n"
|
|
md << "| I | F | Descripción | E |\n"
|
|
md << "| :--- | :--- | :--- | :--- |\n"
|
|
entries.each do |e|
|
|
inicio = e['inicio'] ? e['inicio'][0..4] : '-'
|
|
fin = e['fin'] ? e['fin'][0..4] : '-'
|
|
ia = e['es_ia'] == 't' ? '(IA) ' : ''
|
|
estado = "[#{e['modo']}] #{e['estado']}"
|
|
md << "| #{inicio} | #{fin} | #{ia}#{e['descripcion']} | #{estado} |\n"
|
|
end
|
|
md << "\n"
|
|
end
|
|
|
|
md << "---\n"
|
|
md << "<!-- GENERADO AUTOMÁTICAMENTE desde PostgreSQL el #{Time.now.strftime('%Y-%m-%d %H:%M')} -->\n"
|
|
md << "<!-- Fuente de verdad: DB. Regenerar con: adn db exportar:md #{fecha} -->\n"
|
|
md
|
|
end
|
|
|
|
def default_logger
|
|
Class.new do
|
|
def info(msg); puts "[INFO] #{msg}"; end
|
|
def error(msg); puts "[ERROR] #{msg}"; end
|
|
end.new
|
|
end
|
|
end
|
|
end
|
|
end
|