|
|
import streamlit as st |
|
|
import sqlite3 |
|
|
import plotly.express as px |
|
|
import pandas as pd |
|
|
from PIL import Image |
|
|
import re |
|
|
import secrets |
|
|
import bcrypt |
|
|
import smtplib |
|
|
from email.mime.text import MIMEText |
|
|
import logging |
|
|
from datetime import datetime |
|
|
|
|
|
|
|
|
logging.basicConfig( |
|
|
filename='app.log', |
|
|
level=logging.INFO, |
|
|
format='%(asctime)s - %(levelname)s - %(message)s' |
|
|
) |
|
|
logger = logging.getLogger() |
|
|
|
|
|
|
|
|
def init_db(): |
|
|
try: |
|
|
conn = sqlite3.connect('users.db') |
|
|
c = conn.cursor() |
|
|
c.execute('''CREATE TABLE IF NOT EXISTS users |
|
|
(id INTEGER PRIMARY KEY, name TEXT, username TEXT UNIQUE, email TEXT UNIQUE, password BLOB, reset_token TEXT)''') |
|
|
conn.commit() |
|
|
except sqlite3.Error as e: |
|
|
logger.error(f"Database initialization failed: {str(e)}") |
|
|
st.error("An error occurred while setting up the database.") |
|
|
finally: |
|
|
conn.close() |
|
|
|
|
|
|
|
|
def hash_password(password): |
|
|
return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()) |
|
|
|
|
|
|
|
|
def check_password(password, hashed): |
|
|
return bcrypt.checkpw(password.encode('utf-8'), hashed) |
|
|
|
|
|
|
|
|
def login(username_email, password): |
|
|
try: |
|
|
conn = sqlite3.connect('users.db') |
|
|
c = conn.cursor() |
|
|
c.execute("SELECT * FROM users WHERE (username=? OR email=?)", (username_email, username_email)) |
|
|
user = c.fetchone() |
|
|
if user and check_password(password, user[4]): |
|
|
logger.info(f"User {username_email} logged in successfully.") |
|
|
return user |
|
|
logger.warning(f"Login failed for {username_email}: Invalid credentials.") |
|
|
return None |
|
|
except sqlite3.Error as e: |
|
|
logger.error(f"Login database error: {str(e)}") |
|
|
st.error("A database error occurred during login.") |
|
|
return None |
|
|
finally: |
|
|
conn.close() |
|
|
|
|
|
|
|
|
def create_account(name, username, email, password): |
|
|
try: |
|
|
hashed_password = hash_password(password) |
|
|
conn = sqlite3.connect('users.db') |
|
|
c = conn.cursor() |
|
|
c.execute("INSERT INTO users (name, username, email, password) VALUES (?, ?, ?, ?)", |
|
|
(name, username, email, hashed_password)) |
|
|
conn.commit() |
|
|
logger.info(f"New account created: {username} ({email})") |
|
|
return True |
|
|
except sqlite3.IntegrityError as e: |
|
|
conn.close() |
|
|
if "username" in str(e): |
|
|
st.error("Username already exists!") |
|
|
elif "email" in str(e): |
|
|
st.error("Email already exists!") |
|
|
logger.warning(f"Account creation failed: {str(e)}") |
|
|
return False |
|
|
except sqlite3.Error as e: |
|
|
logger.error(f"Account creation database error: {str(e)}") |
|
|
st.error("A database error occurred during account creation.") |
|
|
return False |
|
|
finally: |
|
|
conn.close() |
|
|
|
|
|
|
|
|
def update_profile(user_id, name, username, email): |
|
|
try: |
|
|
conn = sqlite3.connect('users.db') |
|
|
c = conn.cursor() |
|
|
c.execute("UPDATE users SET name=?, username=?, email=? WHERE id=?", |
|
|
(name, username, email, user_id)) |
|
|
conn.commit() |
|
|
logger.info(f"Profile updated for user ID {user_id}") |
|
|
return True |
|
|
except sqlite3.IntegrityError as e: |
|
|
conn.close() |
|
|
if "username" in str(e): |
|
|
st.error("Username already taken!") |
|
|
elif "email" in str(e): |
|
|
st.error("Email already in use!") |
|
|
logger.warning(f"Profile update failed: {str(e)}") |
|
|
return False |
|
|
except sqlite3.Error as e: |
|
|
logger.error(f"Profile update database error: {str(e)}") |
|
|
st.error("A database error occurred during profile update.") |
|
|
return False |
|
|
finally: |
|
|
conn.close() |
|
|
|
|
|
|
|
|
def send_reset_email(email, token): |
|
|
sender_email = "your_email@example.com" |
|
|
sender_password = "your_password" |
|
|
subject = "Password Reset Request" |
|
|
body = f"Your password reset token is: {token}\n\nThis token is valid for 15 minutes." |
|
|
msg = MIMEText(body) |
|
|
msg['Subject'] = subject |
|
|
msg['From'] = sender_email |
|
|
msg['To'] = email |
|
|
|
|
|
try: |
|
|
with smtplib.SMTP('smtp.gmail.com', 587) as server: |
|
|
server.starttls() |
|
|
server.login(sender_email, sender_password) |
|
|
server.send_message(msg) |
|
|
logger.info(f"Reset email sent to {email}") |
|
|
return True |
|
|
except Exception as e: |
|
|
logger.error(f"Failed to send reset email to {email}: {str(e)}") |
|
|
st.error("Failed to send reset email. Please try again later.") |
|
|
return False |
|
|
|
|
|
|
|
|
def generate_reset_token(email): |
|
|
try: |
|
|
token = secrets.token_urlsafe(16) |
|
|
conn = sqlite3.connect('users.db') |
|
|
c = conn.cursor() |
|
|
c.execute("UPDATE users SET reset_token=? WHERE email=?", (token, email)) |
|
|
affected = conn.total_changes |
|
|
conn.commit() |
|
|
if affected > 0 and send_reset_email(email, token): |
|
|
return token |
|
|
logger.warning(f"No user found with email {email} for reset token") |
|
|
return None |
|
|
except sqlite3.Error as e: |
|
|
logger.error(f"Reset token database error: {str(e)}") |
|
|
st.error("A database error occurred during token generation.") |
|
|
return None |
|
|
finally: |
|
|
conn.close() |
|
|
|
|
|
|
|
|
def reset_password(token, new_password): |
|
|
try: |
|
|
hashed_password = hash_password(new_password) |
|
|
conn = sqlite3.connect('users.db') |
|
|
c = conn.cursor() |
|
|
c.execute("UPDATE users SET password=?, reset_token=NULL WHERE reset_token=?", |
|
|
(hashed_password, token)) |
|
|
affected = conn.total_changes |
|
|
conn.commit() |
|
|
if affected > 0: |
|
|
logger.info(f"Password reset successfully with token {token}") |
|
|
else: |
|
|
logger.warning(f"Password reset failed: Invalid token {token}") |
|
|
return affected > 0 |
|
|
except sqlite3.Error as e: |
|
|
logger.error(f"Reset password database error: {str(e)}") |
|
|
st.error("A database error occurred during password reset.") |
|
|
return False |
|
|
finally: |
|
|
conn.close() |
|
|
|
|
|
|
|
|
def validate_email(email): |
|
|
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' |
|
|
return re.match(pattern, email) is not None |
|
|
|
|
|
def validate_password(password): |
|
|
return len(password) >= 8 and any(c.isupper() for c in password) and any(c.isdigit() for c in password) |
|
|
|
|
|
|
|
|
def load_css(): |
|
|
st.markdown(""" |
|
|
<style> |
|
|
.stApp { |
|
|
background: linear-gradient(135deg, #e0eafc, #cfdef3); |
|
|
font-family: 'Arial', sans-serif; |
|
|
} |
|
|
.stButton>button { |
|
|
background-color: #4CAF50; |
|
|
color: white; |
|
|
border-radius: 8px; |
|
|
padding: 10px 20px; |
|
|
transition: background-color 0.3s; |
|
|
} |
|
|
.stButton>button:hover { |
|
|
background-color: #45a049; |
|
|
} |
|
|
.stTextInput>input { |
|
|
border-radius: 8px; |
|
|
border: 2px solid #ccc; |
|
|
padding: 8px; |
|
|
transition: border-color 0.3s; |
|
|
} |
|
|
.stTextInput>input:focus { |
|
|
border-color: #4CAF50; |
|
|
} |
|
|
.title { |
|
|
color: #2c3e50; |
|
|
font-size: 2.8em; |
|
|
text-align: center; |
|
|
font-weight: bold; |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
.sidebar .sidebar-content { |
|
|
background-color: #ffffff; |
|
|
border-radius: 10px; |
|
|
padding: 15px; |
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); |
|
|
} |
|
|
.stSelectbox { |
|
|
border-radius: 8px; |
|
|
} |
|
|
</style> |
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
def main(): |
|
|
init_db() |
|
|
load_css() |
|
|
|
|
|
if 'page' not in st.session_state: |
|
|
st.session_state.page = 'login' |
|
|
if 'logged_in' not in st.session_state: |
|
|
st.session_state.logged_in = False |
|
|
|
|
|
|
|
|
with st.sidebar: |
|
|
st.image("https://via.placeholder.com/100", caption="App Logo") |
|
|
st.title("Navigation") |
|
|
if st.session_state.logged_in: |
|
|
st.write(f"Welcome, {st.session_state.username}!") |
|
|
st.progress(100) |
|
|
menu = ["Profile", "Data Visualization", "Emotion Analysis", "Logout"] |
|
|
choice = st.selectbox("Menu", menu, key="sidebar_menu") |
|
|
if choice == "Logout" and st.button("Confirm Logout", key="logout"): |
|
|
st.session_state.logged_in = False |
|
|
st.session_state.page = 'login' |
|
|
logger.info(f"User {st.session_state.username} logged out.") |
|
|
st.rerun() |
|
|
elif choice == "Profile": |
|
|
st.session_state.page = 'profile' |
|
|
elif choice == "Data Visualization": |
|
|
st.session_state.page = 'data_visualization' |
|
|
elif choice == "Emotion Analysis": |
|
|
st.session_state.page = 'main' |
|
|
else: |
|
|
col1, col2 = st.columns(2) |
|
|
with col1: |
|
|
if st.button("Create Account", key="create_account_btn"): |
|
|
st.session_state.page = 'create_account' |
|
|
with col2: |
|
|
if st.button("Reset Password", key="reset_pass_btn"): |
|
|
st.session_state.page = 'reset_password' |
|
|
|
|
|
|
|
|
if st.session_state.page == 'login' and not st.session_state.logged_in: |
|
|
st.markdown('<p class="title">Login</p>', unsafe_allow_html=True) |
|
|
with st.form(key='login_form'): |
|
|
username_email = st.text_input("Username or Email", placeholder="Enter username or email") |
|
|
password = st.text_input("Password", type="password", placeholder="Enter password") |
|
|
submit_button = st.form_submit_button(label="Login") |
|
|
|
|
|
if submit_button: |
|
|
if not username_email or not password: |
|
|
st.error("Please fill in all fields!") |
|
|
else: |
|
|
user = login(username_email, password) |
|
|
if user: |
|
|
st.session_state.logged_in = True |
|
|
st.session_state.user_id = user[0] |
|
|
st.session_state.username = user[2] |
|
|
st.session_state.page = 'data_visualization' |
|
|
st.success("Logged in successfully!") |
|
|
st.balloons() |
|
|
st.rerun() |
|
|
else: |
|
|
st.error("Invalid username/email or password!") |
|
|
|
|
|
elif st.session_state.page == 'create_account' and not st.session_state.logged_in: |
|
|
st.markdown('<p class="title">Create Account</p>', unsafe_allow_html=True) |
|
|
with st.form(key='create_account_form'): |
|
|
name = st.text_input("Full Name", placeholder="Enter your full name") |
|
|
username = st.text_input("Username", placeholder="Choose a username") |
|
|
email = st.text_input("Email", placeholder="Enter your email") |
|
|
password = st.text_input("Password", type="password", placeholder="Create a password") |
|
|
submit_button = st.form_submit_button(label="Submit") |
|
|
|
|
|
if submit_button: |
|
|
if not all([name, username, email, password]): |
|
|
st.error("All fields are required!") |
|
|
elif not validate_email(email): |
|
|
st.error("Invalid email format!") |
|
|
elif not validate_password(password): |
|
|
st.error("Password must be 8+ characters with uppercase and numbers!") |
|
|
elif create_account(name, username, email, password): |
|
|
st.success("Account created successfully!") |
|
|
st.session_state.page = 'login' |
|
|
st.rerun() |
|
|
|
|
|
elif st.session_state.page == 'reset_password' and not st.session_state.logged_in: |
|
|
st.markdown('<p class="title">Reset Password</p>', unsafe_allow_html=True) |
|
|
step = st.session_state.get('reset_step', 'request') |
|
|
|
|
|
if step == 'request': |
|
|
with st.form(key='reset_request_form'): |
|
|
email = st.text_input("Email", placeholder="Enter your email") |
|
|
submit_button = st.form_submit_button(label="Request Reset") |
|
|
if submit_button: |
|
|
if not validate_email(email): |
|
|
st.error("Invalid email format!") |
|
|
else: |
|
|
token = generate_reset_token(email) |
|
|
if token: |
|
|
st.success("A reset token has been sent to your email!") |
|
|
st.session_state.reset_step = 'reset' |
|
|
st.session_state.reset_email = email |
|
|
st.rerun() |
|
|
else: |
|
|
st.error("Email not found or email sending failed!") |
|
|
|
|
|
elif step == 'reset': |
|
|
with st.form(key='reset_form'): |
|
|
token = st.text_input("Reset Token", placeholder="Enter the token from your email") |
|
|
new_password = st.text_input("New Password", type="password", placeholder="Enter new password") |
|
|
submit_button = st.form_submit_button(label="Reset Password") |
|
|
if submit_button: |
|
|
if not validate_password(new_password): |
|
|
st.error("Password must be 8+ characters with uppercase and numbers!") |
|
|
elif reset_password(token, new_password): |
|
|
st.success("Password reset successfully!") |
|
|
st.session_state.page = 'login' |
|
|
del st.session_state['reset_step'] |
|
|
st.rerun() |
|
|
else: |
|
|
st.error("Invalid or expired token!") |
|
|
|
|
|
elif st.session_state.logged_in and st.session_state.page == 'profile': |
|
|
st.markdown('<p class="title">User Profile</p>', unsafe_allow_html=True) |
|
|
conn = sqlite3.connect('users.db') |
|
|
c = conn.cursor() |
|
|
c.execute("SELECT name, username, email FROM users WHERE id=?", (st.session_state.user_id,)) |
|
|
user = c.fetchone() |
|
|
conn.close() |
|
|
|
|
|
with st.form(key='profile_form'): |
|
|
name = st.text_input("Full Name", value=user[0], placeholder="Update your name") |
|
|
username = st.text_input("Username", value=user[1], placeholder="Update your username") |
|
|
email = st.text_input("Email", value=user[2], placeholder="Update your email") |
|
|
submit_button = st.form_submit_button(label="Update Profile") |
|
|
|
|
|
if submit_button: |
|
|
if not all([name, username, email]): |
|
|
st.error("All fields are required!") |
|
|
elif not validate_email(email): |
|
|
st.error("Invalid email format!") |
|
|
elif update_profile(st.session_state.user_id, name, username, email): |
|
|
st.session_state.username = username |
|
|
st.success("Profile updated successfully!") |
|
|
st.rerun() |
|
|
|
|
|
elif st.session_state.logged_in and st.session_state.page == 'data_visualization': |
|
|
st.markdown('<p class="title">Data Visualization</p>', unsafe_allow_html=True) |
|
|
st.write(""" |
|
|
### Models Overview |
|
|
- **Custom CNN**: Custom-built Convolutional Neural Network designed for emotion recognition |
|
|
- **MobileNetV2**: Lightweight deep learning model optimized for mobile devices |
|
|
- **VGG19**: Deep 19-layer convolutional neural network for image classification |
|
|
""") |
|
|
emotions = ['Happiness', 'Neutral', 'Sadness', 'Anger', 'Surprise', 'Disgust', 'Fear'] |
|
|
counts = [5000, 6000, 5500, 4500, 4000, 3500, 4200] |
|
|
df = pd.DataFrame({'Emotion': emotions, 'Count': counts}) |
|
|
fig = px.bar(df, x='Emotion', y='Count', title='Emotion Distribution in Dataset', color='Emotion') |
|
|
st.plotly_chart(fig) |
|
|
if st.button("Next Page", key="next_page"): |
|
|
st.session_state.page = 'main' |
|
|
st.rerun() |
|
|
|
|
|
elif st.session_state.logged_in and st.session_state.page == 'main': |
|
|
st.markdown('<p class="title">Emotion Recognition & Analysis</p>', unsafe_allow_html=True) |
|
|
model = st.selectbox("Select Model", ["Custom CNN", "MobileNetV2", "VGG19"], key="model_select") |
|
|
if model: |
|
|
uploaded_file = st.file_uploader("Upload an image", type=['jpg', 'png', 'jpeg'], key="file_uploader") |
|
|
if uploaded_file is not None: |
|
|
image = Image.open(uploaded_file) |
|
|
st.image(image, caption='Uploaded Image', use_container_width=True) |
|
|
with st.spinner(f"Processing with {model}..."): |
|
|
|
|
|
st.write("Analysis complete (placeholder).") |
|
|
|
|
|
if __name__ == "__main__": |
|
|
main() |