Se añadió la creación del directorio `adjuntos/cronologia` en `x1.py` para almacenar imágenes de correos electrónicos. Se actualizó la función `procesar_eml` para manejar imágenes inline y adjuntas, y se refactorizó el código en `email_parser.py` para incluir la lógica de incrustación de imágenes en Markdown. Además, se mejoró la documentación en `MemoriaDeEvolucion.md` para reflejar estos cambios y se optimizó el manejo de errores en varias funciones.
This commit is contained in:
parent
59cb4f4063
commit
fc85347a43
|
@ -39,3 +39,20 @@ Salida Markdown: Escribe el índice seguido del contenido formateado en Markdown
|
|||
- Impacto:
|
||||
- Los `work_dir.json` existentes deben actualizar la clave a `input_directory`.
|
||||
- No hay cambios en claves de `level2` (`cronologia_file`, `attachments_dir`).
|
||||
|
||||
## 2025-08-08 — Manejo de imágenes (inline y adjuntas) y embebido en Markdown
|
||||
|
||||
- Decisión:
|
||||
- Capturar imágenes tanto adjuntas (`attachment`) como inline (`inline`/sin `Content-Disposition`).
|
||||
- Guardar las imágenes en el directorio de adjuntos configurado y además copiar a `adjuntos/cronologia` dentro del `working_directory`.
|
||||
- Incrustar en el Markdown enlaces de Obsidian con ruta absoluta al archivo copiado en `adjuntos/cronologia` usando la sintaxis de embed `![[...]]` bajo una sección `### Imágenes` por mensaje.
|
||||
- Cambios:
|
||||
- `utils/attachment_handler.py`: nueva función `guardar_imagen` que genera nombres a partir de `Content-ID` o hash y evita colisiones por contenido; refactor de hashing de contenido.
|
||||
- `utils/email_parser.py`:
|
||||
- Se amplía la firma de `procesar_eml`/`procesar_eml_interno` para recibir `dir_adjuntos_cronologia` y copiar allí las imágenes.
|
||||
- Se manejan imágenes en partes `attachment` y `inline`, agregando su ruta absoluta copiada a `mensaje.imagenes_cronologia`.
|
||||
- `models/mensaje_email.py`: `to_markdown()` agrega sección `### Imágenes` con `![[ruta_absoluta]]` previo a `### Adjuntos`.
|
||||
- `x1.py`: crea `adjuntos/cronologia` y pasa la ruta al parser.
|
||||
- Impacto:
|
||||
- El `.md` resultante muestra las imágenes embebidas (Obsidian) desde rutas absolutas bajo `.../adjuntos/cronologia/...`.
|
||||
- Se preserva el listado de adjuntos como enlaces `[[archivo]]`.
|
|
@ -6,7 +6,14 @@ from email.utils import parseaddr, parsedate_to_datetime
|
|||
|
||||
|
||||
class MensajeEmail:
|
||||
def __init__(self, remitente, fecha, contenido, subject=None, adjuntos=None):
|
||||
def __init__(
|
||||
self,
|
||||
remitente,
|
||||
fecha,
|
||||
contenido,
|
||||
subject=None,
|
||||
adjuntos=None,
|
||||
):
|
||||
self.remitente = self._estandarizar_remitente(remitente)
|
||||
self.fecha = self._estandarizar_fecha(fecha)
|
||||
self.subject = subject if subject else "Sin Asunto"
|
||||
|
@ -37,17 +44,26 @@ class MensajeEmail:
|
|||
for line in lines:
|
||||
# Skip metadata lines
|
||||
if line.strip().startswith(
|
||||
("Da: ", "Inviato: ", "A: ", "From: ", "Sent: ", "To: ")
|
||||
(
|
||||
"Da: ",
|
||||
"Inviato: ",
|
||||
"A: ",
|
||||
"From: ",
|
||||
"Sent: ",
|
||||
"To: ",
|
||||
)
|
||||
) or line.strip().startswith("Oggetto: "):
|
||||
continue
|
||||
# Limpiar espacios múltiples dentro de cada línea, pero mantener la línea completa
|
||||
# Limpiar espacios múltiples dentro de cada línea, manteniendo
|
||||
# la línea completa
|
||||
cleaned_line = re.sub(r" +", " ", line)
|
||||
cleaned_lines.append(cleaned_line)
|
||||
|
||||
# Unir las líneas preservando los saltos de línea
|
||||
text = "\n".join(cleaned_lines)
|
||||
|
||||
# Limpiar la combinación específica de CRLF+NBSP+CRLF
|
||||
# Limpiar la combinación específica de
|
||||
# CRLF + NBSP + CRLF
|
||||
text = re.sub(r"\r?\n\xa0\r?\n", "\n", text)
|
||||
|
||||
# Reemplazar CRLF por LF
|
||||
|
@ -91,7 +107,10 @@ class MensajeEmail:
|
|||
"""
|
||||
fecha_formato = self.fecha.strftime("%d-%m-%Y")
|
||||
subject_link = self._formatear_subject_para_link(self.subject)
|
||||
return f"- {fecha_formato} - {self.remitente} - [[cronologia#{self.subject}|{subject_link}]]"
|
||||
return (
|
||||
f"- {fecha_formato} - {self.remitente} - [[cronologia#"
|
||||
f"{self.subject}|{subject_link}]]"
|
||||
)
|
||||
|
||||
def _estandarizar_remitente(self, remitente):
|
||||
if "Da:" in remitente:
|
||||
|
@ -103,7 +122,8 @@ class MensajeEmail:
|
|||
if not nombre and email:
|
||||
nombre = email.split("@")[0]
|
||||
elif not nombre and not email:
|
||||
nombre_match = re.search(r"([A-Za-z\s]+)\s*<", remitente)
|
||||
patron_nombre = r"([A-Za-z\s]+)\s*<"
|
||||
nombre_match = re.search(patron_nombre, remitente)
|
||||
if nombre_match:
|
||||
nombre = nombre_match.group(1)
|
||||
else:
|
||||
|
@ -117,17 +137,16 @@ class MensajeEmail:
|
|||
if isinstance(fecha, str):
|
||||
try:
|
||||
return parsedate_to_datetime(fecha)
|
||||
except:
|
||||
except Exception:
|
||||
return datetime.now()
|
||||
return fecha
|
||||
|
||||
def _generar_hash(self):
|
||||
"""
|
||||
Genera un hash único para el mensaje basado en una combinación de campos
|
||||
que identifican únicamente el mensaje
|
||||
Genera un hash único para el mensaje basado en una combinación de
|
||||
campos que identifican únicamente el mensaje
|
||||
"""
|
||||
# Limpiar y normalizar el contenido para el hash
|
||||
# Para el hash, sí normalizamos completamente los espacios
|
||||
# Limpiar y normalizar el contenido para el hash (normaliza espacios)
|
||||
contenido_hash = re.sub(r"\s+", " ", self.contenido).strip()
|
||||
|
||||
# Normalizar el subject
|
||||
|
@ -138,13 +157,11 @@ class MensajeEmail:
|
|||
# Crear una cadena con los elementos clave del mensaje
|
||||
elementos_hash = [
|
||||
self.remitente.strip(),
|
||||
self.fecha.strftime(
|
||||
"%Y%m%d%H%M"
|
||||
), # Solo hasta minutos para permitir pequeñas variaciones
|
||||
# Solo hasta minutos para permitir pequeñas variaciones
|
||||
self.fecha.strftime("%Y%m%d%H%M"),
|
||||
subject_normalizado,
|
||||
contenido_hash[
|
||||
:500
|
||||
], # Usar solo los primeros 500 caracteres del contenido normalizado
|
||||
# Usar solo los primeros 500 caracteres del contenido normalizado
|
||||
contenido_hash[:500],
|
||||
]
|
||||
|
||||
# Unir todos los elementos con un separador único
|
||||
|
@ -152,12 +169,13 @@ class MensajeEmail:
|
|||
|
||||
# Mostrar información de debug para el hash (solo si está habilitado)
|
||||
if hasattr(self, "_debug_hash") and self._debug_hash:
|
||||
print(f" 🔍 Debug Hash:")
|
||||
print(f" - Remitente: '{self.remitente.strip()}'")
|
||||
print(f" - Fecha: '{self.fecha.strftime('%Y%m%d%H%M')}'")
|
||||
print(f" - Subject: '{subject_normalizado}'")
|
||||
print(f" - Contenido (500 chars): '{contenido_hash[:500]}'")
|
||||
print(f" - Texto completo hash: '{texto_hash[:100]}...'")
|
||||
print(" 🔍 Debug Hash:")
|
||||
print(" - Remitente: '" + self.remitente.strip() + "'")
|
||||
print(" - Fecha: '" + self.fecha.strftime("%Y%m%d%H%M") + "'")
|
||||
print(" - Subject: '" + subject_normalizado + "'")
|
||||
preview = contenido_hash[:500]
|
||||
print(" - Contenido (500 chars): '" + preview + "'")
|
||||
print(" - Texto completo hash: '" + texto_hash[:100] + "...'")
|
||||
|
||||
# Generar el hash
|
||||
hash_resultado = hashlib.md5(texto_hash.encode()).hexdigest()
|
||||
|
@ -166,7 +184,8 @@ class MensajeEmail:
|
|||
|
||||
def debug_hash_info(self):
|
||||
"""
|
||||
Muestra información detallada sobre cómo se genera el hash de este mensaje
|
||||
Muestra información detallada de cómo se genera el hash de este
|
||||
mensaje
|
||||
"""
|
||||
self._debug_hash = True
|
||||
hash_result = self._generar_hash()
|
||||
|
|
|
@ -3,31 +3,73 @@ import os
|
|||
import hashlib
|
||||
import re
|
||||
|
||||
|
||||
def _contenido_hash(parte):
|
||||
contenido = parte.get_payload(decode=True) or b""
|
||||
return hashlib.md5(contenido).hexdigest()
|
||||
|
||||
|
||||
def guardar_adjunto(parte, dir_adjuntos):
|
||||
nombre = parte.get_filename()
|
||||
if not nombre:
|
||||
return None
|
||||
|
||||
nombre = re.sub(r'[<>:"/\\|?*]', '_', nombre)
|
||||
nombre = re.sub(r'[<>:"/\\|?*]', "_", nombre)
|
||||
ruta = os.path.join(dir_adjuntos, nombre)
|
||||
|
||||
if os.path.exists(ruta):
|
||||
contenido_nuevo = parte.get_payload(decode=True)
|
||||
hash_nuevo = hashlib.md5(contenido_nuevo).hexdigest()
|
||||
|
||||
with open(ruta, 'rb') as f:
|
||||
hash_nuevo = _contenido_hash(parte)
|
||||
with open(ruta, "rb") as f:
|
||||
hash_existente = hashlib.md5(f.read()).hexdigest()
|
||||
|
||||
if hash_nuevo == hash_existente:
|
||||
return ruta
|
||||
|
||||
base, ext = os.path.splitext(nombre)
|
||||
i = 1
|
||||
while os.path.exists(ruta):
|
||||
ruta = os.path.join(dir_adjuntos, f"{base}_{i}{ext}")
|
||||
i += 1
|
||||
|
||||
with open(ruta, 'wb') as f:
|
||||
with open(ruta, "wb") as f:
|
||||
f.write(parte.get_payload(decode=True))
|
||||
|
||||
return ruta
|
||||
|
||||
|
||||
def guardar_imagen(parte, dir_adjuntos):
|
||||
"""
|
||||
Guarda una imagen (inline o adjunta). Si no tiene filename, genera uno
|
||||
basado en Content-ID o hash, preservando la extensión según el subtype.
|
||||
Devuelve la ruta completa del archivo guardado.
|
||||
"""
|
||||
nombre = parte.get_filename()
|
||||
if not nombre:
|
||||
# Intentar usar Content-ID
|
||||
content_id = parte.get("Content-ID", "") or parte.get("Content-Id", "")
|
||||
content_id = content_id.strip("<>") if content_id else ""
|
||||
ext = f".{parte.get_content_subtype() or 'bin'}"
|
||||
base = (
|
||||
re.sub(r"[^\w\-]+", "_", content_id)
|
||||
if content_id
|
||||
else _contenido_hash(parte)
|
||||
)
|
||||
nombre = f"img_{base}{ext}"
|
||||
|
||||
nombre = re.sub(r'[<>:"/\\|?*]', "_", nombre)
|
||||
ruta = os.path.join(dir_adjuntos, nombre)
|
||||
|
||||
if os.path.exists(ruta):
|
||||
hash_nuevo = _contenido_hash(parte)
|
||||
with open(ruta, "rb") as f:
|
||||
hash_existente = hashlib.md5(f.read()).hexdigest()
|
||||
if hash_nuevo == hash_existente:
|
||||
return ruta
|
||||
base, ext = os.path.splitext(nombre)
|
||||
i = 1
|
||||
while os.path.exists(ruta):
|
||||
ruta = os.path.join(dir_adjuntos, f"{base}_{i}{ext}")
|
||||
i += 1
|
||||
|
||||
with open(ruta, "wb") as f:
|
||||
f.write(parte.get_payload(decode=True))
|
||||
|
||||
return ruta
|
||||
|
|
|
@ -8,8 +8,7 @@ from pathlib import Path
|
|||
from bs4 import BeautifulSoup
|
||||
from email.utils import parsedate_to_datetime
|
||||
from models.mensaje_email import MensajeEmail
|
||||
from utils.attachment_handler import guardar_adjunto
|
||||
import tempfile
|
||||
from utils.attachment_handler import guardar_adjunto, guardar_imagen
|
||||
import os
|
||||
|
||||
|
||||
|
@ -74,9 +73,45 @@ def _should_skip_line(line):
|
|||
return any(line.strip().startswith(header) for header in headers_to_skip)
|
||||
|
||||
|
||||
def _html_a_markdown(html):
|
||||
def _find_vault_root(start_path):
|
||||
"""
|
||||
Convierte contenido HTML a texto markdown, extrayendo el asunto si está presente
|
||||
Busca hacia arriba un directorio que contenga la carpeta hermana '.obsidian'.
|
||||
Devuelve la ruta del directorio raíz del vault o None si no se encuentra.
|
||||
"""
|
||||
current = os.path.abspath(start_path)
|
||||
if os.path.isfile(current):
|
||||
current = os.path.dirname(current)
|
||||
while True:
|
||||
obsidian_dir = os.path.join(current, ".obsidian")
|
||||
if os.path.isdir(obsidian_dir):
|
||||
return current
|
||||
parent = os.path.dirname(current)
|
||||
if parent == current:
|
||||
return None
|
||||
current = parent
|
||||
|
||||
|
||||
def _ruta_relativa_vault(abs_path):
|
||||
"""
|
||||
Convierte una ruta absoluta a una ruta relativa al root del vault Obsidian
|
||||
si se detecta. Si no se detecta, devuelve la ruta original.
|
||||
"""
|
||||
abs_path = os.path.abspath(abs_path)
|
||||
vault_root = _find_vault_root(abs_path)
|
||||
if not vault_root:
|
||||
return abs_path
|
||||
try:
|
||||
rel = os.path.relpath(abs_path, vault_root)
|
||||
# Normalizar separadores a '/'
|
||||
return rel.replace("\\", "/")
|
||||
except Exception:
|
||||
return abs_path
|
||||
|
||||
|
||||
def _html_a_markdown(html, cid_to_link=None):
|
||||
"""
|
||||
Convierte contenido HTML a texto markdown, extrayendo el asunto si está
|
||||
presente
|
||||
"""
|
||||
if html is None:
|
||||
return (None, "")
|
||||
|
@ -89,6 +124,17 @@ def _html_a_markdown(html):
|
|||
|
||||
soup = BeautifulSoup(html, "html.parser")
|
||||
|
||||
# Reemplazar imágenes inline referenciadas por cid en su lugar
|
||||
if cid_to_link:
|
||||
for img in soup.find_all("img"):
|
||||
src = img.get("src", "")
|
||||
if src.startswith("cid:"):
|
||||
cid = src[4:].strip("<>")
|
||||
embed_path = cid_to_link.get(cid)
|
||||
if embed_path:
|
||||
# Obsidian embed (single '!') con ruta relativa al vault
|
||||
img.replace_with(soup.new_string(f"![[{embed_path}]]"))
|
||||
|
||||
# Procesar tablas
|
||||
for table in soup.find_all("table"):
|
||||
try:
|
||||
|
@ -126,7 +172,8 @@ def _html_a_markdown(html):
|
|||
rowspan = int(cell.get("rowspan", 1))
|
||||
colspan = int(cell.get("colspan", 1))
|
||||
|
||||
# Procesar el texto de la celda reemplazando saltos de línea por <br>
|
||||
# Procesar texto de la celda reemplazando saltos de
|
||||
# línea por <br>
|
||||
cell_text = cell.get_text().strip()
|
||||
cell_text = cell_text.replace("\n", "<br>")
|
||||
cell_text = re.sub(
|
||||
|
@ -134,16 +181,15 @@ def _html_a_markdown(html):
|
|||
) # Eliminar <br> múltiples
|
||||
cell_text = cell_text.strip()
|
||||
|
||||
# Rellenar la matriz con el texto y None para las celdas combinadas
|
||||
# Rellenar la matriz con el texto y None para celdas
|
||||
# combinadas
|
||||
for r in range(rowspan):
|
||||
current_row = row_idx + r
|
||||
# Expandir matriz si es necesario
|
||||
while len(table_matrix) <= current_row:
|
||||
table_matrix.append([])
|
||||
# Expandir fila si es necesario
|
||||
while (
|
||||
len(table_matrix[current_row]) <= col_idx + colspan - 1
|
||||
):
|
||||
while len(table_matrix[current_row]) <= col_idx + colspan - 1:
|
||||
table_matrix[current_row].append(None)
|
||||
|
||||
for c in range(colspan):
|
||||
|
@ -198,9 +244,8 @@ def _html_a_markdown(html):
|
|||
|
||||
# Reemplazar la tabla HTML con la versión Markdown
|
||||
if markdown_table:
|
||||
table.replace_with(
|
||||
soup.new_string("\n" + "\n".join(markdown_table) + "\n")
|
||||
)
|
||||
replacement = "\n" + "\n".join(markdown_table) + "\n"
|
||||
table.replace_with(soup.new_string(replacement))
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error procesando tabla: {str(e)}")
|
||||
|
@ -232,7 +277,7 @@ def _html_a_markdown(html):
|
|||
return (None, html if html else "")
|
||||
|
||||
|
||||
def _procesar_email_adjunto(parte, dir_adjuntos):
|
||||
def _procesar_email_adjunto(parte, dir_adjuntos, dir_adjuntos_cronologia=None):
|
||||
"""
|
||||
Procesa un email que viene como adjunto dentro de otro email.
|
||||
"""
|
||||
|
@ -246,17 +291,29 @@ def _procesar_email_adjunto(parte, dir_adjuntos):
|
|||
payload = subparte.get_payload()
|
||||
if isinstance(payload, list):
|
||||
for msg in payload:
|
||||
mensajes.extend(procesar_eml_interno(msg, dir_adjuntos))
|
||||
mensajes.extend(
|
||||
procesar_eml_interno(
|
||||
msg, dir_adjuntos, dir_adjuntos_cronologia
|
||||
)
|
||||
)
|
||||
elif isinstance(payload, email.message.Message):
|
||||
mensajes.extend(procesar_eml_interno(payload, dir_adjuntos))
|
||||
mensajes.extend(
|
||||
procesar_eml_interno(
|
||||
payload, dir_adjuntos, dir_adjuntos_cronologia
|
||||
)
|
||||
)
|
||||
else:
|
||||
# Si no es multipart, intentar procesar como mensaje único
|
||||
payload = parte.get_payload()
|
||||
if isinstance(payload, list):
|
||||
for msg in payload:
|
||||
mensajes.extend(procesar_eml_interno(msg, dir_adjuntos))
|
||||
mensajes.extend(
|
||||
procesar_eml_interno(msg, dir_adjuntos, dir_adjuntos_cronologia)
|
||||
)
|
||||
elif isinstance(payload, email.message.Message):
|
||||
mensajes.extend(procesar_eml_interno(payload, dir_adjuntos))
|
||||
mensajes.extend(
|
||||
procesar_eml_interno(payload, dir_adjuntos, dir_adjuntos_cronologia)
|
||||
)
|
||||
|
||||
return mensajes
|
||||
except Exception as e:
|
||||
|
@ -264,7 +321,7 @@ def _procesar_email_adjunto(parte, dir_adjuntos):
|
|||
return []
|
||||
|
||||
|
||||
def procesar_eml(ruta_archivo, dir_adjuntos):
|
||||
def procesar_eml(ruta_archivo, dir_adjuntos, dir_adjuntos_cronologia=None):
|
||||
"""
|
||||
Punto de entrada principal para procesar archivos .eml
|
||||
"""
|
||||
|
@ -273,7 +330,7 @@ def procesar_eml(ruta_archivo, dir_adjuntos):
|
|||
with open(ruta_archivo, "rb") as eml:
|
||||
mensaje = BytesParser(policy=policy.default).parse(eml)
|
||||
|
||||
mensajes = procesar_eml_interno(mensaje, dir_adjuntos)
|
||||
mensajes = procesar_eml_interno(mensaje, dir_adjuntos, dir_adjuntos_cronologia)
|
||||
print(f" 📧 Procesamiento completado: {len(mensajes)} mensajes extraídos")
|
||||
return mensajes
|
||||
except Exception as e:
|
||||
|
@ -281,7 +338,7 @@ def procesar_eml(ruta_archivo, dir_adjuntos):
|
|||
return []
|
||||
|
||||
|
||||
def procesar_eml_interno(mensaje, dir_adjuntos):
|
||||
def procesar_eml_interno(mensaje, dir_adjuntos, dir_adjuntos_cronologia=None):
|
||||
"""
|
||||
Procesa un mensaje de email, ya sea desde archivo o adjunto
|
||||
"""
|
||||
|
@ -296,13 +353,15 @@ def procesar_eml_interno(mensaje, dir_adjuntos):
|
|||
subject = mensaje.get("subject", "")
|
||||
if subject:
|
||||
# Try to decode if it's encoded
|
||||
subject = str(email.header.make_header(email.header.decode_header(subject)))
|
||||
decoded = email.header.decode_header(subject)
|
||||
subject = str(email.header.make_header(decoded))
|
||||
|
||||
contenido = ""
|
||||
adjuntos = []
|
||||
imagenes = []
|
||||
tiene_html = False
|
||||
|
||||
# First pass: check for HTML content
|
||||
# Primer pase: detectar si hay HTML
|
||||
if mensaje.is_multipart():
|
||||
for parte in mensaje.walk():
|
||||
if parte.get_content_type() == "text/html":
|
||||
|
@ -311,10 +370,29 @@ def procesar_eml_interno(mensaje, dir_adjuntos):
|
|||
else:
|
||||
tiene_html = mensaje.get_content_type() == "text/html"
|
||||
|
||||
# Second pass: process content and attachments
|
||||
# Segundo pase: procesar contenido y adjuntos
|
||||
if mensaje.is_multipart():
|
||||
# Asegurarnos de capturar SOLO una vez el cuerpo principal y no sobrescribirlo
|
||||
# Capturar SOLO una vez el cuerpo principal y no sobrescribirlo
|
||||
contenido_set = False # flag para no re-asignar contenido principal
|
||||
# Construir mapa cid->ruta de embed (relativa al vault) para inline
|
||||
cid_to_link = {}
|
||||
if dir_adjuntos_cronologia:
|
||||
for parte in mensaje.walk():
|
||||
ctype = parte.get_content_type()
|
||||
dispo = parte.get_content_disposition()
|
||||
if ctype.startswith("image/") and dispo in (None, "inline"):
|
||||
cid_header = parte.get("Content-ID", "") or parte.get(
|
||||
"Content-Id", ""
|
||||
)
|
||||
if cid_header:
|
||||
cid_clean = cid_header.strip("<>")
|
||||
# Guardar SOLO en adjuntos/cronologia
|
||||
ruta_img = guardar_imagen(parte, dir_adjuntos_cronologia)
|
||||
if ruta_img:
|
||||
# Convertir a ruta relativa al vault
|
||||
embed_path = _ruta_relativa_vault(ruta_img)
|
||||
cid_to_link[cid_clean] = embed_path
|
||||
|
||||
for parte in mensaje.walk():
|
||||
content_type = parte.get_content_type()
|
||||
|
||||
|
@ -330,7 +408,9 @@ def procesar_eml_interno(mensaje, dir_adjuntos):
|
|||
if content_type == "text/html":
|
||||
html_content = _get_payload_safely(parte)
|
||||
if html_content:
|
||||
part_subject, text = _html_a_markdown(html_content)
|
||||
part_subject, text = _html_a_markdown(
|
||||
html_content, cid_to_link
|
||||
)
|
||||
if not subject and part_subject:
|
||||
subject = part_subject
|
||||
if text:
|
||||
|
@ -346,7 +426,9 @@ def procesar_eml_interno(mensaje, dir_adjuntos):
|
|||
# 2. EMAILS RFC822 ADJUNTOS
|
||||
# -----------------------------
|
||||
elif content_type == "message/rfc822":
|
||||
mensajes_adjuntos = _procesar_email_adjunto(parte, dir_adjuntos)
|
||||
mensajes_adjuntos = _procesar_email_adjunto(
|
||||
parte, dir_adjuntos, dir_adjuntos_cronologia
|
||||
)
|
||||
mensajes.extend(mensajes_adjuntos)
|
||||
|
||||
# -----------------------------
|
||||
|
@ -356,14 +438,28 @@ def procesar_eml_interno(mensaje, dir_adjuntos):
|
|||
nombre = parte.get_filename()
|
||||
if nombre and nombre.lower().endswith(".eml"):
|
||||
mensajes_adjuntos = _procesar_email_adjunto(
|
||||
parte, dir_adjuntos
|
||||
parte, dir_adjuntos, dir_adjuntos_cronologia
|
||||
)
|
||||
mensajes.extend(mensajes_adjuntos)
|
||||
else:
|
||||
# Imagen adjunta (no inline): solo guardar en adjuntos
|
||||
if content_type.startswith("image/"):
|
||||
ruta_img = guardar_imagen(parte, dir_adjuntos)
|
||||
if ruta_img:
|
||||
adjuntos.append(Path(ruta_img).name)
|
||||
else:
|
||||
ruta_adjunto = guardar_adjunto(parte, dir_adjuntos)
|
||||
if ruta_adjunto:
|
||||
adjuntos.append(Path(ruta_adjunto).name)
|
||||
|
||||
# 4. IMÁGENES INLINE: ya manejadas para embebido; no listar
|
||||
elif content_type.startswith("image/") and (
|
||||
parte.get_content_disposition() in (None, "inline")
|
||||
):
|
||||
# Nada que hacer aquí; ya se guardó en cronologia y
|
||||
# se reemplazó en el HTML
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error procesando parte del mensaje: {str(e)}")
|
||||
continue
|
||||
|
@ -371,7 +467,8 @@ def procesar_eml_interno(mensaje, dir_adjuntos):
|
|||
if mensaje.get_content_type() == "text/html":
|
||||
html_content = _get_payload_safely(mensaje)
|
||||
if html_content:
|
||||
part_subject, contenido = _html_a_markdown(html_content)
|
||||
# Para mensajes no multipart, no hay inline cid a resolver
|
||||
part_subject, contenido = _html_a_markdown(html_content, {})
|
||||
if not subject and part_subject:
|
||||
subject = part_subject
|
||||
else:
|
||||
|
@ -386,7 +483,7 @@ def procesar_eml_interno(mensaje, dir_adjuntos):
|
|||
subject=subject,
|
||||
adjuntos=adjuntos,
|
||||
)
|
||||
print(f" ✉️ Mensaje extraído:")
|
||||
print(" ✉️ Mensaje extraído:")
|
||||
print(f" - Subject: {subject}")
|
||||
print(f" - Remitente: {remitente}")
|
||||
print(f" - Fecha: {fecha}")
|
||||
|
@ -395,7 +492,7 @@ def procesar_eml_interno(mensaje, dir_adjuntos):
|
|||
print(f" - Hash generado: {mensaje_nuevo.hash}")
|
||||
mensajes.append(mensaje_nuevo)
|
||||
else:
|
||||
print(f" ⚠️ Mensaje vacío o sin contenido útil - no se agregará")
|
||||
print(" ⚠️ Mensaje vacío o sin contenido útil - no se agregará")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error procesando mensaje: {str(e)}")
|
||||
|
@ -407,7 +504,7 @@ def _parsear_fecha(fecha_str):
|
|||
try:
|
||||
fecha = parsedate_to_datetime(fecha_str)
|
||||
return fecha.replace(tzinfo=None) # Remove timezone info
|
||||
except:
|
||||
except Exception:
|
||||
try:
|
||||
fecha_match = re.search(
|
||||
r"venerd=EC (\d{1,2}) (\w+) (\d{4}) (\d{1,2}):(\d{2})", fecha_str
|
||||
|
@ -430,6 +527,6 @@ def _parsear_fecha(fecha_str):
|
|||
}
|
||||
mes_num = meses_it.get(mes.lower(), 1)
|
||||
return datetime(int(año), mes_num, int(dia), int(hora), int(minuto))
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
return datetime.now()
|
||||
|
|
|
@ -62,6 +62,7 @@ def main():
|
|||
# Construir rutas de salida en working_directory
|
||||
output_file = os.path.join(working_directory, cronologia_file)
|
||||
attachments_path = os.path.join(working_directory, attachments_dir)
|
||||
attachments_crono_path = os.path.join(attachments_path, "cronologia")
|
||||
|
||||
# Debug prints
|
||||
print(f"Working/Output directory: {working_directory}")
|
||||
|
@ -80,6 +81,7 @@ def main():
|
|||
# Asegurar directorios de salida
|
||||
os.makedirs(working_directory, exist_ok=True)
|
||||
os.makedirs(attachments_path, exist_ok=True)
|
||||
os.makedirs(attachments_crono_path, exist_ok=True)
|
||||
|
||||
# Check if input directory exists and has files
|
||||
input_path = Path(input_dir)
|
||||
|
@ -110,7 +112,9 @@ def main():
|
|||
print(f"\n{'='*60}")
|
||||
print(f"Processing file: {archivo}")
|
||||
sys.stdout.flush()
|
||||
nuevos_mensajes = procesar_eml(archivo, attachments_path)
|
||||
nuevos_mensajes = procesar_eml(
|
||||
archivo, attachments_path, attachments_crono_path
|
||||
)
|
||||
print(f"Extracted {len(nuevos_mensajes)} messages from {archivo.name}")
|
||||
sys.stdout.flush()
|
||||
total_procesados += len(nuevos_mensajes)
|
||||
|
|
|
@ -135,6 +135,21 @@
|
|||
position: relative;
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
font-size: 1em;
|
||||
background: white;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
|
||||
select:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #e2e8f0;
|
||||
|
@ -461,6 +476,18 @@
|
|||
<button class="btn btn-secondary" onclick="refreshModels()">
|
||||
🔄 Actualizar
|
||||
</button>
|
||||
<div class="input-group">
|
||||
<label for="sort-select"
|
||||
style="margin-left:10px; margin-right:6px; color:#4a5568; font-weight:600;">Ordenar
|
||||
por:</label>
|
||||
<select id="sort-select">
|
||||
<option value="modified_desc">Última modificación (recientes primero)</option>
|
||||
<option value="modified_asc">Última modificación (antiguos primero)</option>
|
||||
<option value="size_desc">Tamaño (grandes primero)</option>
|
||||
<option value="size_asc">Tamaño (pequeños primero)</option>
|
||||
<option value="name_asc">Nombre (A-Z)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -497,6 +524,7 @@
|
|||
// Estado global de la aplicación
|
||||
let currentModels = [];
|
||||
let isLoading = false;
|
||||
let currentSort = 'modified_desc';
|
||||
|
||||
// Inicializar aplicación
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
|
@ -509,6 +537,16 @@
|
|||
downloadModel();
|
||||
}
|
||||
});
|
||||
|
||||
// Configurar selector de orden
|
||||
const sortSelect = document.getElementById('sort-select');
|
||||
if (sortSelect) {
|
||||
sortSelect.value = currentSort;
|
||||
sortSelect.addEventListener('change', function () {
|
||||
currentSort = this.value;
|
||||
applySortAndRender();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Verificar estado de Ollama
|
||||
|
@ -557,7 +595,7 @@
|
|||
if (data.status === 'success') {
|
||||
currentModels = data.models || [];
|
||||
updateStats(data);
|
||||
renderModels(currentModels);
|
||||
applySortAndRender();
|
||||
} else {
|
||||
showError(`Error al cargar modelos: ${data.message}`);
|
||||
container.innerHTML = `
|
||||
|
@ -578,6 +616,34 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Aplicar orden y renderizar
|
||||
function applySortAndRender() {
|
||||
const sorted = [...currentModels];
|
||||
const getSize = (m) => typeof m.size === 'number' ? m.size : (parseInt(m.size, 10) || 0);
|
||||
const getModifiedTs = (m) => m.modified_at ? (Date.parse(m.modified_at) || 0) : 0;
|
||||
|
||||
switch (currentSort) {
|
||||
case 'size_desc':
|
||||
sorted.sort((a, b) => getSize(b) - getSize(a));
|
||||
break;
|
||||
case 'size_asc':
|
||||
sorted.sort((a, b) => getSize(a) - getSize(b));
|
||||
break;
|
||||
case 'modified_asc':
|
||||
sorted.sort((a, b) => getModifiedTs(a) - getModifiedTs(b));
|
||||
break;
|
||||
case 'name_asc':
|
||||
sorted.sort((a, b) => (a.name || '').localeCompare(b.name || ''));
|
||||
break;
|
||||
case 'modified_desc':
|
||||
default:
|
||||
sorted.sort((a, b) => getModifiedTs(b) - getModifiedTs(a));
|
||||
break;
|
||||
}
|
||||
|
||||
renderModels(sorted);
|
||||
}
|
||||
|
||||
// Actualizar estadísticas
|
||||
function updateStats(data) {
|
||||
const statsElement = document.getElementById('stats');
|
||||
|
|
|
@ -1,5 +1,31 @@
|
|||
{
|
||||
"history": [
|
||||
{
|
||||
"id": "286de385",
|
||||
"group_id": "2",
|
||||
"script_name": "main.py",
|
||||
"executed_date": "2025-08-08T13:31:57.810744Z",
|
||||
"arguments": [],
|
||||
"working_directory": "D:/Proyectos/Scripts/RS485/MaselliSimulatorApp",
|
||||
"python_env": "tia_scripting",
|
||||
"executable_type": "pythonw.exe",
|
||||
"status": "running",
|
||||
"pid": 63664,
|
||||
"execution_time": null
|
||||
},
|
||||
{
|
||||
"id": "19b3a39f",
|
||||
"group_id": "2",
|
||||
"script_name": "main.py",
|
||||
"executed_date": "2025-08-08T12:08:20.490182Z",
|
||||
"arguments": [],
|
||||
"working_directory": "D:/Proyectos/Scripts/RS485/MaselliSimulatorApp",
|
||||
"python_env": "tia_scripting",
|
||||
"executable_type": "pythonw.exe",
|
||||
"status": "running",
|
||||
"pid": 66300,
|
||||
"execution_time": null
|
||||
},
|
||||
{
|
||||
"id": "23248897",
|
||||
"group_id": "2",
|
||||
|
|
214
data/log.txt
214
data/log.txt
|
@ -1,107 +1,107 @@
|
|||
[11:43:49] Iniciando ejecución de x1.py en C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\17 - E5.006880 - Modifica O&U - RSC098...
|
||||
[11:43:49] ✅ Configuración cargada exitosamente
|
||||
[11:43:49] Working/Output directory: C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\17 - E5.006880 - Modifica O&U - RSC098
|
||||
[11:43:49] Input directory: C:\Trabajo\SIDEL\17 - E5.006880 - Modifica O&U - RSC098\Reporte\Emails
|
||||
[11:43:49] Output file: C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\17 - E5.006880 - Modifica O&U - RSC098\cronologia.md
|
||||
[11:43:49] Attachments directory: C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\17 - E5.006880 - Modifica O&U - RSC098\adjuntos
|
||||
[11:43:49] Beautify rules file: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\EmailCrono\config\beautify_rules.json
|
||||
[11:43:49] Found 1 .eml files
|
||||
[11:43:49] Creando cronología nueva (archivo se sobrescribirá)
|
||||
[11:43:49] ============================================================
|
||||
[11:43:49] Processing file: C:\Trabajo\SIDEL\17 - E5.006880 - Modifica O&U - RSC098\Reporte\Emails\R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml
|
||||
[11:43:49] 📧 Abriendo archivo: C:\Trabajo\SIDEL\17 - E5.006880 - Modifica O&U - RSC098\Reporte\Emails\R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml
|
||||
[11:43:49] ✉️ Mensaje extraído:
|
||||
[11:43:49] - Subject: R: {EXT} R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[11:43:49] - Remitente: "Passera, Alessandro" <Alessandro.Passera@sidel.com>
|
||||
[11:43:49] - Fecha: 2025-08-08 07:49:28
|
||||
[11:43:49] - Adjuntos: 0 archivos
|
||||
[11:43:49] - Contenido: 4735 caracteres
|
||||
[11:43:49] - Hash generado: 48f94bf24945f73bc08c1c0cf8c1e8bb
|
||||
[11:43:49] ✉️ Mensaje extraído:
|
||||
[11:43:49] - Subject: RE: {EXT} R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[11:43:49] - Remitente: "Bii, Vickodyne" <vickodyne.bii@sidel.com>
|
||||
[11:43:49] - Fecha: 2025-08-08 05:46:30
|
||||
[11:43:49] - Adjuntos: 0 archivos
|
||||
[11:43:49] - Contenido: 4259 caracteres
|
||||
[11:43:49] - Hash generado: 82709d4677b90d79bb02e13cfe86924e
|
||||
[11:43:49] ✉️ Mensaje extraído:
|
||||
[11:43:49] - Subject: R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[11:43:49] - Remitente: "walter.orsi@teknors.com" <walter.orsi@teknors.com>
|
||||
[11:43:49] - Fecha: 2025-08-07 15:55:58
|
||||
[11:43:49] - Adjuntos: 0 archivos
|
||||
[11:43:49] - Contenido: 3235 caracteres
|
||||
[11:43:49] - Hash generado: cceec9818de1a4491214af4b6d96e143
|
||||
[11:43:49] ✉️ Mensaje extraído:
|
||||
[11:43:49] - Subject: R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[11:43:49] - Remitente: "Passera, Alessandro" <Alessandro.Passera@sidel.com>
|
||||
[11:43:49] - Fecha: 2025-08-07 13:07:11
|
||||
[11:43:49] - Adjuntos: 0 archivos
|
||||
[11:43:49] - Contenido: 2398 caracteres
|
||||
[11:43:49] - Hash generado: dc05b2959920f679cd60e8a29685badc
|
||||
[11:43:49] ✉️ Mensaje extraído:
|
||||
[11:43:49] - Subject: R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[11:43:49] - Remitente: "Passera, Alessandro" <Alessandro.Passera@sidel.com>
|
||||
[11:43:49] - Fecha: 2025-08-07 12:59:15
|
||||
[11:43:49] - Adjuntos: 0 archivos
|
||||
[11:43:49] - Contenido: 1613 caracteres
|
||||
[11:43:49] - Hash generado: a848be3351ae2cc44bafb0f322a78690
|
||||
[11:43:49] ✉️ Mensaje extraído:
|
||||
[11:43:49] - Subject: RE: {EXT} R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[11:43:49] - Remitente: Miguel Angel Vera <miguelverateknors@gmail.com>
|
||||
[11:43:49] - Fecha: 2025-08-08 09:41:58
|
||||
[11:43:49] - Adjuntos: 0 archivos
|
||||
[11:43:49] - Contenido: 4735 caracteres
|
||||
[11:43:49] - Hash generado: 430cc918020c3c8db795995baa26cb78
|
||||
[11:43:49] 📧 Procesamiento completado: 6 mensajes extraídos
|
||||
[11:43:49] Extracted 6 messages from R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml
|
||||
[11:43:49] --- Msg 1/6 from R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml ---
|
||||
[11:43:49] Remitente: Passera, Alessandro
|
||||
[11:43:49] Fecha: 2025-08-08 07:49:28
|
||||
[11:43:49] Subject: R: {EXT} R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[11:43:49] Hash: 48f94bf24945f73bc08c1c0cf8c1e8bb
|
||||
[11:43:49] Adjuntos: []
|
||||
[11:43:49] ✓ NUEVO mensaje - Agregando a la cronología
|
||||
[11:43:49] --- Msg 2/6 from R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml ---
|
||||
[11:43:49] Remitente: Bii, Vickodyne
|
||||
[11:43:49] Fecha: 2025-08-08 05:46:30
|
||||
[11:43:49] Subject: RE: {EXT} R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[11:43:49] Hash: 82709d4677b90d79bb02e13cfe86924e
|
||||
[11:43:49] Adjuntos: []
|
||||
[11:43:49] ✓ NUEVO mensaje - Agregando a la cronología
|
||||
[11:43:49] --- Msg 3/6 from R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml ---
|
||||
[11:43:49] Remitente: walter.orsi@teknors.com
|
||||
[11:43:49] Fecha: 2025-08-07 15:55:58
|
||||
[11:43:49] Subject: R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[11:43:49] Hash: cceec9818de1a4491214af4b6d96e143
|
||||
[11:43:49] Adjuntos: []
|
||||
[11:43:49] ✓ NUEVO mensaje - Agregando a la cronología
|
||||
[11:43:49] --- Msg 4/6 from R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml ---
|
||||
[11:43:49] Remitente: Passera, Alessandro
|
||||
[11:43:49] Fecha: 2025-08-07 13:07:11
|
||||
[11:43:49] Subject: R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[11:43:49] Hash: dc05b2959920f679cd60e8a29685badc
|
||||
[11:43:49] Adjuntos: []
|
||||
[11:43:49] ✓ NUEVO mensaje - Agregando a la cronología
|
||||
[11:43:49] --- Msg 5/6 from R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml ---
|
||||
[11:43:49] Remitente: Passera, Alessandro
|
||||
[11:43:49] Fecha: 2025-08-07 12:59:15
|
||||
[11:43:49] Subject: R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[11:43:49] Hash: a848be3351ae2cc44bafb0f322a78690
|
||||
[11:43:49] Adjuntos: []
|
||||
[11:43:49] ✓ NUEVO mensaje - Agregando a la cronología
|
||||
[11:43:49] --- Msg 6/6 from R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml ---
|
||||
[11:43:49] Remitente: Miguel Angel Vera
|
||||
[11:43:49] Fecha: 2025-08-08 09:41:58
|
||||
[11:43:49] Subject: RE: {EXT} R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[11:43:49] Hash: 430cc918020c3c8db795995baa26cb78
|
||||
[11:43:49] Adjuntos: []
|
||||
[11:43:49] ✓ NUEVO mensaje - Agregando a la cronología
|
||||
[11:43:49] Estadísticas de procesamiento:
|
||||
[11:43:49] - Total mensajes encontrados: 6
|
||||
[11:43:49] - Mensajes únicos añadidos: 6
|
||||
[11:43:49] - Mensajes duplicados ignorados: 0
|
||||
[11:43:49] Writing 6 messages to C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\17 - E5.006880 - Modifica O&U - RSC098\cronologia.md
|
||||
[11:43:49] ✅ Cronología guardada exitosamente en: C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\17 - E5.006880 - Modifica O&U - RSC098\cronologia.md
|
||||
[11:43:49] 📊 Total de mensajes en la cronología: 6
|
||||
[11:43:49] Ejecución de x1.py finalizada (success). Duración: 0:00:00.353973.
|
||||
[11:43:49] Log completo guardado en: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\EmailCrono\.log\log_x1.txt
|
||||
[14:55:47] Iniciando ejecución de x1.py en C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\17 - E5.006880 - Modifica O&U - RSC098...
|
||||
[14:55:47] ✅ Configuración cargada exitosamente
|
||||
[14:55:47] Working/Output directory: C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\17 - E5.006880 - Modifica O&U - RSC098
|
||||
[14:55:47] Input directory: C:\Trabajo\SIDEL\17 - E5.006880 - Modifica O&U - RSC098\Reporte\Emails
|
||||
[14:55:47] Output file: C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\17 - E5.006880 - Modifica O&U - RSC098\cronologia.md
|
||||
[14:55:47] Attachments directory: C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\17 - E5.006880 - Modifica O&U - RSC098\adjuntos
|
||||
[14:55:47] Beautify rules file: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\EmailCrono\config\beautify_rules.json
|
||||
[14:55:47] Found 1 .eml files
|
||||
[14:55:47] Creando cronología nueva (archivo se sobrescribirá)
|
||||
[14:55:47] ============================================================
|
||||
[14:55:47] Processing file: C:\Trabajo\SIDEL\17 - E5.006880 - Modifica O&U - RSC098\Reporte\Emails\R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml
|
||||
[14:55:47] 📧 Abriendo archivo: C:\Trabajo\SIDEL\17 - E5.006880 - Modifica O&U - RSC098\Reporte\Emails\R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml
|
||||
[14:55:48] ✉️ Mensaje extraído:
|
||||
[14:55:48] - Subject: R: {EXT} R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[14:55:48] - Remitente: "Passera, Alessandro" <Alessandro.Passera@sidel.com>
|
||||
[14:55:48] - Fecha: 2025-08-08 07:49:28
|
||||
[14:55:48] - Adjuntos: 0 archivos
|
||||
[14:55:48] - Contenido: 5160 caracteres
|
||||
[14:55:48] - Hash generado: 48f94bf24945f73bc08c1c0cf8c1e8bb
|
||||
[14:55:48] ✉️ Mensaje extraído:
|
||||
[14:55:48] - Subject: RE: {EXT} R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[14:55:48] - Remitente: "Bii, Vickodyne" <vickodyne.bii@sidel.com>
|
||||
[14:55:48] - Fecha: 2025-08-08 05:46:30
|
||||
[14:55:48] - Adjuntos: 0 archivos
|
||||
[14:55:48] - Contenido: 4686 caracteres
|
||||
[14:55:48] - Hash generado: 352a3d37ac274b11822ca5527cd8865b
|
||||
[14:55:48] ✉️ Mensaje extraído:
|
||||
[14:55:48] - Subject: R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[14:55:48] - Remitente: "walter.orsi@teknors.com" <walter.orsi@teknors.com>
|
||||
[14:55:48] - Fecha: 2025-08-07 15:55:58
|
||||
[14:55:48] - Adjuntos: 0 archivos
|
||||
[14:55:48] - Contenido: 3583 caracteres
|
||||
[14:55:48] - Hash generado: b2558be8631ba7d14210a4a3379dfdad
|
||||
[14:55:48] ✉️ Mensaje extraído:
|
||||
[14:55:48] - Subject: R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[14:55:48] - Remitente: "Passera, Alessandro" <Alessandro.Passera@sidel.com>
|
||||
[14:55:48] - Fecha: 2025-08-07 13:07:11
|
||||
[14:55:48] - Adjuntos: 0 archivos
|
||||
[14:55:48] - Contenido: 2485 caracteres
|
||||
[14:55:48] - Hash generado: dc05b2959920f679cd60e8a29685badc
|
||||
[14:55:48] ✉️ Mensaje extraído:
|
||||
[14:55:48] - Subject: R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[14:55:48] - Remitente: "Passera, Alessandro" <Alessandro.Passera@sidel.com>
|
||||
[14:55:48] - Fecha: 2025-08-07 12:59:15
|
||||
[14:55:48] - Adjuntos: 0 archivos
|
||||
[14:55:48] - Contenido: 1700 caracteres
|
||||
[14:55:48] - Hash generado: a848be3351ae2cc44bafb0f322a78690
|
||||
[14:55:48] ✉️ Mensaje extraído:
|
||||
[14:55:48] - Subject: RE: {EXT} R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[14:55:48] - Remitente: Miguel Angel Vera <miguelverateknors@gmail.com>
|
||||
[14:55:48] - Fecha: 2025-08-08 09:41:58
|
||||
[14:55:48] - Adjuntos: 0 archivos
|
||||
[14:55:48] - Contenido: 5160 caracteres
|
||||
[14:55:48] - Hash generado: 430cc918020c3c8db795995baa26cb78
|
||||
[14:55:48] 📧 Procesamiento completado: 6 mensajes extraídos
|
||||
[14:55:48] Extracted 6 messages from R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml
|
||||
[14:55:48] --- Msg 1/6 from R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml ---
|
||||
[14:55:48] Remitente: Passera, Alessandro
|
||||
[14:55:48] Fecha: 2025-08-08 07:49:28
|
||||
[14:55:48] Subject: R: {EXT} R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[14:55:48] Hash: 48f94bf24945f73bc08c1c0cf8c1e8bb
|
||||
[14:55:48] Adjuntos: []
|
||||
[14:55:48] ✓ NUEVO mensaje - Agregando a la cronología
|
||||
[14:55:48] --- Msg 2/6 from R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml ---
|
||||
[14:55:48] Remitente: Bii, Vickodyne
|
||||
[14:55:48] Fecha: 2025-08-08 05:46:30
|
||||
[14:55:48] Subject: RE: {EXT} R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[14:55:48] Hash: 352a3d37ac274b11822ca5527cd8865b
|
||||
[14:55:48] Adjuntos: []
|
||||
[14:55:48] ✓ NUEVO mensaje - Agregando a la cronología
|
||||
[14:55:48] --- Msg 3/6 from R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml ---
|
||||
[14:55:48] Remitente: walter.orsi@teknors.com
|
||||
[14:55:48] Fecha: 2025-08-07 15:55:58
|
||||
[14:55:48] Subject: R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[14:55:48] Hash: b2558be8631ba7d14210a4a3379dfdad
|
||||
[14:55:48] Adjuntos: []
|
||||
[14:55:48] ✓ NUEVO mensaje - Agregando a la cronología
|
||||
[14:55:48] --- Msg 4/6 from R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml ---
|
||||
[14:55:48] Remitente: Passera, Alessandro
|
||||
[14:55:48] Fecha: 2025-08-07 13:07:11
|
||||
[14:55:48] Subject: R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[14:55:48] Hash: dc05b2959920f679cd60e8a29685badc
|
||||
[14:55:48] Adjuntos: []
|
||||
[14:55:48] ✓ NUEVO mensaje - Agregando a la cronología
|
||||
[14:55:48] --- Msg 5/6 from R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml ---
|
||||
[14:55:48] Remitente: Passera, Alessandro
|
||||
[14:55:48] Fecha: 2025-08-07 12:59:15
|
||||
[14:55:48] Subject: R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[14:55:48] Hash: a848be3351ae2cc44bafb0f322a78690
|
||||
[14:55:48] Adjuntos: []
|
||||
[14:55:48] ✓ NUEVO mensaje - Agregando a la cronología
|
||||
[14:55:48] --- Msg 6/6 from R_ E5.006880 - RSC098 - Nigerian Breweries_ URGENT.eml ---
|
||||
[14:55:48] Remitente: Miguel Angel Vera
|
||||
[14:55:48] Fecha: 2025-08-08 09:41:58
|
||||
[14:55:48] Subject: RE: {EXT} R: E5.006880 - RSC098 - Nigerian Breweries: URGENT
|
||||
[14:55:48] Hash: 430cc918020c3c8db795995baa26cb78
|
||||
[14:55:48] Adjuntos: []
|
||||
[14:55:48] ✓ NUEVO mensaje - Agregando a la cronología
|
||||
[14:55:48] Estadísticas de procesamiento:
|
||||
[14:55:48] - Total mensajes encontrados: 6
|
||||
[14:55:48] - Mensajes únicos añadidos: 6
|
||||
[14:55:48] - Mensajes duplicados ignorados: 0
|
||||
[14:55:48] Writing 6 messages to C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\17 - E5.006880 - Modifica O&U - RSC098\cronologia.md
|
||||
[14:55:48] ✅ Cronología guardada exitosamente en: C:\Users\migue\OneDrive\Miguel\Obsidean\Trabajo\VM\04-SIDEL\17 - E5.006880 - Modifica O&U - RSC098\cronologia.md
|
||||
[14:55:48] 📊 Total de mensajes en la cronología: 6
|
||||
[14:55:48] Ejecución de x1.py finalizada (success). Duración: 0:00:00.497219.
|
||||
[14:55:48] Log completo guardado en: D:\Proyectos\Scripts\ParamManagerScripts\backend\script_groups\EmailCrono\.log\log_x1.txt
|
||||
|
|
Loading…
Reference in New Issue