data-analysis-ai / analysis /analysis.py
qsocialnow's picture
-
4ec72fc verified
Raw
History Blame Contribute Delete
12.8 kB
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