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()