Spaces:
Running
Running
| import os | |
| import socketio | |
| import uvicorn | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| import asyncio | |
| import base64 | |
| import cv2 | |
| import numpy as np | |
| import random | |
| import time | |
| from deepface import DeepFace | |
| from supabase import create_client, Client | |
| # --- 1. SETUP SUPABASE --- | |
| SUPABASE_URL = os.getenv("SUPABASE_URL", "https://gwjrwejdjpctizolfkcz.supabase.co") | |
| SUPABASE_KEY = os.getenv("SUPABASE_KEY", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imd3anJ3ZWpkanBjdGl6b2xma2N6Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc2OTA5ODEyNCwiZXhwIjoyMDg0Njc0MTI0fQ.EjU1DGTN-jrdkaC6nJWilFtYZgtu-NKjnfiMVMnHal0") | |
| try: | |
| supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) | |
| print("☁️ SUPABASE CONNECTÉ") | |
| except Exception as e: | |
| print(f"❌ ERREUR SUPABASE: {e}") | |
| # --- 2. CONFIG SOCKET --- | |
| sio = socketio.AsyncServer( | |
| async_mode='asgi', | |
| cors_allowed_origins='*', | |
| ping_timeout=60, | |
| max_http_buffer_size=10000000 | |
| ) | |
| app = FastAPI() | |
| app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"]) | |
| socket_app = socketio.ASGIApp(sio, app) | |
| # --- 3. API ADMIN --- | |
| def get_sessions(): | |
| try: | |
| response = supabase.table('sessions').select("*").order('id', desc=True).execute() | |
| return response.data | |
| except: return [] | |
| def get_session_details(session_id: int): | |
| try: | |
| sess = supabase.table('sessions').select("*").eq('id', session_id).execute() | |
| if not sess.data: raise HTTPException(status_code=404, detail="Session introuvable") | |
| meas = supabase.table('measurements').select("*").eq('session_id', session_id).order('session_time', desc=False).execute() | |
| return {"info": sess.data[0], "data": meas.data} | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| def delete_session(session_id: int): | |
| try: | |
| supabase.table('sessions').delete().eq('id', session_id).execute() | |
| return {"message": "Supprimé"} | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| # --- 4. GESTION DES SESSIONS --- | |
| sessions = {} | |
| async def connect(sid, environ): | |
| print(f"✅ Client: {sid}") | |
| sessions[sid] = { | |
| "active": False, | |
| "start_time": 0, | |
| "db_id": None, | |
| "last_save_time": 0 # Pour éviter de saturer la DB | |
| } | |
| async def disconnect(sid): | |
| if sid in sessions: del sessions[sid] | |
| async def start_session(sid, data): | |
| print(f"▶️ START: {sid}") | |
| if sid in sessions: | |
| sessions[sid]["active"] = True | |
| sessions[sid]["start_time"] = time.time() | |
| sessions[sid]["last_save_time"] = 0 | |
| try: | |
| # Création de la ligne "Session" | |
| new_session = { | |
| "first_name": data.get('firstName', 'Inconnu'), | |
| "last_name": data.get('lastName', ''), | |
| "client_id": data.get('clientId', '') | |
| } | |
| res = supabase.table('sessions').insert(new_session).execute() | |
| sessions[sid]["db_id"] = res.data[0]['id'] | |
| print(f"💾 Session ID {sessions[sid]['db_id']} créée.") | |
| except Exception as e: | |
| print(f"⚠️ Erreur Création Session: {e}") | |
| async def stop_session(sid): | |
| print(f"⏹️ STOP: {sid}") | |
| if sid in sessions: sessions[sid]["active"] = False | |
| async def process_frame(sid, data_uri): | |
| if sid not in sessions: return | |
| try: | |
| # A. Décodage | |
| encoded_data = data_uri.split(',')[1] | |
| nparr = np.frombuffer(base64.b64decode(encoded_data), np.uint8) | |
| frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR) | |
| # B. Analyse IA | |
| result = DeepFace.analyze(frame, actions=['emotion'], enforce_detection=False, silent=True) | |
| data = result[0] if isinstance(result, list) else result | |
| emotion = data['dominant_emotion'] | |
| emotion_score = data['emotion'][emotion] # On récupère le score de confiance (ex: 98.5) | |
| # C. Calcul KPIs & Temps | |
| current_time = 0 | |
| if sessions[sid]["active"]: | |
| current_time = int(time.time() - sessions[sid]["start_time"]) | |
| # Coordonnées visage | |
| region = data['region'] | |
| face_coords = {'x': region['x'], 'y': region['y'], 'w': region['w'], 'h': region['h']} if region['w'] > 0 else None | |
| # Algorithme Métriques | |
| valence = 0.8 if emotion == "happy" else (-0.6 if emotion in ["sad", "angry", "fear"] else 0.0) | |
| arousal = 0.8 if emotion in ["angry", "fear", "surprise"] else 0.3 | |
| def clamp(n): return max(0, min(100, int(n))) | |
| val_eng = clamp((arousal * 100) + random.uniform(0, 10)) | |
| val_sat = clamp(((valence + 1) / 2) * 100) | |
| val_tru = clamp(50 + (valence * 20)) | |
| val_loy = clamp(50 + (valence * 10)) | |
| val_opi = clamp(((valence + 1) / 2) * 100) | |
| # Labels Textuels | |
| lbl_eng = "Fort 🔥" if val_eng > 60 else ("Moyen" if val_eng > 30 else "Faible 💤") | |
| lbl_sat = "Positif 😃" if val_sat > 60 else ("Négatif 😡" if val_sat < 40 else "Neutre 😐") | |
| metrics = { | |
| "engagement": val_eng, "satisfaction": val_sat, "trust": val_tru, "loyalty": val_loy, "opinion": val_opi | |
| } | |
| # D. Envoi au Frontend (Temps Réel) | |
| payload = { | |
| "emotion": emotion, | |
| "face_coords": face_coords, | |
| "metrics": metrics, | |
| "session_time": current_time, | |
| "is_recording": sessions[sid]["active"] | |
| } | |
| await sio.emit('metrics_update', payload, room=sid) | |
| # E. SAUVEGARDE EN BASE DE DONNÉES (La partie qui manquait !) | |
| # On sauvegarde seulement si : | |
| # 1. L'enregistrement est actif | |
| # 2. On a un ID de session valide | |
| # 3. Ça fait plus d'1 seconde depuis la dernière sauvegarde (pour ne pas saturer) | |
| now = time.time() | |
| if sessions[sid]["active"] and sessions[sid]["db_id"]: | |
| if now - sessions[sid]["last_save_time"] >= 1.0: | |
| sessions[sid]["last_save_time"] = now | |
| # Préparation de la ligne à insérer | |
| row_data = { | |
| "session_id": sessions[sid]["db_id"], | |
| "session_time": current_time, | |
| "emotion": emotion, | |
| "emotion_score": float(emotion_score), | |
| "engagement_val": val_eng, "engagement_lbl": lbl_eng, | |
| "satisfaction_val": val_sat, "satisfaction_lbl": lbl_sat, | |
| "trust_val": val_tru, "loyalty_val": val_loy, "opinion_val": val_opi | |
| } | |
| # Insertion asynchrone (on ne bloque pas) | |
| try: | |
| supabase.table('measurements').insert(row_data).execute() | |
| # print(f"💾 Data saved T={current_time}") # Décommenter pour vérifier | |
| except Exception as db_err: | |
| print(f"⚠️ Erreur Insert Mesure: {db_err}") | |
| except Exception as e: | |
| pass # On continue même si une frame est mauvaise | |
| if __name__ == "__main__": | |
| try: | |
| DeepFace.build_model("Emotion") | |
| print("✅ Modèle chargé !") | |
| except: pass | |
| uvicorn.run(socket_app, host="0.0.0.0", port=7860) |