Sentiment-Analyzer / streamlit_app.py
Sumedhzz's picture
Fix: replace GSheetsConnection with gspread for Docker compatibility
91b9b3a
import streamlit as st
from transformers import pipeline
import pandas as pd
from datetime import datetime
import time
import plotly.graph_objects as go
import json
import os
import gspread
from google.oauth2.service_account import Credentials
# --- PAGE CONFIG ---
st.set_page_config(page_title="Sentiment Analyzer AI | Bilingual Engine", page_icon="🌐", layout="wide")
# --- PROFESSIONAL NEUMORPHIC / GLASS CSS ---
st.markdown("""
<style>
.stApp { background: linear-gradient(135deg, #12141d 0%, #1a1c2c 100%); color: #ffffff; }
div[data-baseweb="input"] { background: rgba(255, 255, 255, 0.05) !important; backdrop-filter: blur(10px) !important; border-radius: 15px !important; border: 1px solid rgba(255, 255, 255, 0.1) !important; padding: 5px !important; }
.glass-card { background: rgba(255, 255, 255, 0.05); backdrop-filter: blur(10px); border-radius: 20px; border: 1px solid rgba(255, 255, 255, 0.1); padding: 30px; margin-top: 20px; margin-bottom: 25px; transition: 0.4s ease; }
.stButton>button { background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%); color: white; border: none; border-radius: 12px; font-weight: 600; letter-spacing: 0.5px; height: 3rem; transition: all 0.3s ease; }
.stButton>button:hover { box-shadow: 0 0 20px rgba(79, 172, 254, 0.4); transform: scale(1.02); }
[data-testid="stMetricValue"] { color: #00f2fe; font-weight: 800; }
</style>
""", unsafe_allow_html=True)
# --- GOOGLE SHEETS CONNECTION (gspread - works in Docker) ---
def get_connection():
try:
# Docker Spaces inject secrets as ENV VARS
json_secrets = os.environ.get("GSHEETS_JSON")
sheet_url = os.environ.get("GSHEETS_URL")
# Fallback for local development
if not json_secrets:
json_secrets = st.secrets.get("GSHEETS_JSON")
if not sheet_url:
sheet_url = st.secrets.get("GSHEETS_URL")
if not json_secrets or not sheet_url:
st.error("❌ Secrets not found. Please add GSHEETS_JSON and GSHEETS_URL in Space Settings β†’ Secrets.")
st.stop()
creds_dict = json.loads(json_secrets)
scopes = [
"https://spreadsheets.google.com/feeds",
"https://www.googleapis.com/auth/drive"
]
creds = Credentials.from_service_account_info(creds_dict, scopes=scopes)
client = gspread.authorize(creds)
sheet = client.open_by_url(sheet_url).worksheet("Sheet1")
return sheet, sheet_url
except json.JSONDecodeError:
st.error("❌ GSHEETS_JSON is not valid JSON. Please re-paste your service account key.")
st.stop()
except Exception as e:
st.error(f"❌ Connection Failed: {e}")
st.stop()
# --- INITIALIZATION ---
if 'conn' not in st.session_state or 'url' not in st.session_state:
conn, GSHEETS_URL = get_connection()
st.session_state.conn = conn
st.session_state.url = GSHEETS_URL
else:
conn = st.session_state.conn
GSHEETS_URL = st.session_state.url
# --- SAVE TO GOOGLE SHEETS ---
def save_to_cloud(text, ai_label, ai_score, corrected_label=None):
try:
sheet = st.session_state.conn
new_row = [
datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
text,
ai_label,
f"{ai_score:.2%}",
corrected_label if corrected_label else "N/A"
]
sheet.append_row(new_row)
return True
except Exception as e:
st.error(f"Cloud Save Failed: {e}")
return False
# --- MODEL ENGINE ---
MODEL_PATH = "SumedhGajbhiye/Sentiment-Analyzer"
@st.cache_resource
def load_engine(path):
return pipeline("sentiment-analysis", model=path, tokenizer=path)
# --- UI LAYOUT ---
col_h1, col_h2 = st.columns([3, 1])
with col_h1:
st.title("Sentiment Analyzer")
st.caption("Advanced Bilingual Sentiment Analysis for English, Hindi & Hinglish")
# --- SIDEBAR STATS ---
with st.sidebar:
st.markdown("### πŸ› οΈ ENGINE STATUS")
try:
sheet = st.session_state.conn
all_rows = sheet.get_all_records()
df_log = pd.DataFrame(all_rows)
st.metric("Total Ingested", len(df_log))
st.divider()
st.download_button("πŸ“€ Export Dataset", df_log.to_csv(index=False), "engine_feedback.csv", "text/csv")
except Exception:
df_log = pd.DataFrame()
st.info("Engine is connecting to cloud...")
# --- MAIN LOGIC ---
classifier = load_engine(MODEL_PATH)
if classifier:
user_input = st.text_input("QUERY INPUT:", placeholder="Enter sentence...", key="main_input", label_visibility="collapsed")
if user_input:
with st.status("Initializing Neural Weights...", expanded=False) as status:
time.sleep(0.4)
result = classifier(user_input)[0]
status.update(label="Analysis Complete", state="complete", expanded=False)
label = result['label']
score = result['score']
emoji_map = {"Positive": "🟒", "Neutral": "🟑", "Negative": "πŸ”΄"}
color = "#00ff88" if "POS" in label.upper() else "#ff4b4b" if "NEG" in label.upper() else "#ffaa00"
st.markdown(f'''
<div class="glass-card">
<h4 style="color: #888; margin:0;">CLASSIFICATION RESULT</h4>
<h1 style="color: {color}; margin:0; font-size: 3.5rem;">{label} {emoji_map.get(label, "")}</h1>
<p style="color: #aaa; margin-top: 10px;">Deep linguistic scan detected {label.lower()} intent with {score:.1%} confidence.</p>
</div>
''', unsafe_allow_html=True)
col_chart, col_feed = st.columns([1, 2])
with col_chart:
fig = go.Figure(go.Indicator(
mode="gauge+number", value=score * 100,
gauge={'axis': {'range': [None, 100]}, 'bar': {'color': color}, 'bgcolor': "rgba(0,0,0,0)"}
))
fig.update_layout(height=280, margin=dict(t=50, b=50, l=40, r=40), paper_bgcolor='rgba(0,0,0,0)', font={'color': "#fff"})
st.plotly_chart(fig, use_container_width=True)
with col_feed:
st.markdown("### βš–οΈ HUMAN VERIFICATION")
c1, c2 = st.columns(2)
with c1:
if st.button("CONFIRM ACCURACY"):
if save_to_cloud(user_input, label, score):
st.toast("βœ… Logic logged to cloud database.")
time.sleep(1.0)
st.rerun()
with c2:
correction = st.selectbox("OVERRIDE LABEL:", ["Positive", "Neutral", "Negative"])
if st.button("FORCE UPDATE ENGINE"):
if save_to_cloud(user_input, label, score, corrected_label=correction):
st.toast(f"βœ… Engine forced to {correction}")
time.sleep(1.0)
st.rerun()
# --- RECENT LOGS ---
try:
if not df_log.empty:
with st.expander("πŸ“‚ VIEW SYSTEM LOGS"):
st.dataframe(df_log.tail(10), use_container_width=True)
except Exception:
pass