edullm / core /integrations /doc_converter.py
JairoDanielMT's picture
Update core/integrations/doc_converter.py
c46095c verified
# core/integrations/doc_converter
import os
import re
import uuid
import tempfile
import pypandoc
from loguru import logger
from fastapi.responses import FileResponse
# Control de descargas (máximo 2 por archivo)
_descargas = {}
def limpiar_lineas_hr(markdown_text: str) -> str:
"""Reemplaza líneas horizontales '---' por saltos de línea."""
return re.sub(r"^\s*---\s*$", "\n", markdown_text, flags=re.MULTILINE)
def normalizar_ecuaciones(md: str) -> str:
"""Convierte ecuaciones LaTeX escapadas a formato estándar."""
md = re.sub(r"\\\[\s*(.*?)\s*\\\]", r"$$\1$$", md, flags=re.DOTALL)
md = re.sub(r"\\\(\s*(.*?)\s*\\\)", r"$\1$", md, flags=re.DOTALL)
return md
# def limpiar_backticks(markdown_text: str) -> str:
# """
# Elimina los backticks triples si encapsulan todo el contenido.
# """
# markdown_text = markdown_text.strip()
# if markdown_text.startswith("```") and markdown_text.endswith("```"):
# logger.info("🧹 Eliminando backticks triples de la respuesta LLM.")
# return markdown_text[3:-3].strip()
# return markdown_text
def limpiar_backticks(markdown_text: str) -> str:
"""
Elimina los backticks triples (``` o ```markdown) si encapsulan todo el contenido.
"""
markdown_text = markdown_text.strip()
# Elimina bloque inicial ``` o ```markdown y final ```
if markdown_text.startswith("```") and markdown_text.endswith("```"):
# Reemplaza ` ```markdown\n` por vacío
markdown_text = re.sub(r"^```[a-zA-Z]*\n?", "", markdown_text)
# Quita los ``` finales
markdown_text = re.sub(r"\n?```$", "", markdown_text)
logger.info("🧹 Eliminando bloque ``` del inicio y final.")
return markdown_text.strip()
return markdown_text
def procesar_markdown(markdown_content: str) -> dict:
try:
# Limpieza previa del contenido
markdown_content = limpiar_backticks(markdown_content)
contenido_limpio = normalizar_ecuaciones(limpiar_lineas_hr(markdown_content))
uid = str(uuid.uuid4())
temp_dir = tempfile.gettempdir()
input_md = os.path.join(temp_dir, f"{uid}.md")
output_docx = os.path.join(temp_dir, f"{uid}.docx")
with open(input_md, "w", encoding="utf-8") as f:
f.write(contenido_limpio)
pypandoc.convert_file(
source_file=input_md,
to="docx",
outputfile=output_docx,
format="md",
extra_args=["--standalone"],
)
os.remove(input_md)
_descargas[uid] = 0
logger.success(f"✅ DOCX generado correctamente: {output_docx}")
return {"message": "Archivo DOCX generado exitosamente.", "file_id": uid}
except Exception as e:
logger.error(f"❌ Error al procesar Markdown: {e}")
return {"error": "Fallo en la conversión de Markdown a DOCX."}
def gestionar_descarga(file_id: str):
"""
Controla la descarga de archivos. Permite solo 2 descargas por archivo.
"""
temp_dir = tempfile.gettempdir()
output_docx = os.path.join(temp_dir, f"{file_id}.docx")
if not os.path.exists(output_docx):
logger.warning(f"⚠️ Archivo no encontrado: {output_docx}")
return {"error": "El archivo no existe o fue eliminado.", "status": 404}
if file_id not in _descargas:
logger.warning(f"⚠️ ID inválido de descarga: {file_id}")
return {"error": "ID de archivo no válido.", "status": 400}
if _descargas[file_id] >= 2:
os.remove(output_docx)
del _descargas[file_id]
logger.info(f"🗑️ Archivo eliminado tras exceder descargas: {file_id}")
return {"error": "Límite de descargas alcanzado.", "status": 410}
_descargas[file_id] += 1
logger.info(f"⬇️ Descarga {_descargas[file_id]} de 2 para archivo: {file_id}")
return FileResponse(
path=output_docx,
filename="material_educativo.docx",
media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
)