File size: 10,556 Bytes
adcc112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
"""
Database Manager: SQLite Storage for Meeting Analytics
Stores engagement metrics and session data
"""

import sqlite3
import pandas as pd
from datetime import datetime
from typing import List, Dict, Optional
from pathlib import Path


class DatabaseManager:
    """
    Manages SQLite database for storing meeting engagement data
    """
    
    def __init__(self, db_path: str = "data/meeting_logs.db"):
        self.db_path = db_path
        
        # Ensure data directory exists
        Path(db_path).parent.mkdir(parents=True, exist_ok=True)
        
        # Initialize database
        self._init_database()
    
    def _init_database(self):
        """Create tables if they don't exist"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # Sessions table
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS sessions (
                session_id INTEGER PRIMARY KEY AUTOINCREMENT,
                start_time TIMESTAMP NOT NULL,
                end_time TIMESTAMP,
                avg_focus_score REAL,
                total_frames INTEGER DEFAULT 0,
                notes TEXT
            )
        """)
        
        # Engagement metrics table
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS engagement_metrics (
                metric_id INTEGER PRIMARY KEY AUTOINCREMENT,
                session_id INTEGER NOT NULL,
                timestamp TIMESTAMP NOT NULL,
                gaze_score REAL NOT NULL,
                emotion_score REAL NOT NULL,
                head_stability REAL NOT NULL,
                engagement_score REAL NOT NULL,
                face_detected BOOLEAN NOT NULL,
                FOREIGN KEY (session_id) REFERENCES sessions(session_id)
            )
        """)
        
        # Meeting Sessions table (New)
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS meeting_sessions (
                meeting_id INTEGER PRIMARY KEY AUTOINCREMENT,
                start_time TIMESTAMP NOT NULL,
                end_time TIMESTAMP,
                avg_engagement REAL,
                peak_participants INTEGER DEFAULT 0,
                notes TEXT
            )
        """)
        
        # Meeting Metrics table (New)
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS meeting_metrics (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                meeting_id INTEGER NOT NULL,
                timestamp TIMESTAMP,
                participant_count INTEGER,
                drowsy_count INTEGER,
                distracted_count INTEGER,
                avg_engagement REAL,
                FOREIGN KEY (meeting_id) REFERENCES meeting_sessions(meeting_id)
            )
        """)
        
        conn.commit()
        conn.close()

    # --- Meeting Specific Methods ---

    def create_meeting(self, notes: str = "") -> int:
        """Start a new meeting session"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        cursor.execute("INSERT INTO meeting_sessions (start_time, notes) VALUES (?, ?)", (datetime.now(), notes))
        mid = cursor.lastrowid
        conn.commit()
        conn.close()
        return mid

    def log_meeting(self, meeting_id, participants, drowsy, distracted, engagement):
        """Log aggregate meeting stats"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        cursor.execute("""
            INSERT INTO meeting_metrics (meeting_id, timestamp, participant_count, drowsy_count, distracted_count, avg_engagement)
            VALUES (?, ?, ?, ?, ?, ?)
        """, (meeting_id, datetime.now(), participants, drowsy, distracted, engagement))
        conn.commit()
        conn.close()

    def end_meeting(self, meeting_id):
        """End meeting and calculate summary"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # Calculate averages and peak participants
        cursor.execute("""
            SELECT AVG(avg_engagement), MAX(participant_count)
            FROM meeting_metrics WHERE meeting_id = ?
        """, (meeting_id,))
        stats = cursor.fetchone()
        avg_eng = stats[0] if stats[0] else 0.0
        peak = stats[1] if stats[1] else 0
        
        cursor.execute("""
            UPDATE meeting_sessions 
            SET end_time = ?, avg_engagement = ?, peak_participants = ?
            WHERE meeting_id = ?
        """, (datetime.now(), avg_eng, peak, meeting_id))
        conn.commit()
        conn.close()

    def get_all_meetings(self):
        """Get meeting history"""
        conn = sqlite3.connect(self.db_path)
        df = pd.read_sql_query("SELECT * FROM meeting_sessions ORDER BY start_time DESC", conn)
        conn.close()
        return df
    
    def create_session(self, notes: str = "") -> int:
        """
        Start a new meeting session
        
        Returns:
            session_id
        """
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        cursor.execute("""
            INSERT INTO sessions (start_time, notes)
            VALUES (?, ?)
        """, (datetime.now(), notes))
        
        session_id = cursor.lastrowid
        conn.commit()
        conn.close()
        
        return session_id
    
    def end_session(self, session_id: int):
        """Mark session as ended and calculate statistics"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # Calculate average focus score
        cursor.execute("""
            SELECT AVG(engagement_score), COUNT(*)
            FROM engagement_metrics
            WHERE session_id = ?
        """, (session_id,))
        
        avg_score, total_frames = cursor.fetchone()
        
        # Update session
        cursor.execute("""
            UPDATE sessions
            SET end_time = ?,
                avg_focus_score = ?,
                total_frames = ?
            WHERE session_id = ?
        """, (datetime.now(), avg_score or 0.0, total_frames or 0, session_id))
        
        conn.commit()
        conn.close()
    
    def log_engagement(
        self,
        session_id: int,
        gaze_score: float,
        emotion_score: float,
        head_stability: float,
        engagement_score: float,
        face_detected: bool
    ):
        """Log a single frame's engagement metrics"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        cursor.execute("""
            INSERT INTO engagement_metrics (
                session_id, timestamp, gaze_score, emotion_score,
                head_stability, engagement_score, face_detected
            )
            VALUES (?, ?, ?, ?, ?, ?, ?)
        """, (
            session_id,
            datetime.now(),
            gaze_score,
            emotion_score,
            head_stability,
            engagement_score,
            face_detected
        ))
        
        conn.commit()
        conn.close()
    
    def get_session_data(self, session_id: int) -> pd.DataFrame:
        """Retrieve all metrics for a session as DataFrame"""
        conn = sqlite3.connect(self.db_path)
        
        query = """
            SELECT 
                timestamp,
                gaze_score,
                emotion_score,
                head_stability,
                engagement_score,
                face_detected
            FROM engagement_metrics
            WHERE session_id = ?
            ORDER BY timestamp
        """
        
        df = pd.read_sql_query(query, conn, params=(session_id,))
        conn.close()
        
        # Convert timestamp to datetime
        df['timestamp'] = pd.to_datetime(df['timestamp'])
        
        return df
    
    def get_all_sessions(self) -> pd.DataFrame:
        """Get summary of all sessions"""
        conn = sqlite3.connect(self.db_path)
        
        query = """
            SELECT 
                session_id,
                start_time,
                end_time,
                avg_focus_score,
                total_frames,
                notes
            FROM sessions
            ORDER BY start_time DESC
        """
        
        df = pd.read_sql_query(query, conn)
        conn.close()
        
        return df
    
    def get_dashboard_summary(self) -> Dict:
        """Get aggregate statistics for the dashboard"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # Individual stats
        cursor.execute("SELECT COUNT(*), AVG(avg_focus_score) FROM sessions WHERE end_time IS NOT NULL")
        ind_count, ind_avg = cursor.fetchone()
        
        # Meeting stats
        cursor.execute("SELECT COUNT(*), AVG(avg_engagement) FROM meeting_sessions WHERE end_time IS NOT NULL")
        meet_count, meet_avg = cursor.fetchone()
        
        # Total "Focus Frames" (indicator of time spent)
        cursor.execute("SELECT SUM(total_frames) FROM sessions")
        total_frames = cursor.fetchone()[0] or 0
        
        conn.close()
        
        # Simple heuristic: assuming ~5 FPS, calculate approximate minutes
        focus_minutes = round(total_frames / (5 * 60))
        
        total_sessions = (ind_count or 0) + (meet_count or 0)
        
        # Average engagement (weighted if possible, but simple mean for now)
        scores = []
        if ind_avg: scores.append(ind_avg)
        if meet_avg: scores.append(meet_avg)
        avg_overall = sum(scores) / len(scores) if scores else 0
        
        return {
            "total_sessions": total_sessions,
            "avg_engagement": round(avg_overall),
            "focus_minutes": focus_minutes,
            "individual_sessions": ind_count or 0,
            "meeting_sessions": meet_count or 0
        }

    def calculate_rolling_focus(
        self,
        session_id: int,
        window_seconds: int = 30
    ) -> pd.DataFrame:
        """
        Calculate rolling average focus score
        
        PRODUCT LOGIC: Smooths out momentary distractions
        (e.g., looking away to drink water shouldn't tank the score)
        """
        df = self.get_session_data(session_id)
        
        if df.empty:
            return df
        
        # Set timestamp as index
        df.set_index('timestamp', inplace=True)
        
        # Calculate rolling mean
        df['rolling_focus'] = df['engagement_score'].rolling(
            window=f'{window_seconds}s'
        ).mean()
        
        return df.reset_index()