JairoCesar commited on
Commit
5caf9bf
·
verified ·
1 Parent(s): 5d94c35

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -73
app.py CHANGED
@@ -697,27 +697,28 @@ def reinforce_entities_with_keywords(entities, query, food_map, master_symptom_m
697
  return entities
698
  def find_best_matches_hybrid(entities, data):
699
  """
700
- Motor de búsqueda semántico y ponderado final.
701
- Reintegra el conocimiento de FOOD_TO_COMPOUND_MAP en la búsqueda de alimentos.
 
702
  """
703
  if not entities or not data: return []
704
 
705
- # 1. PROCESAR INPUT DEL USUARIO
706
- user_symptoms_text = " ".join(entities.get("sintomas", []))
707
- user_symptom_keywords = set(re.findall(r'\b\w+\b', sanitize_text(user_symptoms_text)))
708
 
709
- user_foods_raw = entities.get("alimentos", [])
710
- user_food_keywords_base = set(re.findall(r'\b\w+\b', sanitize_text(" ".join(user_foods_raw))))
711
-
712
- # --- PASO CRUCIAL: REINTEGRAR EL CONOCIMIENTO EXPERTO ---
713
- user_food_keywords_expanded = set(user_food_keywords_base)
714
- for food in user_foods_raw:
 
715
  if food in FOOD_TO_COMPOUND_MAP:
716
- user_food_keywords_expanded.update(FOOD_TO_COMPOUND_MAP[food])
 
 
717
 
718
- # Usamos la lista expandida para la búsqueda
719
- user_food_keywords = user_food_keywords_expanded
720
-
721
  RARE_CONDITIONS = [
722
  "Porfiria Aguda Intermitente (PAI).", "Enfermedad de Refsum del Adulto.",
723
  "Ataxia por Gluten.", "Encefalopatía por Gluten.", "Enfermedad de Wilson.",
@@ -726,33 +727,23 @@ def find_best_matches_hybrid(entities, data):
726
 
727
  results = []
728
  for entry in data:
729
- score_details = {'food': 0, 'symptoms': 0, 'bonus': 0}
730
-
731
- # 2. PROCESAR INPUT DE LA BASE DE DATOS
732
  db_food_text = entry.get("compuesto_alimento", "")
733
- db_food_keywords = set(re.findall(r'\b\w+\b', sanitize_text(re.sub(r'\(.*?\)', '', db_food_text))))
734
-
735
  db_symptoms_text = " ".join(entry.get("sintomas_clave", []))
736
- db_symptom_keywords = set(re.findall(r'\b\w+\b', sanitize_text(db_symptoms_text)))
737
-
738
- # 3. COMPARACIÓN INTELIGENTE Y PUNTUACIÓN
739
 
740
- # Puntuación de Alimento con la lista expandida
741
- food_intersection = user_food_keywords.intersection(db_food_keywords)
742
- if food_intersection:
743
- score_details['food'] = 20
744
- # Bonus por alta confianza
745
- if len(food_intersection) / len(user_food_keywords.union(db_food_keywords)) > 0.1: # Umbral más bajo
746
- score_details['bonus'] = 30
747
 
748
- # Puntuación de Síntomas por intersección
749
- symptom_intersection = user_symptom_keywords.intersection(db_symptom_keywords)
750
- score_details['symptoms'] = len(symptom_intersection) * 30
751
 
752
- # Solo considerar resultados que tengan alguna coincidencia de alimento Y síntoma
753
- if score_details['food'] > 0 and score_details['symptoms'] > 0:
754
- base_score = score_details['food'] + score_details['symptoms'] + score_details['bonus']
 
 
755
 
 
756
  condition_name = entry.get("condicion_asociada", "")
757
  if condition_name in RARE_CONDITIONS:
758
  final_score = base_score * 0.4
@@ -761,11 +752,13 @@ def find_best_matches_hybrid(entities, data):
761
 
762
  score_details['total'] = int(final_score)
763
 
764
- results.append({
765
- 'entry': entry,
766
- 'score': score_details,
767
- 'matched_symptoms': list(symptom_intersection)
768
- })
 
 
769
 
770
  if not results: return []
771
  return sorted(results, key=lambda x: x['score']['total'], reverse=True)
@@ -1187,53 +1180,43 @@ if st.session_state.start_analysis:
1187
  st.error("No se pudieron identificar alimentos o síntomas claros en tu descripción. Intenta ser más específico.")
1188
  st.session_state.search_results = []
1189
 
1190
-
1191
  if st.session_state.search_results is not None:
1192
  results = st.session_state.search_results
1193
 
1194
  if not results:
1195
  st.warning(f"No se encontraron coincidencias claras para tu caso: '{st.session_state.user_query}'. Prueba a describir los síntomas de otra manera.")
1196
  else:
1197
- # --- PASO 1: DEFINIR EL DIAGNÓSTICO PRINCIPAL Y GENERAR TODO EL CONTENIDO ---
1198
  best_match_data = results[0]
1199
  best_match = best_match_data['entry']
1200
 
1201
- # Generar análisis de IA (la parte más lenta) y guardarlo en caché
1202
- if 'best_match_analysis' not in st.session_state.analysis_cache:
1203
- with st.spinner("✍️ Generando análisis personalizado con IA... (esto puede tardar un momento)"):
1204
  try:
1205
  analysis_text = generate_detailed_analysis(st.session_state.user_query, best_match)
1206
  st.session_state.analysis_cache['best_match_analysis'] = analysis_text
 
1207
  except Exception as e:
1208
- logger.error(f"Falló la generación del análisis detallado principal: {e}")
1209
- st.session_state.analysis_cache['best_match_analysis'] = "❌ Lo sentimos, no se pudo generar el análisis detallado en este momento debido a un problema con la IA."
1210
-
1211
- ai_analysis_text = st.session_state.analysis_cache['best_match_analysis']
1212
 
1213
- # Generar los otros componentes de texto para el informe
1214
- base_report_text = generate_report_text(st.session_state.user_query, results)
1215
  neuro_report_text = generate_neuro_report_text(st.session_state.entities, FOOD_TO_COMPOUND_MAP, INTEGRATED_NEURO_FOOD_MAP)
1216
  molecular_report_text = generate_molecular_report_text(best_match, st.session_state.entities, foodb_index, FOOD_NAME_TO_FOODB_KEY, COMPOUND_SYNONYM_MAP, KNOWN_TRIGGERS_MAP)
1217
 
1218
- # Unir todo en un solo string para el informe de Word
1219
- complete_report_string = f"{base_report_text}\n\n{ai_analysis_text}\n{neuro_report_text}\n{molecular_report_text}"
1220
-
1221
- # Generar el archivo de Word en memoria
1222
- word_file_buffer = generate_word_report(complete_report_string)
1223
-
1224
- # --- PASO 2: CONSTRUIR LA INTERFAZ DE USUARIO CON EL CONTENIDO YA GENERADO ---
1225
  col1, col2 = st.columns([3,1])
1226
  with col1:
1227
  st.success(f"Hemos encontrado {len(results)} posible(s) causa(s) relacionada(s) con tu caso.")
1228
  with col2:
 
 
 
1229
  if word_file_buffer:
1230
  st.download_button(
1231
  label="📄 Descargar Informe (Word)",
1232
  data=word_file_buffer,
1233
  file_name=f"Informe_Detective_Alimentos_{datetime.now().strftime('%Y%m%d')}.docx",
1234
  mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1235
- key="download_word_report",
1236
- help="Genera y descarga un informe completo en formato Word con todos los análisis."
1237
  )
1238
 
1239
  st.subheader("Análisis de Relevancia de las Coincidencias")
@@ -1244,11 +1227,10 @@ if st.session_state.search_results is not None:
1244
 
1245
  with col1_expander:
1246
  st.markdown("##### Desglose de la Puntuación de Relevancia:")
1247
- score_col1, score_col2, score_col3 = st.columns(3)
1248
- score_col1.metric("Puntos por Alimento(s)", f"{best_match_data['score']['food']}")
1249
- score_col2.metric("Puntos por Síntomas", f"{best_match_data['score']['symptoms']}")
1250
- score_col3.metric("PUNTUACIÓN TOTAL", f"{best_match_data['score']['total']}", delta="Máxima coincidencia")
1251
-
1252
  with col2_expander:
1253
  with st.popover("🔬 Componentes Moleculares"):
1254
  st.markdown(molecular_report_text.replace("=", ""))
@@ -1265,18 +1247,16 @@ if st.session_state.search_results is not None:
1265
  for i, result in enumerate(results[1:5]):
1266
  with st.container(border=True):
1267
  entry = result['entry']
1268
- score = result['score']
1269
-
1270
  st.subheader(f"{i+2}. {entry.get('condicion_asociada')}")
1271
  col_info, col_action = st.columns([3, 1])
1272
 
1273
  with col_info:
1274
- if result.get('matched_symptoms'):
1275
- st.markdown(f"**Pistas Clave (Síntomas Coincidentes):** {', '.join(result['matched_symptoms']).capitalize()}")
1276
  st.markdown(f"**Alimentos Típicos Asociados:** {entry.get('compuesto_alimento')}")
1277
 
1278
  with col_action:
1279
- st.metric("Relevancia", score['total'])
1280
  analysis_key = f"analysis_{i+2}"
1281
 
1282
  if st.button("Generar análisis", key=analysis_key, help=f"Generar análisis de IA para {entry.get('condicion_asociada')}"):
@@ -1285,10 +1265,10 @@ if st.session_state.search_results is not None:
1285
  analysis_text = generate_detailed_analysis(st.session_state.user_query, entry)
1286
  st.session_state.analysis_cache[analysis_key] = analysis_text
1287
  except Exception as e:
1288
- st.session_state.analysis_cache[analysis_key] = f"❌ Error al generar análisis para {entry.get('condicion_asociada')}."
1289
 
1290
  if analysis_key in st.session_state.analysis_cache:
1291
  st.info(st.session_state.analysis_cache[analysis_key])
1292
 
1293
  if i < len(results[1:5]) - 1:
1294
- st.markdown("---")
 
697
  return entities
698
  def find_best_matches_hybrid(entities, data):
699
  """
700
+ Motor de búsqueda semántico y holístico (Versión Final).
701
+ Crea una "nube de palabras clave" para el usuario y para cada entrada de la BD,
702
+ y puntúa basándose en el tamaño de su intersección.
703
  """
704
  if not entities or not data: return []
705
 
706
+ # --- 1. CREAR LA "NUBE DE PALABRAS CLAVE DEL USUARIO" ---
707
+ user_symptoms_list = entities.get("sintomas", [])
708
+ user_foods_list = entities.get("alimentos", [])
709
 
710
+ # Combinar síntomas y alimentos en un solo texto
711
+ user_text = " ".join(user_symptoms_list) + " " + " ".join(user_foods_list)
712
+ user_keywords_base = set(re.findall(r'\b\w+\b', sanitize_text(user_text)))
713
+
714
+ # Expandir con el conocimiento bioquímico de FOOD_TO_COMPOUND_MAP
715
+ user_keywords_expanded = set(user_keywords_base)
716
+ for food in user_foods_list:
717
  if food in FOOD_TO_COMPOUND_MAP:
718
+ user_keywords_expanded.update(FOOD_TO_COMPOUND_MAP[food])
719
+
720
+ user_keywords = user_keywords_expanded
721
 
 
 
 
722
  RARE_CONDITIONS = [
723
  "Porfiria Aguda Intermitente (PAI).", "Enfermedad de Refsum del Adulto.",
724
  "Ataxia por Gluten.", "Encefalopatía por Gluten.", "Enfermedad de Wilson.",
 
727
 
728
  results = []
729
  for entry in data:
730
+ # --- 2. CREAR LA "NUBE DE PALABRAS CLAVE DE LA BASE DE DATOS" ---
 
 
731
  db_food_text = entry.get("compuesto_alimento", "")
 
 
732
  db_symptoms_text = " ".join(entry.get("sintomas_clave", []))
 
 
 
733
 
734
+ db_text = db_food_text + " " + db_symptoms_text
735
+ db_keywords = set(re.findall(r'\b\w+\b', sanitize_text(db_text)))
 
 
 
 
 
736
 
737
+ # --- 3. PUNTUACIÓN POR INTERSECCIÓN ---
738
+ intersection = user_keywords.intersection(db_keywords)
 
739
 
740
+ # Solo procesar si hay al menos una palabra en común
741
+ if intersection:
742
+ score_details = {}
743
+ # La puntuación base es el número de palabras clave coincidentes al cuadrado para dar más peso a coincidencias múltiples
744
+ base_score = (len(intersection) ** 2) * 10
745
 
746
+ # Ponderación por rareza
747
  condition_name = entry.get("condicion_asociada", "")
748
  if condition_name in RARE_CONDITIONS:
749
  final_score = base_score * 0.4
 
752
 
753
  score_details['total'] = int(final_score)
754
 
755
+ # Añadir a resultados si supera un umbral mínimo para evitar ruido
756
+ if score_details['total'] > 10: # Umbral bajo para permitir coincidencias débiles pero relevantes
757
+ results.append({
758
+ 'entry': entry,
759
+ 'score': score_details,
760
+ 'matched_keywords': list(intersection) # Guardamos las palabras coincidentes
761
+ })
762
 
763
  if not results: return []
764
  return sorted(results, key=lambda x: x['score']['total'], reverse=True)
 
1180
  st.error("No se pudieron identificar alimentos o síntomas claros en tu descripción. Intenta ser más específico.")
1181
  st.session_state.search_results = []
1182
 
 
1183
  if st.session_state.search_results is not None:
1184
  results = st.session_state.search_results
1185
 
1186
  if not results:
1187
  st.warning(f"No se encontraron coincidencias claras para tu caso: '{st.session_state.user_query}'. Prueba a describir los síntomas de otra manera.")
1188
  else:
 
1189
  best_match_data = results[0]
1190
  best_match = best_match_data['entry']
1191
 
1192
+ ai_analysis_text = st.session_state.analysis_cache.get('best_match_analysis', "")
1193
+ if not ai_analysis_text:
1194
+ with st.spinner("✍️ Generando análisis personalizado con IA..."):
1195
  try:
1196
  analysis_text = generate_detailed_analysis(st.session_state.user_query, best_match)
1197
  st.session_state.analysis_cache['best_match_analysis'] = analysis_text
1198
+ ai_analysis_text = analysis_text
1199
  except Exception as e:
1200
+ st.session_state.analysis_cache['best_match_analysis'] = " No se pudo generar el análisis detallado."
1201
+ ai_analysis_text = st.session_state.analysis_cache['best_match_analysis']
 
 
1202
 
 
 
1203
  neuro_report_text = generate_neuro_report_text(st.session_state.entities, FOOD_TO_COMPOUND_MAP, INTEGRATED_NEURO_FOOD_MAP)
1204
  molecular_report_text = generate_molecular_report_text(best_match, st.session_state.entities, foodb_index, FOOD_NAME_TO_FOODB_KEY, COMPOUND_SYNONYM_MAP, KNOWN_TRIGGERS_MAP)
1205
 
 
 
 
 
 
 
 
1206
  col1, col2 = st.columns([3,1])
1207
  with col1:
1208
  st.success(f"Hemos encontrado {len(results)} posible(s) causa(s) relacionada(s) con tu caso.")
1209
  with col2:
1210
+ base_report_text = generate_report_text(st.session_state.user_query, results)
1211
+ complete_report_string = f"{base_report_text}\n\n{ai_analysis_text}\n{neuro_report_text}\n{molecular_report_text}"
1212
+ word_file_buffer = generate_word_report(complete_report_string)
1213
  if word_file_buffer:
1214
  st.download_button(
1215
  label="📄 Descargar Informe (Word)",
1216
  data=word_file_buffer,
1217
  file_name=f"Informe_Detective_Alimentos_{datetime.now().strftime('%Y%m%d')}.docx",
1218
  mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1219
+ key="download_word_report"
 
1220
  )
1221
 
1222
  st.subheader("Análisis de Relevancia de las Coincidencias")
 
1227
 
1228
  with col1_expander:
1229
  st.markdown("##### Desglose de la Puntuación de Relevancia:")
1230
+ st.metric("Puntuación de Relevancia Total", f"{best_match_data['score']['total']}", delta="Máxima coincidencia")
1231
+ if 'matched_keywords' in best_match_data and best_match_data['matched_keywords']:
1232
+ st.caption(f"Pistas Clave Coincidentes: {', '.join(best_match_data['matched_keywords'])}")
1233
+
 
1234
  with col2_expander:
1235
  with st.popover("🔬 Componentes Moleculares"):
1236
  st.markdown(molecular_report_text.replace("=", ""))
 
1247
  for i, result in enumerate(results[1:5]):
1248
  with st.container(border=True):
1249
  entry = result['entry']
 
 
1250
  st.subheader(f"{i+2}. {entry.get('condicion_asociada')}")
1251
  col_info, col_action = st.columns([3, 1])
1252
 
1253
  with col_info:
1254
+ if 'matched_keywords' in result and result['matched_keywords']:
1255
+ st.markdown(f"**Pistas Clave Coincidentes:** {', '.join(result['matched_keywords']).capitalize()}")
1256
  st.markdown(f"**Alimentos Típicos Asociados:** {entry.get('compuesto_alimento')}")
1257
 
1258
  with col_action:
1259
+ st.metric("Relevancia", result['score']['total'])
1260
  analysis_key = f"analysis_{i+2}"
1261
 
1262
  if st.button("Generar análisis", key=analysis_key, help=f"Generar análisis de IA para {entry.get('condicion_asociada')}"):
 
1265
  analysis_text = generate_detailed_analysis(st.session_state.user_query, entry)
1266
  st.session_state.analysis_cache[analysis_key] = analysis_text
1267
  except Exception as e:
1268
+ st.session_state.analysis_cache[analysis_key] = f"❌ Error al generar análisis."
1269
 
1270
  if analysis_key in st.session_state.analysis_cache:
1271
  st.info(st.session_state.analysis_cache[analysis_key])
1272
 
1273
  if i < len(results[1:5]) - 1:
1274
+ st.markdown("---")