Spaces:
Runtime error
Runtime error
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
|