joyjonesmark commited on
Commit
0b2fcbb
·
1 Parent(s): b4d9894

frontend only

Browse files
Files changed (1) hide show
  1. app.py +408 -0
app.py ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import sqlite3
3
+ import plotly.express as px
4
+ import pandas as pd
5
+ from PIL import Image
6
+ import re
7
+ import secrets
8
+ import bcrypt
9
+ import smtplib
10
+ from email.mime.text import MIMEText
11
+ import logging
12
+ from datetime import datetime
13
+
14
+ # Logging setup
15
+ logging.basicConfig(
16
+ filename='app.log',
17
+ level=logging.INFO,
18
+ format='%(asctime)s - %(levelname)s - %(message)s'
19
+ )
20
+ logger = logging.getLogger()
21
+
22
+ # Database setup
23
+ def init_db():
24
+ try:
25
+ conn = sqlite3.connect('users.db')
26
+ c = conn.cursor()
27
+ c.execute('''CREATE TABLE IF NOT EXISTS users
28
+ (id INTEGER PRIMARY KEY, name TEXT, username TEXT UNIQUE, email TEXT UNIQUE, password BLOB, reset_token TEXT)''')
29
+ conn.commit()
30
+ except sqlite3.Error as e:
31
+ logger.error(f"Database initialization failed: {str(e)}")
32
+ st.error("An error occurred while setting up the database.")
33
+ finally:
34
+ conn.close()
35
+
36
+ # Hash password
37
+ def hash_password(password):
38
+ return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
39
+
40
+ # Check password
41
+ def check_password(password, hashed):
42
+ return bcrypt.checkpw(password.encode('utf-8'), hashed)
43
+
44
+ # Login function
45
+ def login(username_email, password):
46
+ try:
47
+ conn = sqlite3.connect('users.db')
48
+ c = conn.cursor()
49
+ c.execute("SELECT * FROM users WHERE (username=? OR email=?)", (username_email, username_email))
50
+ user = c.fetchone()
51
+ if user and check_password(password, user[4]): # user[4] is password column (BLOB)
52
+ logger.info(f"User {username_email} logged in successfully.")
53
+ return user
54
+ logger.warning(f"Login failed for {username_email}: Invalid credentials.")
55
+ return None
56
+ except sqlite3.Error as e:
57
+ logger.error(f"Login database error: {str(e)}")
58
+ st.error("A database error occurred during login.")
59
+ return None
60
+ finally:
61
+ conn.close()
62
+
63
+ # Create account function
64
+ def create_account(name, username, email, password):
65
+ try:
66
+ hashed_password = hash_password(password) # Returns bytes
67
+ conn = sqlite3.connect('users.db')
68
+ c = conn.cursor()
69
+ c.execute("INSERT INTO users (name, username, email, password) VALUES (?, ?, ?, ?)",
70
+ (name, username, email, hashed_password))
71
+ conn.commit()
72
+ logger.info(f"New account created: {username} ({email})")
73
+ return True
74
+ except sqlite3.IntegrityError as e:
75
+ conn.close()
76
+ if "username" in str(e):
77
+ st.error("Username already exists!")
78
+ elif "email" in str(e):
79
+ st.error("Email already exists!")
80
+ logger.warning(f"Account creation failed: {str(e)}")
81
+ return False
82
+ except sqlite3.Error as e:
83
+ logger.error(f"Account creation database error: {str(e)}")
84
+ st.error("A database error occurred during account creation.")
85
+ return False
86
+ finally:
87
+ conn.close()
88
+
89
+ # Update profile function
90
+ def update_profile(user_id, name, username, email):
91
+ try:
92
+ conn = sqlite3.connect('users.db')
93
+ c = conn.cursor()
94
+ c.execute("UPDATE users SET name=?, username=?, email=? WHERE id=?",
95
+ (name, username, email, user_id))
96
+ conn.commit()
97
+ logger.info(f"Profile updated for user ID {user_id}")
98
+ return True
99
+ except sqlite3.IntegrityError as e:
100
+ conn.close()
101
+ if "username" in str(e):
102
+ st.error("Username already taken!")
103
+ elif "email" in str(e):
104
+ st.error("Email already in use!")
105
+ logger.warning(f"Profile update failed: {str(e)}")
106
+ return False
107
+ except sqlite3.Error as e:
108
+ logger.error(f"Profile update database error: {str(e)}")
109
+ st.error("A database error occurred during profile update.")
110
+ return False
111
+ finally:
112
+ conn.close()
113
+
114
+ # Send reset email
115
+ def send_reset_email(email, token):
116
+ sender_email = "your_email@example.com" # Replace with your email
117
+ sender_password = "your_password" # Replace with your email password/app-specific password
118
+ subject = "Password Reset Request"
119
+ body = f"Your password reset token is: {token}\n\nThis token is valid for 15 minutes."
120
+ msg = MIMEText(body)
121
+ msg['Subject'] = subject
122
+ msg['From'] = sender_email
123
+ msg['To'] = email
124
+
125
+ try:
126
+ with smtplib.SMTP('smtp.gmail.com', 587) as server: # Example for Gmail
127
+ server.starttls()
128
+ server.login(sender_email, sender_password) # Fixed argument order
129
+ server.send_message(msg)
130
+ logger.info(f"Reset email sent to {email}")
131
+ return True
132
+ except Exception as e:
133
+ logger.error(f"Failed to send reset email to {email}: {str(e)}")
134
+ st.error("Failed to send reset email. Please try again later.")
135
+ return False
136
+
137
+ # Generate reset token
138
+ def generate_reset_token(email):
139
+ try:
140
+ token = secrets.token_urlsafe(16)
141
+ conn = sqlite3.connect('users.db')
142
+ c = conn.cursor()
143
+ c.execute("UPDATE users SET reset_token=? WHERE email=?", (token, email))
144
+ affected = conn.total_changes
145
+ conn.commit()
146
+ if affected > 0 and send_reset_email(email, token):
147
+ return token
148
+ logger.warning(f"No user found with email {email} for reset token")
149
+ return None
150
+ except sqlite3.Error as e:
151
+ logger.error(f"Reset token database error: {str(e)}")
152
+ st.error("A database error occurred during token generation.")
153
+ return None
154
+ finally:
155
+ conn.close()
156
+
157
+ # Reset password function
158
+ def reset_password(token, new_password):
159
+ try:
160
+ hashed_password = hash_password(new_password) # Returns bytes
161
+ conn = sqlite3.connect('users.db')
162
+ c = conn.cursor()
163
+ c.execute("UPDATE users SET password=?, reset_token=NULL WHERE reset_token=?",
164
+ (hashed_password, token))
165
+ affected = conn.total_changes
166
+ conn.commit()
167
+ if affected > 0:
168
+ logger.info(f"Password reset successfully with token {token}")
169
+ else:
170
+ logger.warning(f"Password reset failed: Invalid token {token}")
171
+ return affected > 0
172
+ except sqlite3.Error as e:
173
+ logger.error(f"Reset password database error: {str(e)}")
174
+ st.error("A database error occurred during password reset.")
175
+ return False
176
+ finally:
177
+ conn.close()
178
+
179
+ # Input validation
180
+ def validate_email(email):
181
+ pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
182
+ return re.match(pattern, email) is not None
183
+
184
+ def validate_password(password):
185
+ return len(password) >= 8 and any(c.isupper() for c in password) and any(c.isdigit() for c in password)
186
+
187
+ # Enhanced Custom CSS
188
+ def load_css():
189
+ st.markdown("""
190
+ <style>
191
+ .stApp {
192
+ background: linear-gradient(135deg, #e0eafc, #cfdef3);
193
+ font-family: 'Arial', sans-serif;
194
+ }
195
+ .stButton>button {
196
+ background-color: #4CAF50;
197
+ color: white;
198
+ border-radius: 8px;
199
+ padding: 10px 20px;
200
+ transition: background-color 0.3s;
201
+ }
202
+ .stButton>button:hover {
203
+ background-color: #45a049;
204
+ }
205
+ .stTextInput>input {
206
+ border-radius: 8px;
207
+ border: 2px solid #ccc;
208
+ padding: 8px;
209
+ transition: border-color 0.3s;
210
+ }
211
+ .stTextInput>input:focus {
212
+ border-color: #4CAF50;
213
+ }
214
+ .title {
215
+ color: #2c3e50;
216
+ font-size: 2.8em;
217
+ text-align: center;
218
+ font-weight: bold;
219
+ margin-bottom: 20px;
220
+ }
221
+ .sidebar .sidebar-content {
222
+ background-color: #ffffff;
223
+ border-radius: 10px;
224
+ padding: 15px;
225
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
226
+ }
227
+ .stSelectbox {
228
+ border-radius: 8px;
229
+ }
230
+ </style>
231
+ """, unsafe_allow_html=True)
232
+
233
+ # Main application
234
+ def main():
235
+ init_db()
236
+ load_css()
237
+
238
+ if 'page' not in st.session_state:
239
+ st.session_state.page = 'login'
240
+ if 'logged_in' not in st.session_state:
241
+ st.session_state.logged_in = False
242
+
243
+ # Sidebar
244
+ with st.sidebar:
245
+ st.image("https://via.placeholder.com/100", caption="App Logo") # Placeholder logo
246
+ st.title("Navigation")
247
+ if st.session_state.logged_in:
248
+ st.write(f"Welcome, {st.session_state.username}!")
249
+ st.progress(100) # Example of interactive element
250
+ menu = ["Profile", "Data Visualization", "Emotion Analysis", "Logout"]
251
+ choice = st.selectbox("Menu", menu, key="sidebar_menu")
252
+ if choice == "Logout" and st.button("Confirm Logout", key="logout"):
253
+ st.session_state.logged_in = False
254
+ st.session_state.page = 'login'
255
+ logger.info(f"User {st.session_state.username} logged out.")
256
+ st.rerun()
257
+ elif choice == "Profile":
258
+ st.session_state.page = 'profile'
259
+ elif choice == "Data Visualization":
260
+ st.session_state.page = 'data_visualization'
261
+ elif choice == "Emotion Analysis":
262
+ st.session_state.page = 'main'
263
+ else:
264
+ col1, col2 = st.columns(2)
265
+ with col1:
266
+ if st.button("Create Account", key="create_account_btn"):
267
+ st.session_state.page = 'create_account'
268
+ with col2:
269
+ if st.button("Reset Password", key="reset_pass_btn"):
270
+ st.session_state.page = 'reset_password'
271
+
272
+ # Page routing
273
+ if st.session_state.page == 'login' and not st.session_state.logged_in:
274
+ st.markdown('<p class="title">Login</p>', unsafe_allow_html=True)
275
+ with st.form(key='login_form'):
276
+ username_email = st.text_input("Username or Email", placeholder="Enter username or email")
277
+ password = st.text_input("Password", type="password", placeholder="Enter password")
278
+ submit_button = st.form_submit_button(label="Login")
279
+
280
+ if submit_button:
281
+ if not username_email or not password:
282
+ st.error("Please fill in all fields!")
283
+ else:
284
+ user = login(username_email, password)
285
+ if user:
286
+ st.session_state.logged_in = True
287
+ st.session_state.user_id = user[0]
288
+ st.session_state.username = user[2]
289
+ st.session_state.page = 'data_visualization'
290
+ st.success("Logged in successfully!")
291
+ st.balloons() # Interactive UI element
292
+ st.rerun()
293
+ else:
294
+ st.error("Invalid username/email or password!")
295
+
296
+ elif st.session_state.page == 'create_account' and not st.session_state.logged_in:
297
+ st.markdown('<p class="title">Create Account</p>', unsafe_allow_html=True)
298
+ with st.form(key='create_account_form'):
299
+ name = st.text_input("Full Name", placeholder="Enter your full name")
300
+ username = st.text_input("Username", placeholder="Choose a username")
301
+ email = st.text_input("Email", placeholder="Enter your email")
302
+ password = st.text_input("Password", type="password", placeholder="Create a password")
303
+ submit_button = st.form_submit_button(label="Submit")
304
+
305
+ if submit_button:
306
+ if not all([name, username, email, password]):
307
+ st.error("All fields are required!")
308
+ elif not validate_email(email):
309
+ st.error("Invalid email format!")
310
+ elif not validate_password(password):
311
+ st.error("Password must be 8+ characters with uppercase and numbers!")
312
+ elif create_account(name, username, email, password):
313
+ st.success("Account created successfully!")
314
+ st.session_state.page = 'login'
315
+ st.rerun()
316
+
317
+ elif st.session_state.page == 'reset_password' and not st.session_state.logged_in:
318
+ st.markdown('<p class="title">Reset Password</p>', unsafe_allow_html=True)
319
+ step = st.session_state.get('reset_step', 'request')
320
+
321
+ if step == 'request':
322
+ with st.form(key='reset_request_form'):
323
+ email = st.text_input("Email", placeholder="Enter your email")
324
+ submit_button = st.form_submit_button(label="Request Reset")
325
+ if submit_button:
326
+ if not validate_email(email):
327
+ st.error("Invalid email format!")
328
+ else:
329
+ token = generate_reset_token(email)
330
+ if token:
331
+ st.success("A reset token has been sent to your email!")
332
+ st.session_state.reset_step = 'reset'
333
+ st.session_state.reset_email = email
334
+ st.rerun()
335
+ else:
336
+ st.error("Email not found or email sending failed!")
337
+
338
+ elif step == 'reset':
339
+ with st.form(key='reset_form'):
340
+ token = st.text_input("Reset Token", placeholder="Enter the token from your email")
341
+ new_password = st.text_input("New Password", type="password", placeholder="Enter new password")
342
+ submit_button = st.form_submit_button(label="Reset Password")
343
+ if submit_button:
344
+ if not validate_password(new_password):
345
+ st.error("Password must be 8+ characters with uppercase and numbers!")
346
+ elif reset_password(token, new_password):
347
+ st.success("Password reset successfully!")
348
+ st.session_state.page = 'login'
349
+ del st.session_state['reset_step']
350
+ st.rerun()
351
+ else:
352
+ st.error("Invalid or expired token!")
353
+
354
+ elif st.session_state.logged_in and st.session_state.page == 'profile':
355
+ st.markdown('<p class="title">User Profile</p>', unsafe_allow_html=True)
356
+ conn = sqlite3.connect('users.db')
357
+ c = conn.cursor()
358
+ c.execute("SELECT name, username, email FROM users WHERE id=?", (st.session_state.user_id,))
359
+ user = c.fetchone()
360
+ conn.close()
361
+
362
+ with st.form(key='profile_form'):
363
+ name = st.text_input("Full Name", value=user[0], placeholder="Update your name")
364
+ username = st.text_input("Username", value=user[1], placeholder="Update your username")
365
+ email = st.text_input("Email", value=user[2], placeholder="Update your email")
366
+ submit_button = st.form_submit_button(label="Update Profile")
367
+
368
+ if submit_button:
369
+ if not all([name, username, email]):
370
+ st.error("All fields are required!")
371
+ elif not validate_email(email):
372
+ st.error("Invalid email format!")
373
+ elif update_profile(st.session_state.user_id, name, username, email):
374
+ st.session_state.username = username
375
+ st.success("Profile updated successfully!")
376
+ st.rerun()
377
+
378
+ elif st.session_state.logged_in and st.session_state.page == 'data_visualization':
379
+ st.markdown('<p class="title">Data Visualization</p>', unsafe_allow_html=True)
380
+ st.write("""
381
+ ### Models Overview
382
+ - **Custom CNN**: Custom-built Convolutional Neural Network designed for emotion recognition
383
+ - **MobileNetV2**: Lightweight deep learning model optimized for mobile devices
384
+ - **VGG19**: Deep 19-layer convolutional neural network for image classification
385
+ """)
386
+ emotions = ['Happiness', 'Neutral', 'Sadness', 'Anger', 'Surprise', 'Disgust', 'Fear']
387
+ counts = [5000, 6000, 5500, 4500, 4000, 3500, 4200]
388
+ df = pd.DataFrame({'Emotion': emotions, 'Count': counts})
389
+ fig = px.bar(df, x='Emotion', y='Count', title='Emotion Distribution in Dataset', color='Emotion')
390
+ st.plotly_chart(fig)
391
+ if st.button("Next Page", key="next_page"):
392
+ st.session_state.page = 'main'
393
+ st.rerun()
394
+
395
+ elif st.session_state.logged_in and st.session_state.page == 'main':
396
+ st.markdown('<p class="title">Emotion Recognition & Analysis</p>', unsafe_allow_html=True)
397
+ model = st.selectbox("Select Model", ["Custom CNN", "MobileNetV2", "VGG19"], key="model_select")
398
+ if model:
399
+ uploaded_file = st.file_uploader("Upload an image", type=['jpg', 'png', 'jpeg'], key="file_uploader")
400
+ if uploaded_file is not None:
401
+ image = Image.open(uploaded_file)
402
+ st.image(image, caption='Uploaded Image', use_container_width=True)
403
+ with st.spinner(f"Processing with {model}..."):
404
+ # Placeholder for model prediction
405
+ st.write("Analysis complete (placeholder).")
406
+
407
+ if __name__ == "__main__":
408
+ main()