Sumedhzz commited on
Commit
91b9b3a
Β·
1 Parent(s): c03e843

Fix: replace GSheetsConnection with gspread for Docker compatibility

Browse files
Files changed (2) hide show
  1. requirements.txt +2 -4
  2. streamlit_app.py +50 -50
requirements.txt CHANGED
@@ -4,8 +4,6 @@ torch
4
  torchvision
5
  safetensors
6
  pandas
7
- st-gsheets-connection
8
  plotly
9
- gspread
10
- google-auth
11
- oauth2client
 
4
  torchvision
5
  safetensors
6
  pandas
 
7
  plotly
8
+ gspread
9
+ google-auth
 
streamlit_app.py CHANGED
@@ -6,11 +6,12 @@ import time
6
  import plotly.graph_objects as go
7
  import json
8
  import os
9
- from streamlit_gsheets import GSheetsConnection
10
-
 
11
  # --- PAGE CONFIG ---
12
  st.set_page_config(page_title="Sentiment Analyzer AI | Bilingual Engine", page_icon="🌐", layout="wide")
13
-
14
  # --- PROFESSIONAL NEUMORPHIC / GLASS CSS ---
15
  st.markdown("""
16
  <style>
@@ -22,36 +23,41 @@ st.markdown("""
22
  [data-testid="stMetricValue"] { color: #00f2fe; font-weight: 800; }
23
  </style>
24
  """, unsafe_allow_html=True)
25
-
26
- # --- CLOUD DATA LOGGING (GOOGLE SHEETS) ---
27
- # βœ… FIX: Docker Spaces inject secrets as ENV VARS, not st.secrets
28
  def get_connection():
29
  try:
30
- # Read from environment variables (correct for Docker Spaces)
31
  json_secrets = os.environ.get("GSHEETS_JSON")
32
  sheet_url = os.environ.get("GSHEETS_URL")
33
-
34
- # Fallback: try st.secrets (works if running locally or Streamlit SDK Space)
35
  if not json_secrets:
36
  json_secrets = st.secrets.get("GSHEETS_JSON")
37
  if not sheet_url:
38
  sheet_url = st.secrets.get("GSHEETS_URL")
39
-
40
  if not json_secrets or not sheet_url:
41
  st.error("❌ Secrets not found. Please add GSHEETS_JSON and GSHEETS_URL in Space Settings β†’ Secrets.")
42
  st.stop()
43
-
44
  creds_dict = json.loads(json_secrets)
45
- conn = st.connection("gsheets", type=GSheetsConnection, credentials=creds_dict)
46
- return conn, sheet_url
47
-
 
 
 
 
 
 
48
  except json.JSONDecodeError:
49
  st.error("❌ GSHEETS_JSON is not valid JSON. Please re-paste your service account key.")
50
  st.stop()
51
  except Exception as e:
52
  st.error(f"❌ Connection Failed: {e}")
53
  st.stop()
54
-
55
  # --- INITIALIZATION ---
56
  if 'conn' not in st.session_state or 'url' not in st.session_state:
57
  conn, GSHEETS_URL = get_connection()
@@ -60,75 +66,69 @@ if 'conn' not in st.session_state or 'url' not in st.session_state:
60
  else:
61
  conn = st.session_state.conn
62
  GSHEETS_URL = st.session_state.url
63
-
64
  # --- SAVE TO GOOGLE SHEETS ---
65
  def save_to_cloud(text, ai_label, ai_score, corrected_label=None):
66
  try:
67
- existing_data = conn.read(spreadsheet=GSHEETS_URL, worksheet="Sheet1", ttl=0)
68
-
69
- # βœ… FIX: Ensure columns exist even if sheet is empty
70
- expected_cols = ["Timestamp", "Text", "AI_Label", "Confidence", "Correction"]
71
- if existing_data.empty or list(existing_data.columns) != expected_cols:
72
- existing_data = pd.DataFrame(columns=expected_cols)
73
-
74
- new_entry = pd.DataFrame([{
75
- "Timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
76
- "Text": text,
77
- "AI_Label": ai_label,
78
- "Confidence": f"{ai_score:.2%}",
79
- "Correction": corrected_label if corrected_label else "N/A"
80
- }])
81
-
82
- updated_df = pd.concat([existing_data, new_entry], ignore_index=True)
83
- conn.update(spreadsheet=GSHEETS_URL, worksheet="Sheet1", data=updated_df)
84
  return True
85
  except Exception as e:
86
  st.error(f"Cloud Save Failed: {e}")
87
  return False
88
-
89
  # --- MODEL ENGINE ---
90
  MODEL_PATH = "SumedhGajbhiye/Sentiment-Analyzer"
91
-
92
  @st.cache_resource
93
  def load_engine(path):
94
  return pipeline("sentiment-analysis", model=path, tokenizer=path)
95
-
96
  # --- UI LAYOUT ---
97
  col_h1, col_h2 = st.columns([3, 1])
98
  with col_h1:
99
  st.title("Sentiment Analyzer")
100
  st.caption("Advanced Bilingual Sentiment Analysis for English, Hindi & Hinglish")
101
-
102
  # --- SIDEBAR STATS ---
103
  with st.sidebar:
104
  st.markdown("### πŸ› οΈ ENGINE STATUS")
105
  try:
106
- df_log = conn.read(spreadsheet=GSHEETS_URL, worksheet="Sheet1", ttl=0)
 
 
107
  st.metric("Total Ingested", len(df_log))
108
  st.divider()
109
  st.download_button("πŸ“€ Export Dataset", df_log.to_csv(index=False), "engine_feedback.csv", "text/csv")
110
- except Exception as e:
 
111
  st.info("Engine is connecting to cloud...")
112
- df_log = pd.DataFrame() # βœ… FIX: Prevent NameError later if sidebar fails
113
-
114
  # --- MAIN LOGIC ---
115
  classifier = load_engine(MODEL_PATH)
116
-
117
  if classifier:
118
  user_input = st.text_input("QUERY INPUT:", placeholder="Enter sentence...", key="main_input", label_visibility="collapsed")
119
-
120
  if user_input:
121
  with st.status("Initializing Neural Weights...", expanded=False) as status:
122
  time.sleep(0.4)
123
  result = classifier(user_input)[0]
124
  status.update(label="Analysis Complete", state="complete", expanded=False)
125
-
126
  label = result['label']
127
  score = result['score']
128
-
129
  emoji_map = {"Positive": "🟒", "Neutral": "🟑", "Negative": "πŸ”΄"}
130
  color = "#00ff88" if "POS" in label.upper() else "#ff4b4b" if "NEG" in label.upper() else "#ffaa00"
131
-
132
  st.markdown(f'''
133
  <div class="glass-card">
134
  <h4 style="color: #888; margin:0;">CLASSIFICATION RESULT</h4>
@@ -136,7 +136,7 @@ if classifier:
136
  <p style="color: #aaa; margin-top: 10px;">Deep linguistic scan detected {label.lower()} intent with {score:.1%} confidence.</p>
137
  </div>
138
  ''', unsafe_allow_html=True)
139
-
140
  col_chart, col_feed = st.columns([1, 2])
141
  with col_chart:
142
  fig = go.Figure(go.Indicator(
@@ -145,7 +145,7 @@ if classifier:
145
  ))
146
  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"})
147
  st.plotly_chart(fig, use_container_width=True)
148
-
149
  with col_feed:
150
  st.markdown("### βš–οΈ HUMAN VERIFICATION")
151
  c1, c2 = st.columns(2)
@@ -162,11 +162,11 @@ if classifier:
162
  st.toast(f"βœ… Engine forced to {correction}")
163
  time.sleep(1.0)
164
  st.rerun()
165
-
166
  # --- RECENT LOGS ---
167
  try:
168
  if not df_log.empty:
169
  with st.expander("πŸ“‚ VIEW SYSTEM LOGS"):
170
  st.dataframe(df_log.tail(10), use_container_width=True)
171
  except Exception:
172
- pass # βœ… FIX: Silent fail if df_log never loaded
 
6
  import plotly.graph_objects as go
7
  import json
8
  import os
9
+ import gspread
10
+ from google.oauth2.service_account import Credentials
11
+
12
  # --- PAGE CONFIG ---
13
  st.set_page_config(page_title="Sentiment Analyzer AI | Bilingual Engine", page_icon="🌐", layout="wide")
14
+
15
  # --- PROFESSIONAL NEUMORPHIC / GLASS CSS ---
16
  st.markdown("""
17
  <style>
 
23
  [data-testid="stMetricValue"] { color: #00f2fe; font-weight: 800; }
24
  </style>
25
  """, unsafe_allow_html=True)
26
+
27
+ # --- GOOGLE SHEETS CONNECTION (gspread - works in Docker) ---
 
28
  def get_connection():
29
  try:
30
+ # Docker Spaces inject secrets as ENV VARS
31
  json_secrets = os.environ.get("GSHEETS_JSON")
32
  sheet_url = os.environ.get("GSHEETS_URL")
33
+
34
+ # Fallback for local development
35
  if not json_secrets:
36
  json_secrets = st.secrets.get("GSHEETS_JSON")
37
  if not sheet_url:
38
  sheet_url = st.secrets.get("GSHEETS_URL")
39
+
40
  if not json_secrets or not sheet_url:
41
  st.error("❌ Secrets not found. Please add GSHEETS_JSON and GSHEETS_URL in Space Settings β†’ Secrets.")
42
  st.stop()
43
+
44
  creds_dict = json.loads(json_secrets)
45
+ scopes = [
46
+ "https://spreadsheets.google.com/feeds",
47
+ "https://www.googleapis.com/auth/drive"
48
+ ]
49
+ creds = Credentials.from_service_account_info(creds_dict, scopes=scopes)
50
+ client = gspread.authorize(creds)
51
+ sheet = client.open_by_url(sheet_url).worksheet("Sheet1")
52
+ return sheet, sheet_url
53
+
54
  except json.JSONDecodeError:
55
  st.error("❌ GSHEETS_JSON is not valid JSON. Please re-paste your service account key.")
56
  st.stop()
57
  except Exception as e:
58
  st.error(f"❌ Connection Failed: {e}")
59
  st.stop()
60
+
61
  # --- INITIALIZATION ---
62
  if 'conn' not in st.session_state or 'url' not in st.session_state:
63
  conn, GSHEETS_URL = get_connection()
 
66
  else:
67
  conn = st.session_state.conn
68
  GSHEETS_URL = st.session_state.url
69
+
70
  # --- SAVE TO GOOGLE SHEETS ---
71
  def save_to_cloud(text, ai_label, ai_score, corrected_label=None):
72
  try:
73
+ sheet = st.session_state.conn
74
+ new_row = [
75
+ datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
76
+ text,
77
+ ai_label,
78
+ f"{ai_score:.2%}",
79
+ corrected_label if corrected_label else "N/A"
80
+ ]
81
+ sheet.append_row(new_row)
 
 
 
 
 
 
 
 
82
  return True
83
  except Exception as e:
84
  st.error(f"Cloud Save Failed: {e}")
85
  return False
86
+
87
  # --- MODEL ENGINE ---
88
  MODEL_PATH = "SumedhGajbhiye/Sentiment-Analyzer"
89
+
90
  @st.cache_resource
91
  def load_engine(path):
92
  return pipeline("sentiment-analysis", model=path, tokenizer=path)
93
+
94
  # --- UI LAYOUT ---
95
  col_h1, col_h2 = st.columns([3, 1])
96
  with col_h1:
97
  st.title("Sentiment Analyzer")
98
  st.caption("Advanced Bilingual Sentiment Analysis for English, Hindi & Hinglish")
99
+
100
  # --- SIDEBAR STATS ---
101
  with st.sidebar:
102
  st.markdown("### πŸ› οΈ ENGINE STATUS")
103
  try:
104
+ sheet = st.session_state.conn
105
+ all_rows = sheet.get_all_records()
106
+ df_log = pd.DataFrame(all_rows)
107
  st.metric("Total Ingested", len(df_log))
108
  st.divider()
109
  st.download_button("πŸ“€ Export Dataset", df_log.to_csv(index=False), "engine_feedback.csv", "text/csv")
110
+ except Exception:
111
+ df_log = pd.DataFrame()
112
  st.info("Engine is connecting to cloud...")
113
+
 
114
  # --- MAIN LOGIC ---
115
  classifier = load_engine(MODEL_PATH)
116
+
117
  if classifier:
118
  user_input = st.text_input("QUERY INPUT:", placeholder="Enter sentence...", key="main_input", label_visibility="collapsed")
119
+
120
  if user_input:
121
  with st.status("Initializing Neural Weights...", expanded=False) as status:
122
  time.sleep(0.4)
123
  result = classifier(user_input)[0]
124
  status.update(label="Analysis Complete", state="complete", expanded=False)
125
+
126
  label = result['label']
127
  score = result['score']
128
+
129
  emoji_map = {"Positive": "🟒", "Neutral": "🟑", "Negative": "πŸ”΄"}
130
  color = "#00ff88" if "POS" in label.upper() else "#ff4b4b" if "NEG" in label.upper() else "#ffaa00"
131
+
132
  st.markdown(f'''
133
  <div class="glass-card">
134
  <h4 style="color: #888; margin:0;">CLASSIFICATION RESULT</h4>
 
136
  <p style="color: #aaa; margin-top: 10px;">Deep linguistic scan detected {label.lower()} intent with {score:.1%} confidence.</p>
137
  </div>
138
  ''', unsafe_allow_html=True)
139
+
140
  col_chart, col_feed = st.columns([1, 2])
141
  with col_chart:
142
  fig = go.Figure(go.Indicator(
 
145
  ))
146
  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"})
147
  st.plotly_chart(fig, use_container_width=True)
148
+
149
  with col_feed:
150
  st.markdown("### βš–οΈ HUMAN VERIFICATION")
151
  c1, c2 = st.columns(2)
 
162
  st.toast(f"βœ… Engine forced to {correction}")
163
  time.sleep(1.0)
164
  st.rerun()
165
+
166
  # --- RECENT LOGS ---
167
  try:
168
  if not df_log.empty:
169
  with st.expander("πŸ“‚ VIEW SYSTEM LOGS"):
170
  st.dataframe(df_log.tail(10), use_container_width=True)
171
  except Exception:
172
+ pass