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

Update backend/server.py

Browse files
Files changed (1) hide show
  1. backend/server.py +98 -37
backend/server.py CHANGED
@@ -1,17 +1,18 @@
1
  import os
2
  import socketio
3
  import uvicorn
4
- from fastapi import FastAPI
5
  from fastapi.middleware.cors import CORSMiddleware
6
  import asyncio
7
  import base64
8
  import cv2
9
  import numpy as np
10
  import random
 
11
  from deepface import DeepFace
12
  from supabase import create_client, Client
13
 
14
- # --- SETUP ---
15
  SUPABASE_URL = os.getenv("SUPABASE_URL", "https://gwjrwejdjpctizolfkcz.supabase.co")
16
  SUPABASE_KEY = os.getenv("SUPABASE_KEY", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imd3anJ3ZWpkanBjdGl6b2xma2N6Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc2OTA5ODEyNCwiZXhwIjoyMDg0Njc0MTI0fQ.EjU1DGTN-jrdkaC6nJWilFtYZgtu-NKjnfiMVMnHal0")
17
 
@@ -21,94 +22,154 @@ try:
21
  except Exception as e:
22
  print(f"❌ ERREUR SUPABASE: {e}")
23
 
24
- # --- SOCKET SETUP ---
25
- # On augmente la taille max des messages (ping_timeout) pour éviter les déconnexions si l'image est lourde
26
  sio = socketio.AsyncServer(
27
  async_mode='asgi',
28
  cors_allowed_origins='*',
29
  ping_timeout=60,
30
- max_http_buffer_size=10000000 # 10MB max
31
  )
32
 
33
  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
  # --- GLOBAL STATE ---
38
  sessions = {}
39
 
40
- # --- EVENTS ---
41
 
42
  @sio.event
43
  async def connect(sid, environ):
44
- print(f"✅ NOUVELLE CONNEXION : {sid}")
45
- sessions[sid] = {"active": False}
 
 
 
 
46
 
47
  @sio.event
48
  async def disconnect(sid):
49
- print(f"❌ DECONNEXION : {sid}")
50
  if sid in sessions: del sessions[sid]
51
 
52
  @sio.event
53
  async def start_session(sid, data):
54
- print(f"▶️ DEMANDE DE START REÇUE DE {sid}")
55
- print(f"📦 Données reçues: {data}")
56
  if sid in sessions:
57
  sessions[sid]["active"] = True
58
- print(f"🚀 SESSION ACTIVÉE POUR {sid}")
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  @sio.event
61
  async def stop_session(sid):
62
- print(f"⏹️ STOP REÇU DE {sid}")
63
- if sid in sessions: sessions[sid]["active"] = False
 
64
 
65
  @sio.event
66
  async def process_frame(sid, data_uri):
67
- # C'est ICI le mouchard le plus important
68
- print(f"📨 IMAGE REÇUE DE {sid} (Taille: {len(data_uri)} chars)")
69
-
70
- if sid not in sessions:
71
- print("⚠️ Session inconnue")
72
- return
73
 
74
  try:
75
  # 1. Décodage
76
- print("1️⃣ Décodage image...")
77
  encoded_data = data_uri.split(',')[1]
78
  nparr = np.frombuffer(base64.b64decode(encoded_data), np.uint8)
79
  frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
80
- print("✅ Image décodée avec succès")
81
 
82
  # 2. Analyse DeepFace
83
- print("2️⃣ Lancement DeepFace...")
84
- # On force l'exécution synchrone pour voir si ça bloque ici
85
  result = DeepFace.analyze(frame, actions=['emotion'], enforce_detection=False, silent=True)
86
- print("✅ DeepFace a répondu !")
87
-
88
  data = result[0] if isinstance(result, list) else result
89
  emotion = data['dominant_emotion']
90
- print(f"🧠 Emotion détectée : {emotion}")
91
-
92
- # 3. Réponse
 
 
 
 
 
93
  region = data['region']
94
  face_coords = {'x': region['x'], 'y': region['y'], 'w': region['w'], 'h': region['h']} if region['w'] > 0 else None
95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  payload = {
97
  "emotion": emotion,
98
  "face_coords": face_coords,
99
- "metrics": {"engagement": 50, "satisfaction": 50, "trust": 50, "loyalty": 50, "opinion": 50},
100
- "session_time": 0,
101
  "is_recording": sessions[sid]["active"]
102
  }
103
-
104
- print(f"📤 Envoi de la réponse au client {sid}...")
105
  await sio.emit('metrics_update', payload, room=sid)
106
- print("✅ Réponse envoyée !")
 
 
 
 
107
 
108
  except Exception as e:
109
- print(f"🔥 CRASH PENDANT L'ANALYSE : {str(e)}")
110
- import traceback
111
- traceback.print_exc()
112
 
113
  if __name__ == "__main__":
 
 
 
 
 
 
114
  uvicorn.run(socket_app, host="0.0.0.0", port=7860)
 
1
  import os
2
  import socketio
3
  import uvicorn
4
+ from fastapi import FastAPI, HTTPException
5
  from fastapi.middleware.cors import CORSMiddleware
6
  import asyncio
7
  import base64
8
  import cv2
9
  import numpy as np
10
  import random
11
+ 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
 
 
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='*',
29
  ping_timeout=60,
30
+ max_http_buffer_size=10000000
31
  )
32
 
33
  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):
50
+ try:
51
+ sess = supabase.table('sessions').select("*").eq('id', session_id).execute()
52
+ if not sess.data: raise HTTPException(status_code=404, detail="Session introuvable")
53
+ meas = supabase.table('measurements').select("*").eq('session_id', session_id).order('session_time', desc=False).execute()
54
+ return {"info": sess.data[0], "data": meas.data}
55
+ except Exception as e:
56
+ raise HTTPException(status_code=500, detail=str(e))
57
+
58
+ @app.delete("/api/sessions/{session_id}")
59
+ def delete_session(session_id: int):
60
+ try:
61
+ supabase.table('sessions').delete().eq('id', session_id).execute()
62
+ return {"message": "Supprimé"}
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
81
  async def disconnect(sid):
 
82
  if sid in sessions: del sessions[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', ''),
96
+ "client_id": data.get('clientId', '')
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)