import streamlit as st import requests import pandas as pd from gtts import gTTS import base64 from io import BytesIO from PIL import Image import os st.set_page_config(page_title="NeuroPulse AI", page_icon="๐Ÿง ", layout="wide") logo_path = os.path.join("app", "static", "logo.png") if os.path.exists(logo_path): st.image(logo_path, width=160) # Session state defaults if "review" not in st.session_state: st.session_state.review = "" if "dark_mode" not in st.session_state: st.session_state.dark_mode = False # Shared Sidebar Controls with st.sidebar: st.header("โš™๏ธ Global Settings") st.session_state.dark_mode = st.toggle("๐ŸŒ™ Dark Mode", value=st.session_state.dark_mode) api_token = st.text_input("๐Ÿ” API Token", type="password") backend_url = st.text_input("๐Ÿ–ฅ๏ธ Backend URL", value="http://0.0.0.0:8000") sentiment_model = st.selectbox("๐Ÿ“Š Sentiment Model", [ "distilbert-base-uncased-finetuned-sst-2-english", "nlptown/bert-base-multilingual-uncased-sentiment" ]) industry = st.selectbox("๐Ÿญ Industry Context", [ "Auto-detect", "Generic", "E-commerce", "Healthcare", "Education", "Travel", "Banking", "Insurance", "Gaming", "Food Delivery", "Real Estate", "Fitness", "Entertainment" ]) product_category = st.selectbox("๐Ÿงฉ Product Category", [ "Auto-detect", "General", "Mobile Devices", "Laptops", "Healthcare Devices", "Banking App", "Travel Service", "Educational Tool", "Insurance Portal", "Streaming App", "Wearables", "Home Appliances", "Food Apps" ]) device_type = st.selectbox("๐Ÿ’ป Device Type", [ "Auto-detect", "Web", "Android", "iOS", "Desktop", "Smartwatch", "Kiosk" ]) use_aspects = st.checkbox("๐Ÿ”ฌ Enable Aspect Analysis") use_smart_summary = st.checkbox("๐Ÿง  Use Smart Summary (Single)") use_smart_summary_bulk = st.checkbox("๐Ÿง  Smart Summary for Bulk") follow_up = st.text_input("๐Ÿ” Follow-up Question") voice_lang = st.selectbox("๐Ÿ”ˆ Voice Language", ["en", "fr", "es", "de", "hi", "zh"]) # Text-to-Speech Helper def speak(text, lang='en'): tts = gTTS(text, lang=lang) mp3 = BytesIO() tts.write_to_fp(mp3) b64 = base64.b64encode(mp3.getvalue()).decode() st.markdown(f'', unsafe_allow_html=True) mp3.seek(0) return mp3 # Tabs for modes tab1, tab2 = st.tabs(["๐Ÿง  Single Review", "๐Ÿ“š Bulk CSV"]) # --- SINGLE REVIEW MODE --- with tab1: st.title("๐Ÿง  NeuroPulse AI โ€“ Multimodal Review Analyzer") st.markdown("""
Minimum 50โ€“100 words recommended for optimal insights.
""", unsafe_allow_html=True) review = st.text_area("๐Ÿ“ Enter a Review", value=st.session_state.review, height=180) col1, col2, col3 = st.columns(3) with col1: analyze = st.button("๐Ÿ” Analyze", use_container_width=True, disabled=not api_token) with col2: if st.button("๐ŸŽฒ Example", use_container_width=True): st.session_state.review = "App was smooth, but the transaction failed twice on Android during checkout." st.rerun() with col3: if st.button("๐Ÿงน Clear", use_container_width=True): st.session_state.review = "" st.rerun() if analyze and review: if len(review.split()) < 50: st.error("โš ๏ธ Please enter at least 50 words for meaningful analysis.") else: with st.spinner("Analyzing..."): try: payload = { "text": review, "model": sentiment_model, "industry": industry, "aspects": use_aspects, "follow_up": follow_up, "product_category": product_category, "device": device_type } headers = {"X-API-Key": api_token} params = {"smart": "1"} if use_smart_summary else {} res = requests.post(f"{backend_url}/analyze/", json=payload, headers=headers, params=params) if res.status_code == 200: data = res.json() st.success("โœ… Analysis Complete") st.subheader("๐Ÿ“Œ Summary") st.info(data["summary"]) st.caption(f"๐Ÿง  Summary Type: {'Smart Summary' if use_smart_summary else 'Standard Model'}") st.markdown(f"**Context:** {industry} | {product_category} | {device_type}") st.subheader("๐Ÿ”Š Audio") audio = speak(data["summary"], lang=voice_lang) st.download_button("โฌ‡๏ธ Download Summary Audio", audio.read(), "summary.mp3", mime="audio/mp3") st.metric("๐Ÿ“Š Sentiment", data["sentiment"]["label"], delta=f"{data['sentiment']['score']:.2%}") st.info(f"๐Ÿ’ข Emotion: {data['emotion']}") if data.get("aspects"): st.subheader("๐Ÿ” Aspects") for a in data["aspects"]: st.write(f"๐Ÿ”น {a['aspect']}: {a['sentiment']} ({a['score']:.2%})") if data.get("follow_up"): st.subheader("๐Ÿง  Follow-Up Response") st.warning(data["follow_up"]) else: st.error(f"โŒ API Error: {res.status_code}") except Exception as e: st.error(f"๐Ÿšซ {e}") # --- BULK REVIEW MODE --- with tab2: st.title("๐Ÿ“š Bulk CSV Upload") st.markdown("""
Upload a CSV with the following columns:
review (required), industry, product_category, device (optional)
""", unsafe_allow_html=True) with st.expander("๐Ÿ“„ Sample CSV"): with open("sample_reviews.csv", "rb") as f: st.download_button("โฌ‡๏ธ Download sample CSV", f, file_name="sample_reviews.csv") uploaded_file = st.file_uploader("๐Ÿ“ Upload your CSV", type="csv") if uploaded_file and api_token: try: df = pd.read_csv(uploaded_file) if "review" not in df.columns: st.error("CSV must contain a `review` column.") else: st.success(f"โœ… Loaded {len(df)} reviews") for col in ["industry", "product_category", "device"]: if col not in df.columns: df[col] = [industry if industry != "Auto-detect" else "Generic"] * len(df) df[col] = df[col].fillna("").astype(str) if st.button("๐Ÿ“Š Analyze Bulk Reviews", use_container_width=True): with st.spinner("Processing CSV..."): try: payload = { "reviews": df["review"].tolist(), "model": sentiment_model, "aspects": use_aspects, "industry": df["industry"].tolist(), "product_category": df["product_category"].tolist(), "device": df["device"].tolist() } headers = {"X-API-Key": api_token} params = {"smart": "1"} if use_smart_summary_bulk else {} res = requests.post(f"{backend_url}/bulk/", json=payload, headers=headers, params=params) if res.status_code == 200: results = pd.DataFrame(res.json()["results"]) results["summary_type"] = "Smart" if use_smart_summary_bulk else "Standard" st.dataframe(results) st.download_button("โฌ‡๏ธ Download Results CSV", results.to_csv(index=False), "bulk_results.csv", mime="text/csv") else: st.error(f"โŒ Bulk Analysis Failed: {res.status_code}") except Exception as e: st.error(f"๐Ÿ’ฅ Error: {e}") except Exception as e: st.error(f"โŒ File Error: {e}")