Sentiment-Analyzer / streamlit_app.py
Sumedhzz's picture
Fix: replace GSheetsConnection with gspread for Docker compatibility
91b9b3a
Raw History Blame Contribute Delete
7.11 kB
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