import os import uuid import tempfile import pandas as pd from openai import OpenAI from utils.utils import preparar_bloques_json, preparar_bloques_texto_redes, markdown_a_word_docx, validar_y_leer_excel_encuesta, validar_y_leer_excel_redes, combinar_archivos_docx, clean_text, generar_resumenes_por_red_social from analysis.global_summary import generar_resumen_global from datetime import datetime # Inicializar cliente OpenAI openai = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) def cargar_prompt(path): with open(path, "r", encoding="utf-8") as f: return f.read() def analizar_excel_encuesta_y_generar_pdf(archivo_excel, informe): try: if archivo_excel is None: yield [], "❌ Por favor, sube un archivo Excel válido antes de generar el análisis.", None return df, error_msg = validar_y_leer_excel_encuesta(archivo_excel) if error_msg: yield [], error_msg, None return nombre_excel = archivo_excel.name if informe == "Análisis General de Opiniones y Sentimientos": system_prompt = "Eres un analista especializado en interpretación de encuestas abiertas." user_prompt = cargar_prompt("prompts/general_analysis.txt") elif informe == "Análisis Focalizado por Pregunta": system_prompt = ( "Eres un experto en generación de prompts para analizar encuestas abiertas. " "Tu tarea es crear un prompt completo que combine dos cosas:\n" "1. Una estructura sugerida del informe (capítulos y subtítulos) basada en las preguntas de la encuesta.\n" "2. Una instrucción para analizar respuestas siguiendo esa estructura, en formato Markdown, con ejemplos y síntesis final." ) preguntas = [str(col).strip() for col in df.columns if col.strip() != ""] lista_preguntas = "\n".join(f"- {p}" for p in preguntas) prompt_generador = cargar_prompt("prompts/focused_analysis_generator.txt").replace("{{PREGUNTAS}}", lista_preguntas) response_prompt = openai.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt_generador} ], temperature=0.4 ) prompt_generado = response_prompt.choices[0].message.content if prompt_generado.strip().startswith("```markdown"): prompt_generado = prompt_generado.split("```markdown", 1)[-1].strip() user_prompt = prompt_generado.split("```json")[0].strip() user_prompt += ( "\n\nPor favor, desarrolla cada sección con profundidad. " "Incluye al menos tres párrafos por capítulo, con análisis detallado, patrones observados, inferencias, ejemplos y reflexiones que ayuden a comprender mejor el pensamiento de los encuestados. " "No resumas, sino expande cada punto." ) bloques_json = preparar_bloques_json(nombre_excel) session_id = str(uuid.uuid4())[:8] temp_dir = os.path.join(tempfile.gettempdir(), f"resumenes_{session_id}") os.makedirs(temp_dir, exist_ok=True) resumenes_dir = os.path.join(temp_dir, "resumenes_bloques") os.makedirs(resumenes_dir, exist_ok=True) resumenes_texto = "" for i, bloque_json in enumerate(bloques_json, start=1): response = openai.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt + "\n\n```json\n" + str(bloque_json) + "\n```"} ], temperature=0.4 ) resumen = response.choices[0].message.content resumenes_texto += f"\n\n🧩 **Resumen del bloque {i}/{len(bloques_json)} generado**{'-'*80}" with open(os.path.join(resumenes_dir, f"resumen_bloque_{i}.md"), "w", encoding="utf-8") as f: f.write(resumen) yield None, None, resumenes_texto resumen_global_md = os.path.join(temp_dir, "resumen_global.md") generar_resumen_global(resumenes_dir, resumen_global_md) base_name = os.path.splitext(os.path.basename(nombre_excel))[0] archivo_docx = os.path.join(temp_dir, f"{base_name}_resumen_global.docx") markdown_a_word_docx(resumen_global_md, archivo_docx) if os.path.exists(archivo_docx): yield [archivo_docx], "✅ Análisis completado correctamente.", "✅ Todos los resumenes generados" else: yield [], "⚠️ No se pudo generar el archivo Word.", None except Exception as e: yield [], f"⚠️ Error inesperado: {str(e)}", None def analizar_excel_redes_y_generar_pdf(archivo_excel, informe, nombre_cliente=None): try: if archivo_excel is None: yield [], "❌ Por favor, sube un archivo Excel válido antes de generar el análisis.", None return df, error_msg = validar_y_leer_excel_redes(archivo_excel) if error_msg: yield [], error_msg, None return df["texto"] = df["texto"].apply(clean_text) redes_presentes = sorted(df["red_social"].dropna().unique().tolist()) lista_redes = ", ".join(redes_presentes) if informe == "¿Cuál fue HOY mi desempeño en las Redes?": if not nombre_cliente: yield [], "⚠️ Por favor, ingrese un nombre de Cliente.", None return prompts = { "desempeno": { "nombre": "Desempeño", "system": "Eres un analista que evalúa el impacto de contenidos en redes sociales.", # "user": cargar_prompt("prompts/redes_desempeno.txt").replace("{{CLIENTE}}", nombre_cliente) "user_path": cargar_prompt("prompts/redes_desempeno.txt") \ .replace("{{CLIENTE}}", nombre_cliente) \ .replace("{{REDES_PRESENTES}}", lista_redes) }, "foda": { "nombre": "FODA", "system": "Eres un analista que identifica fortalezas, oportunidades, debilidades y amenazas en el discurso de redes sociales.", "user_path": cargar_prompt("prompts/redes_foda.txt").replace("{{CLIENTE}}", nombre_cliente) }, "recomendaciones": { "nombre": "Recomendaciones", "system": "Eres un asesor político que genera recomendaciones estratégicas a partir de comentarios en redes.", "user_path": cargar_prompt("prompts/redes_recomendaciones.txt").replace("{{CLIENTE}}", nombre_cliente) } } elif informe == "Fortalezas y Debilidades en Redes Sociales": prompts = { "diagnostico": { "nombre": "Diagnostico-Global", "system": "Eres un asesor político que genera recomendaciones estratégicas a partir de comentarios en redes.", "user_path": cargar_prompt("prompts/redes_diagnostico_global_fyd.txt") }, "fyd": { "nombre": "Fortalezas-Debilidades", "system": "Eres un analista experto en comunicación política y análisis de redes sociales. Tu tarea es elaborar informes extensos, profesionales y bien estructurados, únicamente en base al contenido textual que se te proporciona. No debes inventar información ni usar conocimientos externos. Tu objetivo es detectar y explicar fortalezas, debilidades, el desempeño de figuras mencionadas y diferencias entre redes sociales. Siempre trabaja con subtítulos claros por red social, un diagnóstico global y justificaciones basadas en los textos reales. Mantén un tono analítico, claro y profesional.", "user_path": cargar_prompt("prompts/redes_analisis_fortalezas_debilidades.txt") }, "figuras": { "nombre": "Figuras-Principales", "system": "Eres un analista que evalúa el impacto de contenidos en redes sociales.", "user_path": cargar_prompt("prompts/redes_analisis_principales_figuras.txt") } } grouped = df.groupby("red_social")["texto"].apply(list) session_id = str(uuid.uuid4())[:8] temp_dir = os.path.join(tempfile.gettempdir(), f"resumenes_{session_id}") os.makedirs(temp_dir, exist_ok=True) archivos_finales = [] for red, textos in grouped.items(): yield None, None, f"📊 Generando análisis para {red}..." resumen_dir_red = os.path.join(temp_dir, red.lower()) os.makedirs(resumen_dir_red, exist_ok=True) # resumenes_por_prompt = generar_resumenes_por_red_social(red, textos, prompts) bloques = preparar_bloques_texto_redes(textos) for clave, prompt_info in prompts.items(): resumen_dir_clave = os.path.join(resumen_dir_red, f"resumenes_{clave}") os.makedirs(resumen_dir_clave, exist_ok=True) resumenes_texto = "" for i, bloque in enumerate(bloques, start=1): try: response = openai.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": prompt_info["system"]}, {"role": "user", "content": prompt_info["user_path"].replace("{{REDES_PRESENTES}}", red) + "\n\n```json\n" + str(bloque) + "\n```"} ], temperature=0.4 ) resumen = response.choices[0].message.content except Exception as e: resumen = f"⚠️ Error al generar el resumen del bloque {i}: {str(e)}" resumenes_texto += f"\n\n🧩 **Resumen del bloque {i}/{len(bloques)} de la red social ({red}) generado ({prompt_info['nombre']})**{'-'*80}" with open(os.path.join(resumen_dir_clave, f"resumen_bloque_{i}.md"), "w", encoding="utf-8") as f: f.write(resumen) yield None, None, resumenes_texto resumen_md = os.path.join(resumen_dir_red, f"{clave}_resumen.md") generar_resumen_global(resumen_dir_clave, resumen_md) # ✅ Concatenar todos los .md de esta red en resumen_final_red resumen_final_red = os.path.join(resumen_dir_red, f"resumen_final_{red.lower()}.md") with open(resumen_final_red, "w", encoding="utf-8") as f_final: f_final.write(f"# 🧾 Análisis de {red.capitalize()}\n\n") for clave in prompts.keys(): resumen_md = os.path.join(resumen_dir_red, f"{clave}_resumen.md") if os.path.exists(resumen_md): with open(resumen_md, "r", encoding="utf-8") as f_in: contenido = f_in.read() f_final.write(f"\n\n## 📌 Resumen ({clave.upper()})\n\n") f_final.write(contenido) docx_file = os.path.join(resumen_dir_red, f"{red}_resumen.docx") markdown_a_word_docx(resumen_final_red, docx_file) archivos_finales.append(docx_file) # ✅ Combinar los 3 Word en uno solo fecha = datetime.today().strftime("%Y%m%d") nombre_archivo = f"Análisis_{informe.replace(' ', '_')}_{fecha}.docx" archivo_docx_unico = os.path.join(temp_dir, nombre_archivo) combinar_archivos_docx(archivos_finales, archivo_docx_unico) archivos_finales.append(archivo_docx_unico) if all(os.path.exists(f) for f in archivos_finales): yield archivos_finales, "✅ Análisis completo con FODA, desempeño y recomendaciones.", "✅ Documentos generados" else: yield [], "⚠️ Faltan uno o más archivos. Revisa errores.", None except Exception as e: yield [], f"⚠️ Error inesperado: {str(e)}", None