import streamlit as st
import os, json, datetime, hashlib
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from gtts import gTTS, gTTSError
from pathlib import Path
from dotenv import load_dotenv
from sentence_transformers import SentenceTransformer, util
import altair as alt
import speech_recognition as sr
from transformers import pipeline
import torch
import pickle
import re
import matplotlib.pyplot as plt
import numpy as np
import base64
from io import BytesIO
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, Flowable
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
from reportlab.lib import colors
import pandas as pd
import requests
import time
# --- Streamlit Page Configuration (MUST be the first Streamlit command) ---
st.set_page_config(page_title="BridgeYield", page_icon="📈", layout="wide")
# --- Initialize session state (VERY FIRST THING AFTER PAGE CONFIG) ---
if "authenticated" not in st.session_state:
st.session_state.authenticated = False
if "username" not in st.session_state:
st.session_state.username = None
if "is_admin" not in st.session_state:
st.session_state.is_admin = False
if "transcribed_text" not in st.session_state:
st.session_state.transcribed_text = ""
if "uploaded_file_text" not in st.session_state:
st.session_state.uploaded_file_text = ""
if "page" not in st.session_state:
st.session_state.page = "auth"
if 'user_interaction_history' not in st.session_state:
st.session_state.user_interaction_history = []
if 'emotional_levels_for_graph' not in st.session_state:
st.session_state.emotional_levels_for_graph = []
if 'last_graph_path' in st.session_state:
st.session_state.last_graph_path = None
if 'api_key_status' not in st.session_state:
st.session_state.api_key_status = "unverified"
if 'last_interaction' not in st.session_state:
st.session_state.last_interaction = {}
if 'last_output' not in st.session_state:
st.session_state.last_output = None
if 'voice_recording_active' not in st.session_state:
st.session_state.voice_recording_active = False
if 'financial_data' not in st.session_state:
st.session_state.financial_data = {}
# --- NEW: Session state for UI control ---
if "show_quick_login_input" not in st.session_state:
st.session_state.show_quick_login_input = False
if "auth_view" not in st.session_state:
st.session_state.auth_view = "Login"
if 'signup_success_message' not in st.session_state:
st.session_state.signup_success_message = None
# Load environment variables
load_dotenv()
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
CRISIS_KEYWORDS = ["suicide", "kill myself", "end it all", "worthless", "can't go on", "hurt myself", "self harm", "want to disappear", "no reason to live"]
# Admin configuration
ADMIN_USERNAME = os.getenv("ADMIN_USERNAME")
ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD")
# Custom Flowable for a horizontal rule
class HRFlowable(Flowable):
def __init__(self, width, thickness, lineCap, color, spaceBefore, spaceAfter):
Flowable.__init__(self)
self.width = width
self.thickness = thickness
self.lineCap = lineCap
self.color = color
self.spaceBefore = spaceBefore
self.spaceAfter = spaceAfter
def wrap(self, availWidth, availHeight):
self.width = availWidth
return (availWidth, self.thickness)
def draw(self):
self.canv.setStrokeColor(self.color)
self.canv.setLineWidth(self.thickness)
self.canv.setLineCap(self.lineCap)
self.canv.line(0, 0, self.width, 0)
# User management functions
def hash_password(password):
"""Hash password using SHA-256 with salt"""
salt = "wealthtech_secure_salt_2024"
return hashlib.sha256((password + salt).encode()).hexdigest()
def get_secure_users_path():
"""Get path to users file in a hidden directory"""
secure_dir = ".secure_data"
os.makedirs(secure_dir, exist_ok=True)
return os.path.join(secure_dir, "users_encrypted.json")
def load_users():
"""Load users from secure file"""
users_path = get_secure_users_path()
if os.path.exists(users_path):
try:
with open(users_path, "r") as f:
return json.load(f)
except:
return {}
return {}
def save_users(users):
"""Save users to secure file"""
users_path = get_secure_users_path()
with open(users_path, "w") as f:
json.dump(users, f, indent=4)
def create_user_directory(username):
"""Create user-specific directory structure"""
user_dir = f"users/{username}"
os.makedirs(user_dir, exist_ok=True)
return user_dir
def get_user_file_path(username, filename):
"""Get path to user-specific file"""
user_dir = f"users/{username}"
return os.path.join(user_dir, filename)
def signup(username, password, email):
"""Register new user"""
users = load_users()
if username in users:
return False, "Username already exists"
email_pattern = r"^[\w\.-]+@[\w\.-]+\.\w+$"
if not re.match(email_pattern, email):
return False, "Invalid email format"
users[username] = {
"password": hash_password(password),
"email": email,
"created_at": str(datetime.datetime.now())
}
save_users(users)
create_user_directory(username)
return True, "Account created successfully!"
def login(username, password):
"""Authenticate user or admin"""
if username == ADMIN_USERNAME and password == ADMIN_PASSWORD:
return True, "Admin login successful!", True
users = load_users()
if username not in users:
return False, "User not found. Please signup.", False
if users[username]["password"] == hash_password(password):
return True, "Login successful!", False
return False, "Incorrect password", False
# --- NEW: Function for quick login without password ---
def login_by_username_only(username):
"""Authenticate user by username only for quick login."""
if username == ADMIN_USERNAME:
return False, "Admin quick login is not supported.", False
users = load_users()
if username in users:
return True, "Login successful!", False
return False, "User not found. Please sign up or check your username.", False
# Emotion detection
@st.cache_resource
def load_emotion_model():
return pipeline(
"text-classification",
model="j-hartmann/emotion-english-distilroberta-base",
top_k=1,
device=-1
)
def detect_emotion(text):
emotion_pipeline = load_emotion_model()
prediction = emotion_pipeline(text)[0][0]
return prediction['label'].lower(), prediction['score']
def is_crisis(text):
"""Check for crisis keywords"""
return any(phrase in text.lower() for phrase in CRISIS_KEYWORDS)
def build_user_vectorstore(username, quotes):
"""Build and save user-specific vectorstore"""
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
vectorstore = FAISS.from_texts(quotes, embedding=embeddings)
vectorstore_path = get_user_file_path(username, "vectorstore")
vectorstore.save_local(vectorstore_path)
return vectorstore
def load_user_vectorstore(username):
"""Load user-specific vectorstore"""
vectorstore_path = get_user_file_path(username, "vectorstore")
if os.path.exists(vectorstore_path):
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
return FAISS.load_local(vectorstore_path, embeddings, allow_dangerous_deserialization=True)
return None
def save_user_journal(username, user_input, emotion, score, response):
"""Save journal entry for specific user"""
journal_path = get_user_file_path(username, "journal.json")
entry = {
"date": str(datetime.date.today()),
"timestamp": str(datetime.datetime.now()),
"user_input": user_input,
"emotion": emotion,
"confidence": round(score * 100, 2),
"response": response
}
journal = []
if os.path.exists(journal_path):
with open(journal_path, "r") as f:
journal = json.load(f)
journal.append(entry)
with open(journal_path, "w") as f:
json.dump(journal, f, indent=4)
def load_user_journal(username):
"""Load journal for specific user"""
journal_path = get_user_file_path(username, "journal.json")
if os.path.exists(journal_path):
with open(journal_path, "r") as f:
return json.load(f)
return []
def generate_audio_file(text, username):
"""Generate and save audio response, return the path."""
try:
# --- NEW: Added try...except block to handle gTTS errors gracefully ---
tts = gTTS(text=text, lang='en')
audio_path = get_user_file_path(username, "response.mp3")
tts.save(audio_path)
return audio_path
except gTTSError as e:
st.warning(f"Failed to generate audio due to a temporary service error. Please try again later. Error: {e}")
return None
except Exception as e:
st.error(f"An unexpected error occurred during audio generation: {e}")
return None
def transcribe_audio_file(uploaded_audio):
"""Transcribe uploaded audio file"""
recognizer = sr.Recognizer()
try:
with sr.AudioFile(uploaded_audio) as source:
audio_data = recognizer.record(source)
text = recognizer.recognize_google(audio_data)
return text
except Exception as e:
return f"Error: {str(e)}"
def get_gemini_response(prompt, context):
"""Get response from Google's Gemini API"""
if not GEMINI_API_KEY:
st.error("Google Gemini API key not found. Please add it to your .env file.")
return None
api_url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key={GEMINI_API_KEY}"
full_prompt = f"""You are BridgeYield, an empathetic financial support AI companion. Your goal is to guide the user in their financial journey. Use the following statements and user context to provide a detailed, bulleted response with actionable financial advice. The response should be concise, yet informative, and avoid a conversational tone.\n\nContext statements:\n{context}\n\nUser's message:\n{prompt}\n\nProvide a detailed response in bullet points with actionable steps for the user."""
headers = {'Content-Type': 'application/json'}
payload = {
"contents": [
{
"parts": [
{"text": full_prompt}
]
}
]
}
try:
response = requests.post(api_url, headers=headers, data=json.dumps(payload))
response.raise_for_status()
result = response.json()
if 'candidates' in result and len(result['candidates']) > 0:
return result['candidates'][0]['content']['parts'][0]['text']
else:
return "Sorry, I couldn't generate a response. Please try again."
except requests.exceptions.RequestException as e:
st.error(f"API request failed: {e}")
return None
def generate_financial_metrics(user_input_length, emotion_score):
np.random.seed(int(user_input_length * 100 + emotion_score * 100))
base_liquidity = np.random.randint(15000, 35000)
current_liquidity = int(base_liquidity + (emotion_score - 0.5) * 10000)
simulated_liquidity = int(current_liquidity * np.random.uniform(0.1, 0.5))
liquid_percent = np.random.uniform(0.3, 0.7)
liquid_percent = round(liquid_percent, 2)
illiquid_percent = round(1 - liquid_percent, 2)
ratio = round(liquid_percent / illiquid_percent, 1)
assets = {
"ETU": int(np.random.randint(25000, 40000) * (1 + emotion_score)),
"Angel Capital": int(np.random.randint(40000, 60000) * (1 + emotion_score)),
"LP | Estate": int(np.random.randint(100000, 150000) * (1 + emotion_score)),
"Brokerage": int(np.random.randint(20000, 35000) * (1 + emotion_score)),
}
actions = []
# Existing actions
if current_liquidity < 20000: actions.append("Increase cash reserves to >$20K. Create a plan to transfer a set amount each week.")
if ratio < 0.5: actions.append("Rebalance portfolio towards more liquid assets. Consider selling a small portion of illiquid assets to free up cash.")
if simulated_liquidity < 5000: actions.append("Review spending habits to reduce expenses. Categorize your expenses for the last 30 days to identify areas for reduction.")
# New, more detailed actions
total_assets_value = sum(assets.values())
if assets.get("LP | Estate", 0) > 0.5 * total_assets_value:
actions.append("Your illiquid assets are significant. Review your estate plan and beneficiaries to ensure your family's future is secure.")
if emotion_score < 0.3 and current_liquidity > 25000:
actions.append("Your current sentiment is low, which can impact financial decisions. Focus on small, manageable goals like setting up an automatic savings transfer to regain control.")
if assets.get("Brokerage", 0) > 0.3 * total_assets_value:
actions.append("Your brokerage account is a large part of your portfolio. Consider diversifying into other asset classes to reduce risk exposure.")
# Adding more detailed, smartly suggested actions
if total_assets_value > 300000:
actions.append("Consider consulting with a Certified Financial Planner to create a more comprehensive long-term wealth management strategy.")
if "debt" in st.session_state.user_input_text.lower():
actions.append("Analyze your current debt and interest rates. Create a priority list to tackle high-interest loans first, potentially using the 'avalanche method'.")
if "saving" in st.session_state.user_input_text.lower():
actions.append("Set up an automatic contribution to a high-yield savings account or a retirement fund to ensure consistent growth without active management.")
if not actions: actions.append("Continue with your current strategy, it's working well! Your financial health seems stable.")
return {
"liquidity_current": current_liquidity, "liquidity_simulated": simulated_liquidity,
"risk_liquid": int(liquid_percent * 100), "risk_illiquid": int(illiquid_percent * 100),
"risk_ratio": ratio, "assets": assets, "suggested_actions": actions
}
def generate_pdf_report(username, history, last_interaction, graph_image):
buffer = BytesIO()
doc = SimpleDocTemplate(buffer, pagesize=letter, rightMargin=inch, leftMargin=inch, topMargin=inch, bottomMargin=inch)
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(name='Justify', alignment=4))
story = []
title = f"BridgeYield Report for {username}"
story.append(Paragraph(title, styles['h1']))
story.append(Spacer(1, 0.2*inch))
intro_text = f"This report was generated on {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}. It summarizes your recent interactions and sentiment trends with BridgeYield."
story.append(Paragraph(intro_text, styles['Normal']))
story.append(Spacer(1, 0.2*inch))
hr = HRFlowable(width="100%", thickness=1, lineCap=1, color=colors.grey, spaceBefore=1, spaceAfter=1)
story.append(hr)
story.append(Spacer(1, 0.2*inch))
if last_interaction:
story.append(Paragraph("Most Recent Interaction", styles['h2']))
story.append(Spacer(1, 0.1*inch))
last_prompt = f"Your Prompt: {last_interaction.get('prompt', 'N/A')}"
last_response = f"BridgeYield's Response: {last_interaction.get('response', 'N/A')}"
story.append(Paragraph(last_prompt, styles['Normal']))
story.append(Spacer(1, 0.1*inch))
story.append(Paragraph(last_response, styles['Normal']))
story.append(Spacer(1, 0.2*inch))
if graph_image:
story.append(Paragraph("Your Sentiment Trend", styles['h2']))
graph_image.seek(0)
img = Image(graph_image, width=6*inch, height=3*inch)
story.append(img)
story.append(Spacer(1, 0.2*inch))
story.append(Paragraph("Full Interaction History", styles['h2']))
for entry in reversed(history):
story.append(hr)
entry_text = f"Timestamp: {entry['timestamp']}
Detected Sentiment: {entry['emotion'].capitalize()} ({entry['level']}%)
Your Prompt: {entry['prompt']}"
story.append(Paragraph(entry_text, styles['Normal']))
story.append(Spacer(1, 0.1*inch))
story.append(hr)
story.append(Spacer(1, 0.3*inch))
story.append(Paragraph("Path to Improvement", styles['h2']))
improvement_text = "Financial well-being is a journey of continuous learning and adaptation. Reviewing your sentiment trends can offer insights into your emotional responses to financial topics. Use this awareness to build a more resilient and confident financial strategy. Stay consistent, keep tracking your goals, and remember that every step forward, no matter how small, is progress."
story.append(Paragraph(improvement_text, styles['Justify']))
doc.build(story)
buffer.seek(0)
return buffer
def set_background_and_styles():
st.markdown(
"""
""",
unsafe_allow_html=True
)
pin_point_statements = {
"Investing": "Analyze your risk tolerance before making any investment.",
"Budgeting": "A solid budget is the foundation of all financial planning.",
"Goals": "Focus on the goal, not the target.",
"Retirement Planning": "Start saving for retirement as early as possible to take advantage of compound interest.",
"Debt Management": "Prioritize high-interest debt and create a clear plan to pay it off.",
"Estate Planning": "Secure your family's future with a will and clear estate plan.",
"Tax Strategy": "Understand your tax obligations and explore legal deductions to optimize your finances.",
"Emergency Fund": "Aim to save at least 3-6 months of living expenses in an easily accessible emergency fund."
}
def show_auth_page():
set_background_and_styles()
# --- MODIFIED: Top Right Login button to show username input directly ---
_, col2 = st.columns([0.8, 0.2])
with col2:
if st.button("Quick Login", key="top_right_login_btn"):
st.session_state.show_quick_login_input = not st.session_state.show_quick_login_input
st.session_state.auth_view = "Login"
if st.session_state.show_quick_login_input:
st.markdown("
", unsafe_allow_html=True)
st.write("Enter your username to log in quickly.")
quick_login_username = st.text_input("Username", key="quick_login_user", placeholder="Enter your username")
if st.button("Quick Login", key="quick_login_btn", use_container_width=True):
if quick_login_username:
success, message, is_admin = login_by_username_only(quick_login_username)
if success:
st.session_state.authenticated = True
st.session_state.username = quick_login_username
st.session_state.is_admin = is_admin
st.session_state.page = "main_app"
st.session_state.show_quick_login_input = False
st.rerun()
else:
st.error(message)
else:
st.warning("Please enter a username.")
st.markdown("
", unsafe_allow_html=True)
st.markdown(
"""
BridgeYield
Unlock Your Financial Potential
""", unsafe_allow_html=True
)
col1, col2 = st.columns(2)
with col1:
if st.button("Paid Plan", key="paid_plan_btn", use_container_width=True): st.info("COMING SOON")
with col2:
if st.button("Login Below FOR FREE", key="login_free_btn", use_container_width=True): st.info("SCROLL A BIT DOWN BELOW")
st.markdown("""

Custom Investment Strategies
Tailored guidance based on your financial aspirations.

AI-Powered Market Insights
Predictive analysis to help you stay ahead of trends.

Budgeting and Savings Tools
Manage your expenses with smart, automated tools.

Financial Goal Tracking
Visually track your progress toward long-term goals.
""", unsafe_allow_html=True)
st.markdown("", unsafe_allow_html=True)
# --- MODIFIED: Replaced st.tabs with state-driven buttons for better control ---
auth_view = st.session_state.get('auth_view', 'Login')
cols = st.columns(2)
with cols[0]:
if st.button("Login", use_container_width=True, type="secondary" if auth_view != "Login" else "primary"):
st.session_state.auth_view = "Login"
st.rerun()
with cols[1]:
if st.button("Sign Up", use_container_width=True, type="secondary" if auth_view != "Sign Up" else "primary"):
st.session_state.auth_view = "Sign Up"
st.rerun()
st.markdown("
", unsafe_allow_html=True)
if st.session_state.signup_success_message:
st.success(st.session_state.signup_success_message)
st.session_state.signup_success_message = None
if auth_view == "Login":
st.subheader("Login to Your Account")
login_username = st.text_input("Username", key="login_user", placeholder="Enter your username")
login_password = st.text_input("Password", type="password", key="login_pass", placeholder="Enter your password")
if st.button("Login", key="login_btn", use_container_width=True):
if login_username and login_password:
success, message, is_admin = login(login_username, login_password)
if success:
st.session_state.authenticated = True
st.session_state.username = login_username
st.session_state.is_admin = is_admin
st.session_state.page = "main_app" if not is_admin else "admin_dashboard"
st.rerun()
else: st.error(message)
else: st.warning("Please fill in all fields")
elif auth_view == "Sign Up":
st.subheader("Create New Account")
signup_username = st.text_input("Choose Username", key="signup_user", placeholder="Create a username")
signup_email = st.text_input("Email Address", key="signup_email", placeholder="Enter your email")
signup_password = st.text_input("Choose Password", type="password", key="signup_pass", placeholder="Create a strong password")
signup_confirm = st.text_input("Confirm Password", type="password", key="signup_confirm", placeholder="Confirm your password")
if st.button("Create Account", key="signup_btn", use_container_width=True):
if all([signup_username, signup_email, signup_password, signup_confirm]):
if signup_password != signup_confirm: st.error("Passwords don't match!")
elif len(signup_password) < 6: st.error("Password must be at least 6 characters long!")
else:
success, message = signup(signup_username, signup_password, signup_email)
if success:
st.session_state.signup_success_message = message
st.session_state.auth_view = "Login"
st.rerun()
else: st.error(message)
else: st.warning("Please fill in all fields")
st.markdown("", unsafe_allow_html=True)
def show_main_app():
username = st.session_state.username
set_background_and_styles()
header_cols = st.columns([0.85, 0.15])
with header_cols[0]:
st.markdown(
"""
BridgeYield
""", unsafe_allow_html=True
)
with header_cols[1]:
if st.button("Logout", key="logout_btn_top"):
for key in list(st.session_state.keys()):
if key not in ['auth_view']: del st.session_state[key]
st.session_state.page = "auth"
st.rerun()
st.markdown("", unsafe_allow_html=True)
st.markdown("
", unsafe_allow_html=True)
welcome_col, prompt_col = st.columns([1, 2])
with welcome_col:
st.title(f"Welcome back, {username}!")
st.markdown("Your personal financial AI companion")
st.markdown("---")
# MODIFIED: Text within selectbox is also white
st.markdown(
"""
""", unsafe_allow_html=True
)
selected_category = st.selectbox("Choose a category to focus on:", list(pin_point_statements.keys()))
with prompt_col:
st.subheader("What's on your mind?")
prompt_and_mic_cols = st.columns([0.1, 1])
with prompt_and_mic_cols[0]:
if st.button("🎤"):
st.session_state.transcribed_text = ""
st.session_state.uploaded_file_text = ""
with prompt_and_mic_cols[1]:
final_input = st.session_state.uploaded_file_text or st.session_state.transcribed_text
user_input = st.text_area("", value=final_input, height=100, placeholder="Share your thoughts, feelings, or experiences about your financial journey...", key="user_input_text")
with st.expander("Upload a File"):
uploaded_audio = st.file_uploader("Upload a voice message (.wav)", type=["wav"])
uploaded_text_file = st.file_uploader("Upload a text file (.txt)", type=["txt"])
col_audio, col_text = st.columns(2)
with col_audio:
if uploaded_audio and st.button("Transcribe Voice", use_container_width=True):
with st.spinner("Transcribing your voice..."):
transcribed = transcribe_audio_file(uploaded_audio)
if transcribed.startswith("Error:"): st.error(transcribed)
else:
st.session_state.transcribed_text = transcribed
st.session_state.uploaded_file_text = ""
st.success("Voice transcribed successfully!")
with col_text:
if uploaded_text_file and st.button("Process Text File", use_container_width=True):
with st.spinner("Processing text file..."):
file_content = uploaded_text_file.read().decode("utf-8")
st.session_state.uploaded_file_text = file_content
st.session_state.transcribed_text = ""
st.success("Text file processed successfully!")
if st.button("Talk to BridgeYield", use_container_width=True, key="talk_btn"):
input_to_process = st.session_state.user_input_text.strip()
if not input_to_process:
st.warning("Please enter something to share, upload a voice message, or a text file.")
else:
with st.spinner("BridgeYield is thinking..."):
emotion, score = detect_emotion(input_to_process)
emotional_level = round(score * 100)
selected_statement = pin_point_statements[selected_category]
response = get_gemini_response(input_to_process, selected_statement)
st.session_state.financial_data = generate_financial_metrics(len(input_to_process), score)
if response:
audio_path = generate_audio_file(response, username)
st.session_state.last_output = {"emotion": emotion, "score": score, "is_crisis": is_crisis(input_to_process), "selected_statement": selected_statement, "response": response, "audio_path": audio_path}
st.session_state.last_interaction = {"prompt": input_to_process, "response": response}
st.session_state.user_interaction_history.append({"prompt": input_to_process, "emotion": emotion, "level": emotional_level, "timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")})
st.session_state.emotional_levels_for_graph.append(emotional_level)
save_user_journal(username, input_to_process, emotion, score, response)
st.session_state.transcribed_text = ""
st.session_state.uploaded_file_text = ""
st.rerun()
if st.session_state.last_output:
st.markdown("---")
st.subheader("Your Latest Analysis")
output = st.session_state.last_output
financials = st.session_state.financial_data
# MODIFIED: Adjusted column widths to prevent overlap
col1, col2, col3 = st.columns([1, 1.8, 1.3])
with col1:
st.markdown(f"**Sentiment Detected:**
{output['emotion'].capitalize()}", unsafe_allow_html=True)
st.markdown(f"**Confidence Level:**
{round(output['score']*100)}%", unsafe_allow_html=True)
if output['is_crisis']: st.error("**Crisis Detected!** Please reach out to a mental health professional immediately.")
st.markdown("### **Liquidity**", unsafe_allow_html=True)
st.markdown(f"**Current**: ${financials['liquidity_current']:,}", unsafe_allow_html=True)
st.markdown(f"**Simulated**: ${financials['liquidity_simulated']:,}", unsafe_allow_html=True)
st.markdown("### **Frequency**", unsafe_allow_html=True)
freq_fig, freq_ax = plt.subplots(figsize=(6, 3))
freq_ax.bar(np.arange(1, 11), np.random.randint(10, 100, size=10), color='#FFFFFF')
freq_ax.set_facecolor('#1E1E1E'); freq_fig.patch.set_facecolor('#1E1E1E')
freq_ax.tick_params(axis='x', colors='white'); freq_ax.tick_params(axis='y', colors='white')
for spine in ['left', 'bottom']: freq_ax.spines[spine].set_color('white')
for spine in ['top', 'right']: freq_ax.spines[spine].set_color('#1E1E1E')
st.pyplot(freq_fig); plt.close(freq_fig)
st.markdown("### **Risk Profile**", unsafe_allow_html=True)
st.markdown(f"**Liquid**: {financials['risk_liquid']}%", unsafe_allow_html=True)
st.markdown(f"**Illiquid**: {financials['risk_illiquid']}%", unsafe_allow_html=True)
st.markdown(f"**Ratio**: {financials['risk_ratio']}", unsafe_allow_html=True)
with col2:
st.markdown("
", unsafe_allow_html=True)
st.markdown(f"**Insight for you:** _{output['selected_statement']}_")
st.markdown(f"**BridgeYield's Response:**
{output['response']}
", unsafe_allow_html=True)
st.markdown("
", unsafe_allow_html=True)
st.markdown("---")
st.markdown("### **Suggested Actions**", unsafe_allow_html=True)
for action in financials['suggested_actions']: st.markdown(f" - **{action}**", unsafe_allow_html=True)
with col3:
st.markdown(f"**Audio Response:**", unsafe_allow_html=True)
if output['audio_path']: st.audio(output['audio_path'])
else: st.info("Audio generation failed. Displaying text response instead.")
st.markdown("---"); st.subheader("Your Sentiment Trend")
if st.session_state.emotional_levels_for_graph:
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(st.session_state.emotional_levels_for_graph, marker='o', linestyle='-', color='#FFFFFF')
ax.set_title("Interaction Sentiment Level Over Time", color='#FFFFFF'); ax.set_xlabel("Interaction Count", color='#FFFFFF'); ax.set_ylabel("Sentiment Level (%)", color='#FFFFFF')
ax.grid(True, linestyle='--', alpha=0.7, color='#555555'); ax.set_facecolor('#1E1E1E'); fig.patch.set_facecolor('#1E1E1E')
ax.tick_params(axis='x', colors='white'); ax.tick_params(axis='y', colors='white')
for spine in ['left', 'bottom']: ax.spines[spine].set_color('white')
for spine in ['top', 'right']: ax.spines[spine].set_color('#1E1E1E')
st.pyplot(fig)
buf = BytesIO(); fig.savefig(buf, format='png', bbox_inches='tight'); st.session_state.last_graph_path = buf; plt.close(fig)
else: st.info("Interact with BridgeYield to see your sentiment trends here!")
st.markdown("---"); st.markdown("### **Assets**", unsafe_allow_html=True)
for asset, value in financials['assets'].items(): st.markdown(f" - **{asset}:** ${value:,}", unsafe_allow_html=True)
st.markdown("---")
st.header("History of User Interactions")
if st.session_state.user_interaction_history:
all_levels = [entry['level'] for entry in st.session_state.user_interaction_history]
overall_avg_level = np.mean(all_levels) if all_levels else 0
st.markdown(f"**Overall Sentiment Level Achieved Till Today: {overall_avg_level:.2f}%**")
for i, entry in reversed(list(enumerate(st.session_state.user_interaction_history))):
st.markdown(f"
Interaction {len(st.session_state.user_interaction_history) - i} (on {entry['timestamp']})
Your Prompt: {entry['prompt']}
Detected Sentiment: {entry['emotion'].capitalize()}
Sentiment Level: {entry['level']}%
", unsafe_allow_html=True)
else: st.info("Your interaction history will appear here after you talk to BridgeYield.")
st.markdown("---")
if st.session_state.user_interaction_history:
pdf_buffer = generate_pdf_report(st.session_state.username, st.session_state.user_interaction_history, st.session_state.get('last_interaction'), st.session_state.get('last_graph_path'))
st.download_button(label="Download Report as PDF", data=pdf_buffer, file_name=f"BridgeYield_Report_{st.session_state.username}_{datetime.date.today()}.pdf", mime="application/pdf", use_container_width=True)
else: st.info("Complete at least one interaction to download your report.")
st.markdown("
", unsafe_allow_html=True)
def show_admin_dashboard():
if not st.session_state.is_admin:
st.error("Access Denied: You must be an administrator to view this page.")
st.session_state.page = "auth"; st.rerun(); return
set_background_and_styles()
col1, col2, col3 = st.columns([1, 4, 1])
with col2:
st.markdown(
"""
BridgeYield
""", unsafe_allow_html=True)
with col3:
if st.button("Logout", key="logout_btn_top"):
st.session_state.authenticated = False; st.session_state.page = "auth"; st.session_state.username = None; st.rerun()
st.markdown("", unsafe_allow_html=True)
st.title("Admin Dashboard"); st.markdown("---"); st.subheader("User Management")
users = load_users()
user_data_display = [{"Username": u, "Email": d.get("email", "N/A"), "Created At": d.get("created_at", "N/A")} for u, d in users.items()]
st.dataframe(user_data_display, use_container_width=True)
st.subheader("System Statistics"); st.info(f"Total Registered Users: {len(users)}")
st.markdown("
", unsafe_allow_html=True)
def main():
if not st.session_state.authenticated:
show_auth_page()
elif st.session_state.is_admin:
show_admin_dashboard()
else:
show_main_app()
st.markdown("""""", unsafe_allow_html=True)
if __name__ == "__main__":
main()