Spaces:
Runtime error
Runtime error
Upload 2 files
Browse files- client/pages/nutri.py +62 -8
- client/pages/user__course_list.py +70 -9
client/pages/nutri.py
CHANGED
|
@@ -17,13 +17,16 @@ from server.db.dbmanager import (
|
|
| 17 |
delete_conversation,
|
| 18 |
load_chatbot_suggestions,
|
| 19 |
save_chatbot_suggestions,
|
|
|
|
|
|
|
| 20 |
)
|
|
|
|
| 21 |
import logging
|
| 22 |
|
|
|
|
| 23 |
logging.basicConfig(level=logging.INFO, handlers=[logging.StreamHandler()])
|
| 24 |
logger = logging.getLogger(__name__)
|
| 25 |
|
| 26 |
-
|
| 27 |
# 🔹 Chargement des variables de session pour éviter les rechargements inutiles
|
| 28 |
if "id_conversation" not in st.session_state:
|
| 29 |
st.session_state.id_conversation = None
|
|
@@ -57,7 +60,7 @@ except Exception as e:
|
|
| 57 |
# 🔹 Chargement de la base de données
|
| 58 |
db_manager = st.session_state["db_manager"]
|
| 59 |
user_id = st.session_state["user_id"]
|
| 60 |
-
|
| 61 |
if "chatbot_suggestions" not in st.session_state:
|
| 62 |
st.session_state["chatbot_suggestions"] = load_chatbot_suggestions(
|
| 63 |
db_manager, user_id
|
|
@@ -242,8 +245,6 @@ if prompt := st.chat_input("Dîtes quelque-chose"):
|
|
| 242 |
|
| 243 |
time.sleep(0.03)
|
| 244 |
|
| 245 |
-
# 🔹 Vérifier si la réponse contient une suggestion de recette
|
| 246 |
-
|
| 247 |
# 🔹 Vérifier si la réponse contient des suggestions de recettes
|
| 248 |
keywords = ["recette", "plat", "préparer", "ingrédients"]
|
| 249 |
|
|
@@ -278,18 +279,55 @@ if prompt := st.chat_input("Dîtes quelque-chose"):
|
|
| 278 |
save_chatbot_suggestions(
|
| 279 |
db_manager, user_id, new_recipes
|
| 280 |
)
|
|
|
|
| 281 |
except Exception as e:
|
| 282 |
print(
|
| 283 |
f"❌ Erreur lors de l'extraction des suggestions : {e}"
|
| 284 |
)
|
| 285 |
|
| 286 |
break # On ne veut ajouter qu'une seule suggestion par réponse
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 287 |
|
| 288 |
-
|
| 289 |
-
# latency = round(end_time - start_time, 2) # 🔹 Calcul de la latence
|
| 290 |
|
| 291 |
-
|
| 292 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 293 |
except Exception as e:
|
| 294 |
if hasattr(e, "status_code") and e.status_code == 429:
|
| 295 |
retries += 1
|
|
@@ -368,3 +406,19 @@ if prompt := st.chat_input("Dîtes quelque-chose"):
|
|
| 368 |
"🤖 Entraînement du guardrail à reconnaître le prompt comme dangereux effectué avec succès"
|
| 369 |
)
|
| 370 |
st.stop()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
delete_conversation,
|
| 18 |
load_chatbot_suggestions,
|
| 19 |
save_chatbot_suggestions,
|
| 20 |
+
save_recipes_with_ingredients,
|
| 21 |
+
add_ingredients_column_if_not_exists
|
| 22 |
)
|
| 23 |
+
|
| 24 |
import logging
|
| 25 |
|
| 26 |
+
|
| 27 |
logging.basicConfig(level=logging.INFO, handlers=[logging.StreamHandler()])
|
| 28 |
logger = logging.getLogger(__name__)
|
| 29 |
|
|
|
|
| 30 |
# 🔹 Chargement des variables de session pour éviter les rechargements inutiles
|
| 31 |
if "id_conversation" not in st.session_state:
|
| 32 |
st.session_state.id_conversation = None
|
|
|
|
| 60 |
# 🔹 Chargement de la base de données
|
| 61 |
db_manager = st.session_state["db_manager"]
|
| 62 |
user_id = st.session_state["user_id"]
|
| 63 |
+
add_ingredients_column_if_not_exists(db_manager)
|
| 64 |
if "chatbot_suggestions" not in st.session_state:
|
| 65 |
st.session_state["chatbot_suggestions"] = load_chatbot_suggestions(
|
| 66 |
db_manager, user_id
|
|
|
|
| 245 |
|
| 246 |
time.sleep(0.03)
|
| 247 |
|
|
|
|
|
|
|
| 248 |
# 🔹 Vérifier si la réponse contient des suggestions de recettes
|
| 249 |
keywords = ["recette", "plat", "préparer", "ingrédients"]
|
| 250 |
|
|
|
|
| 279 |
save_chatbot_suggestions(
|
| 280 |
db_manager, user_id, new_recipes
|
| 281 |
)
|
| 282 |
+
|
| 283 |
except Exception as e:
|
| 284 |
print(
|
| 285 |
f"❌ Erreur lors de l'extraction des suggestions : {e}"
|
| 286 |
)
|
| 287 |
|
| 288 |
break # On ne veut ajouter qu'une seule suggestion par réponse
|
| 289 |
+
time.sleep(1)
|
| 290 |
+
# 🔹 EXTRACTION ET ENREGISTREMENT DES RECETTES
|
| 291 |
+
# 🔍 Vérifier si la réponse contient des suggestions de recettes
|
| 292 |
+
# 🔹 EXTRACTION ET ENREGISTREMENT DES RECETTES
|
| 293 |
+
# 🔍 Vérifier si la réponse contient des suggestions de recettes
|
| 294 |
+
for word in keywords:
|
| 295 |
+
if word in response.lower():
|
| 296 |
+
try:
|
| 297 |
+
print("🔍 Détection de recettes, extraction en cours...")
|
| 298 |
+
|
| 299 |
+
# 🔹 Utiliser `extract_recipes_and_ingredients` pour extraire plusieurs recettes
|
| 300 |
+
extracted_recipes = mistral.extract_recipes_and_ingredients(response)
|
| 301 |
+
|
| 302 |
+
if extracted_recipes:
|
| 303 |
+
print(f"✅ {len(extracted_recipes)} recettes détectées : {extracted_recipes}")
|
| 304 |
+
|
| 305 |
+
# 🔹 Sauvegarder les recettes et leurs ingrédients dans la base de données
|
| 306 |
+
for recipe in extracted_recipes:
|
| 307 |
+
title = recipe["titre"].lstrip("-").strip() # Supprime les tirets et espaces inutiles
|
| 308 |
+
ingredients = recipe["ingredients"].strip()
|
| 309 |
+
|
| 310 |
+
# 🔹 Supprimer une virgule finale si présente dans le titre
|
| 311 |
+
title_cleaned = title.rstrip(",") # Enlève uniquement la virgule à la fin
|
| 312 |
|
| 313 |
+
print(f"💾 Enregistrement de la recette '{title_cleaned}' avec les ingrédients : {ingredients}")
|
|
|
|
| 314 |
|
| 315 |
+
save_recipes_with_ingredients(db_manager, user_id, title_cleaned, ingredients)
|
| 316 |
+
|
| 317 |
+
else:
|
| 318 |
+
print("⚠️ Aucune recette détectée.")
|
| 319 |
+
|
| 320 |
+
except Exception as e:
|
| 321 |
+
print(f"❌ Erreur lors de l'extraction des recettes et ingrédients : {e}")
|
| 322 |
+
|
| 323 |
+
break # Éviter d'ajouter plusieurs fois la même recette
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
end_time = time.time() # 🔹 Fin du chronomètre
|
| 327 |
+
latency = round(end_time - start_time, 2) # 🔹 Calcul de la latence
|
| 328 |
+
|
| 329 |
+
print(f"✅ Réponse générée en {latency} secondes.")
|
| 330 |
+
print(f"✅ Nombre de tokens de sortie : {output_tokens}")
|
| 331 |
except Exception as e:
|
| 332 |
if hasattr(e, "status_code") and e.status_code == 429:
|
| 333 |
retries += 1
|
|
|
|
| 406 |
"🤖 Entraînement du guardrail à reconnaître le prompt comme dangereux effectué avec succès"
|
| 407 |
)
|
| 408 |
st.stop()
|
| 409 |
+
# def check_if_ingredients_saved(db_manager, user_id):
|
| 410 |
+
# query = "SELECT repas_suggestion, ingredients FROM suggestions_repas WHERE id_utilisateur = ?"
|
| 411 |
+
# recipes = db_manager.execute_safe(query, (user_id,), fetch=True)
|
| 412 |
+
|
| 413 |
+
# if recipes:
|
| 414 |
+
# print("✅ Recettes enregistrées en base :")
|
| 415 |
+
# for recipe in recipes:
|
| 416 |
+
# recette_nom = recipe[0]
|
| 417 |
+
# ingredients = recipe[1] if recipe[1] else "⚠️ Aucun ingrédient enregistré"
|
| 418 |
+
# print(f"📌 {recette_nom} - Ingrédients : {ingredients}")
|
| 419 |
+
# else:
|
| 420 |
+
# print("⚠️ Aucune recette enregistrée.")
|
| 421 |
+
|
| 422 |
+
# # Appel pour tester
|
| 423 |
+
# check_if_ingredients_saved(db_manager, user_id)
|
| 424 |
+
|
client/pages/user__course_list.py
CHANGED
|
@@ -1,17 +1,78 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
-
|
|
|
|
|
|
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
-
# Page des courses
|
| 6 |
def course_list():
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
-
if
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
else:
|
| 15 |
-
st.
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import unicodedata
|
| 4 |
+
from server.db.dbmanager import get_db_manager
|
| 5 |
|
| 6 |
+
def normalize_text(text):
|
| 7 |
+
"""Normalise un texte en supprimant les accents et en le mettant en minuscules"""
|
| 8 |
+
text = unicodedata.normalize("NFKD", text).encode("ASCII", "ignore").decode("utf-8").strip().lower()
|
| 9 |
+
return text
|
| 10 |
|
|
|
|
| 11 |
def course_list():
|
| 12 |
+
# Récupérer l'ID utilisateur
|
| 13 |
+
user_id = st.session_state.get("user_id")
|
| 14 |
+
if not user_id:
|
| 15 |
+
st.warning("⚠️ Vous devez être connecté pour voir votre liste de courses.")
|
| 16 |
+
return
|
| 17 |
|
| 18 |
+
# Charger la base de données
|
| 19 |
+
db_manager = get_db_manager()
|
| 20 |
+
|
| 21 |
+
# Charger les recettes et leurs ingrédients depuis la base de données
|
| 22 |
+
query = "SELECT repas_suggestion, ingredients FROM suggestions_repas WHERE id_utilisateur = ?"
|
| 23 |
+
raw_recipes = db_manager.execute_safe(query, (user_id,), fetch=True)
|
| 24 |
|
| 25 |
+
if not raw_recipes:
|
| 26 |
+
st.warning("⚠️ Aucune recette enregistrée pour générer une liste de courses.")
|
| 27 |
+
return
|
| 28 |
+
|
| 29 |
+
# ✅ Normalisation et suppression des doublons
|
| 30 |
+
recipes = {}
|
| 31 |
+
formatted_titles = {}
|
| 32 |
+
|
| 33 |
+
for recipe in raw_recipes:
|
| 34 |
+
title, ingredients = recipe["repas_suggestion"], recipe["ingredients"]
|
| 35 |
+
normalized_title = normalize_text(title)
|
| 36 |
+
|
| 37 |
+
if normalized_title not in recipes or (not recipes[normalized_title] and ingredients):
|
| 38 |
+
recipes[normalized_title] = ingredients
|
| 39 |
+
formatted_titles[normalized_title] = title
|
| 40 |
+
|
| 41 |
+
recipe_titles = list(formatted_titles.values())
|
| 42 |
+
|
| 43 |
+
# Sélectionner des recettes
|
| 44 |
+
selected_recipes = st.multiselect("📌 Sélectionnez une ou plusieurs recettes", recipe_titles)
|
| 45 |
+
|
| 46 |
+
# Initialiser all_ingredients pour éviter UnboundLocalError
|
| 47 |
+
all_ingredients = []
|
| 48 |
+
|
| 49 |
+
# Récupérer les ingrédients des recettes sélectionnées
|
| 50 |
+
selected_ingredients = []
|
| 51 |
+
for recipe in selected_recipes:
|
| 52 |
+
normalized_recipe = normalize_text(recipe)
|
| 53 |
+
ingredients = recipes.get(normalized_recipe)
|
| 54 |
+
if ingredients:
|
| 55 |
+
selected_ingredients.extend(ingredients.split(", "))
|
| 56 |
+
|
| 57 |
+
# ✅ Option pour afficher la liste complète de courses
|
| 58 |
+
if st.checkbox("👀 Afficher la liste complète des courses"):
|
| 59 |
+
all_ingredients = list({ing for ing_list in recipes.values() if ing_list for ing in ing_list.split(", ")})
|
| 60 |
+
|
| 61 |
+
# Affichage des ingrédients récupérés
|
| 62 |
+
ingredients_to_display = selected_ingredients if selected_ingredients else all_ingredients
|
| 63 |
+
if ingredients_to_display:
|
| 64 |
+
st.subheader("📋 Ingrédients nécessaires")
|
| 65 |
+
for ingredient in ingredients_to_display:
|
| 66 |
+
st.write(f"🛒 {ingredient}")
|
| 67 |
else:
|
| 68 |
+
st.warning("⚠️ Aucun ingrédient enregistré.")
|
| 69 |
+
|
| 70 |
+
# 📥 Exporter la liste des courses
|
| 71 |
+
df = pd.DataFrame({"Ingrédients": ingredients_to_display})
|
| 72 |
|
| 73 |
+
if st.download_button(
|
| 74 |
+
label="📥 Télécharger la liste des courses",
|
| 75 |
+
data=df.to_csv(index=False, sep=";").encode("utf-8-sig"),
|
| 76 |
+
file_name="liste_courses.csv",
|
| 77 |
+
mime="text/csv"):
|
| 78 |
+
st.success("✅ Liste des courses exportée en CSV avec succès !")
|