Feat(ADN): Implementado y ejecutado archivar:md para mover Markdowns obsoletos a histórico DB-First
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
require 'date'
|
||||
require 'time'
|
||||
require 'yaml'
|
||||
require 'digest'
|
||||
|
||||
module BitacorasDB
|
||||
module Importers
|
||||
@@ -43,14 +44,16 @@ module BitacorasDB
|
||||
'⏸️' => '⏸️',
|
||||
'⏳' => '⏳',
|
||||
'👁️' => '⏳', # Sincronización mapea a ⏳
|
||||
'➡️' => '⏳' # Tarea continúa en otro día
|
||||
'➡️' => '⏳', # Tarea continúa en otro día
|
||||
'🎭' => '✅' # Evento/Servicio
|
||||
}.freeze
|
||||
|
||||
# Modos posibles en archivos .md
|
||||
MODOS_MD = {
|
||||
'[P]' => 'P', # Presencial
|
||||
'[R]' => 'R', # Remoto
|
||||
'[P/R]' => 'P' # Presencial/Remoto -> Presencial por defecto
|
||||
'[P/R]' => 'P', # Presencial/Remoto -> Presencial por defecto
|
||||
'[S]' => 'S' # Servicio (No nodo técnico)
|
||||
}.freeze
|
||||
|
||||
# Tipos de nodo inferidos del nombre
|
||||
@@ -61,6 +64,7 @@ module BitacorasDB
|
||||
/^pcv-/ => 'vm',
|
||||
/^pc-/ => 'pc',
|
||||
/^srvv-/ => 'vm',
|
||||
/^dtic-/ => 'servicio',
|
||||
/-pmox\d+$/ => 'hipervisor',
|
||||
/-xen\d+$/ => 'hipervisor'
|
||||
}.freeze
|
||||
@@ -264,7 +268,7 @@ module BitacorasDB
|
||||
end
|
||||
|
||||
# Detectar inicio de tabla de entradas
|
||||
if linea.match?(/^\| I \| F \| Descripción \| E \|$/)
|
||||
if linea.match?(/^\| I \| F \| Descripción \| (?:J \| )?E \|$/)
|
||||
en_tabla_entradas = true
|
||||
encabezados_tabla = linea.split('|').map(&:strip).reject(&:empty?)
|
||||
entradas_tabla = []
|
||||
@@ -343,20 +347,32 @@ module BitacorasDB
|
||||
|
||||
# Parsea línea de tema (ej: "✅ - SEC01 - Refactorización ns8-candados")
|
||||
def parsear_tema(linea)
|
||||
# Patrón: estado - id - título (id puede contener puntos, como ADN01.1)
|
||||
match = linea.match(/^([✅❌⚠️⏸️⏳👁️➡️]+)\s*-\s*([A-Z0-9.]+)\s*-\s*(.+)$/)
|
||||
return nil unless match
|
||||
# Intentar formato estándar: estado - id - título (ej: ✅ - ADN01 - Título)
|
||||
match_std = linea.match(/^([^\s-]+)\s*-\s*([A-Z0-9.]+)\s*-\s*(.+)$/)
|
||||
if match_std
|
||||
return {
|
||||
id_tema: match_std[2],
|
||||
titulo: match_std[3].strip,
|
||||
estado: ESTADOS_MD[match_std[1]] || '⏳',
|
||||
descripcion: match_std[3].strip
|
||||
}
|
||||
end
|
||||
|
||||
estado = match[1]
|
||||
id_tema = match[2]
|
||||
titulo = match[3].strip
|
||||
# Intentar formato simplificado: estado - título (ej: 🎭 - Servicio Auditorio)
|
||||
match_simple = linea.match(/^([^\s-]+)\s*-\s*(.+)$/)
|
||||
if match_simple
|
||||
titulo = match_simple[2].strip
|
||||
# Generar un ID determinista basado en el título para estabilidad
|
||||
id_gen = "GEN" + Digest::MD5.hexdigest(titulo)[0..5].upcase
|
||||
return {
|
||||
id_tema: id_gen,
|
||||
titulo: titulo,
|
||||
estado: ESTADOS_MD[match_simple[1]] || '⏳',
|
||||
descripcion: titulo
|
||||
}
|
||||
end
|
||||
|
||||
{
|
||||
id_tema: id_tema,
|
||||
titulo: titulo,
|
||||
estado: ESTADOS_MD[estado] || '⏳',
|
||||
descripcion: titulo # Por defecto, usar título como descripción
|
||||
}
|
||||
nil
|
||||
end
|
||||
|
||||
# Parsea líneas de tabla de entradas
|
||||
@@ -370,6 +386,7 @@ module BitacorasDB
|
||||
when 'i' then columnas[:inicio] = idx
|
||||
when 'f' then columnas[:fin] = idx
|
||||
when 'descripción' then columnas[:descripcion] = idx
|
||||
when 'j' then columnas[:jornada] = idx
|
||||
when 'e' then columnas[:estado] = idx
|
||||
end
|
||||
end
|
||||
@@ -379,28 +396,39 @@ module BitacorasDB
|
||||
next if linea.match?(/^[\|\s:-]+$/) # Solo pipes, espacios, guiones y dos puntos
|
||||
next if linea.match?(/^\|?\s*:?-+:?\s*\|/) # Separadores con :---, ---, :-:, etc.
|
||||
|
||||
celdas = linea.split('|').map(&:strip).reject(&:empty?)
|
||||
# Limpiar pipes iniciales y finales antes de split
|
||||
linea_limpia = linea.strip.gsub(/^\||\|$/, '')
|
||||
celdas = linea_limpia.split('|').map(&:strip)
|
||||
next if celdas.length < 3 # Mínimo: inicio, descripción, estado
|
||||
|
||||
# Validar que la celda de inicio no sea un separador
|
||||
# Validar que la celda de inicio tenga contenido
|
||||
valor_inicio = celdas[columnas[:inicio]]
|
||||
next if valor_inicio.nil? || valor_inicio.match?(/^:?-+:?$/)
|
||||
next if valor_inicio.nil? || valor_inicio.empty? || valor_inicio.match?(/^:?-+:?$/)
|
||||
|
||||
fin_valor = nil
|
||||
if columnas[:fin] && celdas[columnas[:fin]] != '-'
|
||||
fin_valor = celdas[columnas[:fin]]
|
||||
if columnas[:fin] && !celdas[columnas[:fin]].nil?
|
||||
raw_fin = celdas[columnas[:fin]].strip
|
||||
fin_valor = raw_fin if !raw_fin.empty? && raw_fin != '-'
|
||||
end
|
||||
|
||||
entrada = {
|
||||
inicio: celdas[columnas[:inicio]],
|
||||
fin: fin_valor,
|
||||
inicio: valor_inicio.gsub(/\s*\(.*?\)\s*/, '').strip,
|
||||
fin: fin_valor ? fin_valor.gsub(/\s*\(.*?\)\s*/, '').strip : nil,
|
||||
descripcion: celdas[columnas[:descripcion]],
|
||||
estado_modo: celdas[columnas[:estado]]
|
||||
}
|
||||
|
||||
# Parsear estado y modo (ej: "[P] ✅")
|
||||
if entrada[:estado_modo]
|
||||
estado_modo = parsear_estado_modo(entrada[:estado_modo])
|
||||
# Parsear estado y modo
|
||||
if columnas[:jornada] && columnas[:estado]
|
||||
# Nuevo formato: J y E separados
|
||||
# Unimos temporalmente para usar parsear_estado_modo existente
|
||||
campo_combinado = "[#{celdas[columnas[:jornada]]}] #{celdas[columnas[:estado]]}"
|
||||
estado_modo = parsear_estado_modo(campo_combinado)
|
||||
entrada[:estado] = estado_modo[:estado]
|
||||
entrada[:modo] = estado_modo[:modo]
|
||||
elsif columnas[:estado]
|
||||
# Formato viejo o solo E
|
||||
estado_modo = parsear_estado_modo(celdas[columnas[:estado]])
|
||||
entrada[:estado] = estado_modo[:estado]
|
||||
entrada[:modo] = estado_modo[:modo]
|
||||
end
|
||||
@@ -456,6 +484,16 @@ module BitacorasDB
|
||||
|
||||
bitacora_id = bitacora['id']
|
||||
|
||||
# 1b. Si se solicita sobrescribir, limpiar entradas previas de esta bitácora
|
||||
if opciones[:sobrescribir]
|
||||
@logger.info("Limpiando datos previos de la bitácora #{estructura[:fecha]}...")
|
||||
# Borrar entradas primero (FK)
|
||||
@db.execute("DELETE FROM bitacoras.entradas WHERE bitacora_id = $1", [bitacora_id])
|
||||
# Borrar temas (FK)
|
||||
@db.execute("DELETE FROM bitacoras.temas WHERE bitacora_id = $1", [bitacora_id])
|
||||
@estadisticas[:datos_limpiados] = true
|
||||
end
|
||||
|
||||
# 2. Procesar cada nodo
|
||||
estructura[:nodos].each do |nombre_nodo, datos_nodo|
|
||||
# Buscar o crear nodo
|
||||
@@ -505,19 +543,43 @@ module BitacorasDB
|
||||
# Validar campos requeridos
|
||||
next unless entrada_md[:inicio] && entrada_md[:descripcion]
|
||||
|
||||
# Crear entrada en BD
|
||||
@db.create_entrada(
|
||||
inicio: entrada_md[:inicio],
|
||||
fin: entrada_md[:fin],
|
||||
descripcion: entrada_md[:descripcion],
|
||||
estado: entrada_md[:estado] || '⏳',
|
||||
modo: entrada_md[:modo] || 'P',
|
||||
es_ia: false, # Las importadas de .md no son IA
|
||||
tema_id: tema_id,
|
||||
bitacora_id: bitacora_id,
|
||||
nodo_id: nodo_id
|
||||
)
|
||||
@estadisticas[:entradas_creadas] += 1
|
||||
# Intentar buscar entrada existente para evitar duplicados (Upsert)
|
||||
# Buscamos por: bitacora_id, nodo_id y hora de inicio (única para un nodo en un día)
|
||||
entradas_nodo = @db.list_entradas(bitacora_id: bitacora_id, nodo_id: nodo_id, limit: 100)
|
||||
# Normalizar hora para comparación
|
||||
inicio_norm = @db.send(:parse_time, entrada_md[:inicio])
|
||||
|
||||
existente = entradas_nodo.find { |e| e['inicio'] == inicio_norm }
|
||||
|
||||
begin
|
||||
if existente
|
||||
# Actualizar si ya existe
|
||||
@db.update_entrada(existente['id'], {
|
||||
fin: entrada_md[:fin],
|
||||
descripcion: entrada_md[:descripcion],
|
||||
estado: entrada_md[:estado] || '⏳',
|
||||
modo: entrada_md[:modo] || 'P'
|
||||
})
|
||||
@estadisticas[:entradas_actualizadas] ||= 0
|
||||
@estadisticas[:entradas_actualizadas] += 1
|
||||
else
|
||||
# Crear si no existe
|
||||
@db.create_entrada(
|
||||
inicio: entrada_md[:inicio],
|
||||
fin: entrada_md[:fin],
|
||||
descripcion: entrada_md[:descripcion],
|
||||
estado: entrada_md[:estado] || '⏳',
|
||||
modo: entrada_md[:modo] || 'P',
|
||||
es_ia: false,
|
||||
tema_id: tema_id,
|
||||
bitacora_id: bitacora_id,
|
||||
nodo_id: nodo_id
|
||||
)
|
||||
@estadisticas[:entradas_creadas] += 1
|
||||
end
|
||||
rescue => e
|
||||
@logger.error("Error procesando entrada '#{entrada_md[:inicio]} - #{entrada_md[:descripcion]}': #{e.message}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user