2121from config_loader import config
2222from improved_webcam_processor import ImprovedWebcamProcessor
2323from mediapipe_processors .deepface_emotion_detector import DeepFaceEmotionDetector
24- from mediapipe_processors .poster_emotion_detector import POSTEREmotionDetector
25- from mediapipe_processors .efficientnet_emotion_detector import EfficientNetEmotionDetector
2624
2725# Configure logging
2826logging .basicConfig (
@@ -111,6 +109,14 @@ def __init__(self):
111109 self .unfocused_time_seconds = 0
112110 self .distracted_events = 0
113111
112+ # ENHANCED: Unfocus analytics (GazeRecorder-style)
113+ self .unfocus_intervals = [] # List of {'start': float, 'end': float, 'duration': float, 'reason': str}
114+ self .unfocus_count = 0 # Total number of unfocus events
115+ self .first_unfocus_time = None # Timestamp of first unfocus event
116+ self .last_unfocus_time = None # Timestamp of last unfocus event
117+ self .current_unfocus_start = None # Start time of current unfocus event (if active)
118+ self .current_focus_start = None # Start time of current focus period (for tracking focus duration)
119+
114120 def _format_time (self , seconds ):
115121 """Format seconds to HH:MM:SS"""
116122 seconds = int (seconds )
@@ -182,6 +188,14 @@ def to_dict(self):
182188 3
183189 ),
184190 },
191+ # ENHANCED: Unfocus analytics (GazeRecorder-style)
192+ 'unfocus_analytics' : {
193+ 'unfocus_count' : self .unfocus_count ,
194+ 'first_unfocus_time' : self .first_unfocus_time ,
195+ 'last_unfocus_time' : self .last_unfocus_time ,
196+ 'intervals_count' : len (self .unfocus_intervals ),
197+ 'recent_intervals' : self .unfocus_intervals [- 5 :] if len (self .unfocus_intervals ) > 5 else self .unfocus_intervals ,
198+ },
185199 }
186200
187201# Global state
@@ -194,8 +208,8 @@ def to_dict(self):
194208# Import improved modular webcam processor
195209from improved_webcam_processor import ImprovedWebcamProcessor
196210
197- # Global webcam processor instance
198- webcam = ImprovedWebcamProcessor (state )
211+ # Global webcam processor instance with socketio reference
212+ webcam = ImprovedWebcamProcessor (state , socketio = socketio )
199213
200214# ============================================================================
201215# FLASK ROUTES
@@ -260,6 +274,57 @@ def get_metrics():
260274 """Get current metrics"""
261275 return jsonify (state .to_dict ())
262276
277+ @app .route ('/api/analytics/unfocus' , methods = ['GET' ])
278+ def get_unfocus_analytics ():
279+ """
280+ ENHANCED: Get detailed unfocus analytics (GazeRecorder-style)
281+
282+ Returns comprehensive unfocus statistics including:
283+ - Unfocus count, durations, rates
284+ - Time to first unfocus
285+ - Common unfocus reasons
286+ - Recent unfocus intervals
287+ """
288+ try :
289+ analytics = webcam .calculate_unfocus_analytics ()
290+
291+ # Format times for display
292+ def format_duration (seconds ):
293+ if seconds is None :
294+ return "N/A"
295+ mins = int (seconds // 60 )
296+ secs = int (seconds % 60 )
297+ return f"{ mins } m { secs } s"
298+
299+ return jsonify ({
300+ 'status' : 'success' ,
301+ 'analytics' : {
302+ 'unfocus_count' : analytics ['unfocus_count' ],
303+ 'total_unfocus_time' : analytics ['total_duration' ],
304+ 'total_unfocus_time_formatted' : format_duration (analytics ['total_duration' ]),
305+ 'avg_duration' : round (analytics ['avg_duration' ], 1 ),
306+ 'avg_duration_formatted' : format_duration (analytics ['avg_duration' ]),
307+ 'min_duration' : round (analytics ['min_duration' ], 1 ),
308+ 'max_duration' : round (analytics ['max_duration' ], 1 ),
309+ 'time_to_first_unfocus' : round (analytics ['time_to_first_unfocus' ], 1 ) if analytics ['time_to_first_unfocus' ] else None ,
310+ 'time_to_first_unfocus_formatted' : format_duration (analytics ['time_to_first_unfocus' ]),
311+ 'unfocus_rate_per_hour' : analytics ['unfocus_rate' ],
312+ 'common_reasons' : analytics ['common_reasons' ],
313+ 'recent_intervals' : [
314+ {
315+ 'start' : interval ['start' ],
316+ 'end' : interval ['end' ],
317+ 'duration' : round (interval ['duration' ], 1 ),
318+ 'reason' : interval ['reason' ]
319+ }
320+ for interval in analytics ['recent_intervals' ]
321+ ]
322+ }
323+ }), 200
324+ except Exception as e :
325+ logger .error (f"Error calculating unfocus analytics: { e } " )
326+ return jsonify ({'status' : 'error' , 'message' : str (e )}), 500
327+
263328@app .route ('/api/calibration/start' , methods = ['POST' ])
264329def start_calibration ():
265330 """Start calibration session"""
0 commit comments