persee-tech commited on
Commit
6728a2e
·
verified ·
1 Parent(s): f748ff6

Update backend/main.py

Browse files
Files changed (1) hide show
  1. backend/main.py +126 -27
backend/main.py CHANGED
@@ -11,7 +11,7 @@ import random
11
  from deepface import DeepFace
12
  from supabase import create_client, Client
13
 
14
- # --- CONFIGURATION SUPABASE ---
15
  SUPABASE_URL = os.getenv("SUPABASE_URL", "https://gwjrwejdjpctizolfkcz.supabase.co")
16
  SUPABASE_KEY = os.getenv("SUPABASE_KEY")
17
 
@@ -20,57 +20,156 @@ try:
20
  supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
21
  print("☁️ Connecté à Supabase")
22
  else:
23
- print("⚠️ Pas de clé Supabase")
24
  except:
25
  pass
26
 
27
- # --- CONFIGURATION SOCKET.IO (LE BYPASS) ---
28
- # Cette fonction force l'acceptation et imprime l'origine
29
  def force_allow_origins(origin, environ, **kwargs):
30
- print(f"🔍 ORIGIN DETECTED: {origin}") # Debug Log
31
- return True # Force YES
32
 
33
  sio = socketio.AsyncServer(
34
  async_mode='asgi',
35
- cors_allowed_origins=force_allow_origins, # On utilise la fonction
36
- logger=True,
37
- engineio_logger=True,
38
  always_connect=True
39
  )
40
 
41
  app = FastAPI()
42
  app.add_middleware(
43
- CORSMiddleware,
44
- allow_origins=["*"],
45
- allow_credentials=True,
46
- allow_methods=["*"],
47
- allow_headers=["*"],
48
  )
49
  socket_app = socketio.ASGIApp(sio, app)
50
 
51
- # --- ROUTES ---
52
- @app.get("/")
53
- def home(): return {"status": "ok"}
54
 
55
- @app.get("/api/sessions")
56
- def get_sessions():
 
 
 
 
 
 
 
57
  try:
58
- return supabase.table('sessions').select("*").order('id', desc=True).execute().data
59
- except: return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
- # --- SOCKET EVENTS ---
62
  @sio.event
63
  async def connect(sid, environ):
64
- print(f"✅ CONNECTED: {sid}")
 
 
 
 
 
65
 
66
  @sio.event
67
- async def process_frame(sid, data):
68
- # (Votre logique DeepFace simplifiée pour le test)
69
- pass
70
 
71
  @sio.event
72
  async def start_session(sid, data):
73
- print(f"▶️ START SESSION: {data}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
  if __name__ == "__main__":
 
 
 
76
  uvicorn.run(socket_app, host="0.0.0.0", port=7860)
 
11
  from deepface import DeepFace
12
  from supabase import create_client, Client
13
 
14
+ # --- 1. CONFIGURATION SUPABASE ---
15
  SUPABASE_URL = os.getenv("SUPABASE_URL", "https://gwjrwejdjpctizolfkcz.supabase.co")
16
  SUPABASE_KEY = os.getenv("SUPABASE_KEY")
17
 
 
20
  supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
21
  print("☁️ Connecté à Supabase")
22
  else:
23
+ print("⚠️ ATTENTION: SUPABASE_KEY manquante !")
24
  except:
25
  pass
26
 
27
+ # --- 2. SERVER SETUP ---
 
28
  def force_allow_origins(origin, environ, **kwargs):
29
+ return True
 
30
 
31
  sio = socketio.AsyncServer(
32
  async_mode='asgi',
33
+ cors_allowed_origins=force_allow_origins,
34
+ logger=False, # On réduit les logs pour la performance
35
+ engineio_logger=False,
36
  always_connect=True
37
  )
38
 
39
  app = FastAPI()
40
  app.add_middleware(
41
+ CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"],
 
 
 
 
42
  )
43
  socket_app = socketio.ASGIApp(sio, app)
44
 
45
+ # --- 3. VARIABLES GLOBALES ---
46
+ # On stocke l'état pour chaque client connecté (sid)
47
+ sessions_data = {}
48
 
49
+ def get_default_kpis():
50
+ return {
51
+ "engagement": 0, "satisfaction": 50, "trust": 50, "loyalty": 50, "opinion": 50,
52
+ "lbl_eng": "En attente...", "lbl_sat": "Analyse..."
53
+ }
54
+
55
+ # --- 4. LOGIQUE MÉTIER ---
56
+ def analyze_image_task(frame):
57
+ # Cette fonction sera exécutée dans un thread séparé pour ne pas bloquer
58
  try:
59
+ result = DeepFace.analyze(frame, actions=['emotion'], enforce_detection=False, silent=True)
60
+ data = result[0] if isinstance(result, list) else result
61
+ emotion = data['dominant_emotion']
62
+ region = data['region']
63
+ face_coords = {'x': region['x'], 'y': region['y'], 'w': region['w'], 'h': region['h']} if region['w'] > 0 else None
64
+
65
+ # Calcul KPI (Algorithme)
66
+ valence = 0.0; arousal = 0.0
67
+ if emotion == "happy": valence = 0.8; arousal = 0.6
68
+ elif emotion == "sad": valence = -0.6; arousal = 0.2
69
+ elif emotion == "angry": valence = -0.7; arousal = 0.8
70
+ elif emotion == "fear": valence = -0.7; arousal = 0.8
71
+ elif emotion == "surprise": valence = 0.2; arousal = 0.9
72
+ else: valence = 0.0; arousal = 0.3 # neutral
73
+
74
+ def clamp(n): return max(0, min(100, int(n)))
75
+ kpis = {
76
+ "engagement": clamp((arousal * 100) + random.uniform(0, 10)),
77
+ "satisfaction": clamp(((valence + 1) / 2) * 100),
78
+ "trust": clamp(50 + (valence * 20)),
79
+ "loyalty": clamp(50 + (valence * 10)),
80
+ "opinion": clamp(((valence + 1) / 2) * 100),
81
+ "lbl_eng": "Fort 🔥" if arousal > 0.6 else "Moyen",
82
+ "lbl_sat": "Positif 😃" if valence > 0.2 else ("Négatif 😡" if valence < -0.2 else "Neutre 😐")
83
+ }
84
+ return {"emotion": emotion, "face_coords": face_coords, "kpis": kpis}
85
+ except Exception as e:
86
+ print(f"DeepFace Error: {e}")
87
+ return None
88
 
89
+ # --- 5. SOCKET EVENTS ---
90
  @sio.event
91
  async def connect(sid, environ):
92
+ print(f"✅ Client CONNECTÉ: {sid}")
93
+ sessions_data[sid] = {
94
+ "is_recording": False, "session_time": 0, "db_id": None,
95
+ "last_data": {"emotion": "neutral", "face_coords": None, "kpis": get_default_kpis()},
96
+ "is_processing": False # Verrou pour éviter la surcharge
97
+ }
98
 
99
  @sio.event
100
+ async def disconnect(sid):
101
+ if sid in sessions_data: del sessions_data[sid]
 
102
 
103
  @sio.event
104
  async def start_session(sid, data):
105
+ if sid in sessions_data:
106
+ sessions_data[sid]["is_recording"] = True
107
+ sessions_data[sid]["session_time"] = 0
108
+ print(f"▶️ SESSION START pour {sid}")
109
+ # Optionnel: Création DB ici
110
+
111
+ @sio.event
112
+ async def stop_session(sid):
113
+ if sid in sessions_data:
114
+ sessions_data[sid]["is_recording"] = False
115
+ print(f"⏹️ SESSION STOP pour {sid}")
116
+
117
+ @sio.event
118
+ async def process_frame(sid, data_uri):
119
+ # Si le client n'existe pas ou si le serveur est déjà occupé à traiter une image pour ce client : ON IGNORE
120
+ if sid not in sessions_data: return
121
+ if sessions_data[sid]["is_processing"]: return
122
+
123
+ sessions_data[sid]["is_processing"] = True # On verrouille
124
+
125
+ try:
126
+ # 1. Décodage Image
127
+ encoded_data = data_uri.split(',')[1]
128
+ nparr = np.frombuffer(base64.b64decode(encoded_data), np.uint8)
129
+ frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
130
+
131
+ # 2. Analyse dans un THREAD SÉPARÉ (Magie Asyncio)
132
+ # Cela empêche le serveur de se figer
133
+ result = await asyncio.to_thread(analyze_image_task, frame)
134
+
135
+ # 3. Mise à jour des données si succès
136
+ if result:
137
+ sessions_data[sid]["last_data"]["emotion"] = result["emotion"]
138
+ sessions_data[sid]["last_data"]["face_coords"] = result["face_coords"]
139
+ sessions_data[sid]["last_data"]["kpis"] = result["kpis"]
140
+
141
+ except Exception as e:
142
+ print(f"Erreur Frame: {e}")
143
+ finally:
144
+ sessions_data[sid]["is_processing"] = False # On déverrouille
145
+
146
+ # --- 6. BOUCLE D'ENVOI (Le Coeur) ---
147
+ async def broadcast_loop():
148
+ while True:
149
+ await asyncio.sleep(1) # 1 Tick par seconde pour l'horloge
150
+
151
+ for sid, session in list(sessions_data.items()):
152
+ # Gestion du chrono
153
+ if session["is_recording"]:
154
+ session["session_time"] += 1
155
+
156
+ # Préparation du paquet
157
+ payload = {
158
+ "emotion": session["last_data"]["emotion"],
159
+ "face_coords": session["last_data"]["face_coords"],
160
+ "metrics": session["last_data"]["kpis"],
161
+ "session_time": session["session_time"],
162
+ "is_recording": session["is_recording"]
163
+ }
164
+
165
+ # Envoi au Frontend
166
+ try:
167
+ await sio.emit('metrics_update', payload, room=sid)
168
+ except:
169
+ pass
170
 
171
  if __name__ == "__main__":
172
+ @app.on_event("startup")
173
+ async def startup():
174
+ asyncio.create_task(broadcast_loop())
175
  uvicorn.run(socket_app, host="0.0.0.0", port=7860)