Spaces:
Running
Running
# 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", | |
) | |