-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
462 lines (387 loc) · 15.8 KB
/
app.py
File metadata and controls
462 lines (387 loc) · 15.8 KB
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
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
from flask import Flask, request, jsonify, render_template, session, redirect, Response
from lifeCoach import LifeCoach
import os
import uuid
from mood_rec import emotion_to_content
from database import db
from ar_breathing_exercise import ARBreathingExercise
from emotion_recognition_feed import EmotionRecognitionFeed
app = Flask(__name__)
app.secret_key = os.urandom(24) # For session management
# Initialize the LifeCoach instances dictionary
coach_instances = {}
# Get API key from environment variable or use default
api_key = os.environ.get('GEMINI_API_KEY', "AIzaSyCrGgr7E9k_XHp2TsUlPW4BYWJjFjhY5WQ")
# Initialize AR breathing exercise
ar_exercise = ARBreathingExercise()
# Initialize emotion recognition feed
emotion_feed = EmotionRecognitionFeed()
@app.route('/')
def home():
# Create a unique session ID if not exists
if 'session_id' not in session:
session['session_id'] = str(uuid.uuid4())
# Initialize a coach for this session if not exists
if session['session_id'] not in coach_instances:
coach_instances[session['session_id']] = LifeCoach(api_key=api_key)
return render_template('index.html')
@app.route('/journal')
def journal():
# Create a unique session ID if not exists
if 'session_id' not in session:
session['session_id'] = str(uuid.uuid4())
# Get the user's current emotion from the coach if available
current_emotion = "neutral"
session_id = session.get('session_id')
if session_id and session_id in coach_instances:
current_emotion = coach_instances[session_id].get_current_emotion()
# Get a random prompt based on the user's emotion
prompt = db.get_random_prompt(current_emotion)
return render_template('journal.html', prompt=prompt)
@app.route('/chat')
def chat_page():
# Create a unique session ID if not exists
if 'session_id' not in session:
session['session_id'] = str(uuid.uuid4())
# Initialize a coach for this session if not exists
if session['session_id'] not in coach_instances:
coach_instances[session['session_id']] = LifeCoach(api_key=api_key)
return render_template('chat.html')
@app.route('/chat', methods=['POST'])
def chat():
data = request.get_json()
user_message = data.get('message', '')
conversation_history = data.get('history', [])
# Get the session ID
session_id = session.get('session_id')
if not session_id or session_id not in coach_instances:
# Create a new session if needed
session_id = str(uuid.uuid4())
session['session_id'] = session_id
coach_instances[session_id] = LifeCoach(api_key=api_key)
# Get the coach instance for this session
coach = coach_instances[session_id]
try:
# If the client sent a conversation history, use it to update the coach
if conversation_history and len(conversation_history) > 1: # Skip if only the greeting is present
# First reset the conversation to make sure we start fresh
coach.reset_conversation()
# Then add all messages except the latest (which we'll process next)
for msg in conversation_history[:-1]:
if msg['role'] == 'user':
# For user messages, silently process them to build context
coach.update_conversation_history(msg['content'], 'user')
elif msg['role'] == 'assistant':
coach.update_conversation_history(msg['content'], 'assistant')
# Get response from LifeCoach for the current message
response_text = coach.get_response(user_message)
# Get the detected emotion
current_emotion = coach.get_current_emotion()
# Get recommendations based on emotion
recommendations = get_recommendations(current_emotion)
return jsonify({
'message': response_text,
'emotion': current_emotion,
'recommendations': recommendations
})
except Exception as e:
return jsonify({
'error': str(e)
}), 500
@app.route('/journal/save', methods=['POST'])
def save_journal():
try:
data = request.get_json()
if not data:
print("No JSON data received")
return jsonify({
'status': 'error',
'message': 'No data received'
}), 400
entry_text = data.get('entry_text', '')
prompt_used = data.get('prompt_used', '')
manual_emotion = data.get('emotion', None)
if not entry_text or entry_text.strip() == '':
print("Empty journal entry text")
return jsonify({
'status': 'error',
'message': 'Journal entry text cannot be empty'
}), 400
# Get the session ID
session_id = session.get('session_id')
if not session_id:
print("Creating new session ID")
session_id = str(uuid.uuid4())
session['session_id'] = session_id
print(f"Processing journal entry for session {session_id}")
# Analyze the entry for emotions if a coach instance exists
emotion = manual_emotion
sentiment_score = None
emotions_detected = None
if session_id in coach_instances:
coach = coach_instances[session_id]
# Only detect emotion if it wasn't manually specified
if not manual_emotion:
print("Detecting emotion from text...")
detected_emotion, confidence, explanation = coach.detect_emotion(entry_text)
emotion = detected_emotion
sentiment_score = confidence / 10.0 # Convert 0-10 to 0-1 scale
emotions_detected = {
"primary": detected_emotion,
"confidence": confidence,
"explanation": explanation
}
print(f"Detected emotion: {emotion} with confidence {confidence}")
# Save the journal entry to the database
print(f"Saving journal entry to database: {len(entry_text)} chars, emotion: {emotion}")
entry_id = db.save_journal_entry(
user_id=session_id,
entry_text=entry_text,
emotion=emotion,
sentiment_score=sentiment_score,
emotions_detected=emotions_detected,
prompt_used=prompt_used
)
if entry_id:
print(f"Journal entry saved successfully with ID: {entry_id}")
return jsonify({
'status': 'success',
'entry_id': entry_id,
'detected_emotion': emotion,
'sentiment_score': sentiment_score
})
else:
print("Failed to save journal entry - no entry_id returned")
return jsonify({
'status': 'error',
'message': 'Failed to save journal entry'
}), 500
except Exception as e:
print(f"Exception in save_journal: {str(e)}")
import traceback
traceback.print_exc()
return jsonify({
'status': 'error',
'message': f'Server error: {str(e)}'
}), 500
@app.route('/journal/entries', methods=['GET'])
def get_journal_entries():
# Get the session ID
session_id = session.get('session_id')
if not session_id:
return jsonify({
'status': 'error',
'message': 'No active session'
}), 401
# Get pagination parameters
limit = request.args.get('limit', 10, type=int)
offset = request.args.get('offset', 0, type=int)
# Get journal entries from the database
entries = db.get_journal_entries(session_id, limit, offset)
return jsonify({
'status': 'success',
'entries': entries
})
@app.route('/journal/entry/<int:entry_id>', methods=['GET'])
def get_journal_entry(entry_id):
# Get the session ID
session_id = session.get('session_id')
if not session_id:
return jsonify({
'status': 'error',
'message': 'No active session'
}), 401
# Get journal entry from the database
entry = db.get_entry_by_id(entry_id, session_id)
if entry:
return jsonify({
'status': 'success',
'entry': entry
})
else:
return jsonify({
'status': 'error',
'message': 'Entry not found'
}), 404
@app.route('/journal/prompt', methods=['GET'])
def get_journal_prompt():
# Get emotion parameter if provided
emotion = request.args.get('emotion', None)
# Get a random prompt from the database
prompt = db.get_random_prompt(emotion)
return jsonify({
'status': 'success',
'prompt': prompt
})
@app.route('/journal/favorite/<int:entry_id>', methods=['POST'])
def toggle_favorite(entry_id):
# Get the session ID
session_id = session.get('session_id')
if not session_id:
return jsonify({
'status': 'error',
'message': 'No active session'
}), 401
# Toggle favorite status in the database
success = db.toggle_favorite(entry_id, session_id)
if success:
return jsonify({
'status': 'success',
'message': 'Favorite status toggled'
})
else:
return jsonify({
'status': 'error',
'message': 'Failed to toggle favorite status'
}), 500
@app.route('/journal/analytics', methods=['GET'])
def get_journal_analytics():
# Get the session ID
session_id = session.get('session_id')
if not session_id:
return jsonify({
'status': 'error',
'message': 'No active session'
}), 401
# Get days parameter if provided
days = request.args.get('days', 30, type=int)
# Get mood analytics from the database
analytics = db.get_mood_analytics(session_id, days)
return jsonify({
'status': 'success',
'analytics': analytics
})
def get_recommendations(emotion):
"""Get content recommendations based on detected emotion"""
# Map emotions from our system to those in mood_rec.py
emotion_mapping = {
# Direct mappings
"happy": "happy",
"sad": "sad",
"angry": "angry",
"neutral": "neutral",
"surprised": "surprise",
"anxious": "fear",
"disgusted": "disgust",
# Additional mappings for emotions not directly in mood_rec
"frustrated": "angry",
"confused": "neutral",
"hopeful": "happy",
"grateful": "happy",
"lonely": "sad",
"overwhelmed": "fear",
"excited": "happy",
"calm": "neutral",
"nervous": "fear",
"proud": "happy",
"disappointed": "sad",
"worried": "fear",
"stressed": "fear",
"relaxed": "neutral",
"content": "happy"
}
# Map the detected emotion to the mood_rec emotion system
mapped_emotion = emotion_mapping.get(emotion.lower(), "neutral")
# Get recommendations for the mapped emotion
recommendations = emotion_to_content.get(mapped_emotion, emotion_to_content["neutral"])
return recommendations
@app.route('/reset', methods=['POST'])
def reset_conversation():
session_id = session.get('session_id')
if session_id and session_id in coach_instances:
coach_instances[session_id].reset_conversation()
return jsonify({'status': 'success', 'message': 'Conversation reset'})
return jsonify({'status': 'error', 'message': 'Session not found'}), 404
@app.route('/history', methods=['GET'])
def get_history():
session_id = session.get('session_id')
if session_id and session_id in coach_instances:
coach = coach_instances[session_id]
return jsonify({
'history': coach.get_conversation_history(),
'emotions': coach.get_emotion_history()
})
return jsonify({'status': 'error', 'message': 'Session not found'}), 404
# Clean up inactive sessions periodically
@app.before_request
def cleanup_inactive_sessions():
# This would typically involve checking timestamps and removing old sessions
# For simplicity, we're just ensuring the dict doesn't grow too large
if len(coach_instances) > 1000: # Arbitrary limit
# Remove oldest sessions (we would normally use timestamps)
oldest_keys = list(coach_instances.keys())[:100]
for key in oldest_keys:
if key in coach_instances:
del coach_instances[key]
@app.route('/breathing')
def breathing_exercise():
# Create a unique session ID if not exists
if 'session_id' not in session:
session['session_id'] = str(uuid.uuid4())
# Get the user's current emotion from the coach if available
current_emotion = "neutral"
session_id = session.get('session_id')
if session_id and session_id in coach_instances:
current_emotion = coach_instances[session_id].get_current_emotion()
# Get recommended exercise type based on emotion
recommended_exercise = ar_exercise.get_exercise_for_emotion(current_emotion)
# Get exercise types and descriptions
exercise_types = ar_exercise.get_exercise_instructions()
# Get detailed info for the recommended exercise
exercise_info = ar_exercise.get_exercise_instructions(recommended_exercise)
return render_template('breathing.html',
emotion=current_emotion,
recommended_exercise=recommended_exercise,
exercise_types=exercise_types,
exercise_info=exercise_info)
@app.route('/breathing/info')
def breathing_exercise_info():
# Get exercise type from query parameter
exercise_type = request.args.get('type', 'focus')
# Get detailed info for the exercise
exercise_info = ar_exercise.get_exercise_instructions(exercise_type)
return jsonify(exercise_info)
@app.route('/video_feed')
def video_feed():
# Get exercise type from query parameter
exercise_type = request.args.get('type', 'focus')
# Get the user's current emotion from the coach if available
current_emotion = "neutral"
session_id = session.get('session_id')
if session_id and session_id in coach_instances:
current_emotion = coach_instances[session_id].get_current_emotion()
# Return the video feed response
return ar_exercise.get_video_feed(current_emotion, exercise_type)
@app.route('/breathing/toggle_mode')
def toggle_breathing_mode():
# Toggle between guided and detected breathing modes
ar_exercise.breathing_guided = not ar_exercise.breathing_guided
return jsonify({'guided': ar_exercise.breathing_guided})
@app.route('/emotion_feed')
def emotion_video_feed():
"""Route for emotion recognition camera feed"""
return emotion_feed.get_video_feed()
if __name__ == '__main__':
# Create templates folder if it doesn't exist
if not os.path.exists('templates'):
os.makedirs('templates')
# Delete the database file if it exists and is corrupt
try:
import sqlite3
conn = sqlite3.connect('journal.db')
cursor = conn.cursor()
cursor.execute('SELECT COUNT(*) FROM sqlite_master')
conn.close()
print("Database verified successfully")
except sqlite3.Error as e:
print(f"Database error: {e}")
try:
import os
if os.path.exists('journal.db'):
print("Attempting to delete corrupt database")
os.remove('journal.db')
print("Deleted corrupt database, a new one will be created")
except Exception as del_error:
print(f"Could not delete database: {del_error}")
print(f"Starting Flask app with Empathetic Gemini AI. Using API key: {api_key[:5]}...")
app.run(debug=True)