JairoCesar commited on
Commit
cff3d40
·
verified ·
1 Parent(s): 5a55c0b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +97 -44
app.py CHANGED
@@ -1,5 +1,5 @@
1
- # ==================== El Detective de Alimentos (Versión 9.0 - Maestra) =====================================
2
- # Mejoras: Corrección de errores de sintaxis, expansión masiva de diccionarios y refinamientos de UI.
3
 
4
  import streamlit as st
5
  import google.generativeai as genai
@@ -11,6 +11,7 @@ import re
11
  import pandas as pd
12
  import altair as alt
13
  from datetime import datetime
 
14
 
15
  st.set_page_config(
16
  page_title="El Detective de Alimentos",
@@ -18,34 +19,28 @@ st.set_page_config(
18
  layout="wide"
19
  )
20
 
21
- # --- CONFIGURACIÓN Y CARGA DE DATOS (SIN CAMBIOS) ---
22
- # Configurar logging
23
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
24
  logger = logging.getLogger("food_detective_app")
25
- # --- CONFIGURACIÓN DE GEMINI ---
26
  try:
27
  if 'GEMINI_API_KEY' in st.secrets:
28
  GEMINI_API_KEY = st.secrets['GEMINI_API_KEY']
29
  else:
30
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
31
  if not GEMINI_API_KEY:
32
- st.error("No se encontró la GEMINI_API_KEY. Por favor, configúrala en los Secrets de Streamlit.")
33
- logger.error("GEMINI_API_KEY no encontrada.")
34
  st.stop()
35
  genai.configure(api_key=GEMINI_API_KEY)
36
- logger.info("✅ Configuración de Gemini API realizada.")
37
  except Exception as e:
38
  st.error(f"❌ Error al configurar Gemini API: {e}")
39
- logger.error(f"Error al configurar Gemini API: {e}")
40
  st.stop()
41
  @st.cache_resource
42
  def get_gemini_model():
43
- logger.info("🔄 Cargando modelo Gemini (gemini-1.5-flash)...")
44
  try:
45
  return genai.GenerativeModel("gemini-1.5-flash")
46
  except Exception as e:
47
  st.error(f"❌ No se pudo cargar el modelo Gemini: {e}")
48
- logger.error(f"Error cargando modelo Gemini: {e}")
49
  return None
50
  model = get_gemini_model()
51
  @st.cache_data
@@ -60,21 +55,13 @@ def load_data():
60
  if os.path.exists(path_foodb_index):
61
  with open(path_foodb_index, 'r', encoding='utf-8') as f:
62
  foodb_index = json.load(f)
63
- logger.info(f"✅ Índice científico de FoodB cargado con {len(foodb_index)} alimentos.")
64
- else:
65
- logger.warning("Advertencia: No se encontró 'foodb_index.json'. La funcionalidad de profundización científica estará desactivada.")
66
- logger.info(f"✅ Base de datos principal cargada con {len(data_alimentos)} registros.")
67
  return data_alimentos, lista_condiciones, foodb_index
68
- except FileNotFoundError as e:
69
- st.error(f"❌ Error: No se encontró el archivo '{e.filename}'. Asegúrate de que está en la carpeta 'DATOS'.")
70
- return None, None, None
71
- except json.JSONDecodeError as e:
72
- st.error(f"❌ Error: Un archivo JSON tiene un formato incorrecto: {e}")
73
  return None, None, None
74
  alimentos_data, lista_condiciones, foodb_index = load_data()
75
 
76
-
77
- # --- DICCIONARIOS DE MAPEADO EXPANDIDOS ---
78
  FOOD_TO_COMPOUND_MAP = {
79
  "pan": ["gluten"], "trigo": ["gluten"], "harina": ["gluten"], "cebada": ["gluten"], "centeno": ["gluten"], "pasta": ["gluten"], "galletas": ["gluten"], "avena": ["gluten"], "pizza": ["gluten"], "torta": ["gluten"], "pastel": ["gluten"], "cerveza": ["gluten", "histamina", "tiramina", "purinas"],
80
  "leche": ["lácteos", "caseína", "lactosa"], "queso": ["lácteos", "caseína", "lactosa", "histamina", "tiramina"], "yogur": ["lácteos", "caseína", "lactosa"], "mantequilla": ["lácteos", "caseína", "lactosa"], "crema": ["lácteos"], "helado": ["lácteos"],
@@ -82,7 +69,7 @@ FOOD_TO_COMPOUND_MAP = {
82
  "brócoli": ["salicilatos", "goitrógenos", "fodmaps"], "coliflor": ["goitrógenos", "fodmaps"], "repollo": ["goitrógenos", "fodmaps"], "col": ["goitrógenos", "fodmaps"], "cúrcuma": ["salicilatos"],
83
  "azucar": ["azúcar", "fructosa"], "dulces": ["azúcar"], "refrescos": ["azúcar", "fructosa"], "gaseosas": ["azúcar", "fructosa"], "miel": ["fructosa", "fodmaps"], "jarabe de maiz": ["fructosa"],
84
  "vino tinto": ["histamina", "tiramina", "sulfitos"], "vino rojo": ["histamina", "tiramina", "sulfitos"], "vino": ["histamina", "tiramina", "sulfitos"], "chocolate": ["cafeína", "tiramina", "níquel", "arginina", "oxalatos"],
85
- "embutidos": ["histamina", "tiramina", "nitritos"], "pescado enlatado": ["histamina"], "atún en lata": ["histamina"], "sardinas": ["histamina", "purinas"], "tomate": ["histamina", "solaninas", "ácidos", "lectinas"],
86
  "aguacate": ["fodmaps", "polioles", "histamina"], "cebolla": ["fodmaps", "fructanos"], "ajo": ["fodmaps", "fructanos"], "legumbres": ["fodmaps", "gos", "lectinas", "fitatos"], "lentejas": ["fodmaps", "gos", "lectinas"], "garbanzos": ["fodmaps", "gos", "lectinas"], "frijoles": ["fodmaps", "gos", "lectinas"],
87
  "carne": ["alfa-gal", "purinas", "hierro", "histamina"], "carnes rojas": ["purinas", "alfa-gal", "hierro"], "hígado": ["purinas", "hierro", "vitamina a"],
88
  "mariscos": ["purinas", "sulfitos", "alérgenos", "yodo", "níquel"], "huevo": ["alérgenos"], "soya": ["alérgenos", "fitatos", "goitrógenos"], "soja": ["alérgenos", "fitatos", "goitrógenos"],
@@ -94,8 +81,6 @@ CONDITION_SYNONYMS = {
94
  "gota / hiperuricemia.": ["acido urico aumentado"], "intolerancia a la lactosa.": ["déficit de lactasa"],
95
  "enfermedad celíaca (clásica).": ["dermatitis herpetiforme"], "migraña.": ["dolor de cabeza", "cefalea"]
96
  }
97
-
98
- # --- DICCIONARIO TRADUCTOR CORREGIDO Y EXPANDIDO ---
99
  FOOD_NAME_TO_FOODB_KEY = {
100
  "pan": ["bread"], "pasta": ["pasta"], "galleta": ["cookie"], "pizza": ["pizza"], "cebada": ["barley"], "centeno": ["rye"],
101
  "leche": ["milk"], "queso": ["cheese"], "huevo": ["egg"], "carne": ["beef", "pork", "lamb", "meat"], "ternera": ["beef"], "cerdo": ["pork"], "cordero": ["lamb"], "pollo": ["chicken"],
@@ -106,7 +91,8 @@ FOOD_NAME_TO_FOODB_KEY = {
106
  "pescado": ["fish"], "atún": ["tuna"], "salmón": ["salmon"], "marisco": ["shellfish"], "camarón": ["shrimp"], "sardina": ["sardine"]
107
  }
108
 
109
- # --- LÓGICA DE BÚSQUEDA Y ANÁLISIS (SIN CAMBIOS) ---
 
110
  def sanitize_text(text):
111
  if not text: return ""
112
  return re.sub(r'[.,;()]', '', text).lower().strip()
@@ -158,7 +144,6 @@ def find_best_matches_hybrid(entities, data):
158
  filtered_data.append(entry)
159
  if filtered_data:
160
  target_data = filtered_data
161
- logger.info(f"Modo Gatekeeper activado. Buscando solo entre {len(target_data)} condiciones.")
162
  results = []
163
  for entry in target_data:
164
  score_details = {'condition': 0, 'food': 0, 'symptoms': 0, 'total': 0}
@@ -186,7 +171,7 @@ def find_best_matches_hybrid(entities, data):
186
  def generate_detailed_analysis(query, match):
187
  if not model: return "Error: El modelo de IA no está disponible."
188
  prompt_parts = [
189
- "Eres un asistente de IA experto en nutrición personalizada. Tu tono es empático, claro y muy educativo. NO actúas como un médico, sino como un guía informativo.",
190
  f'El usuario ha descrito el siguiente caso: "{query}"',
191
  f'Tu sistema ha encontrado la siguiente posible conexión:',
192
  f'- Condición: {match.get("condicion_asociada")}',
@@ -222,6 +207,7 @@ def generate_detailed_analysis(query, match):
222
  logger.error(f"Error generando análisis detallado con Gemini: {e}")
223
  return "No se pudo generar el análisis detallado."
224
  def create_relevance_chart(results):
 
225
  top_results = results[:5]
226
  condition_names = [re.sub(r'\(.*\)', '', res['entry']['condicion_asociada']).strip() for res in top_results]
227
  chart_data = {"Condición": condition_names, "Relevancia": [res['score']['total'] for res in top_results]}
@@ -233,11 +219,43 @@ def create_relevance_chart(results):
233
  ).properties(title='Principales Coincidencias según tu Caso').configure_axis(labelFontSize=12, titleFontSize=14).configure_title(fontSize=16, anchor='start')
234
  return chart
235
  def log_feedback(query, result, feedback):
 
236
  log_entry = {"timestamp": datetime.now().isoformat(), "query": query, "best_match_condition": result['entry']['condicion_asociada'], "score": result['score']['total'], "feedback": feedback}
237
  os.makedirs("logs", exist_ok=True)
238
  with open(os.path.join("logs", "feedback_log.txt"), "a", encoding="utf-8") as f:
239
  f.write(json.dumps(log_entry, ensure_ascii=False) + "\n")
240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  # --- INTERFAZ DE USUARIO Y LÓGICA PRINCIPAL ---
242
  col_img, col_text = st.columns([1, 4], gap="medium")
243
  with col_img:
@@ -248,10 +266,12 @@ with col_text:
248
  st.markdown("##### Describe lo que sientes y lo que comiste para descubrir posibles intolerancias.")
249
  st.markdown("---")
250
 
 
251
  if 'search_results' not in st.session_state: st.session_state.search_results = None
252
  if 'user_query' not in st.session_state: st.session_state.user_query = ""
253
  if 'entities' not in st.session_state: st.session_state.entities = None
254
  if 'analysis_cache' not in st.session_state: st.session_state.analysis_cache = {}
 
255
 
256
  def clear_search_state():
257
  st.session_state.search_results = None
@@ -259,18 +279,36 @@ def clear_search_state():
259
  st.session_state.entities = None
260
  st.session_state.analysis_cache = {}
261
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  with st.form(key="search_form"):
263
- query = st.text_area("Describe tu caso aquí:", height=150, placeholder="Ej: Cuando como mucha carne me duele el dedo gordo del pie...")
264
  submitted = st.form_submit_button("Analizar mi caso", type="primary")
265
 
266
  if submitted:
267
  clear_search_state()
 
268
  if not query:
269
  st.warning("Por favor, describe lo que sientes y lo que comiste.")
270
  elif alimentos_data is None:
271
  st.error("La base de datos de alimentos no está disponible.")
272
  else:
273
- st.session_state.user_query = query
274
  with st.spinner("🧠 Interpretando tu caso y buscando pistas con IA..."):
275
  entities = extract_and_infer_with_gemini(query, lista_condiciones)
276
  st.session_state.entities = entities
@@ -286,15 +324,24 @@ if submitted:
286
  st.session_state.search_results = []
287
 
288
  if st.session_state.search_results is not None:
289
- st.button("Realizar nueva consulta", on_click=clear_search_state, type="primary")
290
- st.markdown("---")
291
-
292
  results = st.session_state.search_results
293
 
294
  if not results:
295
  st.warning(f"No se encontraron coincidencias claras para tu caso: '{st.session_state.user_query}'.")
296
  else:
297
- st.success(f"Hemos encontrado {len(results)} posible(s) causa(s) relacionada(s) con tu caso.")
 
 
 
 
 
 
 
 
 
 
 
 
298
  st.subheader("Análisis de Relevancia de las Coincidencias")
299
  chart = create_relevance_chart(results)
300
  st.altair_chart(chart, use_container_width=True)
@@ -302,6 +349,7 @@ if st.session_state.search_results is not None:
302
  best_match_data = results[0]
303
  best_match = best_match_data['entry']
304
  with st.expander(f"**Análisis Detallado de la Principal Coincidencia: {best_match.get('condicion_asociada')}**", expanded=True):
 
305
  col1, col2 = st.columns([3, 1])
306
  with col1:
307
  st.markdown("##### Desglose de la Puntuación de Relevancia:")
@@ -344,14 +392,20 @@ if st.session_state.search_results is not None:
344
  st.session_state.analysis_cache['best_match_analysis'] = generate_detailed_analysis(st.session_state.user_query, best_match)
345
  st.markdown(st.session_state.analysis_cache['best_match_analysis'])
346
  st.markdown("---")
347
- st.write("**¿Te fue útil este análisis?**")
348
- feedback_cols = st.columns(8)
349
- if feedback_cols[0].button("👍 Útil", key=f"util_{best_match['condicion_asociada']}"):
350
- log_feedback(st.session_state.user_query, best_match_data, "util")
351
- st.success("¡Gracias por tu feedback!")
352
- if feedback_cols[1].button("👎 No útil", key=f"no_util_{best_match['condicion_asociada']}"):
353
- log_feedback(st.session_state.user_query, best_match_data, "no_util")
354
- st.warning("Gracias. Usaremos tu feedback para mejorar.")
 
 
 
 
 
 
355
 
356
  if len(results) > 1:
357
  with st.expander("**Otras Posibilidades Relevantes (Diagnóstico Diferencial)**"):
@@ -372,5 +426,4 @@ if st.session_state.search_results is not None:
372
  with st.spinner("Generando análisis..."):
373
  st.session_state.analysis_cache[analysis_key] = generate_detailed_analysis(st.session_state.user_query, entry)
374
  if analysis_key in st.session_state.analysis_cache:
375
- st.info(st.session_state.analysis_cache[analysis_key])
376
-
 
1
+ # ==================== El Detective de Alimentos (Versión 10.0 - Producción) =====================================
2
+ # Mejoras: Generador de informes, sugerencias de consulta y refinamientos finales de UI.
3
 
4
  import streamlit as st
5
  import google.generativeai as genai
 
11
  import pandas as pd
12
  import altair as alt
13
  from datetime import datetime
14
+ import time
15
 
16
  st.set_page_config(
17
  page_title="El Detective de Alimentos",
 
19
  layout="wide"
20
  )
21
 
22
+ # --- CONFIGURACIÓN Y CARGA DE DATOS ---
23
+ # (Esta sección no tiene cambios)
24
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
25
  logger = logging.getLogger("food_detective_app")
 
26
  try:
27
  if 'GEMINI_API_KEY' in st.secrets:
28
  GEMINI_API_KEY = st.secrets['GEMINI_API_KEY']
29
  else:
30
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
31
  if not GEMINI_API_KEY:
32
+ st.error("No se encontró la GEMINI_API_KEY.")
 
33
  st.stop()
34
  genai.configure(api_key=GEMINI_API_KEY)
 
35
  except Exception as e:
36
  st.error(f"❌ Error al configurar Gemini API: {e}")
 
37
  st.stop()
38
  @st.cache_resource
39
  def get_gemini_model():
 
40
  try:
41
  return genai.GenerativeModel("gemini-1.5-flash")
42
  except Exception as e:
43
  st.error(f"❌ No se pudo cargar el modelo Gemini: {e}")
 
44
  return None
45
  model = get_gemini_model()
46
  @st.cache_data
 
55
  if os.path.exists(path_foodb_index):
56
  with open(path_foodb_index, 'r', encoding='utf-8') as f:
57
  foodb_index = json.load(f)
 
 
 
 
58
  return data_alimentos, lista_condiciones, foodb_index
59
+ except Exception as e:
60
+ st.error(f"Error cargando los archivos de datos: {e}")
 
 
 
61
  return None, None, None
62
  alimentos_data, lista_condiciones, foodb_index = load_data()
63
 
64
+ # --- DICCIONARIOS DE MAPEADO (EXPANDIDOS Y CORREGIDOS) ---
 
65
  FOOD_TO_COMPOUND_MAP = {
66
  "pan": ["gluten"], "trigo": ["gluten"], "harina": ["gluten"], "cebada": ["gluten"], "centeno": ["gluten"], "pasta": ["gluten"], "galletas": ["gluten"], "avena": ["gluten"], "pizza": ["gluten"], "torta": ["gluten"], "pastel": ["gluten"], "cerveza": ["gluten", "histamina", "tiramina", "purinas"],
67
  "leche": ["lácteos", "caseína", "lactosa"], "queso": ["lácteos", "caseína", "lactosa", "histamina", "tiramina"], "yogur": ["lácteos", "caseína", "lactosa"], "mantequilla": ["lácteos", "caseína", "lactosa"], "crema": ["lácteos"], "helado": ["lácteos"],
 
69
  "brócoli": ["salicilatos", "goitrógenos", "fodmaps"], "coliflor": ["goitrógenos", "fodmaps"], "repollo": ["goitrógenos", "fodmaps"], "col": ["goitrógenos", "fodmaps"], "cúrcuma": ["salicilatos"],
70
  "azucar": ["azúcar", "fructosa"], "dulces": ["azúcar"], "refrescos": ["azúcar", "fructosa"], "gaseosas": ["azúcar", "fructosa"], "miel": ["fructosa", "fodmaps"], "jarabe de maiz": ["fructosa"],
71
  "vino tinto": ["histamina", "tiramina", "sulfitos"], "vino rojo": ["histamina", "tiramina", "sulfitos"], "vino": ["histamina", "tiramina", "sulfitos"], "chocolate": ["cafeína", "tiramina", "níquel", "arginina", "oxalatos"],
72
+ "embutidos": ["histamina", "tiramina", "nitritos"], "pescado enlatado": ["histamina"], "atún en lata": ["histamina"], "sardinas": ["histamina", "purinas"], "tomate": ["histamina", "solaninas", "lectinas"],
73
  "aguacate": ["fodmaps", "polioles", "histamina"], "cebolla": ["fodmaps", "fructanos"], "ajo": ["fodmaps", "fructanos"], "legumbres": ["fodmaps", "gos", "lectinas", "fitatos"], "lentejas": ["fodmaps", "gos", "lectinas"], "garbanzos": ["fodmaps", "gos", "lectinas"], "frijoles": ["fodmaps", "gos", "lectinas"],
74
  "carne": ["alfa-gal", "purinas", "hierro", "histamina"], "carnes rojas": ["purinas", "alfa-gal", "hierro"], "hígado": ["purinas", "hierro", "vitamina a"],
75
  "mariscos": ["purinas", "sulfitos", "alérgenos", "yodo", "níquel"], "huevo": ["alérgenos"], "soya": ["alérgenos", "fitatos", "goitrógenos"], "soja": ["alérgenos", "fitatos", "goitrógenos"],
 
81
  "gota / hiperuricemia.": ["acido urico aumentado"], "intolerancia a la lactosa.": ["déficit de lactasa"],
82
  "enfermedad celíaca (clásica).": ["dermatitis herpetiforme"], "migraña.": ["dolor de cabeza", "cefalea"]
83
  }
 
 
84
  FOOD_NAME_TO_FOODB_KEY = {
85
  "pan": ["bread"], "pasta": ["pasta"], "galleta": ["cookie"], "pizza": ["pizza"], "cebada": ["barley"], "centeno": ["rye"],
86
  "leche": ["milk"], "queso": ["cheese"], "huevo": ["egg"], "carne": ["beef", "pork", "lamb", "meat"], "ternera": ["beef"], "cerdo": ["pork"], "cordero": ["lamb"], "pollo": ["chicken"],
 
91
  "pescado": ["fish"], "atún": ["tuna"], "salmón": ["salmon"], "marisco": ["shellfish"], "camarón": ["shrimp"], "sardina": ["sardine"]
92
  }
93
 
94
+ # --- LÓGICA DE BÚSQUEDA Y ANÁLISIS ---
95
+ # (Todas las funciones auxiliares se mantienen igual que en la versión anterior)
96
  def sanitize_text(text):
97
  if not text: return ""
98
  return re.sub(r'[.,;()]', '', text).lower().strip()
 
144
  filtered_data.append(entry)
145
  if filtered_data:
146
  target_data = filtered_data
 
147
  results = []
148
  for entry in target_data:
149
  score_details = {'condition': 0, 'food': 0, 'symptoms': 0, 'total': 0}
 
171
  def generate_detailed_analysis(query, match):
172
  if not model: return "Error: El modelo de IA no está disponible."
173
  prompt_parts = [
174
+ "Eres un asistente de IA experto en nutrición personalizada...", # (Prompt sin cambios)
175
  f'El usuario ha descrito el siguiente caso: "{query}"',
176
  f'Tu sistema ha encontrado la siguiente posible conexión:',
177
  f'- Condición: {match.get("condicion_asociada")}',
 
207
  logger.error(f"Error generando análisis detallado con Gemini: {e}")
208
  return "No se pudo generar el análisis detallado."
209
  def create_relevance_chart(results):
210
+ # ... (Sin cambios)
211
  top_results = results[:5]
212
  condition_names = [re.sub(r'\(.*\)', '', res['entry']['condicion_asociada']).strip() for res in top_results]
213
  chart_data = {"Condición": condition_names, "Relevancia": [res['score']['total'] for res in top_results]}
 
219
  ).properties(title='Principales Coincidencias según tu Caso').configure_axis(labelFontSize=12, titleFontSize=14).configure_title(fontSize=16, anchor='start')
220
  return chart
221
  def log_feedback(query, result, feedback):
222
+ # ... (Sin cambios)
223
  log_entry = {"timestamp": datetime.now().isoformat(), "query": query, "best_match_condition": result['entry']['condicion_asociada'], "score": result['score']['total'], "feedback": feedback}
224
  os.makedirs("logs", exist_ok=True)
225
  with open(os.path.join("logs", "feedback_log.txt"), "a", encoding="utf-8") as f:
226
  f.write(json.dumps(log_entry, ensure_ascii=False) + "\n")
227
 
228
+ # --- NUEVA FUNCIÓN PARA GENERAR EL INFORME ---
229
+ def generate_report_text(query, results):
230
+ report_lines = []
231
+ report_lines.append("="*50)
232
+ report_lines.append("INFORME DEL DETECTIVE DE ALIMENTOS")
233
+ report_lines.append("="*50)
234
+ report_lines.append(f"Fecha: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
235
+ report_lines.append(f"CONSULTA ORIGINAL DEL USUARIO:\n'{query}'\n")
236
+ report_lines.append("-"*50)
237
+
238
+ # Análisis del resultado principal
239
+ if results:
240
+ best_match = results[0]['entry']
241
+ report_lines.append("PRINCIPAL COINCIDENCIA ENCONTRADA:\n")
242
+ report_lines.append(f"Condición: {best_match.get('condicion_asociada', 'N/A')}")
243
+ report_lines.append(f"Mecanismo Posible: {best_match.get('mecanismo_fisiologico', 'N/A')}")
244
+ report_lines.append(f"Recomendaciones Generales: {best_match.get('recomendaciones_examenes', 'N/A')}\n")
245
+
246
+ # Diagnóstico diferencial
247
+ if len(results) > 1:
248
+ report_lines.append("-"*50)
249
+ report_lines.append("OTRAS POSIBILIDADES CONSIDERADAS (DIAGNÓSTICO DIFERENCIAL):\n")
250
+ for i, res in enumerate(results[1:4]):
251
+ entry = res['entry']
252
+ report_lines.append(f"{i+2}. {entry.get('condicion_asociada', 'N/A')} (Puntuación: {res['score']['total']})")
253
+
254
+ report_lines.append("\n" + "="*50)
255
+ report_lines.append("IMPORTANTE: Este informe es generado por una herramienta de IA y no constituye un diagnóstico médico. Su propósito es facilitar la conversación con un profesional de la salud cualificado.")
256
+
257
+ return "\n".join(report_lines)
258
+
259
  # --- INTERFAZ DE USUARIO Y LÓGICA PRINCIPAL ---
260
  col_img, col_text = st.columns([1, 4], gap="medium")
261
  with col_img:
 
266
  st.markdown("##### Describe lo que sientes y lo que comiste para descubrir posibles intolerancias.")
267
  st.markdown("---")
268
 
269
+ # MANEJO DE ESTADO
270
  if 'search_results' not in st.session_state: st.session_state.search_results = None
271
  if 'user_query' not in st.session_state: st.session_state.user_query = ""
272
  if 'entities' not in st.session_state: st.session_state.entities = None
273
  if 'analysis_cache' not in st.session_state: st.session_state.analysis_cache = {}
274
+ if 'query' not in st.session_state: st.session_state.query = ""
275
 
276
  def clear_search_state():
277
  st.session_state.search_results = None
 
279
  st.session_state.entities = None
280
  st.session_state.analysis_cache = {}
281
 
282
+ def set_query_from_example(example_text):
283
+ st.session_state.query = example_text
284
+
285
+ # NUEVA SECCIÓN: EJEMPLOS DE CONSULTA
286
+ st.write("**¿No sabes por dónde empezar? Prueba con un ejemplo:**")
287
+ example_cols = st.columns(3)
288
+ example_queries = [
289
+ "Cuando como mucha carne me duele el dedo gordo del pie.",
290
+ "Después de tomar leche, tengo muchos gases e hinchazón.",
291
+ "El vino tinto siempre me da dolor de cabeza."
292
+ ]
293
+ if example_cols[0].button(example_queries[0]):
294
+ set_query_from_example(example_queries[0])
295
+ if example_cols[1].button(example_queries[1]):
296
+ set_query_from_example(example_queries[1])
297
+ if example_cols[2].button(example_queries[2]):
298
+ set_query_from_example(example_queries[2])
299
+
300
  with st.form(key="search_form"):
301
+ query = st.text_area("Describe tu caso aquí:", height=150, key="query")
302
  submitted = st.form_submit_button("Analizar mi caso", type="primary")
303
 
304
  if submitted:
305
  clear_search_state()
306
+ st.session_state.user_query = query
307
  if not query:
308
  st.warning("Por favor, describe lo que sientes y lo que comiste.")
309
  elif alimentos_data is None:
310
  st.error("La base de datos de alimentos no está disponible.")
311
  else:
 
312
  with st.spinner("🧠 Interpretando tu caso y buscando pistas con IA..."):
313
  entities = extract_and_infer_with_gemini(query, lista_condiciones)
314
  st.session_state.entities = entities
 
324
  st.session_state.search_results = []
325
 
326
  if st.session_state.search_results is not None:
 
 
 
327
  results = st.session_state.search_results
328
 
329
  if not results:
330
  st.warning(f"No se encontraron coincidencias claras para tu caso: '{st.session_state.user_query}'.")
331
  else:
332
+ # --- NUEVO BOTÓN DE DESCARGA ---
333
+ col1, col2 = st.columns([3,1])
334
+ with col1:
335
+ st.success(f"Hemos encontrado {len(results)} posible(s) causa(s) relacionada(s) con tu caso.")
336
+ with col2:
337
+ report_data = generate_report_text(st.session_state.user_query, results)
338
+ st.download_button(
339
+ label="📄 Descargar Informe para mi Médico",
340
+ data=report_data,
341
+ file_name=f"informe_detective_alimentos_{datetime.now().strftime('%Y%m%d')}.txt",
342
+ mime="text/plain"
343
+ )
344
+
345
  st.subheader("Análisis de Relevancia de las Coincidencias")
346
  chart = create_relevance_chart(results)
347
  st.altair_chart(chart, use_container_width=True)
 
349
  best_match_data = results[0]
350
  best_match = best_match_data['entry']
351
  with st.expander(f"**Análisis Detallado de la Principal Coincidencia: {best_match.get('condicion_asociada')}**", expanded=True):
352
+ # ... (código del popover y desglose de puntuación no cambia)
353
  col1, col2 = st.columns([3, 1])
354
  with col1:
355
  st.markdown("##### Desglose de la Puntuación de Relevancia:")
 
392
  st.session_state.analysis_cache['best_match_analysis'] = generate_detailed_analysis(st.session_state.user_query, best_match)
393
  st.markdown(st.session_state.analysis_cache['best_match_analysis'])
394
  st.markdown("---")
395
+ feedback_placeholder = st.empty()
396
+ with feedback_placeholder.container():
397
+ st.write("**¿Te fue útil este análisis?**")
398
+ feedback_cols = st.columns(8)
399
+ if feedback_cols[0].button("👍 Útil", key=f"util_{best_match['condicion_asociada']}"):
400
+ log_feedback(st.session_state.user_query, best_match_data, "util")
401
+ feedback_placeholder.success("¡Gracias por tu feedback!")
402
+ time.sleep(2)
403
+ feedback_placeholder.empty()
404
+ if feedback_cols[1].button("👎 No útil", key=f"no_util_{best_match['condicion_asociada']}"):
405
+ log_feedback(st.session_state.user_query, best_match_data, "no_util")
406
+ feedback_placeholder.warning("Gracias. Usaremos tu feedback para mejorar.")
407
+ time.sleep(2)
408
+ feedback_placeholder.empty()
409
 
410
  if len(results) > 1:
411
  with st.expander("**Otras Posibilidades Relevantes (Diagnóstico Diferencial)**"):
 
426
  with st.spinner("Generando análisis..."):
427
  st.session_state.analysis_cache[analysis_key] = generate_detailed_analysis(st.session_state.user_query, entry)
428
  if analysis_key in st.session_state.analysis_cache:
429
+ st.info(st.session_state.analysis_cache[analysis_key])