Spaces:
Sleeping
Sleeping
Upload 4 files
Browse files- server/db/db.py +314 -33
- server/db/dbmanager.py +798 -221
- server/db/schema.json +3 -85
- server/db/schema_postgreSQL.json +3 -0
server/db/db.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1 |
import streamlit as st
|
2 |
import psycopg2
|
|
|
|
|
3 |
from datetime import datetime
|
4 |
import logging
|
5 |
from typing import List, Dict
|
@@ -10,62 +12,206 @@ logging.basicConfig(level=logging.INFO, handlers=[logging.StreamHandler()])
|
|
10 |
logger = logging.getLogger(__name__)
|
11 |
|
12 |
|
13 |
-
|
14 |
# Fonction pour obtenir la connexion à la base de données
|
15 |
-
def get_db_connection():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
try:
|
17 |
-
conn =
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
user=st.secrets["DB_USER"],
|
22 |
-
password=st.secrets["DB_PASSWORD"]
|
23 |
-
)
|
24 |
return conn
|
25 |
-
except
|
26 |
-
logger.error(f"Erreur de connexion à la base de données: {e}")
|
27 |
return None
|
28 |
|
|
|
29 |
# Connexion à la base de données pour récupérer le nombre total de recettes
|
30 |
-
def get_recipes_count():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
conn = get_db_connection()
|
|
|
|
|
32 |
if conn is None:
|
33 |
return 0
|
|
|
34 |
try:
|
|
|
35 |
cursor = conn.cursor()
|
|
|
|
|
36 |
cursor.execute("SELECT COUNT(*) FROM suggestions_repas")
|
|
|
|
|
37 |
result = cursor.fetchone()
|
|
|
|
|
38 |
return result[0] # Le nombre total de recettes
|
39 |
-
|
|
|
|
|
40 |
logger.error(f"Erreur lors de la récupération du nombre de recettes : {e}")
|
41 |
return 0
|
|
|
42 |
finally:
|
|
|
43 |
cursor.close()
|
44 |
conn.close()
|
45 |
|
|
|
46 |
# Fonction pour récupérer la latence moyenne des messages
|
47 |
-
def get_average_latency():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
conn = get_db_connection()
|
|
|
|
|
49 |
if conn is None:
|
50 |
return 0.0
|
|
|
51 |
try:
|
|
|
52 |
cursor = conn.cursor()
|
53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
result = cursor.fetchone()
|
|
|
|
|
55 |
return round(result[0], 2) if result[0] is not None else 0.0
|
56 |
-
|
|
|
|
|
57 |
logger.error(f"Erreur de connexion à la base de données pour la latence : {e}")
|
58 |
return 0.0
|
|
|
59 |
finally:
|
|
|
60 |
cursor.close()
|
61 |
conn.close()
|
62 |
|
|
|
63 |
# Fonction pour récupérer le nombre de requêtes par jour
|
64 |
-
def get_daily_requests():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
conn = get_db_connection()
|
|
|
|
|
66 |
if conn is None:
|
67 |
return pd.DataFrame()
|
|
|
68 |
try:
|
|
|
69 |
query = """
|
70 |
SELECT
|
71 |
DATE(timestamp) AS date,
|
@@ -77,61 +223,196 @@ def get_daily_requests():
|
|
77 |
ORDER BY
|
78 |
date;
|
79 |
"""
|
|
|
|
|
80 |
df = pd.read_sql(query, conn)
|
|
|
|
|
81 |
return df
|
82 |
-
|
|
|
|
|
83 |
logger.error(f"Erreur lors de la récupération des requêtes par jour : {e}")
|
84 |
return pd.DataFrame()
|
|
|
85 |
finally:
|
|
|
86 |
conn.close()
|
87 |
|
88 |
|
89 |
# Fonction pour récupérer les ingrédients depuis la base de données
|
90 |
-
def get_ingredients():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
conn = get_db_connection()
|
|
|
|
|
|
|
|
|
|
|
92 |
cursor = conn.cursor()
|
|
|
93 |
try:
|
|
|
94 |
cursor.execute("SELECT ingredients FROM liste_courses")
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
finally:
|
101 |
-
#
|
102 |
cursor.close()
|
103 |
conn.close()
|
104 |
|
|
|
105 |
# Fonction pour récupérer le coût total des requêtes
|
106 |
-
def get_total_cost():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
conn = get_db_connection()
|
|
|
|
|
108 |
if conn is None:
|
109 |
return 0.0
|
|
|
|
|
|
|
110 |
try:
|
111 |
-
|
112 |
-
cursor.execute(
|
|
|
|
|
|
|
|
|
113 |
result = cursor.fetchone()
|
|
|
|
|
114 |
return round(result[0], 2) if result[0] is not None else 0.0
|
115 |
-
|
|
|
|
|
116 |
logger.error(f"Erreur lors de la récupération du coût total : {e}")
|
117 |
return 0.0
|
|
|
118 |
finally:
|
|
|
119 |
cursor.close()
|
120 |
conn.close()
|
121 |
|
|
|
122 |
# Fonction pour récupérer l'impact écologique estimé
|
123 |
-
def get_total_impact():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
124 |
conn = get_db_connection()
|
|
|
|
|
125 |
if conn is None:
|
126 |
return 0.0
|
|
|
|
|
|
|
127 |
try:
|
128 |
-
|
129 |
-
cursor.execute(
|
|
|
|
|
|
|
|
|
130 |
result = cursor.fetchone()
|
|
|
|
|
131 |
return round(result[0], 2) if result[0] is not None else 0.0
|
132 |
-
|
|
|
|
|
133 |
logger.error(f"Erreur lors de la récupération de l'impact écologique : {e}")
|
134 |
return 0.0
|
|
|
135 |
finally:
|
|
|
136 |
cursor.close()
|
137 |
conn.close()
|
|
|
1 |
import streamlit as st
|
2 |
import psycopg2
|
3 |
+
import sqlite3
|
4 |
+
from sqlite3 import Connection
|
5 |
from datetime import datetime
|
6 |
import logging
|
7 |
from typing import List, Dict
|
|
|
12 |
logger = logging.getLogger(__name__)
|
13 |
|
14 |
|
|
|
15 |
# Fonction pour obtenir la connexion à la base de données
|
16 |
+
# def get_db_connection():
|
17 |
+
# try:
|
18 |
+
# conn = psycopg2.connect(
|
19 |
+
# host=st.secrets["DB_HOST"],
|
20 |
+
# port=st.secrets["DB_PORT"],
|
21 |
+
# dbname=st.secrets["DB_NAME"],
|
22 |
+
# user=st.secrets["DB_USER"],
|
23 |
+
# password=st.secrets["DB_PASSWORD"]
|
24 |
+
# )
|
25 |
+
# return conn
|
26 |
+
# except Exception as e:
|
27 |
+
# logger.error(f"Erreur de connexion à la base de données: {e}")
|
28 |
+
# return None
|
29 |
+
|
30 |
+
|
31 |
+
def get_db_connection() -> Connection:
|
32 |
+
"""
|
33 |
+
Établit une connexion avec la base SQLite.
|
34 |
+
|
35 |
+
Returns:
|
36 |
+
Connection : le client de connexion à la base.
|
37 |
+
"""
|
38 |
try:
|
39 |
+
conn = sqlite3.connect(
|
40 |
+
st.secrets["DB_NAME"], check_same_thread=False
|
41 |
+
) # Spécifiez ici le chemin de votre fichier SQLite
|
42 |
+
conn.row_factory = sqlite3.Row # Pour des résultats sous forme de dictionnaire
|
|
|
|
|
|
|
43 |
return conn
|
44 |
+
except sqlite3.Error as e:
|
45 |
+
logger.error(f"Erreur de connexion à la base de données SQLite: {e}")
|
46 |
return None
|
47 |
|
48 |
+
|
49 |
# Connexion à la base de données pour récupérer le nombre total de recettes
|
50 |
+
# def get_recipes_count():
|
51 |
+
# conn = get_db_connection()
|
52 |
+
# if conn is None:
|
53 |
+
# return 0
|
54 |
+
# try:
|
55 |
+
# cursor = conn.cursor()
|
56 |
+
# cursor.execute("SELECT COUNT(*) FROM suggestions_repas")
|
57 |
+
# result = cursor.fetchone()
|
58 |
+
# return result[0] # Le nombre total de recettes
|
59 |
+
# except Exception as e:
|
60 |
+
# logger.error(f"Erreur lors de la récupération du nombre de recettes : {e}")
|
61 |
+
# return 0
|
62 |
+
# finally:
|
63 |
+
# cursor.close()
|
64 |
+
# conn.close()
|
65 |
+
|
66 |
+
|
67 |
+
def get_recipes_count() -> int:
|
68 |
+
"""
|
69 |
+
Récupère le nombre total de recettes enregistrées dans la table `suggestions_repas` de la base de données SQLite.
|
70 |
+
|
71 |
+
Cette fonction se connecte à la base de données SQLite, exécute une requête pour compter le nombre d'entrées
|
72 |
+
dans la table `suggestions_repas`, puis retourne ce nombre.
|
73 |
+
|
74 |
+
Returns:
|
75 |
+
int: Le nombre total de recettes dans la table `suggestions_repas`. Retourne 0 en cas d'erreur ou si la connexion échoue.
|
76 |
+
"""
|
77 |
+
# Connexion à la base de données SQLite
|
78 |
conn = get_db_connection()
|
79 |
+
|
80 |
+
# Si la connexion échoue, on retourne 0
|
81 |
if conn is None:
|
82 |
return 0
|
83 |
+
|
84 |
try:
|
85 |
+
# Création du curseur pour exécuter la requête
|
86 |
cursor = conn.cursor()
|
87 |
+
|
88 |
+
# Exécution de la requête SQL pour compter les entrées de recettes dans la table 'suggestions_repas'
|
89 |
cursor.execute("SELECT COUNT(*) FROM suggestions_repas")
|
90 |
+
|
91 |
+
# Récupération du résultat de la requête
|
92 |
result = cursor.fetchone()
|
93 |
+
|
94 |
+
# Retour du nombre total de recettes
|
95 |
return result[0] # Le nombre total de recettes
|
96 |
+
|
97 |
+
except sqlite3.Error as e:
|
98 |
+
# En cas d'erreur, on enregistre l'erreur dans les logs et on retourne 0
|
99 |
logger.error(f"Erreur lors de la récupération du nombre de recettes : {e}")
|
100 |
return 0
|
101 |
+
|
102 |
finally:
|
103 |
+
# Fermeture du curseur et de la connexion
|
104 |
cursor.close()
|
105 |
conn.close()
|
106 |
|
107 |
+
|
108 |
# Fonction pour récupérer la latence moyenne des messages
|
109 |
+
# def get_average_latency():
|
110 |
+
# conn = get_db_connection()
|
111 |
+
# if conn is None:
|
112 |
+
# return 0.0
|
113 |
+
# try:
|
114 |
+
# cursor = conn.cursor()
|
115 |
+
# cursor.execute("SELECT AVG(temps_traitement) FROM messages WHERE temps_traitement IS NOT NULL")
|
116 |
+
# result = cursor.fetchone()
|
117 |
+
# return round(result[0], 2) if result[0] is not None else 0.0
|
118 |
+
# except Exception as e:
|
119 |
+
# logger.error(f"Erreur de connexion à la base de données pour la latence : {e}")
|
120 |
+
# return 0.0
|
121 |
+
# finally:
|
122 |
+
# cursor.close()
|
123 |
+
# conn.close()
|
124 |
+
|
125 |
+
|
126 |
+
def get_average_latency() -> float:
|
127 |
+
"""
|
128 |
+
Récupère la latence moyenne des traitements enregistrés dans la table `messages` de la base de données SQLite.
|
129 |
+
|
130 |
+
Cette fonction se connecte à la base de données SQLite, exécute une requête pour calculer la moyenne des valeurs
|
131 |
+
dans la colonne `temps_traitement` de la table `messages`, et retourne cette moyenne avec une précision de deux décimales.
|
132 |
+
|
133 |
+
Returns:
|
134 |
+
float: La latence moyenne des traitements en secondes. Retourne 0.0 en cas d'erreur ou si aucune donnée valide n'est disponible.
|
135 |
+
"""
|
136 |
+
# Connexion à la base de données SQLite
|
137 |
conn = get_db_connection()
|
138 |
+
|
139 |
+
# Si la connexion échoue, on retourne 0.0
|
140 |
if conn is None:
|
141 |
return 0.0
|
142 |
+
|
143 |
try:
|
144 |
+
# Création du curseur pour exécuter la requête
|
145 |
cursor = conn.cursor()
|
146 |
+
|
147 |
+
# Exécution de la requête SQL pour calculer la moyenne de la colonne 'temps_traitement'
|
148 |
+
cursor.execute(
|
149 |
+
"SELECT AVG(temps_traitement) FROM messages WHERE temps_traitement IS NOT NULL"
|
150 |
+
)
|
151 |
+
|
152 |
+
# Récupération du résultat de la requête
|
153 |
result = cursor.fetchone()
|
154 |
+
|
155 |
+
# Retour de la moyenne arrondie à 2 décimales, ou 0.0 si aucun résultat
|
156 |
return round(result[0], 2) if result[0] is not None else 0.0
|
157 |
+
|
158 |
+
except sqlite3.Error as e:
|
159 |
+
# En cas d'erreur, on enregistre l'erreur dans les logs et on retourne 0.0
|
160 |
logger.error(f"Erreur de connexion à la base de données pour la latence : {e}")
|
161 |
return 0.0
|
162 |
+
|
163 |
finally:
|
164 |
+
# Fermeture du curseur et de la connexion
|
165 |
cursor.close()
|
166 |
conn.close()
|
167 |
|
168 |
+
|
169 |
# Fonction pour récupérer le nombre de requêtes par jour
|
170 |
+
# def get_daily_requests():
|
171 |
+
# conn = get_db_connection()
|
172 |
+
# if conn is None:
|
173 |
+
# return pd.DataFrame()
|
174 |
+
# try:
|
175 |
+
# query = """
|
176 |
+
# SELECT
|
177 |
+
# DATE(timestamp) AS date,
|
178 |
+
# COUNT(*) AS nombre_requetes
|
179 |
+
# FROM
|
180 |
+
# messages
|
181 |
+
# GROUP BY
|
182 |
+
# date
|
183 |
+
# ORDER BY
|
184 |
+
# date;
|
185 |
+
# """
|
186 |
+
# df = pd.read_sql(query, conn)
|
187 |
+
# return df
|
188 |
+
# except Exception as e:
|
189 |
+
# logger.error(f"Erreur lors de la récupération des requêtes par jour : {e}")
|
190 |
+
# return pd.DataFrame()
|
191 |
+
# finally:
|
192 |
+
# conn.close()
|
193 |
+
|
194 |
+
|
195 |
+
def get_daily_requests() -> pd.DataFrame:
|
196 |
+
"""
|
197 |
+
Récupère les requêtes quotidiennes à partir de la table `messages` de la base de données SQLite.
|
198 |
+
|
199 |
+
Cette fonction se connecte à la base de données SQLite, exécute une requête SQL pour compter le nombre de requêtes
|
200 |
+
(messages) par jour, et retourne les résultats sous forme de DataFrame pandas.
|
201 |
+
|
202 |
+
Returns:
|
203 |
+
pd.DataFrame: Un DataFrame contenant les dates et le nombre de requêtes pour chaque jour.
|
204 |
+
Retourne un DataFrame vide en cas d'erreur.
|
205 |
+
"""
|
206 |
+
# Connexion à la base de données SQLite
|
207 |
conn = get_db_connection()
|
208 |
+
|
209 |
+
# Si la connexion échoue, retourner un DataFrame vide
|
210 |
if conn is None:
|
211 |
return pd.DataFrame()
|
212 |
+
|
213 |
try:
|
214 |
+
# Requête SQL pour récupérer le nombre de requêtes par jour
|
215 |
query = """
|
216 |
SELECT
|
217 |
DATE(timestamp) AS date,
|
|
|
223 |
ORDER BY
|
224 |
date;
|
225 |
"""
|
226 |
+
|
227 |
+
# Exécution de la requête et récupération du résultat sous forme de DataFrame
|
228 |
df = pd.read_sql(query, conn)
|
229 |
+
|
230 |
+
# Retour du DataFrame contenant les résultats
|
231 |
return df
|
232 |
+
|
233 |
+
except sqlite3.Error as e:
|
234 |
+
# En cas d'erreur, on enregistre l'erreur dans les logs et on retourne un DataFrame vide
|
235 |
logger.error(f"Erreur lors de la récupération des requêtes par jour : {e}")
|
236 |
return pd.DataFrame()
|
237 |
+
|
238 |
finally:
|
239 |
+
# Fermeture de la connexion à la base de données
|
240 |
conn.close()
|
241 |
|
242 |
|
243 |
# Fonction pour récupérer les ingrédients depuis la base de données
|
244 |
+
# def get_ingredients():
|
245 |
+
# conn = get_db_connection()
|
246 |
+
# cursor = conn.cursor()
|
247 |
+
# try:
|
248 |
+
# cursor.execute("SELECT ingredients FROM liste_courses")
|
249 |
+
# ingredients_list = cursor.fetchall() # Récupère tous les résultats
|
250 |
+
# return ingredients_list
|
251 |
+
# except Exception as e:
|
252 |
+
# logger.error(f"Erreur lors de la récupération des requêtes par jour : {e}")
|
253 |
+
# return pd.DataFrame()
|
254 |
+
# finally:
|
255 |
+
# # Fermer la connexion
|
256 |
+
# cursor.close()
|
257 |
+
# conn.close()
|
258 |
+
|
259 |
+
|
260 |
+
def get_ingredients() -> list:
|
261 |
+
"""
|
262 |
+
Récupère la liste des ingrédients stockée dans la table `liste_courses` de la base de données SQLite.
|
263 |
+
|
264 |
+
Cette fonction se connecte à la base de données SQLite, exécute une requête SQL pour récupérer la colonne `ingredients`
|
265 |
+
de la table `liste_courses`, et retourne les résultats sous forme de liste.
|
266 |
+
|
267 |
+
Returns:
|
268 |
+
list: Une liste contenant les ingrédients récupérés de la base de données.
|
269 |
+
Retourne une liste vide en cas d'erreur.
|
270 |
+
"""
|
271 |
+
# Connexion à la base de données SQLite
|
272 |
conn = get_db_connection()
|
273 |
+
|
274 |
+
# Si la connexion échoue, retourner une liste vide
|
275 |
+
if conn is None:
|
276 |
+
return []
|
277 |
+
|
278 |
cursor = conn.cursor()
|
279 |
+
|
280 |
try:
|
281 |
+
# Requête SQL pour récupérer la colonne 'ingredients' de la table 'liste_courses'
|
282 |
cursor.execute("SELECT ingredients FROM liste_courses")
|
283 |
+
|
284 |
+
# Récupère tous les résultats de la requête
|
285 |
+
ingredients_list = cursor.fetchall()
|
286 |
+
|
287 |
+
# Retourne la liste des ingrédients (sous forme de liste de tuples)
|
288 |
+
return [ingredient[0] for ingredient in ingredients_list]
|
289 |
+
|
290 |
+
except sqlite3.Error as e:
|
291 |
+
# En cas d'erreur, on enregistre l'erreur dans les logs et on retourne une liste vide
|
292 |
+
logger.error(f"Erreur lors de la récupération des ingrédients : {e}")
|
293 |
+
return []
|
294 |
+
|
295 |
finally:
|
296 |
+
# Fermeture du curseur et de la connexion à la base de données
|
297 |
cursor.close()
|
298 |
conn.close()
|
299 |
|
300 |
+
|
301 |
# Fonction pour récupérer le coût total des requêtes
|
302 |
+
# def get_total_cost():
|
303 |
+
# conn = get_db_connection()
|
304 |
+
# if conn is None:
|
305 |
+
# return 0.0
|
306 |
+
# try:
|
307 |
+
# cursor = conn.cursor()
|
308 |
+
# cursor.execute("SELECT SUM(total_cout) FROM messages WHERE total_cout IS NOT NULL")
|
309 |
+
# result = cursor.fetchone()
|
310 |
+
# return round(result[0], 2) if result[0] is not None else 0.0
|
311 |
+
# except Exception as e:
|
312 |
+
# logger.error(f"Erreur lors de la récupération du coût total : {e}")
|
313 |
+
# return 0.0
|
314 |
+
# finally:
|
315 |
+
# cursor.close()
|
316 |
+
# conn.close()
|
317 |
+
|
318 |
+
|
319 |
+
def get_total_cost() -> float:
|
320 |
+
"""
|
321 |
+
Récupère le coût total des messages dans la table `messages` de la base de données SQLite.
|
322 |
+
|
323 |
+
Cette fonction se connecte à la base de données SQLite, exécute une requête SQL pour récupérer la somme des valeurs
|
324 |
+
présentes dans la colonne `total_cout` de la table `messages`, et retourne le total arrondi à 2 décimales.
|
325 |
+
|
326 |
+
Returns:
|
327 |
+
float: Le coût total des messages. Retourne 0.0 si aucune donnée n'est disponible ou en cas d'erreur.
|
328 |
+
"""
|
329 |
+
# Connexion à la base de données SQLite
|
330 |
conn = get_db_connection()
|
331 |
+
|
332 |
+
# Si la connexion échoue, retourner 0.0
|
333 |
if conn is None:
|
334 |
return 0.0
|
335 |
+
|
336 |
+
cursor = conn.cursor()
|
337 |
+
|
338 |
try:
|
339 |
+
# Requête SQL pour récupérer la somme de la colonne 'total_cout' de la table 'messages'
|
340 |
+
cursor.execute(
|
341 |
+
"SELECT SUM(total_cout) FROM messages WHERE total_cout IS NOT NULL"
|
342 |
+
)
|
343 |
+
|
344 |
+
# Récupère le résultat de la requête
|
345 |
result = cursor.fetchone()
|
346 |
+
|
347 |
+
# Retourne la somme arrondie à 2 décimales si un résultat est trouvé, sinon retourne 0.0
|
348 |
return round(result[0], 2) if result[0] is not None else 0.0
|
349 |
+
|
350 |
+
except sqlite3.Error as e:
|
351 |
+
# En cas d'erreur, on log l'erreur et on retourne 0.0
|
352 |
logger.error(f"Erreur lors de la récupération du coût total : {e}")
|
353 |
return 0.0
|
354 |
+
|
355 |
finally:
|
356 |
+
# Fermeture du curseur et de la connexion à la base de données
|
357 |
cursor.close()
|
358 |
conn.close()
|
359 |
|
360 |
+
|
361 |
# Fonction pour récupérer l'impact écologique estimé
|
362 |
+
# def get_total_impact():
|
363 |
+
# conn = get_db_connection()
|
364 |
+
# if conn is None:
|
365 |
+
# return 0.0
|
366 |
+
# try:
|
367 |
+
# cursor = conn.cursor()
|
368 |
+
# cursor.execute("SELECT SUM(impact_eco) FROM messages WHERE impact_eco IS NOT NULL")
|
369 |
+
# result = cursor.fetchone()
|
370 |
+
# return round(result[0], 2) if result[0] is not None else 0.0
|
371 |
+
# except Exception as e:
|
372 |
+
# logger.error(f"Erreur lors de la récupération de l'impact écologique : {e}")
|
373 |
+
# return 0.0
|
374 |
+
# finally:
|
375 |
+
# cursor.close()
|
376 |
+
# conn.close()
|
377 |
+
|
378 |
+
|
379 |
+
def get_total_impact() -> float:
|
380 |
+
"""
|
381 |
+
Récupère l'impact écologique total des messages dans la table `messages` de la base de données SQLite.
|
382 |
+
|
383 |
+
Cette fonction se connecte à la base de données SQLite, exécute une requête SQL pour récupérer la somme des valeurs
|
384 |
+
présentes dans la colonne `impact_eco` de la table `messages`, et retourne le total arrondi à 2 décimales.
|
385 |
+
|
386 |
+
Returns:
|
387 |
+
float: L'impact écologique total des messages. Retourne 0.0 si aucune donnée n'est disponible ou en cas d'erreur.
|
388 |
+
"""
|
389 |
+
# Connexion à la base de données SQLite
|
390 |
conn = get_db_connection()
|
391 |
+
|
392 |
+
# Si la connexion échoue, retourner 0.0
|
393 |
if conn is None:
|
394 |
return 0.0
|
395 |
+
|
396 |
+
cursor = conn.cursor()
|
397 |
+
|
398 |
try:
|
399 |
+
# Requête SQL pour récupérer la somme de la colonne 'impact_eco' de la table 'messages'
|
400 |
+
cursor.execute(
|
401 |
+
"SELECT SUM(impact_eco) FROM messages WHERE impact_eco IS NOT NULL"
|
402 |
+
)
|
403 |
+
|
404 |
+
# Récupère le résultat de la requête
|
405 |
result = cursor.fetchone()
|
406 |
+
|
407 |
+
# Retourne la somme arrondie à 2 décimales si un résultat est trouvé, sinon retourne 0.0
|
408 |
return round(result[0], 2) if result[0] is not None else 0.0
|
409 |
+
|
410 |
+
except sqlite3.Error as e:
|
411 |
+
# En cas d'erreur, on log l'erreur et on retourne 0.0
|
412 |
logger.error(f"Erreur lors de la récupération de l'impact écologique : {e}")
|
413 |
return 0.0
|
414 |
+
|
415 |
finally:
|
416 |
+
# Fermeture du curseur et de la connexion à la base de données
|
417 |
cursor.close()
|
418 |
conn.close()
|
server/db/dbmanager.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
import streamlit as st
|
2 |
import psycopg2
|
|
|
3 |
from psycopg2 import extras
|
4 |
from datetime import datetime
|
5 |
import logging
|
@@ -7,32 +8,34 @@ import json
|
|
7 |
import pandas as pd
|
8 |
from typing import List, Dict, Tuple
|
9 |
import os
|
10 |
-
import sys
|
11 |
|
12 |
# Configuration du logging
|
13 |
logging.basicConfig(level=logging.INFO, handlers=[logging.StreamHandler()])
|
14 |
logger = logging.getLogger(__name__)
|
15 |
|
16 |
-
sys.stdout.reconfigure(encoding=
|
17 |
|
18 |
# Configuration de la base de données
|
19 |
db_config = {
|
20 |
"database": st.secrets["DB_NAME"],
|
21 |
-
"user": st.secrets["DB_USER"],
|
22 |
-
"password": st.secrets["DB_PASSWORD"],
|
23 |
-
"host": st.secrets["DB_HOST"],
|
24 |
-
"port": st.secrets["DB_PORT"]
|
25 |
}
|
26 |
|
27 |
######################### CLASSES #########################
|
28 |
|
|
|
29 |
class DBManager:
|
30 |
-
def __init__(self, db_config: Dict, schema_file: str):
|
31 |
"""
|
32 |
Initialise la connexion à la base PostgreSQL et charge le schéma.
|
33 |
-
|
34 |
-
:
|
35 |
-
|
|
|
36 |
"""
|
37 |
|
38 |
self.db_config = db_config
|
@@ -42,134 +45,293 @@ class DBManager:
|
|
42 |
self._load_schema()
|
43 |
self._connect_to_database()
|
44 |
self._create_database()
|
45 |
-
self.cursor.execute("SET NAMES 'UTF8'")
|
46 |
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
"""
|
51 |
if not os.path.exists(self.schema_file):
|
52 |
raise FileNotFoundError(f"Fichier non trouvé : {self.schema_file}")
|
53 |
-
|
54 |
with open(self.schema_file, "r", encoding="utf-8") as file:
|
55 |
self.schema = json.load(file)
|
56 |
|
57 |
-
def _connect_to_database(self):
|
58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
try:
|
60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
self.cursor = self.connection.cursor()
|
62 |
-
except
|
63 |
logger.error(f"Erreur de connexion : {err}")
|
64 |
return
|
65 |
|
66 |
-
def _create_database(self):
|
67 |
-
|
68 |
-
|
69 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
try:
|
71 |
self.cursor.execute(create_table_query)
|
72 |
-
except
|
73 |
-
logger.error(
|
|
|
|
|
74 |
return
|
75 |
finally:
|
76 |
self.connection.commit()
|
77 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
def _generate_create_table_query(self, table_name: str, columns: List[Dict]) -> str:
|
79 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
column_definitions = []
|
81 |
for column in columns:
|
82 |
column_definition = f"{column['name']} {column['type']}"
|
83 |
-
|
84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
column_definitions.append(column_definition)
|
|
|
86 |
columns_str = ", ".join(column_definitions)
|
87 |
return f"CREATE TABLE IF NOT EXISTS {table_name} ({columns_str});"
|
88 |
|
89 |
-
def insert_data_from_dict(self, table_name: str, data: List[Dict], id_column: str) -> List[int]:
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
"""
|
96 |
-
|
97 |
-
placeholders = ", ".join(['%s' for _ in data[0].keys()])
|
98 |
|
99 |
-
|
100 |
-
|
|
|
101 |
|
102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
try:
|
104 |
for row in data:
|
105 |
self.cursor.execute(query, tuple(row.values()))
|
106 |
-
inserted_id =
|
|
|
|
|
107 |
ids.append(inserted_id)
|
108 |
return ids
|
109 |
-
except
|
110 |
-
logger.error(
|
|
|
|
|
111 |
return
|
112 |
finally:
|
113 |
self.connection.commit()
|
114 |
|
115 |
-
def
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
"""
|
122 |
-
columns = ", ".join(data[0].keys())
|
123 |
-
placeholders = ", ".join(['%s' for _ in data[0].keys()])
|
124 |
-
|
125 |
-
# Requête pour insérer les données et retourner l'ID dynamique
|
126 |
-
query = f"INSERT INTO {table_name} ({columns}) VALUES ({placeholders}) RETURNING {id_column}"
|
127 |
-
|
128 |
-
ids = [] # Liste pour stocker les IDs retournés
|
129 |
-
for row in data:
|
130 |
-
self.cursor.execute(query, tuple(row.values()))
|
131 |
-
inserted_id = self.cursor.fetchone()[0] # Récupère le premier (et unique) élément de la ligne retournée
|
132 |
-
ids.append(inserted_id)
|
133 |
-
|
134 |
-
self.connection.commit()
|
135 |
-
return ids
|
136 |
-
|
137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
138 |
|
139 |
def insert_data_from_csv(self, table_name: str, csv_file: str) -> None:
|
140 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
df = pd.read_csv(csv_file)
|
142 |
columns = df.columns.tolist()
|
143 |
-
placeholders = ", ".join(
|
144 |
-
|
145 |
-
|
|
|
|
|
|
|
|
|
146 |
try:
|
147 |
for row in df.itertuples(index=False, name=None):
|
148 |
-
self.cursor.execute(
|
149 |
-
|
150 |
-
|
151 |
-
|
|
|
|
|
|
|
152 |
finally:
|
153 |
self.connection.commit()
|
154 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
def fetch_all(self, table_name: str) -> List[Tuple]:
|
156 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
157 |
try:
|
158 |
self.cursor.execute(f"SELECT * FROM {table_name}")
|
159 |
return self.cursor.fetchall()
|
160 |
-
except
|
161 |
-
logger.error(
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
166 |
"""
|
167 |
Exécute une requête SQL avec gestion centralisée des erreurs.
|
168 |
|
169 |
-
:
|
170 |
-
|
171 |
-
|
172 |
-
|
|
|
|
|
|
|
173 |
"""
|
174 |
try:
|
175 |
self.cursor.execute(query, params)
|
@@ -177,300 +339,715 @@ class DBManager:
|
|
177 |
return self.cursor.fetchall() # Retourner les résultats si demandé
|
178 |
else:
|
179 |
self.connection.commit() # Valider les modifications
|
180 |
-
except
|
181 |
-
logger.error(f"Erreur de
|
182 |
self.connection.rollback() # Annuler la transaction en cas d'erreur
|
183 |
-
raise RuntimeError(
|
|
|
|
|
184 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
185 |
|
186 |
-
def fetch_by_condition(
|
187 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
query = f"SELECT * FROM {table_name} WHERE {condition}"
|
189 |
try:
|
190 |
-
|
191 |
return self.execute_safe(query, params, fetch=True)
|
192 |
-
except
|
193 |
-
logger.error(
|
194 |
-
|
195 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
|
197 |
-
def update_data(
|
198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
199 |
query = f"UPDATE {table_name} SET {set_clause} WHERE {condition}"
|
200 |
try:
|
201 |
self.cursor.execute(query, params)
|
202 |
-
except
|
203 |
-
logger.error(
|
204 |
-
|
|
|
205 |
finally:
|
206 |
-
self.connection.commit()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
207 |
|
208 |
def delete_data(self, table_name: str, condition: str, params: Tuple) -> None:
|
209 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
210 |
query = f"DELETE FROM {table_name} WHERE {condition}"
|
211 |
try:
|
212 |
self.cursor.execute(query, params)
|
213 |
-
except
|
214 |
-
logger.error(
|
215 |
-
|
|
|
216 |
finally:
|
217 |
-
self.connection.commit()
|
|
|
|
|
|
|
|
|
|
|
|
|
218 |
|
219 |
def close_connection(self) -> None:
|
220 |
-
"""
|
|
|
|
|
221 |
if self.connection:
|
222 |
-
|
223 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
224 |
|
225 |
def create_index(self, table_name: str, column_name: str) -> None:
|
226 |
-
"""
|
|
|
|
|
227 |
query = f"CREATE INDEX IF NOT EXISTS idx_{table_name}_{column_name} ON {table_name} ({column_name})"
|
228 |
try:
|
229 |
self.cursor.execute(query)
|
230 |
-
except
|
231 |
-
logger.error(
|
232 |
-
|
|
|
233 |
finally:
|
234 |
-
self.connection.commit()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
235 |
|
236 |
def select(self, query: str, params: Tuple = ()) -> List[Tuple]:
|
237 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
238 |
try:
|
239 |
self.cursor.execute(query, params)
|
240 |
return self.cursor.fetchall()
|
241 |
-
except
|
242 |
-
logger.error(f"Erreur de
|
243 |
-
return
|
244 |
-
|
245 |
-
def query(self, query, params=None):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
246 |
"""
|
247 |
-
Exécute une requête SQL, en utilisant les paramètres fournis,
|
248 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
249 |
"""
|
250 |
try:
|
251 |
-
|
252 |
-
|
253 |
-
|
|
|
|
|
|
|
254 |
return
|
255 |
finally:
|
256 |
# Si la requête est un SELECT, récupérer les résultats
|
257 |
if query.strip().upper().startswith("SELECT"):
|
258 |
return self.cursor.fetchall()
|
259 |
-
else:
|
260 |
self.connection.commit()
|
261 |
-
return None
|
262 |
-
|
263 |
|
264 |
|
265 |
-
|
266 |
-
|
267 |
######################### FONCTIONS #########################
|
268 |
|
269 |
# Mettre DBManager en cache
|
270 |
@st.cache_resource
|
271 |
def get_db_manager():
|
272 |
-
return DBManager(db_config, os.path.join("server","db","schema.json"))
|
|
|
273 |
|
|
|
|
|
|
|
274 |
|
275 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
276 |
"""
|
277 |
Sauvegarde un message dans la base de données, en associant l'utilisateur à la conversation.
|
278 |
|
279 |
-
:
|
280 |
-
|
281 |
-
|
282 |
-
|
|
|
|
|
|
|
|
|
283 |
"""
|
284 |
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
285 |
-
data = [
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
|
|
|
|
|
|
294 |
try:
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
299 |
|
300 |
def create_conversation(db_manager, title: str, id_utilisateur: int) -> int:
|
301 |
"""
|
302 |
Crée une nouvelle conversation dans la base de données, en associant l'utilisateur à la conversation.
|
303 |
|
304 |
-
:
|
305 |
-
|
306 |
-
|
307 |
-
|
|
|
|
|
|
|
|
|
308 |
"""
|
309 |
created_at = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
310 |
-
data = [
|
311 |
-
"created_at": created_at,
|
312 |
-
|
313 |
-
|
314 |
-
}]
|
315 |
try:
|
316 |
-
result = db_manager.insert_data_from_dict("conversations", data
|
317 |
-
return result[0]
|
318 |
-
except
|
319 |
-
logger.error(f"Erreur de
|
320 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
321 |
|
322 |
def load_messages(db_manager, id_conversation: int) -> List[Dict]:
|
323 |
"""
|
324 |
Charge les messages associés à une conversation.
|
325 |
|
326 |
-
:
|
327 |
-
|
328 |
-
|
|
|
|
|
329 |
"""
|
330 |
query = """
|
331 |
-
SELECT
|
332 |
FROM messages
|
333 |
-
WHERE id_conversation =
|
334 |
ORDER BY timestamp ASC
|
335 |
"""
|
336 |
try:
|
337 |
result = db_manager.query(query, (id_conversation,))
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
342 |
|
343 |
def load_conversations(db_manager, id_utilisateur: int) -> List[Dict]:
|
344 |
"""
|
345 |
Charge toutes les conversations enregistrées pour un utilisateur donné.
|
346 |
|
347 |
-
:
|
348 |
-
|
349 |
-
|
|
|
|
|
350 |
"""
|
351 |
query = """
|
352 |
-
SELECT
|
353 |
-
|
|
|
354 |
ORDER BY created_at DESC
|
355 |
"""
|
356 |
try:
|
357 |
result = db_manager.query(query, (id_utilisateur,))
|
358 |
-
|
359 |
-
|
360 |
return [
|
361 |
-
{
|
|
|
|
|
|
|
|
|
|
|
362 |
]
|
363 |
-
except
|
364 |
-
logger.error(f"Erreur
|
365 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
366 |
|
367 |
def update_conversation(db_manager, id_conversation: int, id_utilisateur: int) -> None:
|
368 |
"""
|
369 |
Met à jour le champ `created_at` d'une conversation donnée pour un utilisateur.
|
370 |
|
371 |
-
:
|
372 |
-
|
373 |
-
|
|
|
374 |
"""
|
375 |
new_timer = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
376 |
query = """
|
377 |
UPDATE conversations
|
378 |
-
SET created_at =
|
379 |
-
WHERE id_conversation =
|
380 |
"""
|
381 |
try:
|
382 |
db_manager.query(query, (new_timer, id_conversation, id_utilisateur))
|
383 |
-
except
|
384 |
-
logger.error(f"Erreur de
|
385 |
return
|
386 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
387 |
def update_conversation_title(db_manager, id_conversation: int, new_title: str) -> None:
|
388 |
"""
|
389 |
Met à jour le titre d'une conversation si celui-ci est encore "Nouvelle conversation".
|
390 |
|
391 |
-
:
|
392 |
-
|
393 |
-
|
|
|
394 |
"""
|
395 |
query = """
|
396 |
UPDATE conversations
|
397 |
-
SET title =
|
398 |
-
WHERE id_conversation =
|
399 |
"""
|
400 |
try:
|
401 |
db_manager.query(query, (new_title, id_conversation))
|
402 |
-
except
|
403 |
-
logger.error(
|
|
|
|
|
404 |
return
|
405 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
406 |
def get_conversation_title(db_manager, id_conversation: int) -> str:
|
407 |
"""
|
408 |
Récupère le titre d'une conversation spécifique en utilisant `fetch_by_condition`.
|
409 |
|
410 |
-
:
|
411 |
-
|
412 |
-
|
|
|
|
|
|
|
413 |
"""
|
414 |
table_name = "conversations"
|
415 |
-
condition = "id_conversation =
|
416 |
try:
|
417 |
-
results = db_manager.fetch_by_condition(
|
|
|
|
|
418 |
if results:
|
419 |
-
#
|
420 |
-
return results[0][
|
|
|
|
|
421 |
return "Nouvelle conversation"
|
422 |
-
except
|
423 |
-
logger.error(
|
424 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
425 |
|
426 |
def delete_conversation(db_manager, id_conversation: int) -> None:
|
427 |
"""
|
428 |
Supprime une conversation et ses messages associés de la base de données.
|
429 |
|
430 |
-
:
|
431 |
-
|
|
|
432 |
"""
|
433 |
try:
|
434 |
# Supprimer les messages liés à la conversation
|
435 |
-
query_delete_messages = "DELETE FROM messages WHERE id_conversation =
|
436 |
db_manager.query(query_delete_messages, (id_conversation,))
|
437 |
|
438 |
# Supprimer la conversation elle-même
|
439 |
-
query_delete_conversation =
|
|
|
|
|
440 |
db_manager.query(query_delete_conversation, (id_conversation,))
|
441 |
|
442 |
-
|
443 |
-
|
444 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
445 |
|
446 |
-
|
|
|
447 |
"""
|
448 |
Charge les suggestions du chatbot enregistrées pour un utilisateur donné.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
449 |
"""
|
450 |
-
query = "SELECT repas_suggestion FROM suggestions_repas WHERE id_utilisateur =
|
451 |
try:
|
452 |
db_manager.cursor.execute(query, (user_id,))
|
453 |
suggestions = [row[0] for row in db_manager.cursor.fetchall()]
|
454 |
return suggestions
|
455 |
-
except
|
456 |
logger.error(f"Erreur lors du chargement des suggestions : {err}")
|
457 |
return []
|
458 |
|
459 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
460 |
|
461 |
-
|
|
|
462 |
"""
|
463 |
Sauvegarde les suggestions du chatbot dans la base de données.
|
|
|
|
|
|
|
|
|
|
|
|
|
464 |
"""
|
465 |
query = """
|
466 |
INSERT INTO suggestions_repas (id_utilisateur, repas_suggestion, motif_suggestion)
|
467 |
-
VALUES (
|
468 |
"""
|
469 |
try:
|
470 |
for suggestion in suggestions:
|
471 |
db_manager.cursor.execute(query, (user_id, suggestion, "Chatbot"))
|
472 |
db_manager.connection.commit()
|
473 |
-
except
|
474 |
logger.error(f"Erreur lors de l'enregistrement des suggestions : {err}")
|
475 |
-
|
476 |
-
|
|
|
1 |
import streamlit as st
|
2 |
import psycopg2
|
3 |
+
import sqlite3
|
4 |
from psycopg2 import extras
|
5 |
from datetime import datetime
|
6 |
import logging
|
|
|
8 |
import pandas as pd
|
9 |
from typing import List, Dict, Tuple
|
10 |
import os
|
11 |
+
import sys
|
12 |
|
13 |
# Configuration du logging
|
14 |
logging.basicConfig(level=logging.INFO, handlers=[logging.StreamHandler()])
|
15 |
logger = logging.getLogger(__name__)
|
16 |
|
17 |
+
sys.stdout.reconfigure(encoding="utf-8")
|
18 |
|
19 |
# Configuration de la base de données
|
20 |
db_config = {
|
21 |
"database": st.secrets["DB_NAME"],
|
22 |
+
# "user": st.secrets["DB_USER"],
|
23 |
+
# "password": st.secrets["DB_PASSWORD"],
|
24 |
+
# "host": st.secrets["DB_HOST"],
|
25 |
+
# "port": st.secrets["DB_PORT"]
|
26 |
}
|
27 |
|
28 |
######################### CLASSES #########################
|
29 |
|
30 |
+
|
31 |
class DBManager:
|
32 |
+
def __init__(self, db_config: Dict, schema_file: str) -> None:
|
33 |
"""
|
34 |
Initialise la connexion à la base PostgreSQL et charge le schéma.
|
35 |
+
|
36 |
+
Args:
|
37 |
+
db_config (Dict) : dictionnaire avec les informations de connexion (host, database, user, password).
|
38 |
+
schema_file (str) : chemin vers le fichier JSON contenant le schéma de la base.
|
39 |
"""
|
40 |
|
41 |
self.db_config = db_config
|
|
|
45 |
self._load_schema()
|
46 |
self._connect_to_database()
|
47 |
self._create_database()
|
48 |
+
# self.cursor.execute("SET NAMES 'UTF8'")
|
49 |
|
50 |
+
def _load_schema(self) -> None:
|
51 |
+
"""
|
52 |
+
Charge le schéma de base de données depuis un fichier JSON.
|
53 |
+
"""
|
54 |
if not os.path.exists(self.schema_file):
|
55 |
raise FileNotFoundError(f"Fichier non trouvé : {self.schema_file}")
|
56 |
+
|
57 |
with open(self.schema_file, "r", encoding="utf-8") as file:
|
58 |
self.schema = json.load(file)
|
59 |
|
60 |
+
# def _connect_to_database(self):
|
61 |
+
# """Établit une connexion avec la base PostgreSQL."""
|
62 |
+
# try:
|
63 |
+
# self.connection = psycopg2.connect(**self.db_config, cursor_factory=extras.DictCursor)
|
64 |
+
# self.cursor = self.connection.cursor()
|
65 |
+
# except psycopg2.Error as err:
|
66 |
+
# logger.error(f"Erreur de connexion : {err}")
|
67 |
+
# return
|
68 |
+
|
69 |
+
def _connect_to_database(self) -> None:
|
70 |
+
"""
|
71 |
+
Établit une connexion avec la base SQLite.
|
72 |
+
"""
|
73 |
try:
|
74 |
+
# Connexion à la base de données SQLite
|
75 |
+
self.connection = sqlite3.connect(
|
76 |
+
self.db_config["database"], check_same_thread=False
|
77 |
+
)
|
78 |
+
self.connection.row_factory = (
|
79 |
+
sqlite3.Row
|
80 |
+
) # Pour des résultats sous forme de dictionnaire
|
81 |
self.cursor = self.connection.cursor()
|
82 |
+
except sqlite3.Error as err:
|
83 |
logger.error(f"Erreur de connexion : {err}")
|
84 |
return
|
85 |
|
86 |
+
# def _create_database(self):
|
87 |
+
# """Crée les tables définies dans le schéma JSON."""
|
88 |
+
# for table_name, table_info in self.schema['tables'].items():
|
89 |
+
# create_table_query = self._generate_create_table_query(table_name, table_info['columns'])
|
90 |
+
# try:
|
91 |
+
# self.cursor.execute(create_table_query)
|
92 |
+
# except psycopg2.Error as err:
|
93 |
+
# logger.error(f"Erreur de connexion : {err}")
|
94 |
+
# return
|
95 |
+
# finally:
|
96 |
+
# self.connection.commit()
|
97 |
+
|
98 |
+
import sqlite3
|
99 |
+
|
100 |
+
def _create_database(self) -> None:
|
101 |
+
"""
|
102 |
+
Crée les tables définies dans le schéma JSON.
|
103 |
+
"""
|
104 |
+
for table_name, table_info in self.schema["tables"].items():
|
105 |
+
create_table_query = self._generate_create_table_query(
|
106 |
+
table_name, table_info["columns"]
|
107 |
+
)
|
108 |
try:
|
109 |
self.cursor.execute(create_table_query)
|
110 |
+
except sqlite3.Error as err:
|
111 |
+
logger.error(
|
112 |
+
f"Erreur lors de la création de la table {table_name}: {err}"
|
113 |
+
)
|
114 |
return
|
115 |
finally:
|
116 |
self.connection.commit()
|
117 |
|
118 |
+
# def _generate_create_table_query(self, table_name: str, columns: List[Dict]) -> str:
|
119 |
+
# """Génère une requête SQL pour créer une table en fonction du schéma."""
|
120 |
+
# column_definitions = []
|
121 |
+
# for column in columns:
|
122 |
+
# column_definition = f"{column['name']} {column['type']}"
|
123 |
+
# if 'constraints' in column and column['constraints']:
|
124 |
+
# column_definition += " " + " ".join(column['constraints'])
|
125 |
+
# column_definitions.append(column_definition)
|
126 |
+
# columns_str = ", ".join(column_definitions)
|
127 |
+
# return f"CREATE TABLE IF NOT EXISTS {table_name} ({columns_str});"
|
128 |
+
|
129 |
def _generate_create_table_query(self, table_name: str, columns: List[Dict]) -> str:
|
130 |
+
"""
|
131 |
+
Génère une requête SQL pour créer une table en fonction du schéma.
|
132 |
+
|
133 |
+
Args:
|
134 |
+
table_name (str): nom de la table.
|
135 |
+
columns (List[Dict]): colonnes de la table à créer.
|
136 |
+
|
137 |
+
Returns:
|
138 |
+
str : la requête SQL CREATE TABLE paramétrée.
|
139 |
+
|
140 |
+
"""
|
141 |
column_definitions = []
|
142 |
for column in columns:
|
143 |
column_definition = f"{column['name']} {column['type']}"
|
144 |
+
|
145 |
+
# Conversion quand le type n'est pas compatible avec SQLite (ex. : SERIAL -> INTEGER PRIMARY KEY AUTOINCREMENT)
|
146 |
+
if column["type"] == "SERIAL":
|
147 |
+
column_definition = (
|
148 |
+
f"{column['name']} INTEGER PRIMARY KEY AUTOINCREMENT"
|
149 |
+
)
|
150 |
+
|
151 |
+
if "constraints" in column and column["constraints"]:
|
152 |
+
column_definition += " " + " ".join(column["constraints"])
|
153 |
+
|
154 |
column_definitions.append(column_definition)
|
155 |
+
|
156 |
columns_str = ", ".join(column_definitions)
|
157 |
return f"CREATE TABLE IF NOT EXISTS {table_name} ({columns_str});"
|
158 |
|
159 |
+
# def insert_data_from_dict(self, table_name: str, data: List[Dict], id_column: str) -> List[int]:
|
160 |
+
# """Insère des données dans une table à partir d'une liste de dictionnaires et retourne les IDs insérés.
|
161 |
+
|
162 |
+
# table_name : str : nom de la table
|
163 |
+
# data : List[Dict] : données à insérer
|
164 |
+
# id_column : str : nom de la colonne d'ID à retourner
|
165 |
+
# """
|
166 |
+
# columns = ", ".join(data[0].keys())
|
167 |
+
# placeholders = ", ".join(['%s' for _ in data[0].keys()])
|
168 |
+
|
169 |
+
# # Requête pour insérer les données et retourner l'ID dynamique
|
170 |
+
# query = f"INSERT INTO {table_name} ({columns}) VALUES ({placeholders}) RETURNING {id_column}"
|
171 |
+
|
172 |
+
# ids = []
|
173 |
+
# try:
|
174 |
+
# for row in data:
|
175 |
+
# self.cursor.execute(query, tuple(row.values()))
|
176 |
+
# inserted_id = self.cursor.fetchone()[0]
|
177 |
+
# ids.append(inserted_id)
|
178 |
+
# return ids
|
179 |
+
# except psycopg2.Error as err:
|
180 |
+
# logger.error(f"Erreur de connexion : {err}")
|
181 |
+
# return
|
182 |
+
# finally:
|
183 |
+
# self.connection.commit()
|
184 |
+
|
185 |
+
def insert_data_from_dict(self, table_name: str, data: List[Dict]) -> List[int]:
|
186 |
"""
|
187 |
+
Insère des données dans une table à partir d'une liste de dictionnaires et retourne les IDs insérés.
|
|
|
188 |
|
189 |
+
Args:
|
190 |
+
table_name (str): nom de la table.
|
191 |
+
data (List[Dict]): données à insérer.
|
192 |
|
193 |
+
Returns:
|
194 |
+
List[int]: liste des ID des données insérées.
|
195 |
+
"""
|
196 |
+
columns = ", ".join(data[0].keys())
|
197 |
+
placeholders = ", ".join(
|
198 |
+
["?" for _ in data[0].keys()]
|
199 |
+
) # SQLite utilise '?' pour les placeholders
|
200 |
+
|
201 |
+
# Requête pour insérer les données
|
202 |
+
query = f"INSERT INTO {table_name} ({columns}) VALUES ({placeholders})"
|
203 |
+
|
204 |
+
ids = []
|
205 |
try:
|
206 |
for row in data:
|
207 |
self.cursor.execute(query, tuple(row.values()))
|
208 |
+
inserted_id = (
|
209 |
+
self.cursor.lastrowid
|
210 |
+
) # Récupère l'ID du dernier enregistrement inséré
|
211 |
ids.append(inserted_id)
|
212 |
return ids
|
213 |
+
except sqlite3.Error as err:
|
214 |
+
logger.error(
|
215 |
+
f"Erreur lors de l'insertion des données dans {table_name}: {err}"
|
216 |
+
)
|
217 |
return
|
218 |
finally:
|
219 |
self.connection.commit()
|
220 |
|
221 |
+
# def insert_data_from_csv(self, table_name: str, csv_file: str) -> None:
|
222 |
+
# """Insère des données dans une table à partir d'un fichier CSV."""
|
223 |
+
# df = pd.read_csv(csv_file)
|
224 |
+
# columns = df.columns.tolist()
|
225 |
+
# placeholders = ", ".join(['%s' for _ in columns])
|
226 |
+
# query = f"INSERT INTO {table_name} ({', '.join(columns)}) VALUES ({placeholders})"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
227 |
|
228 |
+
# try:
|
229 |
+
# for row in df.itertuples(index=False, name=None):
|
230 |
+
# self.cursor.execute(query, row)
|
231 |
+
# except psycopg2.Error as err:
|
232 |
+
# logger.error(f"Erreur de connexion : {err}")
|
233 |
+
# return
|
234 |
+
# finally:
|
235 |
+
# self.connection.commit()
|
236 |
|
237 |
def insert_data_from_csv(self, table_name: str, csv_file: str) -> None:
|
238 |
+
"""
|
239 |
+
Insère des données dans une table à partir d'un fichier CSV.
|
240 |
+
|
241 |
+
Args:
|
242 |
+
table_name (str): nom de la table dans laquelle insérer des données.
|
243 |
+
csv_file (str): lien du fichier csv contenant les données.
|
244 |
+
"""
|
245 |
df = pd.read_csv(csv_file)
|
246 |
columns = df.columns.tolist()
|
247 |
+
placeholders = ", ".join(
|
248 |
+
["?" for _ in columns]
|
249 |
+
) # Utilisation de '?' pour SQLite
|
250 |
+
query = (
|
251 |
+
f"INSERT INTO {table_name} ({', '.join(columns)}) VALUES ({placeholders})"
|
252 |
+
)
|
253 |
+
|
254 |
try:
|
255 |
for row in df.itertuples(index=False, name=None):
|
256 |
+
self.cursor.execute(
|
257 |
+
query, row
|
258 |
+
) # Exécution de la requête avec les valeurs du CSV
|
259 |
+
except sqlite3.Error as err:
|
260 |
+
logger.error(
|
261 |
+
f"Erreur lors de l'insertion des données depuis {csv_file} : {err}"
|
262 |
+
)
|
263 |
finally:
|
264 |
self.connection.commit()
|
265 |
|
266 |
+
# def fetch_all(self, table_name: str) -> List[Tuple]:
|
267 |
+
# """
|
268 |
+
# Récupère toutes les données d'une table.
|
269 |
+
|
270 |
+
# Args:
|
271 |
+
# table_name (str): nom de la table de laquelle récupérer des données.
|
272 |
+
|
273 |
+
# Returns:
|
274 |
+
# List[Tuple]: liste des enregistrements récupérés à partir de la table.
|
275 |
+
# """
|
276 |
+
# try:
|
277 |
+
# self.cursor.execute(f"SELECT * FROM {table_name}")
|
278 |
+
# return self.cursor.fetchall()
|
279 |
+
# except sqlite3.Error as err:
|
280 |
+
# logger.error(f"Erreur de connexion : {err}")
|
281 |
+
# return
|
282 |
+
|
283 |
def fetch_all(self, table_name: str) -> List[Tuple]:
|
284 |
+
"""
|
285 |
+
Récupère toutes les données d'une table.
|
286 |
+
|
287 |
+
Args:
|
288 |
+
table_name (str): nom de la table de laquelle récupérer des données.
|
289 |
+
|
290 |
+
Returns:
|
291 |
+
List[Tuple]: liste des enregistrements récupérés à partir de la table.
|
292 |
+
"""
|
293 |
try:
|
294 |
self.cursor.execute(f"SELECT * FROM {table_name}")
|
295 |
return self.cursor.fetchall()
|
296 |
+
except sqlite3.Error as err:
|
297 |
+
logger.error(
|
298 |
+
f"Erreur lors de la récupération des données de la table {table_name} : {err}"
|
299 |
+
)
|
300 |
+
return [] # Retourne une liste vide en cas d'erreur
|
301 |
+
|
302 |
+
# def execute_safe(self, query: str, params: Tuple = (), fetch: bool = False):
|
303 |
+
# """
|
304 |
+
# Exécute une requête SQL avec gestion centralisée des erreurs.
|
305 |
+
|
306 |
+
# :param query: Requête SQL à exécuter.
|
307 |
+
# :param params: Paramètres de la requête.
|
308 |
+
# :param fetch: Indique si les résultats doivent être récupérés.
|
309 |
+
# :return: Résultats de la requête (si fetch est True), sinon None.
|
310 |
+
# """
|
311 |
+
# try:
|
312 |
+
# self.cursor.execute(query, params)
|
313 |
+
# if fetch:
|
314 |
+
# return self.cursor.fetchall() # Retourner les résultats si demandé
|
315 |
+
# else:
|
316 |
+
# self.connection.commit() # Valider les modifications
|
317 |
+
# except psycopg2.Error as err:
|
318 |
+
# logger.error(f"Erreur de connexion : {err}")
|
319 |
+
# self.connection.rollback() # Annuler la transaction en cas d'erreur
|
320 |
+
# raise RuntimeError(f"Erreur SQL : {err} | Query : {query} | Params : {params}")
|
321 |
+
|
322 |
+
def execute_safe(
|
323 |
+
self, query: str, params: Tuple = (), fetch: bool = False
|
324 |
+
) -> List[Tuple]:
|
325 |
"""
|
326 |
Exécute une requête SQL avec gestion centralisée des erreurs.
|
327 |
|
328 |
+
Args:
|
329 |
+
query (str): requête SQL à exécuter.
|
330 |
+
params (Tuple): paramètres de la requête.
|
331 |
+
fetch (bool): indique si les résultats doivent être récupérés.
|
332 |
+
|
333 |
+
Returns:
|
334 |
+
List[Tuple]: résultats de la requête (si fetch est True), sinon None.
|
335 |
"""
|
336 |
try:
|
337 |
self.cursor.execute(query, params)
|
|
|
339 |
return self.cursor.fetchall() # Retourner les résultats si demandé
|
340 |
else:
|
341 |
self.connection.commit() # Valider les modifications
|
342 |
+
except sqlite3.Error as err:
|
343 |
+
logger.error(f"Erreur lors de l'exécution de la requête : {err}")
|
344 |
self.connection.rollback() # Annuler la transaction en cas d'erreur
|
345 |
+
raise RuntimeError(
|
346 |
+
f"Erreur SQL : {err} | Query : {query} | Params : {params}"
|
347 |
+
)
|
348 |
|
349 |
+
# def fetch_by_condition(self, table_name: str, condition: str, params: Tuple = ()) -> List[Tuple]:
|
350 |
+
# """Récupère les données d'une table avec une condition."""
|
351 |
+
# query = f"SELECT * FROM {table_name} WHERE {condition}"
|
352 |
+
# try:
|
353 |
+
# self.cursor.execute(query, params)
|
354 |
+
# return self.execute_safe(query, params, fetch=True)
|
355 |
+
# except psycopg2.Error as err:
|
356 |
+
# logger.error(f"Erreur de connexion : {err}")
|
357 |
+
# return
|
358 |
|
359 |
+
def fetch_by_condition(
|
360 |
+
self, table_name: str, condition: str, params: Tuple = ()
|
361 |
+
) -> List[Tuple]:
|
362 |
+
"""
|
363 |
+
Récupère les données d'une table avec une condition.
|
364 |
+
|
365 |
+
Args:
|
366 |
+
table_name (str): nom de la table de laquelle récupérer des données.
|
367 |
+
condition (str): condition qui sera inclue dans la clause WHERE pour filtrage.
|
368 |
+
params (Tuple): paramètres de la requête.
|
369 |
+
|
370 |
+
Returns:
|
371 |
+
List[Tuple]: résultats de la requête (si fetch est True), sinon None (via la fonction execute_safe).
|
372 |
+
"""
|
373 |
query = f"SELECT * FROM {table_name} WHERE {condition}"
|
374 |
try:
|
375 |
+
# Utilise execute_safe pour exécuter la requête et récupérer les résultats
|
376 |
return self.execute_safe(query, params, fetch=True)
|
377 |
+
except sqlite3.Error as err:
|
378 |
+
logger.error(
|
379 |
+
f"Erreur lors de la récupération des données de {table_name} avec condition '{condition}': {err}"
|
380 |
+
)
|
381 |
+
return []
|
382 |
+
|
383 |
+
# def update_data(self, table_name: str, set_clause: str, condition: str, params: Tuple) -> None:
|
384 |
+
# """Met à jour des données dans une table."""
|
385 |
+
# query = f"UPDATE {table_name} SET {set_clause} WHERE {condition}"
|
386 |
+
# try:
|
387 |
+
# self.cursor.execute(query, params)
|
388 |
+
# except psycopg2.Error as err:
|
389 |
+
# logger.error(f"Erreur de connexion : {err}")
|
390 |
+
# return
|
391 |
+
# finally:
|
392 |
+
# self.connection.commit()
|
393 |
|
394 |
+
def update_data(
|
395 |
+
self, table_name: str, set_clause: str, condition: str, params: Tuple
|
396 |
+
) -> None:
|
397 |
+
"""
|
398 |
+
Met à jour des données dans une table.
|
399 |
+
|
400 |
+
Args:
|
401 |
+
table_name (str): nom de la table dont les données vont être mises à jour.
|
402 |
+
set_clause (str): information qui sera inclue dans la clause SET pour la mise à jour.
|
403 |
+
condition (str): condition qui sera inclue dans la clause WHERE pour filtrage.
|
404 |
+
params (Tuple): paramètres de la requête.
|
405 |
+
"""
|
406 |
query = f"UPDATE {table_name} SET {set_clause} WHERE {condition}"
|
407 |
try:
|
408 |
self.cursor.execute(query, params)
|
409 |
+
except sqlite3.Error as err:
|
410 |
+
logger.error(
|
411 |
+
f"Erreur lors de la mise à jour des données dans {table_name} : {err}"
|
412 |
+
)
|
413 |
finally:
|
414 |
+
self.connection.commit() # Valider les modifications
|
415 |
+
|
416 |
+
# def delete_data(self, table_name: str, condition: str, params: Tuple) -> None:
|
417 |
+
# """Supprime des données d'une table selon une condition."""
|
418 |
+
# query = f"DELETE FROM {table_name} WHERE {condition}"
|
419 |
+
# try:
|
420 |
+
# self.cursor.execute(query, params)
|
421 |
+
# except psycopg2.Error as err:
|
422 |
+
# logger.error(f"Erreur de connexion : {err}")
|
423 |
+
# return
|
424 |
+
# finally:
|
425 |
+
# self.connection.commit()
|
426 |
|
427 |
def delete_data(self, table_name: str, condition: str, params: Tuple) -> None:
|
428 |
+
"""
|
429 |
+
Supprime des données d'une table selon une condition.
|
430 |
+
|
431 |
+
Args:
|
432 |
+
table_name (str): nom de la table dont les données vont être suprimées.
|
433 |
+
condition (str): condition qui sera inclue dans la clause WHERE pour filtrage.
|
434 |
+
params (Tuple): paramètres de la requête.
|
435 |
+
"""
|
436 |
query = f"DELETE FROM {table_name} WHERE {condition}"
|
437 |
try:
|
438 |
self.cursor.execute(query, params)
|
439 |
+
except sqlite3.Error as err:
|
440 |
+
logger.error(
|
441 |
+
f"Erreur lors de la suppression des données dans {table_name} : {err}"
|
442 |
+
)
|
443 |
finally:
|
444 |
+
self.connection.commit() # Valider les modifications
|
445 |
+
|
446 |
+
# def close_connection(self) -> None:
|
447 |
+
# """Ferme la connexion à la base de données."""
|
448 |
+
# if self.connection:
|
449 |
+
# self.cursor.close()
|
450 |
+
# self.connection.close()
|
451 |
|
452 |
def close_connection(self) -> None:
|
453 |
+
"""
|
454 |
+
Ferme la connexion à la base de données.
|
455 |
+
"""
|
456 |
if self.connection:
|
457 |
+
try:
|
458 |
+
self.cursor.close() # Fermer le curseur
|
459 |
+
self.connection.close() # Fermer la connexion
|
460 |
+
except sqlite3.Error as err:
|
461 |
+
logger.error(f"Erreur lors de la fermeture de la connexion : {err}")
|
462 |
+
|
463 |
+
# def create_index(self, table_name: str, column_name: str) -> None:
|
464 |
+
# """Crée un index sur une colonne spécifique pour améliorer les performances de recherche."""
|
465 |
+
# query = f"CREATE INDEX IF NOT EXISTS idx_{table_name}_{column_name} ON {table_name} ({column_name})"
|
466 |
+
# try:
|
467 |
+
# self.cursor.execute(query)
|
468 |
+
# except psycopg2.Error as err:
|
469 |
+
# logger.error(f"Erreur de connexion : {err}")
|
470 |
+
# return
|
471 |
+
# finally:
|
472 |
+
# self.connection.commit()
|
473 |
|
474 |
def create_index(self, table_name: str, column_name: str) -> None:
|
475 |
+
"""
|
476 |
+
Crée un index sur une colonne spécifique pour améliorer les performances de recherche.
|
477 |
+
"""
|
478 |
query = f"CREATE INDEX IF NOT EXISTS idx_{table_name}_{column_name} ON {table_name} ({column_name})"
|
479 |
try:
|
480 |
self.cursor.execute(query)
|
481 |
+
except sqlite3.Error as err:
|
482 |
+
logger.error(
|
483 |
+
f"Erreur lors de la création de l'index sur {table_name} ({column_name}) : {err}"
|
484 |
+
)
|
485 |
finally:
|
486 |
+
self.connection.commit() # Valider les modifications
|
487 |
+
|
488 |
+
# def select(self, query: str, params: Tuple = ()) -> List[Tuple]:
|
489 |
+
# """Exécute une requête SELECT personnalisée et retourne les résultats."""
|
490 |
+
# try:
|
491 |
+
# self.cursor.execute(query, params)
|
492 |
+
# return self.cursor.fetchall()
|
493 |
+
# except psycopg2.Error as err:
|
494 |
+
# logger.error(f"Erreur de connexion : {err}")
|
495 |
+
# return
|
496 |
|
497 |
def select(self, query: str, params: Tuple = ()) -> List[Tuple]:
|
498 |
+
"""
|
499 |
+
Exécute une requête SELECT personnalisée et retourne les résultats.
|
500 |
+
|
501 |
+
Args:
|
502 |
+
query (str): requête SELECT.
|
503 |
+
params (Tuple): paramètres de la requête.
|
504 |
+
|
505 |
+
Returns:
|
506 |
+
List[Tuple]: liste des enregistrements récupérés.
|
507 |
+
"""
|
508 |
try:
|
509 |
self.cursor.execute(query, params)
|
510 |
return self.cursor.fetchall()
|
511 |
+
except sqlite3.Error as err:
|
512 |
+
logger.error(f"Erreur lors de l'exécution de la requête SELECT : {err}")
|
513 |
+
return []
|
514 |
+
|
515 |
+
# def query(self, query, params=None):
|
516 |
+
# """
|
517 |
+
# Exécute une requête SQL, en utilisant les paramètres fournis,
|
518 |
+
# et retourne les résultats si nécessaire.
|
519 |
+
# """
|
520 |
+
# try:
|
521 |
+
# self.cursor.execute(query, params)
|
522 |
+
# except psycopg2.Error as err:
|
523 |
+
# logger.error(f"Erreur de connexion : {err}")
|
524 |
+
# return
|
525 |
+
# finally:
|
526 |
+
# # Si la requête est un SELECT, récupérer les résultats
|
527 |
+
# if query.strip().upper().startswith("SELECT"):
|
528 |
+
# return self.cursor.fetchall()
|
529 |
+
# else: # Si ce n'est pas un SELECT, ne rien retourner (utile pour INSERT/UPDATE)
|
530 |
+
# self.connection.commit()
|
531 |
+
# return None
|
532 |
+
|
533 |
+
def query(self, query: str, params: Tuple = None) -> List[Tuple]:
|
534 |
"""
|
535 |
+
Exécute une requête SQL, en utilisant les paramètres fournis, et retourne les résultats si nécessaire.
|
536 |
+
|
537 |
+
Args:
|
538 |
+
query (str): reqête SQL.
|
539 |
+
params (Tuple): paramètres de la requête.
|
540 |
+
|
541 |
+
Returns:
|
542 |
+
List[Tuple]: list des enregistrements récupérés s'il s'agit d'une requête SELECT, None sinon.
|
543 |
"""
|
544 |
try:
|
545 |
+
if params is None:
|
546 |
+
self.cursor.execute(query)
|
547 |
+
else:
|
548 |
+
self.cursor.execute(query, params)
|
549 |
+
except sqlite3.Error as err:
|
550 |
+
logger.error(f"Erreur lors de l'exécution de la requête : {err}")
|
551 |
return
|
552 |
finally:
|
553 |
# Si la requête est un SELECT, récupérer les résultats
|
554 |
if query.strip().upper().startswith("SELECT"):
|
555 |
return self.cursor.fetchall()
|
556 |
+
else: # Si ce n'est pas un SELECT, ne rien retourner (utile pour INSERT/UPDATE)
|
557 |
self.connection.commit()
|
558 |
+
return None
|
|
|
559 |
|
560 |
|
|
|
|
|
561 |
######################### FONCTIONS #########################
|
562 |
|
563 |
# Mettre DBManager en cache
|
564 |
@st.cache_resource
|
565 |
def get_db_manager():
|
566 |
+
return DBManager(db_config, os.path.join("server", "db", "schema.json"))
|
567 |
+
|
568 |
|
569 |
+
# def save_message(db_manager, id_conversation: int, role: str, content: str,temps_traitement, total_cout, impact_eco) -> None:
|
570 |
+
# """
|
571 |
+
# Sauvegarde un message dans la base de données, en associant l'utilisateur à la conversation.
|
572 |
|
573 |
+
# :param db_manager: Instance de DBManager.
|
574 |
+
# :param id_conversation: ID de la conversation associée.
|
575 |
+
# :param role: Rôle de l'intervenant (ex. 'user' ou 'assistant').
|
576 |
+
# :param content: Contenu du message.
|
577 |
+
# """
|
578 |
+
# timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
579 |
+
# data = [{
|
580 |
+
# "id_conversation": id_conversation,
|
581 |
+
# "role": role,
|
582 |
+
# "content": content,
|
583 |
+
# "timestamp": timestamp,
|
584 |
+
# "temps_traitement":temps_traitement,
|
585 |
+
# "total_cout": total_cout,
|
586 |
+
# "impact_eco": impact_eco
|
587 |
+
# }]
|
588 |
+
# try:
|
589 |
+
# db_manager.insert_data_from_dict("messages", data, id_column="id_message")
|
590 |
+
# except psycopg2.Error as err:
|
591 |
+
# logger.error(f"Erreur de connexion: {err}")
|
592 |
+
# return
|
593 |
+
|
594 |
+
|
595 |
+
def save_message(
|
596 |
+
db_manager,
|
597 |
+
id_conversation: int,
|
598 |
+
role: str,
|
599 |
+
content: str,
|
600 |
+
temps_traitement: float,
|
601 |
+
total_cout: float,
|
602 |
+
impact_eco: float,
|
603 |
+
) -> None:
|
604 |
"""
|
605 |
Sauvegarde un message dans la base de données, en associant l'utilisateur à la conversation.
|
606 |
|
607 |
+
Args:
|
608 |
+
db_manager: instance de DBManager.
|
609 |
+
id_conversation (int): ID de la conversation associée.
|
610 |
+
role (str): rôle de l'intervenant (ex. 'user' ou 'assistant').
|
611 |
+
content (str): contenu du message.
|
612 |
+
temps_traitement (float): temps de traitement.
|
613 |
+
total_cout (float): coût total associé au message.
|
614 |
+
impact_eco (float): impact économique du message.
|
615 |
"""
|
616 |
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
617 |
+
data = [
|
618 |
+
{
|
619 |
+
"id_conversation": id_conversation,
|
620 |
+
"role": role,
|
621 |
+
"content": content,
|
622 |
+
"timestamp": timestamp,
|
623 |
+
"temps_traitement": temps_traitement,
|
624 |
+
"total_cout": total_cout,
|
625 |
+
"impact_eco": impact_eco,
|
626 |
+
}
|
627 |
+
]
|
628 |
+
|
629 |
try:
|
630 |
+
# Insertion des données dans la table "messages" en utilisant la méthode d'insertion adaptée
|
631 |
+
db_manager.insert_data_from_dict("messages", data)
|
632 |
+
except sqlite3.Error as err: # Utilisation de sqlite3.Error pour gérer les exceptions SQLite
|
633 |
+
logger.error(f"Erreur lors de la sauvegarde du message : {err}")
|
634 |
+
return
|
635 |
+
|
636 |
+
|
637 |
+
# def create_conversation(db_manager, title: str, id_utilisateur: int) -> int:
|
638 |
+
# """
|
639 |
+
# Crée une nouvelle conversation dans la base de données, en associant l'utilisateur à la conversation.
|
640 |
+
|
641 |
+
# :param db_manager: Instance de DBManager.
|
642 |
+
# :param title: Titre de la conversation.
|
643 |
+
# :param id_utilisateur: ID de l'utilisateur associé.
|
644 |
+
# :return: ID de la conversation nouvellement créée.
|
645 |
+
# """
|
646 |
+
# created_at = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
647 |
+
# data = [{
|
648 |
+
# "created_at": created_at,
|
649 |
+
# "title": title,
|
650 |
+
# "id_utilisateur": id_utilisateur,
|
651 |
+
# }]
|
652 |
+
# try:
|
653 |
+
# result = db_manager.insert_data_from_dict("conversations", data, id_column="id_conversation")
|
654 |
+
# return result[0]
|
655 |
+
# except psycopg2.Error as err:
|
656 |
+
# logger.error(f"Erreur de connexion : {err}")
|
657 |
+
# return
|
658 |
+
|
659 |
|
660 |
def create_conversation(db_manager, title: str, id_utilisateur: int) -> int:
|
661 |
"""
|
662 |
Crée une nouvelle conversation dans la base de données, en associant l'utilisateur à la conversation.
|
663 |
|
664 |
+
Args:
|
665 |
+
db_manager: instance de DBManager.
|
666 |
+
title (str): titre de la conversation.
|
667 |
+
id_utilisateur (int): ID de l'utilisateur associé.
|
668 |
+
|
669 |
+
Returns:
|
670 |
+
int: ID de la conversation nouvellement créée.
|
671 |
+
|
672 |
"""
|
673 |
created_at = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
674 |
+
data = [
|
675 |
+
{"created_at": created_at, "title": title, "id_utilisateur": id_utilisateur,}
|
676 |
+
]
|
677 |
+
|
|
|
678 |
try:
|
679 |
+
result = db_manager.insert_data_from_dict("conversations", data)
|
680 |
+
return result[0] # Retourne l'ID de la conversation nouvellement créée
|
681 |
+
except sqlite3.Error as err: # Gestion des erreurs avec sqlite3
|
682 |
+
logger.error(f"Erreur lors de la création de la conversation : {err}")
|
683 |
+
return None
|
684 |
+
|
685 |
+
|
686 |
+
# def load_messages(db_manager, id_conversation: int) -> List[Dict]:
|
687 |
+
# """
|
688 |
+
# Charge les messages associés à une conversation.
|
689 |
+
|
690 |
+
# :param db_manager: Instance de DBManager.
|
691 |
+
# :param id_conversation: ID de la conversation.
|
692 |
+
# :return: Liste des messages sous forme de dictionnaires.
|
693 |
+
# """
|
694 |
+
# query = """
|
695 |
+
# SELECT *
|
696 |
+
# FROM messages
|
697 |
+
# WHERE id_conversation = %s
|
698 |
+
# ORDER BY timestamp ASC
|
699 |
+
# """
|
700 |
+
# try:
|
701 |
+
# result = db_manager.query(query, (id_conversation,))
|
702 |
+
# return [{"role": row["role"], "content": row["content"], "timestamp":row["timestamp"], "temps_traitement":row["temps_traitement"]} for row in result]
|
703 |
+
# except psycopg2.Error as err:
|
704 |
+
# logger.error(f"Erreur de connexion : {err}")
|
705 |
+
# return
|
706 |
+
|
707 |
|
708 |
def load_messages(db_manager, id_conversation: int) -> List[Dict]:
|
709 |
"""
|
710 |
Charge les messages associés à une conversation.
|
711 |
|
712 |
+
Args:
|
713 |
+
db_manager: instance de DBManager.
|
714 |
+
id_conversation (int): ID de la conversation.
|
715 |
+
Returns:
|
716 |
+
List[Dict]: liste des messages sous forme de dictionnaires.
|
717 |
"""
|
718 |
query = """
|
719 |
+
SELECT role, content, timestamp, temps_traitement
|
720 |
FROM messages
|
721 |
+
WHERE id_conversation = ?
|
722 |
ORDER BY timestamp ASC
|
723 |
"""
|
724 |
try:
|
725 |
result = db_manager.query(query, (id_conversation,))
|
726 |
+
# Résultats sous forme de dictionnaires, si la fonction query retourne des tuples ou dictionnaires
|
727 |
+
return [
|
728 |
+
{
|
729 |
+
"role": row["role"],
|
730 |
+
"content": row["content"],
|
731 |
+
"timestamp": row["timestamp"],
|
732 |
+
"temps_traitement": row["temps_traitement"],
|
733 |
+
}
|
734 |
+
for row in result
|
735 |
+
]
|
736 |
+
except sqlite3.Error as err: # Utilisation de sqlite3.Error pour capturer les erreurs SQLite
|
737 |
+
logger.error(f"Erreur lors du chargement des messages : {err}")
|
738 |
+
return []
|
739 |
+
|
740 |
+
|
741 |
+
# def load_conversations(db_manager, id_utilisateur: int) -> List[Dict]:
|
742 |
+
# """
|
743 |
+
# Charge toutes les conversations enregistrées pour un utilisateur donné.
|
744 |
+
|
745 |
+
# :param db_manager: Instance de DBManager.
|
746 |
+
# :param id_utilisateur: ID de l'utilisateur.
|
747 |
+
# :return: Liste des conversations.
|
748 |
+
# """
|
749 |
+
# query = """
|
750 |
+
# SELECT * FROM conversations
|
751 |
+
# WHERE id_utilisateur = %s
|
752 |
+
# ORDER BY created_at DESC
|
753 |
+
# """
|
754 |
+
# try:
|
755 |
+
# result = db_manager.query(query, (id_utilisateur,))
|
756 |
+
|
757 |
+
|
758 |
+
# return [
|
759 |
+
# {"id_conversation": row["id_conversation"], "created_at": row["created_at"], "title": row["title"]} for row in result
|
760 |
+
# ]
|
761 |
+
# except psycopg2.Error as err:
|
762 |
+
# logger.error(f"Erreur de connexion : {err}")
|
763 |
+
# return
|
764 |
+
|
765 |
|
766 |
def load_conversations(db_manager, id_utilisateur: int) -> List[Dict]:
|
767 |
"""
|
768 |
Charge toutes les conversations enregistrées pour un utilisateur donné.
|
769 |
|
770 |
+
Args:
|
771 |
+
db_manager: instance de DBManager.
|
772 |
+
id_utilisateur (int): ID de l'utilisateur.
|
773 |
+
Returns:
|
774 |
+
List[Dict]: liste des conversations.
|
775 |
"""
|
776 |
query = """
|
777 |
+
SELECT id_conversation, created_at, title
|
778 |
+
FROM conversations
|
779 |
+
WHERE id_utilisateur = ?
|
780 |
ORDER BY created_at DESC
|
781 |
"""
|
782 |
try:
|
783 |
result = db_manager.query(query, (id_utilisateur,))
|
|
|
|
|
784 |
return [
|
785 |
+
{
|
786 |
+
"id_conversation": row["id_conversation"],
|
787 |
+
"created_at": row["created_at"],
|
788 |
+
"title": row["title"],
|
789 |
+
}
|
790 |
+
for row in result
|
791 |
]
|
792 |
+
except sqlite3.Error as err: # Gestion des erreurs avec sqlite3
|
793 |
+
logger.error(f"Erreur lors du chargement des conversations : {err}")
|
794 |
+
return []
|
795 |
+
|
796 |
+
|
797 |
+
# def update_conversation(db_manager, id_conversation: int, id_utilisateur: int) -> None:
|
798 |
+
# """
|
799 |
+
# Met à jour le champ `created_at` d'une conversation donnée pour un utilisateur.
|
800 |
+
|
801 |
+
# :param db_manager: Instance de DBManager.
|
802 |
+
# :param id_conversation: ID de la conversation à mettre à jour.
|
803 |
+
# :param id_utilisateur: ID de l'utilisateur.
|
804 |
+
# """
|
805 |
+
# new_timer = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
806 |
+
# query = """
|
807 |
+
# UPDATE conversations
|
808 |
+
# SET created_at = %s
|
809 |
+
# WHERE id_conversation = %s AND id_utilisateur = %s
|
810 |
+
# """
|
811 |
+
# try:
|
812 |
+
# db_manager.query(query, (new_timer, id_conversation, id_utilisateur))
|
813 |
+
# except psycopg2.Error as err:
|
814 |
+
# logger.error(f"Erreur de connexion : {err}")
|
815 |
+
# return
|
816 |
+
|
817 |
|
818 |
def update_conversation(db_manager, id_conversation: int, id_utilisateur: int) -> None:
|
819 |
"""
|
820 |
Met à jour le champ `created_at` d'une conversation donnée pour un utilisateur.
|
821 |
|
822 |
+
Args:
|
823 |
+
db_manager: instance de DBManager.
|
824 |
+
id_conversation (int): ID de la conversation à mettre à jour.
|
825 |
+
id_utilisateur (int): ID de l'utilisateur.
|
826 |
"""
|
827 |
new_timer = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
828 |
query = """
|
829 |
UPDATE conversations
|
830 |
+
SET created_at = ?
|
831 |
+
WHERE id_conversation = ? AND id_utilisateur = ?
|
832 |
"""
|
833 |
try:
|
834 |
db_manager.query(query, (new_timer, id_conversation, id_utilisateur))
|
835 |
+
except sqlite3.Error as err: # Utilisation de sqlite3.Error pour capturer les erreurs SQLite
|
836 |
+
logger.error(f"Erreur lors de la mise à jour de la conversation : {err}")
|
837 |
return
|
838 |
|
839 |
+
|
840 |
+
# def update_conversation_title(db_manager, id_conversation: int, new_title: str) -> None:
|
841 |
+
# """
|
842 |
+
# Met à jour le titre d'une conversation si celui-ci est encore "Nouvelle conversation".
|
843 |
+
|
844 |
+
# :param db_manager: Instance de DBManager.
|
845 |
+
# :param id_conversation: ID de la conversation à mettre à jour.
|
846 |
+
# :param new_title: Nouveau titre de la conversation.
|
847 |
+
# """
|
848 |
+
# query = """
|
849 |
+
# UPDATE conversations
|
850 |
+
# SET title = %s
|
851 |
+
# WHERE id_conversation = %s AND title = 'Nouvelle conversation'
|
852 |
+
# """
|
853 |
+
# try:
|
854 |
+
# db_manager.query(query, (new_title, id_conversation))
|
855 |
+
# except psycopg2.Error as err:
|
856 |
+
# logger.error(f"Erreur de connexion : {err}")
|
857 |
+
# return
|
858 |
+
|
859 |
+
|
860 |
def update_conversation_title(db_manager, id_conversation: int, new_title: str) -> None:
|
861 |
"""
|
862 |
Met à jour le titre d'une conversation si celui-ci est encore "Nouvelle conversation".
|
863 |
|
864 |
+
Args:
|
865 |
+
db_manager: instance de DBManager.
|
866 |
+
id_conversation (int): ID de la conversation à mettre à jour.
|
867 |
+
new_title (str): nouveau titre de la conversation.
|
868 |
"""
|
869 |
query = """
|
870 |
UPDATE conversations
|
871 |
+
SET title = ?
|
872 |
+
WHERE id_conversation = ? AND title = 'Nouvelle conversation'
|
873 |
"""
|
874 |
try:
|
875 |
db_manager.query(query, (new_title, id_conversation))
|
876 |
+
except sqlite3.Error as err: # Utilisation de sqlite3.Error pour gérer les erreurs SQLite
|
877 |
+
logger.error(
|
878 |
+
f"Erreur lors de la mise à jour du titre de la conversation : {err}"
|
879 |
+
)
|
880 |
return
|
881 |
|
882 |
+
|
883 |
+
# def get_conversation_title(db_manager, id_conversation: int) -> str:
|
884 |
+
# """
|
885 |
+
# Récupère le titre d'une conversation spécifique en utilisant `fetch_by_condition`.
|
886 |
+
|
887 |
+
# :param db_manager: Instance de DBManager.
|
888 |
+
# :param id_conversation: ID de la conversation à interroger.
|
889 |
+
# :return: Le titre de la conversation ou "Nouvelle conversation".
|
890 |
+
# """
|
891 |
+
# table_name = "conversations"
|
892 |
+
# condition = "id_conversation = %s"
|
893 |
+
# try:
|
894 |
+
# results = db_manager.fetch_by_condition(table_name, condition, (id_conversation,))
|
895 |
+
# if results:
|
896 |
+
# # Suppose que `title` est la troisième colonne
|
897 |
+
# return results[0][2]
|
898 |
+
# return "Nouvelle conversation"
|
899 |
+
# except psycopg2.Error as err:
|
900 |
+
# logger.error(f"Erreur de connexion : {err}")
|
901 |
+
# return
|
902 |
+
|
903 |
+
|
904 |
def get_conversation_title(db_manager, id_conversation: int) -> str:
|
905 |
"""
|
906 |
Récupère le titre d'une conversation spécifique en utilisant `fetch_by_condition`.
|
907 |
|
908 |
+
Args:
|
909 |
+
db_manager: instance de DBManager.
|
910 |
+
id_conversation (int): ID de la conversation à interroger.
|
911 |
+
|
912 |
+
Returns:
|
913 |
+
str: le titre de la conversation ou "Nouvelle conversation".
|
914 |
"""
|
915 |
table_name = "conversations"
|
916 |
+
condition = "id_conversation = ?"
|
917 |
try:
|
918 |
+
results = db_manager.fetch_by_condition(
|
919 |
+
table_name, condition, (id_conversation,)
|
920 |
+
)
|
921 |
if results:
|
922 |
+
# Assume that `title` is the second column
|
923 |
+
return results[0][
|
924 |
+
1
|
925 |
+
] # 1 corresponds to the index of the title column in the result
|
926 |
return "Nouvelle conversation"
|
927 |
+
except sqlite3.Error as err: # Utilisation de sqlite3.Error pour gérer les erreurs SQLite
|
928 |
+
logger.error(
|
929 |
+
f"Erreur lors de la récupération du titre de la conversation : {err}"
|
930 |
+
)
|
931 |
+
return "Nouvelle conversation"
|
932 |
+
|
933 |
+
|
934 |
+
# def delete_conversation(db_manager, id_conversation: int) -> None:
|
935 |
+
# """
|
936 |
+
# Supprime une conversation et ses messages associés de la base de données.
|
937 |
+
|
938 |
+
# :param db_manager: Instance de DBManager.
|
939 |
+
# :param id_conversation: ID de la conversation à supprimer.
|
940 |
+
# """
|
941 |
+
# try:
|
942 |
+
# # Supprimer les messages liés à la conversation
|
943 |
+
# query_delete_messages = "DELETE FROM messages WHERE id_conversation = %s"
|
944 |
+
# db_manager.query(query_delete_messages, (id_conversation,))
|
945 |
+
|
946 |
+
# # Supprimer la conversation elle-même
|
947 |
+
# query_delete_conversation = "DELETE FROM conversations WHERE id_conversation = %s"
|
948 |
+
# db_manager.query(query_delete_conversation, (id_conversation,))
|
949 |
+
|
950 |
+
# print(f"✅ Conversation {id_conversation} supprimée avec succès.")
|
951 |
+
# except Exception as e:
|
952 |
+
# print(f"❌ Erreur lors de la suppression de la conversation {id_conversation}: {e}")
|
953 |
+
|
954 |
|
955 |
def delete_conversation(db_manager, id_conversation: int) -> None:
|
956 |
"""
|
957 |
Supprime une conversation et ses messages associés de la base de données.
|
958 |
|
959 |
+
Args:
|
960 |
+
db_manager: instance de DBManager.
|
961 |
+
id_conversation (int): ID de la conversation à supprimer.
|
962 |
"""
|
963 |
try:
|
964 |
# Supprimer les messages liés à la conversation
|
965 |
+
query_delete_messages = "DELETE FROM messages WHERE id_conversation = ?"
|
966 |
db_manager.query(query_delete_messages, (id_conversation,))
|
967 |
|
968 |
# Supprimer la conversation elle-même
|
969 |
+
query_delete_conversation = (
|
970 |
+
"DELETE FROM conversations WHERE id_conversation = ?"
|
971 |
+
)
|
972 |
db_manager.query(query_delete_conversation, (id_conversation,))
|
973 |
|
974 |
+
logger.info(f"✅ Conversation {id_conversation} supprimée avec succès.")
|
975 |
+
# print(f"✅ Conversation {id_conversation} supprimée avec succès.")
|
976 |
+
except sqlite3.Error as e: # Utilisation de sqlite3.Error pour capturer les erreurs SQLite
|
977 |
+
logger.error(
|
978 |
+
f"❌ Erreur lors de la suppression de la conversation {id_conversation}: {e}"
|
979 |
+
)
|
980 |
+
# print(f"❌ Erreur lors de la suppression de la conversation {id_conversation}: {e}")
|
981 |
+
|
982 |
+
|
983 |
+
# def load_chatbot_suggestions(db_manager, user_id):
|
984 |
+
# """
|
985 |
+
# Charge les suggestions du chatbot enregistrées pour un utilisateur donné.
|
986 |
+
# """
|
987 |
+
# query = "SELECT repas_suggestion FROM suggestions_repas WHERE id_utilisateur = %s"
|
988 |
+
# try:
|
989 |
+
# db_manager.cursor.execute(query, (user_id,))
|
990 |
+
# suggestions = [row[0] for row in db_manager.cursor.fetchall()]
|
991 |
+
# return suggestions
|
992 |
+
# except psycopg2.Error as err:
|
993 |
+
# logger.error(f"Erreur lors du chargement des suggestions : {err}")
|
994 |
+
# return []
|
995 |
|
996 |
+
|
997 |
+
def load_chatbot_suggestions(db_manager, user_id: str) -> List[Tuple]:
|
998 |
"""
|
999 |
Charge les suggestions du chatbot enregistrées pour un utilisateur donné.
|
1000 |
+
|
1001 |
+
Args:
|
1002 |
+
db_manager: instance de DBManager.
|
1003 |
+
user_id (int): ID de l'utilisateur.
|
1004 |
+
|
1005 |
+
Returns:
|
1006 |
+
List[Tuple]: list des suggestions du chatbot.
|
1007 |
"""
|
1008 |
+
query = "SELECT repas_suggestion FROM suggestions_repas WHERE id_utilisateur = ?"
|
1009 |
try:
|
1010 |
db_manager.cursor.execute(query, (user_id,))
|
1011 |
suggestions = [row[0] for row in db_manager.cursor.fetchall()]
|
1012 |
return suggestions
|
1013 |
+
except sqlite3.Error as err: # Utilisation de sqlite3.Error pour capturer les erreurs SQLite
|
1014 |
logger.error(f"Erreur lors du chargement des suggestions : {err}")
|
1015 |
return []
|
1016 |
|
1017 |
|
1018 |
+
# def save_chatbot_suggestions(db_manager, user_id, suggestions):
|
1019 |
+
# """
|
1020 |
+
# Sauvegarde les suggestions du chatbot dans la base de données.
|
1021 |
+
# """
|
1022 |
+
# query = """
|
1023 |
+
# INSERT INTO suggestions_repas (id_utilisateur, repas_suggestion, motif_suggestion)
|
1024 |
+
# VALUES (%s, %s, %s)
|
1025 |
+
# """
|
1026 |
+
# try:
|
1027 |
+
# for suggestion in suggestions:
|
1028 |
+
# db_manager.cursor.execute(query, (user_id, suggestion, "Chatbot"))
|
1029 |
+
# db_manager.connection.commit()
|
1030 |
+
# except psycopg2.Error as err:
|
1031 |
+
# logger.error(f"Erreur lors de l'enregistrement des suggestions : {err}")
|
1032 |
|
1033 |
+
|
1034 |
+
def save_chatbot_suggestions(db_manager, user_id, suggestions: List[Tuple]):
|
1035 |
"""
|
1036 |
Sauvegarde les suggestions du chatbot dans la base de données.
|
1037 |
+
|
1038 |
+
Args:
|
1039 |
+
db_manager: instance de DBManager.
|
1040 |
+
user_id (int): ID de l'utilisateur.
|
1041 |
+
suggestions (List[Tuple]): list des suggestions du chatbot.
|
1042 |
+
|
1043 |
"""
|
1044 |
query = """
|
1045 |
INSERT INTO suggestions_repas (id_utilisateur, repas_suggestion, motif_suggestion)
|
1046 |
+
VALUES (?, ?, ?)
|
1047 |
"""
|
1048 |
try:
|
1049 |
for suggestion in suggestions:
|
1050 |
db_manager.cursor.execute(query, (user_id, suggestion, "Chatbot"))
|
1051 |
db_manager.connection.commit()
|
1052 |
+
except sqlite3.Error as err: # Remplacer psycopg2.Error par sqlite3.Error pour SQLite
|
1053 |
logger.error(f"Erreur lors de l'enregistrement des suggestions : {err}")
|
|
|
|
server/db/schema.json
CHANGED
@@ -1,85 +1,3 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
"columns": [
|
5 |
-
{"name": "id_utilisateur", "type": "SERIAL", "constraints": ["PRIMARY KEY"]},
|
6 |
-
{"name": "login", "type": "TEXT", "constraints": ["UNIQUE", "NOT NULL"]},
|
7 |
-
{"name": "mot_de_passe", "type": "TEXT", "constraints": ["NOT NULL"]},
|
8 |
-
{"name": "nom", "type": "TEXT", "constraints": []},
|
9 |
-
{"name": "objectifs_nutritionnels", "type": "TEXT", "constraints": []},
|
10 |
-
{"name": "poids", "type": "INTEGER", "constraints": []},
|
11 |
-
{"name": "Taille", "type": "INTEGER", "constraints": []},
|
12 |
-
{"name": "activite_physique", "type": "TEXT", "constraints": []},
|
13 |
-
{"name": "objectif_calorique", "type": "TEXT", "constraints": []},
|
14 |
-
{"name": "regime_particulier", "type": "TEXT", "constraints": []},
|
15 |
-
{"name": "email", "type": "TEXT", "constraints": []},
|
16 |
-
{"name": "date_creation", "type": "TIMESTAMP", "constraints": ["DEFAULT CURRENT_TIMESTAMP"]},
|
17 |
-
{"name": "date_derniere_connexion", "type": "TIMESTAMP", "constraints": []}
|
18 |
-
]
|
19 |
-
},
|
20 |
-
"recettes": {
|
21 |
-
"columns": [
|
22 |
-
{"name": "id_recette", "type": "SERIAL", "constraints": ["PRIMARY KEY"]},
|
23 |
-
{"name": "titre", "type": "TEXT", "constraints": ["NOT NULL"]},
|
24 |
-
{"name": "infos", "type": "TEXT", "constraints": []},
|
25 |
-
{"name": "ingredients", "type": "TEXT", "constraints": ["NOT NULL"]},
|
26 |
-
{"name": "instructions", "type": "TEXT", "constraints": ["NOT NULL"]},
|
27 |
-
{"name": "temps_preparation", "type": "TEXT", "constraints": []},
|
28 |
-
{"name": "infos_regime", "type": "TEXT", "constraints": []},
|
29 |
-
{"name": "valeurs_100g", "type": "TEXT", "constraints": []},
|
30 |
-
{"name": "valeurs_portion", "type": "TEXT", "constraints": []},
|
31 |
-
{"name": "all_infos", "type": "TEXT", "constraints": []},
|
32 |
-
{"name": "cleaned_infos", "type": "TEXT", "constraints": []},
|
33 |
-
{"name": "cluster2", "type": "INTEGER", "constraints": []}
|
34 |
-
]
|
35 |
-
},
|
36 |
-
"historique_repas": {
|
37 |
-
"columns": [
|
38 |
-
{"name": "id_historique", "type": "SERIAL", "constraints": ["PRIMARY KEY"]},
|
39 |
-
{"name": "id_utilisateur", "type": "INTEGER", "constraints": ["NOT NULL", "REFERENCES utilisateurs(id_utilisateur)"]},
|
40 |
-
{"name": "id_recette", "type": "INTEGER", "constraints": ["NOT NULL", "REFERENCES recettes(id_recette)"]},
|
41 |
-
{"name": "date_heure", "type": "TIMESTAMP", "constraints": ["DEFAULT CURRENT_TIMESTAMP"]},
|
42 |
-
{"name": "quantite", "type": "INTEGER", "constraints": []}
|
43 |
-
]
|
44 |
-
},
|
45 |
-
"conversations": {
|
46 |
-
"columns": [
|
47 |
-
{"name": "id_conversation", "type": "SERIAL", "constraints": ["PRIMARY KEY"]},
|
48 |
-
{"name": "id_utilisateur", "type": "INTEGER", "constraints": ["NOT NULL", "REFERENCES utilisateurs(id_utilisateur)"]},
|
49 |
-
{"name": "title", "type": "TEXT", "constraints": ["NOT NULL"]},
|
50 |
-
{"name": "created_at", "type": "TIMESTAMP", "constraints": ["NOT NULL"]}
|
51 |
-
]
|
52 |
-
},
|
53 |
-
"messages": {
|
54 |
-
"columns": [
|
55 |
-
{"name": "id_message", "type": "SERIAL", "constraints": ["PRIMARY KEY"]},
|
56 |
-
{"name": "id_conversation", "type": "INTEGER", "constraints": ["NOT NULL", "REFERENCES conversations(id_conversation)"]},
|
57 |
-
{"name": "role", "type": "TEXT", "constraints": ["NOT NULL"]},
|
58 |
-
{"name": "content", "type": "TEXT", "constraints": ["NOT NULL"]},
|
59 |
-
{"name": "timestamp", "type": "TIMESTAMP", "constraints": ["DEFAULT CURRENT_TIMESTAMP"]},
|
60 |
-
{"name": "temps_traitement", "type": "REAL", "constraints": []},
|
61 |
-
{"name": "contexte", "type": "TEXT", "constraints": []},
|
62 |
-
{"name": "total_cout", "type": "REAL", "constraints": []},
|
63 |
-
{"name": "impact_eco", "type": "REAL", "constraints": []}
|
64 |
-
]
|
65 |
-
},
|
66 |
-
"liste_courses": {
|
67 |
-
"columns": [
|
68 |
-
{"name": "id_liste", "type": "SERIAL", "constraints": ["PRIMARY KEY"]},
|
69 |
-
{"name": "id_utilisateur", "type": "INTEGER", "constraints": ["NOT NULL", "REFERENCES utilisateurs(id_utilisateur)"]},
|
70 |
-
{"name": "ingredients", "type": "TEXT", "constraints": ["NOT NULL"]},
|
71 |
-
{"name": "date_creation", "type": "TIMESTAMP", "constraints": ["DEFAULT CURRENT_TIMESTAMP"]},
|
72 |
-
{"name": "status", "type": "TEXT", "constraints": []}
|
73 |
-
]
|
74 |
-
},
|
75 |
-
"suggestions_repas": {
|
76 |
-
"columns": [
|
77 |
-
{"name": "id_suggestion", "type": "SERIAL", "constraints": ["PRIMARY KEY"]},
|
78 |
-
{"name": "id_utilisateur", "type": "INTEGER", "constraints": ["NOT NULL", "REFERENCES utilisateurs(id_utilisateur)"]},
|
79 |
-
{"name": "repas_suggestion", "type": "TEXT", "constraints": ["NOT NULL"]},
|
80 |
-
{"name": "date_heure", "type": "TIMESTAMP", "constraints": ["DEFAULT CURRENT_TIMESTAMP"]},
|
81 |
-
{"name": "motif_suggestion", "type": "TEXT", "constraints": []}
|
82 |
-
]
|
83 |
-
}
|
84 |
-
}
|
85 |
-
}
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:92b0932c8f60048b055922f2022bccf01e3558d75e547e3a0b0a22634328ae4d
|
3 |
+
size 4830
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
server/db/schema_postgreSQL.json
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:97dd9dd3388d2749d3690c7f6fa7bc2ae4e3d91eca472f89b7f8016620c99c02
|
3 |
+
size 5018
|