persee-tech commited on
Commit
093f21c
·
verified ·
1 Parent(s): 993f1a0

Update backend/server.py

Browse files
Files changed (1) hide show
  1. backend/server.py +66 -43
backend/server.py CHANGED
@@ -12,17 +12,17 @@ import time
12
  from deepface import DeepFace
13
  from supabase import create_client, Client
14
 
15
- # --- SETUP SUPABASE ---
16
  SUPABASE_URL = os.getenv("SUPABASE_URL", "https://gwjrwejdjpctizolfkcz.supabase.co")
17
  SUPABASE_KEY = os.getenv("SUPABASE_KEY", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imd3anJ3ZWpkanBjdGl6b2xma2N6Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc2OTA5ODEyNCwiZXhwIjoyMDg0Njc0MTI0fQ.EjU1DGTN-jrdkaC6nJWilFtYZgtu-NKjnfiMVMnHal0")
18
 
19
  try:
20
  supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
21
- print("☁️ SUPABASE OK")
22
  except Exception as e:
23
  print(f"❌ ERREUR SUPABASE: {e}")
24
 
25
- # --- SOCKET SETUP (La config qui marche !) ---
26
  sio = socketio.AsyncServer(
27
  async_mode='asgi',
28
  cors_allowed_origins='*',
@@ -34,16 +34,13 @@ app = FastAPI()
34
  app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
35
  socket_app = socketio.ASGIApp(sio, app)
36
 
37
- # --- API REST (RETOUR DE L'ADMIN) ---
38
  @app.get("/api/sessions")
39
  def get_sessions():
40
- # Récupère l'historique pour la page Admin
41
  try:
42
  response = supabase.table('sessions').select("*").order('id', desc=True).execute()
43
  return response.data
44
- except Exception as e:
45
- print(f"API Error: {e}")
46
- return []
47
 
48
  @app.get("/api/sessions/{session_id}")
49
  def get_session_details(session_id: int):
@@ -63,18 +60,17 @@ def delete_session(session_id: int):
63
  except Exception as e:
64
  raise HTTPException(status_code=500, detail=str(e))
65
 
66
- # --- GLOBAL STATE ---
67
  sessions = {}
68
 
69
- # --- EVENTS SOCKET ---
70
-
71
  @sio.event
72
  async def connect(sid, environ):
73
- print(f"✅ CONNECTÉ : {sid}")
74
  sessions[sid] = {
75
  "active": False,
76
  "start_time": 0,
77
- "db_id": None
 
78
  }
79
 
80
  @sio.event
@@ -83,13 +79,14 @@ async def disconnect(sid):
83
 
84
  @sio.event
85
  async def start_session(sid, data):
86
- print(f"▶️ START : {sid}")
87
  if sid in sessions:
88
  sessions[sid]["active"] = True
89
- sessions[sid]["start_time"] = time.time() # On note l'heure de départ
 
90
 
91
- # Création dans la DB
92
  try:
 
93
  new_session = {
94
  "first_name": data.get('firstName', 'Inconnu'),
95
  "last_name": data.get('lastName', ''),
@@ -97,79 +94,105 @@ async def start_session(sid, data):
97
  }
98
  res = supabase.table('sessions').insert(new_session).execute()
99
  sessions[sid]["db_id"] = res.data[0]['id']
100
- print(f"💾 Session sauvée en DB : ID {sessions[sid]['db_id']}")
101
  except Exception as e:
102
- print(f"⚠️ Erreur DB: {e}")
103
 
104
  @sio.event
105
  async def stop_session(sid):
106
- print(f"⏹️ STOP : {sid}")
107
- if sid in sessions:
108
- sessions[sid]["active"] = False
109
 
110
  @sio.event
111
  async def process_frame(sid, data_uri):
112
  if sid not in sessions: return
113
 
114
  try:
115
- # 1. Décodage
116
  encoded_data = data_uri.split(',')[1]
117
  nparr = np.frombuffer(base64.b64decode(encoded_data), np.uint8)
118
  frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
119
 
120
- # 2. Analyse DeepFace
121
- # enforce_detection=False permet de ne pas planter si le visage est flou
122
  result = DeepFace.analyze(frame, actions=['emotion'], enforce_detection=False, silent=True)
123
  data = result[0] if isinstance(result, list) else result
 
124
  emotion = data['dominant_emotion']
 
125
 
126
- # 3. Calculs KPIS & Temps
127
- # Calcul du temps écoulé
128
  current_time = 0
129
  if sessions[sid]["active"]:
130
  current_time = int(time.time() - sessions[sid]["start_time"])
131
 
132
- # Logique des carrés visages
133
  region = data['region']
134
  face_coords = {'x': region['x'], 'y': region['y'], 'w': region['w'], 'h': region['h']} if region['w'] > 0 else None
135
 
136
- # Logique Fake KPIs intelligente
137
  valence = 0.8 if emotion == "happy" else (-0.6 if emotion in ["sad", "angry", "fear"] else 0.0)
138
  arousal = 0.8 if emotion in ["angry", "fear", "surprise"] else 0.3
139
 
140
  def clamp(n): return max(0, min(100, int(n)))
 
 
 
 
 
 
 
 
 
 
 
141
  metrics = {
142
- "engagement": clamp((arousal * 100) + random.uniform(0, 10)),
143
- "satisfaction": clamp(((valence + 1) / 2) * 100),
144
- "trust": clamp(50 + (valence * 20)),
145
- "loyalty": clamp(50 + (valence * 10)),
146
- "opinion": clamp(((valence + 1) / 2) * 100)
147
  }
148
 
149
- # 4. Envoi Réponse
150
  payload = {
151
  "emotion": emotion,
152
  "face_coords": face_coords,
153
  "metrics": metrics,
154
- "session_time": current_time, # LE TEMPS EST MAINTENANT CALCULÉ
155
  "is_recording": sessions[sid]["active"]
156
  }
157
  await sio.emit('metrics_update', payload, room=sid)
158
 
159
- # 5. Sauvegarde en DB (toutes les secondes environ pour ne pas spammer)
160
- if sessions[sid]["active"] and sessions[sid]["db_id"] and current_time % 1 == 0:
161
- # On pourrait ajouter une logique ici pour ne pas sauvegarder à chaque frame
162
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
  except Exception as e:
165
- # On ignore les erreurs d'analyse pour fluidifier
166
- pass
167
 
168
  if __name__ == "__main__":
169
- # Pré-chargement au démarrage
170
  try:
171
  DeepFace.build_model("Emotion")
172
  print("✅ Modèle chargé !")
173
  except: pass
174
-
175
  uvicorn.run(socket_app, host="0.0.0.0", port=7860)
 
12
  from deepface import DeepFace
13
  from supabase import create_client, Client
14
 
15
+ # --- 1. SETUP SUPABASE ---
16
  SUPABASE_URL = os.getenv("SUPABASE_URL", "https://gwjrwejdjpctizolfkcz.supabase.co")
17
  SUPABASE_KEY = os.getenv("SUPABASE_KEY", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imd3anJ3ZWpkanBjdGl6b2xma2N6Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc2OTA5ODEyNCwiZXhwIjoyMDg0Njc0MTI0fQ.EjU1DGTN-jrdkaC6nJWilFtYZgtu-NKjnfiMVMnHal0")
18
 
19
  try:
20
  supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
21
+ print("☁️ SUPABASE CONNECTÉ")
22
  except Exception as e:
23
  print(f"❌ ERREUR SUPABASE: {e}")
24
 
25
+ # --- 2. CONFIG SOCKET ---
26
  sio = socketio.AsyncServer(
27
  async_mode='asgi',
28
  cors_allowed_origins='*',
 
34
  app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
35
  socket_app = socketio.ASGIApp(sio, app)
36
 
37
+ # --- 3. API ADMIN ---
38
  @app.get("/api/sessions")
39
  def get_sessions():
 
40
  try:
41
  response = supabase.table('sessions').select("*").order('id', desc=True).execute()
42
  return response.data
43
+ except: return []
 
 
44
 
45
  @app.get("/api/sessions/{session_id}")
46
  def get_session_details(session_id: int):
 
60
  except Exception as e:
61
  raise HTTPException(status_code=500, detail=str(e))
62
 
63
+ # --- 4. GESTION DES SESSIONS ---
64
  sessions = {}
65
 
 
 
66
  @sio.event
67
  async def connect(sid, environ):
68
+ print(f"✅ Client: {sid}")
69
  sessions[sid] = {
70
  "active": False,
71
  "start_time": 0,
72
+ "db_id": None,
73
+ "last_save_time": 0 # Pour éviter de saturer la DB
74
  }
75
 
76
  @sio.event
 
79
 
80
  @sio.event
81
  async def start_session(sid, data):
82
+ print(f"▶️ START: {sid}")
83
  if sid in sessions:
84
  sessions[sid]["active"] = True
85
+ sessions[sid]["start_time"] = time.time()
86
+ sessions[sid]["last_save_time"] = 0
87
 
 
88
  try:
89
+ # Création de la ligne "Session"
90
  new_session = {
91
  "first_name": data.get('firstName', 'Inconnu'),
92
  "last_name": data.get('lastName', ''),
 
94
  }
95
  res = supabase.table('sessions').insert(new_session).execute()
96
  sessions[sid]["db_id"] = res.data[0]['id']
97
+ print(f"💾 Session ID {sessions[sid]['db_id']} créée.")
98
  except Exception as e:
99
+ print(f"⚠️ Erreur Création Session: {e}")
100
 
101
  @sio.event
102
  async def stop_session(sid):
103
+ print(f"⏹️ STOP: {sid}")
104
+ if sid in sessions: sessions[sid]["active"] = False
 
105
 
106
  @sio.event
107
  async def process_frame(sid, data_uri):
108
  if sid not in sessions: return
109
 
110
  try:
111
+ # A. Décodage
112
  encoded_data = data_uri.split(',')[1]
113
  nparr = np.frombuffer(base64.b64decode(encoded_data), np.uint8)
114
  frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
115
 
116
+ # B. Analyse IA
 
117
  result = DeepFace.analyze(frame, actions=['emotion'], enforce_detection=False, silent=True)
118
  data = result[0] if isinstance(result, list) else result
119
+
120
  emotion = data['dominant_emotion']
121
+ emotion_score = data['emotion'][emotion] # On récupère le score de confiance (ex: 98.5)
122
 
123
+ # C. Calcul KPIs & Temps
 
124
  current_time = 0
125
  if sessions[sid]["active"]:
126
  current_time = int(time.time() - sessions[sid]["start_time"])
127
 
128
+ # Coordonnées visage
129
  region = data['region']
130
  face_coords = {'x': region['x'], 'y': region['y'], 'w': region['w'], 'h': region['h']} if region['w'] > 0 else None
131
 
132
+ # Algorithme Métriques
133
  valence = 0.8 if emotion == "happy" else (-0.6 if emotion in ["sad", "angry", "fear"] else 0.0)
134
  arousal = 0.8 if emotion in ["angry", "fear", "surprise"] else 0.3
135
 
136
  def clamp(n): return max(0, min(100, int(n)))
137
+
138
+ val_eng = clamp((arousal * 100) + random.uniform(0, 10))
139
+ val_sat = clamp(((valence + 1) / 2) * 100)
140
+ val_tru = clamp(50 + (valence * 20))
141
+ val_loy = clamp(50 + (valence * 10))
142
+ val_opi = clamp(((valence + 1) / 2) * 100)
143
+
144
+ # Labels Textuels
145
+ lbl_eng = "Fort 🔥" if val_eng > 60 else ("Moyen" if val_eng > 30 else "Faible 💤")
146
+ lbl_sat = "Positif 😃" if val_sat > 60 else ("Négatif 😡" if val_sat < 40 else "Neutre 😐")
147
+
148
  metrics = {
149
+ "engagement": val_eng, "satisfaction": val_sat, "trust": val_tru, "loyalty": val_loy, "opinion": val_opi
 
 
 
 
150
  }
151
 
152
+ # D. Envoi au Frontend (Temps Réel)
153
  payload = {
154
  "emotion": emotion,
155
  "face_coords": face_coords,
156
  "metrics": metrics,
157
+ "session_time": current_time,
158
  "is_recording": sessions[sid]["active"]
159
  }
160
  await sio.emit('metrics_update', payload, room=sid)
161
 
162
+ # E. SAUVEGARDE EN BASE DE DONNÉES (La partie qui manquait !)
163
+ # On sauvegarde seulement si :
164
+ # 1. L'enregistrement est actif
165
+ # 2. On a un ID de session valide
166
+ # 3. Ça fait plus d'1 seconde depuis la dernière sauvegarde (pour ne pas saturer)
167
+ now = time.time()
168
+ if sessions[sid]["active"] and sessions[sid]["db_id"]:
169
+ if now - sessions[sid]["last_save_time"] >= 1.0:
170
+ sessions[sid]["last_save_time"] = now
171
+
172
+ # Préparation de la ligne à insérer
173
+ row_data = {
174
+ "session_id": sessions[sid]["db_id"],
175
+ "session_time": current_time,
176
+ "emotion": emotion,
177
+ "emotion_score": float(emotion_score),
178
+ "engagement_val": val_eng, "engagement_lbl": lbl_eng,
179
+ "satisfaction_val": val_sat, "satisfaction_lbl": lbl_sat,
180
+ "trust_val": val_tru, "loyalty_val": val_loy, "opinion_val": val_opi
181
+ }
182
+
183
+ # Insertion asynchrone (on ne bloque pas)
184
+ try:
185
+ supabase.table('measurements').insert(row_data).execute()
186
+ # print(f"💾 Data saved T={current_time}") # Décommenter pour vérifier
187
+ except Exception as db_err:
188
+ print(f"⚠️ Erreur Insert Mesure: {db_err}")
189
 
190
  except Exception as e:
191
+ pass # On continue même si une frame est mauvaise
 
192
 
193
  if __name__ == "__main__":
 
194
  try:
195
  DeepFace.build_model("Emotion")
196
  print("✅ Modèle chargé !")
197
  except: pass
 
198
  uvicorn.run(socket_app, host="0.0.0.0", port=7860)