ahmadsanafarooq commited on
Commit
e6cdb3e
·
verified ·
1 Parent(s): ce0543e

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +784 -0
app.py ADDED
@@ -0,0 +1,784 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import os, json, datetime, hashlib
3
+ from langchain_community.vectorstores import FAISS
4
+ from langchain_community.embeddings import HuggingFaceEmbeddings
5
+ from langchain_groq import ChatGroq
6
+ from langchain.chains import LLMChain
7
+ from langchain.prompts import PromptTemplate
8
+ from gtts import gTTS
9
+ from pathlib import Path
10
+ from dotenv import load_dotenv
11
+ from sentence_transformers import SentenceTransformer, util
12
+ import altair as alt
13
+ import speech_recognition as sr
14
+ from transformers import pipeline
15
+ import torch
16
+ import pickle
17
+ import re
18
+
19
+ # Load environment variables
20
+ load_dotenv()
21
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
22
+
23
+ CRISIS_KEYWORDS = ["suicide", "kill myself", "end it all", "worthless", "can't go on", "hurt myself", "self harm", "want to disappear", "no reason to live"]
24
+
25
+ # Initialize session state
26
+ if "authenticated" not in st.session_state:
27
+ st.session_state.authenticated = False
28
+ if "username" not in st.session_state:
29
+ st.session_state.username = None
30
+ if "is_admin" not in st.session_state:
31
+ st.session_state.is_admin = False
32
+ if "transcribed_text" not in st.session_state:
33
+ st.session_state.transcribed_text = ""
34
+
35
+ # Admin configuration
36
+ ADMIN_USERNAME = os.getenv("ADMIN_USERNAME") # Set in HF Spaces secrets
37
+ ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD") # Set in HF Spaces secrets
38
+
39
+ # User management functions
40
+ def hash_password(password):
41
+ """Hash password using SHA-256 with salt"""
42
+ salt = "dilbot_secure_salt_2024" # You can change this
43
+ return hashlib.sha256((password + salt).encode()).hexdigest()
44
+
45
+ def get_secure_users_path():
46
+ """Get path to users file in a hidden directory"""
47
+ secure_dir = ".secure_data"
48
+ os.makedirs(secure_dir, exist_ok=True)
49
+ return os.path.join(secure_dir, "users_encrypted.json")
50
+
51
+ def load_users():
52
+ """Load users from secure file"""
53
+ users_path = get_secure_users_path()
54
+ if os.path.exists(users_path):
55
+ try:
56
+ with open(users_path, "r") as f:
57
+ return json.load(f)
58
+ except:
59
+ return {}
60
+ return {}
61
+
62
+ def save_users(users):
63
+ """Save users to secure file"""
64
+ users_path = get_secure_users_path()
65
+ with open(users_path, "w") as f:
66
+ json.dump(users, f, indent=4)
67
+
68
+ def create_user_directory(username):
69
+ """Create user-specific directory structure"""
70
+ user_dir = f"users/{username}"
71
+ os.makedirs(user_dir, exist_ok=True)
72
+ return user_dir
73
+
74
+ def get_user_file_path(username, filename):
75
+ """Get path to user-specific file"""
76
+ user_dir = f"users/{username}"
77
+ return os.path.join(user_dir, filename)
78
+
79
+ def signup(username, password, email):
80
+ """Register new user"""
81
+ users = load_users()
82
+ if username in users:
83
+ return False, "Username already exists"
84
+
85
+ email_pattern = r"^[\w\.-]+@[\w\.-]+\.\w+$"
86
+ if not re.match(email_pattern, email):
87
+ return False, "Invalid email format"
88
+
89
+ users[username] = {
90
+ "password": hash_password(password),
91
+ "email": email,
92
+ "created_at": str(datetime.datetime.now())
93
+ }
94
+ save_users(users)
95
+ create_user_directory(username)
96
+ return True, "Account created successfully!"
97
+
98
+ def login(username, password):
99
+ """Authenticate user or admin"""
100
+ # Check if admin login
101
+ if username == ADMIN_USERNAME and password == ADMIN_PASSWORD:
102
+ return True, "Admin login successful!", True
103
+
104
+ # Regular user login
105
+ users = load_users()
106
+ if username not in users:
107
+ return False, "User not found.Please signup.", False
108
+
109
+ if users[username]["password"] == hash_password(password):
110
+ return True, "Login successful!", False
111
+ return False, "Incorrect password", False
112
+
113
+ # Emotion detection
114
+ @st.cache_resource
115
+ def load_emotion_model():
116
+ return pipeline(
117
+ "text-classification",
118
+ model="j-hartmann/emotion-english-distilroberta-base",
119
+ top_k=1,
120
+ device=-1
121
+ )
122
+
123
+ def detect_emotion(text):
124
+ emotion_pipeline = load_emotion_model()
125
+ prediction = emotion_pipeline(text)[0][0]
126
+ return prediction['label'].lower(), prediction['score']
127
+
128
+ # Authentication UI
129
+ def show_auth_page():
130
+ st.set_page_config(page_title="DilBot - Login", page_icon="🧠")
131
+ st.title("🧠 DilBot - Emotional AI Companion")
132
+ st.markdown("Welcome! Please login or create an account to continue.")
133
+
134
+ tab1, tab2 = st.tabs(["Login", "Sign Up"])
135
+
136
+ with tab1:
137
+ st.subheader("Login to Your Account")
138
+ login_username = st.text_input("Username", key="login_user")
139
+ login_password = st.text_input("Password", type="password", key="login_pass")
140
+
141
+ if st.button("Login", key="login_btn"):
142
+ if login_username and login_password:
143
+ success, message, is_admin = login(login_username, login_password)
144
+ if success:
145
+ st.session_state.authenticated = True
146
+ st.session_state.username = login_username
147
+ st.session_state.is_admin = is_admin
148
+ st.success(message)
149
+ st.rerun()
150
+ else:
151
+ st.error(message)
152
+ else:
153
+ st.warning("Please fill in all fields")
154
+
155
+ with tab2:
156
+ st.subheader("Create New Account")
157
+ signup_username = st.text_input("Choose Username", key="signup_user")
158
+ signup_email = st.text_input("Email Address", key="signup_email")
159
+ signup_password = st.text_input("Choose Password", type="password", key="signup_pass")
160
+ signup_confirm = st.text_input("Confirm Password", type="password", key="signup_confirm")
161
+
162
+ if st.button("Create Account", key="signup_btn"):
163
+ if all([signup_username, signup_email, signup_password, signup_confirm]):
164
+ if signup_password != signup_confirm:
165
+ st.error("Passwords don't match!")
166
+ elif len(signup_password) < 6:
167
+ st.error("Password must be at least 6 characters long!")
168
+ else:
169
+ success, message = signup(signup_username, signup_password, signup_email)
170
+ if success:
171
+ st.success(message)
172
+ st.info("You can now login with your credentials!")
173
+ else:
174
+ st.error(message)
175
+ else:
176
+ st.warning("Please fill in all fields")
177
+
178
+ # Main app functions
179
+ def build_user_vectorstore(username, quotes):
180
+ """Build and save user-specific vectorstore"""
181
+ embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
182
+ vectorstore = FAISS.from_texts(quotes, embedding=embeddings)
183
+
184
+ # Save vectorstore for user
185
+ vectorstore_path = get_user_file_path(username, "vectorstore")
186
+ vectorstore.save_local(vectorstore_path)
187
+ return vectorstore
188
+
189
+ def load_user_vectorstore(username):
190
+ """Load user-specific vectorstore"""
191
+ vectorstore_path = get_user_file_path(username, "vectorstore")
192
+ if os.path.exists(vectorstore_path):
193
+ embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
194
+ return FAISS.load_local(vectorstore_path, embeddings, allow_dangerous_deserialization=True)
195
+ return None
196
+
197
+ def save_user_journal(username, user_input, emotion, score, response):
198
+ """Save journal entry for specific user"""
199
+ journal_path = get_user_file_path(username, "journal.json")
200
+ entry = {
201
+ "date": str(datetime.date.today()),
202
+ "timestamp": str(datetime.datetime.now()),
203
+ "user_input": user_input,
204
+ "emotion": emotion,
205
+ "confidence": round(score * 100, 2),
206
+ "response": response
207
+ }
208
+
209
+ journal = []
210
+ if os.path.exists(journal_path):
211
+ with open(journal_path, "r") as f:
212
+ journal = json.load(f)
213
+
214
+ journal.append(entry)
215
+ with open(journal_path, "w") as f:
216
+ json.dump(journal, f, indent=4)
217
+
218
+ def load_user_journal(username):
219
+ """Load journal for specific user"""
220
+ journal_path = get_user_file_path(username, "journal.json")
221
+ if os.path.exists(journal_path):
222
+ with open(journal_path, "r") as f:
223
+ return json.load(f)
224
+ return []
225
+
226
+ def get_admin_stats():
227
+ """Get comprehensive admin statistics"""
228
+ users = load_users()
229
+ stats = {
230
+ "total_users": len(users),
231
+ "users_today": 0,
232
+ "users_this_week": 0,
233
+ "total_conversations": 0,
234
+ "active_users": 0,
235
+ "user_details": []
236
+ }
237
+
238
+ today = datetime.date.today()
239
+ week_ago = today - datetime.timedelta(days=7)
240
+
241
+ for username, user_data in users.items():
242
+ created_date = datetime.datetime.fromisoformat(user_data["created_at"]).date()
243
+
244
+ # Count registrations
245
+ if created_date == today:
246
+ stats["users_today"] += 1
247
+ if created_date >= week_ago:
248
+ stats["users_this_week"] += 1
249
+
250
+ # Get user journal stats
251
+ journal_data = load_user_journal(username)
252
+ conversation_count = len(journal_data)
253
+ stats["total_conversations"] += conversation_count
254
+
255
+ if conversation_count > 0:
256
+ stats["active_users"] += 1
257
+ last_activity = journal_data[-1]["date"] if journal_data else "Never"
258
+ else:
259
+ last_activity = "Never"
260
+
261
+ # Get emotion breakdown
262
+ emotions = [entry['emotion'] for entry in journal_data]
263
+ emotion_counts = {}
264
+ for emotion in emotions:
265
+ emotion_counts[emotion] = emotion_counts.get(emotion, 0) + 1
266
+
267
+ most_common_emotion = max(emotion_counts, key=emotion_counts.get) if emotion_counts else "None"
268
+
269
+ stats["user_details"].append({
270
+ "username": username,
271
+ "email": user_data["email"],
272
+ "joined": created_date.strftime("%Y-%m-%d"),
273
+ "conversations": conversation_count,
274
+ "last_activity": last_activity,
275
+ "most_common_emotion": most_common_emotion.capitalize(),
276
+ "emotions_breakdown": emotion_counts
277
+ })
278
+
279
+ return stats
280
+
281
+ def log_admin_activity(action, details=""):
282
+ """Log admin activities"""
283
+ admin_log_path = "data/admin_log.json" # Persistent and visible
284
+ log_entry = {
285
+ "timestamp": str(datetime.datetime.now()),
286
+ "action": action,
287
+ "details": details,
288
+ "admin": st.session_state.username
289
+ }
290
+
291
+ admin_log = []
292
+ if os.path.exists(admin_log_path):
293
+ with open(admin_log_path, "r") as f:
294
+ admin_log = json.load(f)
295
+
296
+ admin_log.append(log_entry)
297
+
298
+ # Keep only last 100 entries
299
+ admin_log = admin_log[-100:]
300
+
301
+ os.makedirs("data", exist_ok=True)
302
+ with open(admin_log_path, "w") as f:
303
+ json.dump(admin_log, f, indent=4)
304
+
305
+ def get_admin_logs():
306
+ """Get admin activity logs"""
307
+ admin_log_path = "data/admin_log.json"
308
+ if os.path.exists(admin_log_path):
309
+ with open(admin_log_path, "r") as f:
310
+ return json.load(f)
311
+ return []
312
+
313
+ def is_crisis(text):
314
+ """Check for crisis keywords"""
315
+ return any(phrase in text.lower() for phrase in CRISIS_KEYWORDS)
316
+
317
+ def show_admin_dashboard():
318
+ """Admin dashboard for monitoring users and app usage"""
319
+ st.set_page_config(page_title="DilBot Admin Dashboard", page_icon="👑")
320
+
321
+ # Header
322
+ col1, col2 = st.columns([4, 1])
323
+ with col1:
324
+ st.title("👑 DilBot Admin Dashboard")
325
+ st.markdown("Monitor users, conversations, and app analytics")
326
+ with col2:
327
+ if st.button("Logout", key="admin_logout"):
328
+ st.session_state.authenticated = False
329
+ st.session_state.username = None
330
+ st.session_state.is_admin = False
331
+ st.rerun()
332
+
333
+ # Log admin access
334
+ log_admin_activity("Dashboard Access", "Viewed admin dashboard")
335
+
336
+ # Get statistics
337
+ stats = get_admin_stats()
338
+
339
+ # Overview metrics
340
+ st.header("📊 Overview")
341
+ col1, col2, col3, col4 = st.columns(4)
342
+
343
+ with col1:
344
+ st.metric("Total Users", stats["total_users"])
345
+ with col2:
346
+ st.metric("Active Users", stats["active_users"])
347
+ with col3:
348
+ st.metric("New Today", stats["users_today"])
349
+ with col4:
350
+ st.metric("Total Conversations", stats["total_conversations"])
351
+
352
+ # User registration trend
353
+ st.header("📈 User Registration Trend")
354
+ if stats["user_details"]:
355
+ # Create registration data
356
+ reg_data = {}
357
+ for user in stats["user_details"]:
358
+ date = user["joined"]
359
+ reg_data[date] = reg_data.get(date, 0) + 1
360
+
361
+ chart_data = [{"date": date, "registrations": count} for date, count in sorted(reg_data.items())]
362
+
363
+ if chart_data:
364
+ chart = alt.Chart(alt.Data(values=chart_data)).mark_line(point=True).encode(
365
+ x=alt.X('date:T', title='Date'),
366
+ y=alt.Y('registrations:Q', title='New Registrations'),
367
+ tooltip=['date:T', 'registrations:Q']
368
+ ).properties(
369
+ width=700,
370
+ height=300,
371
+ title="Daily User Registrations"
372
+ )
373
+ st.altair_chart(chart, use_container_width=True)
374
+
375
+ # Detailed user table
376
+ st.header("👥 User Details")
377
+
378
+ # Search and filter
379
+ col1, col2 = st.columns([2, 1])
380
+ with col1:
381
+ search_term = st.text_input("🔍 Search users", placeholder="Search by username or email")
382
+ with col2:
383
+ min_conversations = st.number_input("Min conversations", min_value=0, value=0)
384
+
385
+ # Filter users
386
+ filtered_users = stats["user_details"]
387
+ if search_term:
388
+ filtered_users = [u for u in filtered_users if
389
+ search_term.lower() in u["username"].lower() or
390
+ search_term.lower() in u["email"].lower()]
391
+
392
+ if min_conversations > 0:
393
+ filtered_users = [u for u in filtered_users if u["conversations"] >= min_conversations]
394
+
395
+ # Display user table
396
+ if filtered_users:
397
+ for user in filtered_users:
398
+ with st.expander(f"👤 {user['username']} ({user['conversations']} conversations)"):
399
+ col1, col2 = st.columns(2)
400
+
401
+ with col1:
402
+ st.write(f"**Email:** {user['email']}")
403
+ st.write(f"**Joined:** {user['joined']}")
404
+ st.write(f"**Last Activity:** {user['last_activity']}")
405
+
406
+ with col2:
407
+ st.write(f"**Conversations:** {user['conversations']}")
408
+ st.write(f"**Most Common Emotion:** {user['most_common_emotion']}")
409
+
410
+ # Show emotion breakdown
411
+ if user['emotions_breakdown']:
412
+ st.write("**Emotion Breakdown:**")
413
+ for emotion, count in user['emotions_breakdown'].items():
414
+ st.write(f" - {emotion.capitalize()}: {count}")
415
+
416
+ # Quick actions
417
+ col1, col2, col3 = st.columns(3)
418
+ with col1:
419
+ if st.button(f"View Journal", key=f"view_{user['username']}"):
420
+ # Show user's recent conversations
421
+ user_journal = load_user_journal(user['username'])
422
+ if user_journal:
423
+ st.subheader(f"Recent conversations for {user['username']}")
424
+ for entry in user_journal[-5:]:
425
+ st.text_area(
426
+ f"{entry['date']} - {entry['emotion'].capitalize()}",
427
+ f"User: {entry['user_input']}\nDilBot: {entry['response']}",
428
+ height=100,
429
+ disabled=True
430
+ )
431
+ else:
432
+ st.info("No conversations found")
433
+
434
+ with col2:
435
+ reset_key = f"reset_{user['username']}"
436
+ confirm_key = f"confirm_{user['username']}"
437
+
438
+ if st.button(f"Reset Data", key=reset_key):
439
+ st.session_state[confirm_key] = True # Flag to show confirmation
440
+
441
+ if st.session_state.get(confirm_key, False):
442
+ st.warning(f"Are you sure you want to reset data for {user['username']}?")
443
+ if st.button(f"Yes, Reset {user['username']}", key=f"confirm_reset_{user['username']}"):
444
+ # Clear user's journal
445
+ journal_path = get_user_file_path(user['username'], "journal.json")
446
+ if os.path.exists(journal_path):
447
+ os.remove(journal_path)
448
+
449
+ log_admin_activity("User Data Reset", f"Reset data for {user['username']}")
450
+ st.success(f"Data reset for {user['username']}")
451
+ st.session_state[confirm_key] = False # Reset confirmation flag
452
+ st.rerun()
453
+ if st.button(f"Cancel", key=f"cancel_reset_{user['username']}"):
454
+ st.session_state[confirm_key] = False # Cancel confirmation
455
+ st.rerun()
456
+
457
+ else:
458
+ st.info("No users found matching your criteria")
459
+
460
+ # System Analytics
461
+ st.header("🔧 System Analytics")
462
+
463
+ col1, col2 = st.columns(2)
464
+
465
+ with col1:
466
+ st.subheader("Emotion Distribution (All Users)")
467
+ # Aggregate all emotions
468
+ all_emotions = {}
469
+ for user in stats["user_details"]:
470
+ for emotion, count in user['emotions_breakdown'].items():
471
+ all_emotions[emotion] = all_emotions.get(emotion, 0) + count
472
+
473
+ if all_emotions:
474
+ emotion_chart_data = [{"emotion": emotion.capitalize(), "count": count}
475
+ for emotion, count in all_emotions.items()]
476
+
477
+ emotion_chart = alt.Chart(alt.Data(values=emotion_chart_data)).mark_bar().encode(
478
+ x=alt.X('emotion:N', title='Emotion'),
479
+ y=alt.Y('count:Q', title='Frequency'),
480
+ color=alt.Color('emotion:N', legend=None),
481
+ tooltip=['emotion:N', 'count:Q']
482
+ ).properties(
483
+ width=400,
484
+ height=300,
485
+ title="Overall Emotion Distribution"
486
+ )
487
+ st.altair_chart(emotion_chart, use_container_width=True)
488
+
489
+ with col2:
490
+ st.subheader("User Activity Levels")
491
+ activity_levels = {"Inactive (0)": 0, "Light (1-5)": 0, "Moderate (6-20)": 0, "Heavy (21+)": 0}
492
+
493
+ for user in stats["user_details"]:
494
+ conv_count = user["conversations"]
495
+ if conv_count == 0:
496
+ activity_levels["Inactive (0)"] += 1
497
+ elif conv_count <= 5:
498
+ activity_levels["Light (1-5)"] += 1
499
+ elif conv_count <= 20:
500
+ activity_levels["Moderate (6-20)"] += 1
501
+ else:
502
+ activity_levels["Heavy (21+)"] += 1
503
+
504
+ activity_data = [{"level": level, "users": count} for level, count in activity_levels.items()]
505
+
506
+ activity_chart = alt.Chart(alt.Data(values=activity_data)).mark_arc().encode(
507
+ theta=alt.Theta('users:Q'),
508
+ color=alt.Color('level:N', title="Activity Level"),
509
+ tooltip=['level:N', 'users:Q']
510
+ ).properties(
511
+ width=300,
512
+ height=300,
513
+ title="User Activity Distribution"
514
+ )
515
+ st.altair_chart(activity_chart, use_container_width=True)
516
+
517
+ # Admin logs
518
+ st.header("📋 Admin Activity Logs")
519
+ admin_logs = get_admin_logs()
520
+
521
+ if admin_logs:
522
+ # Show recent admin activities
523
+ for log_entry in reversed(admin_logs[-10:]): # Last 10 activities
524
+ timestamp = datetime.datetime.fromisoformat(log_entry["timestamp"])
525
+ st.text(f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')} - {log_entry['action']}: {log_entry['details']}")
526
+ else:
527
+ st.info("No admin activities logged yet")
528
+
529
+ # Export functionality
530
+ st.header("💾 Data Export")
531
+ col1, col2 = st.columns(2)
532
+
533
+ with col1:
534
+ if st.button("Export User Data (JSON)"):
535
+ export_data = {
536
+ "export_timestamp": str(datetime.datetime.now()),
537
+ "statistics": stats,
538
+ "admin_logs": admin_logs
539
+ }
540
+
541
+ st.download_button(
542
+ label="Download User Data",
543
+ data=json.dumps(export_data, indent=4),
544
+ file_name=f"dilbot_export_{datetime.date.today()}.json",
545
+ mime="application/json"
546
+ )
547
+
548
+ log_admin_activity("Data Export", "Exported user data")
549
+
550
+ with col2:
551
+ if st.button("Clear Admin Logs"):
552
+ admin_log_path = "data/admin_log.json"
553
+ if os.path.exists(admin_log_path):
554
+ os.remove(admin_log_path)
555
+ st.success("Admin logs cleared!")
556
+ st.rerun()
557
+
558
+ def speak(text, username):
559
+ """Generate and play audio response"""
560
+ tts = gTTS(text=text, lang='en')
561
+ audio_path = get_user_file_path(username, "response.mp3")
562
+ tts.save(audio_path)
563
+ st.audio(audio_path, format="audio/mp3")
564
+
565
+
566
+ def transcribe_audio_file(uploaded_audio):
567
+ """Transcribe uploaded audio file"""
568
+ recognizer = sr.Recognizer()
569
+ try:
570
+ with sr.AudioFile(uploaded_audio) as source:
571
+ audio_data = recognizer.record(source)
572
+ text = recognizer.recognize_google(audio_data)
573
+ return text
574
+ except Exception as e:
575
+ return f"Error: {str(e)}"
576
+
577
+ def show_main_app():
578
+ """Main DilBot application"""
579
+ username = st.session_state.username
580
+
581
+ st.set_page_config(page_title="DilBot - Emotional AI", page_icon="🧠")
582
+
583
+ # Header with logout
584
+ col1, col2 = st.columns([4, 1])
585
+ with col1:
586
+ st.title(f"🧠 DilBot - Welcome back, {username}!")
587
+ st.markdown("Your personal emotional AI companion")
588
+ with col2:
589
+ if st.button("Logout", key="logout_btn"):
590
+ st.session_state.authenticated = False
591
+ st.session_state.username = None
592
+ st.rerun()
593
+
594
+ # Quote categories
595
+ quote_categories = {
596
+ "Grief": ["Grief is the price we pay for love.", "Tears are the silent language of grief.", "What we have once enjoyed we can never lose; all that we love deeply becomes a part of us."],
597
+ "Motivation": ["Believe in yourself and all that you are.", "Tough times never last, but tough people do.", "The only way to do great work is to love what you do."],
598
+ "Healing": ["Every wound has its own time to heal.", "It's okay to take your time to feel better.", "Healing is not linear, and that's perfectly okay."],
599
+ "Relationships": ["The best relationships are built on trust.", "Love is not about possession but appreciation.", "Healthy relationships require both people to show up authentically."]
600
+ }
601
+
602
+ # UI for quote selection and file upload
603
+ col1, col2 = st.columns(2)
604
+ with col1:
605
+ selected_category = st.selectbox("🎯 Choose a quote theme:", list(quote_categories.keys()))
606
+ with col2:
607
+ uploaded_quotes = st.file_uploader("📁 Upload your own quotes (.txt)", type=["txt"])
608
+
609
+ uploaded_audio = st.file_uploader("🎤 Upload a voice message (.wav)", type=["wav"])
610
+
611
+ # Handle vectorstore
612
+ current_quotes = []
613
+ vectorstore = None
614
+
615
+ if uploaded_quotes:
616
+ custom_quotes = uploaded_quotes.read().decode("utf-8").splitlines()
617
+ custom_quotes = [quote.strip() for quote in custom_quotes if quote.strip()]
618
+ vectorstore = build_user_vectorstore(username, custom_quotes)
619
+ current_quotes = custom_quotes
620
+ st.success(f"✅ {len(custom_quotes)} custom quotes uploaded and saved!")
621
+ else:
622
+ default_quotes = quote_categories[selected_category]
623
+ vectorstore = load_user_vectorstore(username)
624
+ if vectorstore is None:
625
+ vectorstore = build_user_vectorstore(username, default_quotes)
626
+ current_quotes = default_quotes
627
+
628
+ # Voice transcription
629
+ if uploaded_audio and st.button("🎙️ Transcribe Voice"):
630
+ with st.spinner("Transcribing your voice..."):
631
+ transcribed = transcribe_audio_file(uploaded_audio)
632
+ if transcribed.startswith("Error:"):
633
+ st.error(transcribed)
634
+ else:
635
+ st.session_state.transcribed_text = transcribed
636
+ st.success("✅ Voice transcribed successfully!")
637
+
638
+ # Input area
639
+ user_input = st.text_area(
640
+ "💬 What's on your mind?",
641
+ value=st.session_state.transcribed_text,
642
+ height=100,
643
+ placeholder="Share your thoughts, feelings, or experiences..."
644
+ )
645
+
646
+ final_input = user_input.strip() or st.session_state.transcribed_text.strip()
647
+
648
+ # Main interaction button
649
+ if st.button("🧠 Talk to DilBot", type="primary"):
650
+ if not final_input:
651
+ st.warning("⚠️ Please enter something to share or upload a voice message.")
652
+ else:
653
+ with st.spinner("DilBot is thinking and feeling..."):
654
+ # Emotion detection
655
+ emotion, score = detect_emotion(final_input)
656
+
657
+ # Get AI response
658
+ prompt_template = PromptTemplate(
659
+ input_variables=["context", "user_input", "username"],
660
+ template="""You are DilBot, an empathetic emotional support AI companion for {username}.
661
+ Use the following emotional quote context to respond gently, supportively, and personally.
662
+ Context quotes:
663
+ {context}
664
+ User's message:
665
+ {user_input}
666
+ Respond as DilBot with warmth, empathy, and understanding. Keep it conversational and supportive."""
667
+ )
668
+
669
+ # Get similar quotes
670
+ similar_docs = vectorstore.similarity_search(final_input, k=2)
671
+ context = "\n".join([doc.page_content for doc in similar_docs])
672
+
673
+ # Generate response
674
+ groq_llm = ChatGroq(api_key=GROQ_API_KEY, model="llama3-70b-8192")
675
+ chain = LLMChain(llm=groq_llm, prompt=prompt_template)
676
+ response = chain.run(context=context, user_input=final_input, username=username)
677
+
678
+ # Save to user's journal
679
+ save_user_journal(username, final_input, emotion, score, response)
680
+
681
+ # Display results
682
+ col1, col2 = st.columns([2, 1])
683
+ with col1:
684
+ st.success(f"**Emotion Detected:** {emotion.capitalize()} ({round(score*100)}% confidence)")
685
+ with col2:
686
+ if is_crisis(final_input):
687
+ st.error("🚨 Crisis detected! Please reach out to a mental health professional immediately.")
688
+
689
+ # Show relevant quote
690
+ if current_quotes:
691
+ model = SentenceTransformer("all-MiniLM-L6-v2")
692
+ quote_embeddings = model.encode(current_quotes, convert_to_tensor=True)
693
+ user_embedding = model.encode(final_input, convert_to_tensor=True)
694
+ sims = util.pytorch_cos_sim(user_embedding, quote_embeddings)[0]
695
+ best_match = sims.argmax().item()
696
+ selected_quote = current_quotes[best_match]
697
+ st.info(f"💭 **Quote for you:** *{selected_quote}*")
698
+
699
+ # Show response
700
+ st.markdown("### 🤖 DilBot's Response:")
701
+ st.markdown(f"> {response}")
702
+
703
+ # Audio response
704
+ speak(response, username)
705
+
706
+ # Clear transcribed text after successful interaction
707
+ st.session_state.transcribed_text = ""
708
+
709
+ # User's personal dashboard
710
+ st.markdown("---")
711
+ st.header("📊 Your Personal Dashboard")
712
+
713
+ # Load user's journal
714
+ journal_data = load_user_journal(username)
715
+
716
+ if journal_data:
717
+ # Mood tracker
718
+ st.subheader("📈 Your Daily Mood Tracker")
719
+
720
+ # Prepare data for chart
721
+ df_data = []
722
+ for entry in journal_data:
723
+ df_data.append({
724
+ "date": entry["date"],
725
+ "emotion": entry["emotion"].capitalize(),
726
+ "confidence": entry["confidence"]
727
+ })
728
+
729
+ if df_data:
730
+ chart = alt.Chart(alt.Data(values=df_data)).mark_bar().encode(
731
+ x=alt.X('date:N', title='Date'),
732
+ y=alt.Y('count():Q', title='Frequency'),
733
+ color=alt.Color('emotion:N', title='Emotion'),
734
+ tooltip=['date:N', 'emotion:N', 'count():Q']
735
+ ).properties(
736
+ width=600,
737
+ height=300,
738
+ title="Your Emotional Journey Over Time"
739
+ )
740
+ st.altair_chart(chart, use_container_width=True)
741
+
742
+ # Recent conversations
743
+ st.subheader("💬 Recent Conversations")
744
+ recent_entries = journal_data[-5:] if len(journal_data) >= 5 else journal_data
745
+
746
+ for i, entry in enumerate(reversed(recent_entries)):
747
+ with st.expander(f"📅 {entry['date']} - {entry['emotion'].capitalize()} ({entry['confidence']}%)"):
748
+ st.markdown(f"**You said:** {entry['user_input']}")
749
+ st.markdown(f"**DilBot replied:** {entry['response']}")
750
+
751
+ # Statistics
752
+ st.subheader("📊 Your Emotional Statistics")
753
+ col1, col2, col3 = st.columns(3)
754
+
755
+ with col1:
756
+ st.metric("Total Conversations", len(journal_data))
757
+
758
+ with col2:
759
+ emotions = [entry['emotion'] for entry in journal_data]
760
+ most_common = max(set(emotions), key=emotions.count) if emotions else "None"
761
+ st.metric("Most Common Emotion", most_common.capitalize())
762
+
763
+ with col3:
764
+ if journal_data:
765
+ avg_confidence = sum(entry['confidence'] for entry in journal_data) / len(journal_data)
766
+ st.metric("Avg. Confidence", f"{avg_confidence:.1f}%")
767
+
768
+ else:
769
+ st.info("🌟 Start your first conversation with DilBot to see your personal dashboard!")
770
+
771
+ st.markdown("---")
772
+ st.caption("Built by Members of CSG Hackathon Team | Your data is stored privately and securely")
773
+
774
+ # Main app logic
775
+ def main():
776
+ if not st.session_state.authenticated:
777
+ show_auth_page()
778
+ elif st.session_state.is_admin:
779
+ show_admin_dashboard()
780
+ else:
781
+ show_main_app()
782
+
783
+ if __name__ == "__main__":
784
+ main()