From 36094d2392096fb7fec2df5b6fa1d47cae67061b Mon Sep 17 00:00:00 2001 From: Avinash-Alapati Date: Mon, 26 Jan 2026 16:12:58 +0530 Subject: [PATCH 1/3] feat: add real-time gaze ingestion and WebSocket streaming per session --- app/main.py | 42 ++++++++++++++++++++++++++++++++++++++++++ app/requirements.txt | 4 +++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/app/main.py b/app/main.py index 0635c72..c94d797 100644 --- a/app/main.py +++ b/app/main.py @@ -1,5 +1,8 @@ from flask import Flask, request, Response, jsonify from flask_cors import CORS +from flask_socketio import SocketIO, join_room, emit +from collections import defaultdict +import time # Local imports from app from app.routes import session as session_route @@ -9,6 +12,7 @@ app = Flask(__name__) CORS(app) +socketio = SocketIO(app, cors_allowed_origins="*", async_mode="threading") # @app.route('/', methods=['GET']) # def welcome(): @@ -77,3 +81,41 @@ def batch_predict(): if request.method == 'POST': return session_route.batch_predict() return Response('Invalid request method for route', status=405, mimetype='application/json') + +# TODO: replace with persistent storage for replay & analytics +gaze_buffer = defaultdict(list) + +@socketio.on("join_session") +def handle_join(data): + session_id = data.get("session_id") + if session_id: + join_room(session_id) + emit("joined", {"session_id": session_id}) + +@app.route("/api/session/gaze", methods=["POST"]) +def ingest_gaze(): + data = request.get_json() + + session_id = data.get("session_id") + x = data.get("x") + y = data.get("y") + timestamp = data.get("timestamp", time.time()) + + if not session_id or x is None or y is None: + return Response("Invalid payload", status=400) + + point = { + "x": x, + "y": y, + "timestamp": timestamp + } + + gaze_buffer[session_id].append(point) + + socketio.emit("gaze_point", point, room=session_id) + + return jsonify({"status": "ok"}) + +if __name__ == "__main__": + socketio.run(app, host="0.0.0.0", port=5000) + diff --git a/app/requirements.txt b/app/requirements.txt index ba4582f..247cc10 100644 --- a/app/requirements.txt +++ b/app/requirements.txt @@ -17,4 +17,6 @@ threadpoolctl==3.6.0 tzdata==2025.2 Werkzeug==3.1.3 gunicorn==23.0.0 -requests==2.31.0 \ No newline at end of file +requests==2.31.0 +flask-socketio +eventlet \ No newline at end of file From 498c8d4b940b7e1c5241c6b0de8c2fc0faef2d4e Mon Sep 17 00:00:00 2001 From: Avinash-Alapati Date: Mon, 26 Jan 2026 16:26:54 +0530 Subject: [PATCH 2/3] feat: add real-time gaze ingestion and WebSocket streaming per session --- app/requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/requirements.txt b/app/requirements.txt index 247cc10..fb488f8 100644 --- a/app/requirements.txt +++ b/app/requirements.txt @@ -18,5 +18,4 @@ tzdata==2025.2 Werkzeug==3.1.3 gunicorn==23.0.0 requests==2.31.0 -flask-socketio -eventlet \ No newline at end of file +flask-socketio==5.3.6 \ No newline at end of file From 74c3efb09cdc394ffd9563a9786d66a97b924589 Mon Sep 17 00:00:00 2001 From: Avinash-Alapati Date: Tue, 27 Jan 2026 16:49:59 +0530 Subject: [PATCH 3/3] feat: add real-time gaze ingestion and WebSocket streaming per session --- app/main.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/main.py b/app/main.py index c94d797..58a56c8 100644 --- a/app/main.py +++ b/app/main.py @@ -88,14 +88,19 @@ def batch_predict(): @socketio.on("join_session") def handle_join(data): session_id = data.get("session_id") + print("OBSERVER JOIN:", session_id, flush=True) if session_id: join_room(session_id) emit("joined", {"session_id": session_id}) + @app.route("/api/session/gaze", methods=["POST"]) -def ingest_gaze(): +def receive_gaze(): data = request.get_json() + if not data: + return Response("Invalid JSON", status=400) + session_id = data.get("session_id") x = data.get("x") y = data.get("y") @@ -105,13 +110,17 @@ def ingest_gaze(): return Response("Invalid payload", status=400) point = { + "session_id": session_id, "x": x, "y": y, - "timestamp": timestamp + "timestamp": timestamp, + "phase": data.get("phase"), } - gaze_buffer[session_id].append(point) + print("GAZE:", point, flush=True) + # store for replay later + gaze_buffer[session_id].append(point) socketio.emit("gaze_point", point, room=session_id) return jsonify({"status": "ok"})