charantejapolavarapu commited on
Commit
d2aa4a3
·
verified ·
1 Parent(s): f5d7c29

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +205 -110
src/streamlit_app.py CHANGED
@@ -1,4 +1,3 @@
1
-
2
  import streamlit as st
3
  import feedparser
4
  import requests
@@ -7,94 +6,135 @@ import os
7
  from bs4 import BeautifulSoup
8
  from transformers import MarianMTModel, MarianTokenizer
9
  import whisper
10
- from streamlit_audio_recorder import st_audiorec
11
 
 
12
  st.set_page_config(page_title="📍 BharatPulse - Local News & Sentiment", layout="wide")
13
 
14
- # ------------------------------
15
- # User Auth Functions
16
- # ------------------------------
 
17
  USERS_FILE = "users.json"
18
 
19
  def load_users():
 
20
  if not os.path.exists(USERS_FILE):
21
  return {}
22
- with open(USERS_FILE, "r") as f:
23
- return json.load(f)
 
 
 
 
 
24
 
25
  def save_users(users):
 
26
  with open(USERS_FILE, "w") as f:
27
  json.dump(users, f, indent=2)
28
 
29
  def register_user(username, email, password, location):
 
30
  users = load_users()
31
  if email in users:
32
- return False, "User already exists."
33
  users[email] = {
34
  "username": username,
35
  "email": email,
36
- "password": password,
37
  "location": location
38
  }
39
  save_users(users)
40
- return True, "Registration successful."
41
 
42
  def login_user(email, password):
 
43
  users = load_users()
44
  user = users.get(email)
45
- if user and user["password"] == password:
46
  return True, user
47
- return False, "Invalid credentials."
48
 
49
- # ------------------------------
50
- # Whisper Model
51
- # ------------------------------
52
- @st.cache_resource
53
  def load_whisper_model():
54
- return whisper.load_model("base")
 
 
 
 
55
 
56
  def transcribe_audio(audio_bytes):
 
 
57
  path = "/tmp/voice.wav"
58
- with open(path, "wb") as f:
59
- f.write(audio_bytes)
60
- model = load_whisper_model()
61
- result = model.transcribe(path, language="te")
62
- return result["text"]
63
-
64
- # ------------------------------
65
- # Translation Model
66
- # ------------------------------
67
- @st.cache_resource
 
 
 
 
 
 
 
68
  def load_translation_model():
 
 
69
  model_name = "Helsinki-NLP/opus-mt-en-te"
70
  tokenizer = MarianTokenizer.from_pretrained(model_name)
71
  model = MarianMTModel.from_pretrained(model_name)
 
72
  return tokenizer, model
73
 
74
  def translate_to_telugu(text):
75
- tokenizer, model = load_translation_model()
76
- tokens = tokenizer.prepare_seq2seq_batch([text], return_tensors="pt", truncation=True, max_length=512)
77
- translated = model.generate(**tokens)
78
- return tokenizer.decode(translated[0], skip_special_tokens=True)
79
-
80
- # ------------------------------
81
- # Extract Article Summary
82
- # ------------------------------
 
 
 
 
83
  def extract_article_content(url):
 
84
  try:
85
- response = requests.get(url, timeout=5)
 
 
 
 
 
86
  soup = BeautifulSoup(response.text, "html.parser")
87
  paragraphs = soup.find_all('p')
 
88
  content = ' '.join([p.get_text() for p in paragraphs[:5]])
89
- return content
 
 
 
 
90
  except Exception as e:
91
- return str(e)
92
 
93
- # ------------------------------
94
- # Auth UI
95
- # ------------------------------
96
  if "logged_in" not in st.session_state:
97
  st.session_state.logged_in = False
 
 
98
 
99
  if not st.session_state.logged_in:
100
  st.title("🔐 Login to BharatPulse")
@@ -102,100 +142,155 @@ if not st.session_state.logged_in:
102
  tab1, tab2 = st.tabs(["🔑 Login", "📝 Register"])
103
 
104
  with tab1:
105
- email = st.text_input("Email")
106
- password = st.text_input("Password", type="password")
107
- if st.button("Login"):
 
108
  success, user_or_msg = login_user(email, password)
109
  if success:
110
  st.session_state.logged_in = True
111
  st.session_state.user = user_or_msg
112
- st.rerun()
 
113
  else:
114
  st.error(user_or_msg)
115
 
116
  with tab2:
117
- new_username = st.text_input("Username")
118
- new_email = st.text_input("New Email")
119
- new_password = st.text_input("New Password", type="password")
120
- new_location = st.text_input("Location")
121
- if st.button("Register"):
122
- success, message = register_user(new_username, new_email, new_password, new_location)
123
- if success:
124
- st.success(message)
 
 
 
 
125
  else:
126
- st.error(message)
127
- st.stop()
128
 
129
- # ------------------------------
130
- # Main News UI
131
- # ------------------------------
132
  st.sidebar.success(f"Logged in as: {st.session_state.user['username']}")
 
 
133
  if st.sidebar.button("🚪 Logout"):
134
  st.session_state.logged_in = False
135
  st.session_state.user = None
136
- st.rerun()
137
 
138
- st.title("📍 BharatPulse")
139
- tabs = st.tabs(["📰 Headlines", "🔍 Search by City", "🎤 Voice Search"])
140
 
 
 
141
  feeds = {
142
  "Sakshi - Medak": "https://www.sakshi.com/rss/district-news/medak.xml",
143
- "Eenadu - AP": "https://www.eenadu.net/rss/andhra-pradesh.xml"
 
144
  }
145
 
146
- # ------------------------------
147
- # 📰 Headlines Tab
148
- # ------------------------------
149
  with tabs[0]:
150
- st.header("📰 Local Telugu News")
151
  for name, url in feeds.items():
152
  st.subheader(name)
153
- feed = feedparser.parse(url)
154
- for entry in feed.entries[:3]:
155
- st.markdown(f"### {entry.title}")
156
- st.write(entry.published)
157
- st.markdown(f"[Read Full Article]({entry.link})")
158
- summary = extract_article_content(entry.link)
159
- st.markdown("**English Summary:** " + summary)
160
- telugu_summary = translate_to_telugu(summary)
161
- st.markdown("**తెలుగు అనువాదం:** " + telugu_summary)
162
- st.markdown("---")
163
-
164
- # ------------------------------
165
- # 🔍 Search by City
166
- # ------------------------------
 
 
 
 
 
 
 
 
167
  with tabs[1]:
168
- st.header("🔎 Search by City / District")
169
- city = st.text_input("Enter your city/district name in Telugu or English")
 
 
170
 
171
- if city:
172
- st.success(f"Showing news for: {city}")
 
173
  for name, url in feeds.items():
174
- if city.lower() in name.lower():
175
- st.subheader(name)
176
- feed = feedparser.parse(url)
177
- for entry in feed.entries[:2]:
178
- st.markdown(f"**🗞️ {entry.title}**")
179
- summary = extract_article_content(entry.link)
180
- telugu_summary = translate_to_telugu(summary)
181
- st.markdown("**తెలుగు అనువాదం:** " + telugu_summary)
182
-
183
- # ------------------------------
184
- # 🎤 Voice Search Tab
185
- # ------------------------------
 
 
 
 
 
 
 
 
 
 
186
  with tabs[2]:
187
  st.header("🎤 Search by Telugu Voice")
188
- audio = st_audiorec()
189
- if audio:
190
- transcribed = transcribe_audio(audio)
191
- st.success(f"Voice Input Recognized: {transcribed}")
192
- city = transcribed
193
- for name, url in feeds.items():
194
- if city.lower() in name.lower():
195
- st.subheader(name)
196
- feed = feedparser.parse(url)
197
- for entry in feed.entries[:2]:
198
- st.markdown(f"**🗞️ {entry.title}**")
199
- summary = extract_article_content(entry.link)
200
- telugu_summary = translate_to_telugu(summary)
201
- st.markdown("**తెలుగు అనువాదం:** " + telugu_summary)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
  import feedparser
3
  import requests
 
6
  from bs4 import BeautifulSoup
7
  from transformers import MarianMTModel, MarianTokenizer
8
  import whisper
9
+ from streamlit_audio_recorder import st_audiorec # Make sure this library is installed
10
 
11
+ # Configure the Streamlit page
12
  st.set_page_config(page_title="📍 BharatPulse - Local News & Sentiment", layout="wide")
13
 
14
+ # --- User Authentication Functions ---
15
+ # IMPORTANT: For production, this 'users.json' file will not persist across deployments
16
+ # on cloud platforms like Streamlit Community Cloud. Consider using a database (e.g., Firebase, PostgreSQL)
17
+ # for persistent user data.
18
  USERS_FILE = "users.json"
19
 
20
  def load_users():
21
+ """Loads user data from the JSON file."""
22
  if not os.path.exists(USERS_FILE):
23
  return {}
24
+ try:
25
+ with open(USERS_FILE, "r") as f:
26
+ return json.load(f)
27
+ except json.JSONDecodeError:
28
+ # Handle case where file might be empty or corrupted
29
+ st.error("Error loading user data. Initializing empty user database.")
30
+ return {}
31
 
32
  def save_users(users):
33
+ """Saves user data to the JSON file."""
34
  with open(USERS_FILE, "w") as f:
35
  json.dump(users, f, indent=2)
36
 
37
  def register_user(username, email, password, location):
38
+ """Registers a new user."""
39
  users = load_users()
40
  if email in users:
41
+ return False, "User already exists with this email."
42
  users[email] = {
43
  "username": username,
44
  "email": email,
45
+ "password": password, # In a real app, hash passwords!
46
  "location": location
47
  }
48
  save_users(users)
49
+ return True, "Registration successful. You can now log in."
50
 
51
  def login_user(email, password):
52
+ """Logs in an existing user."""
53
  users = load_users()
54
  user = users.get(email)
55
+ if user and user["password"] == password: # In a real app, compare hashed passwords!
56
  return True, user
57
+ return False, "Invalid email or password."
58
 
59
+ # --- Whisper Model for Audio Transcription ---
60
+ @st.cache_resource # Caches the model to avoid reloading on rerun
 
 
61
  def load_whisper_model():
62
+ """Loads the Whisper base model."""
63
+ st.info("Loading Whisper model (this may take a moment)...")
64
+ model = whisper.load_model("base")
65
+ st.success("Whisper model loaded!")
66
+ return model
67
 
68
  def transcribe_audio(audio_bytes):
69
+ """Transcribes audio bytes to text using Whisper."""
70
+ # Save audio to a temporary file
71
  path = "/tmp/voice.wav"
72
+ try:
73
+ with open(path, "wb") as f:
74
+ f.write(audio_bytes)
75
+ model = load_whisper_model()
76
+ # Specify language for better accuracy if known
77
+ result = model.transcribe(path, language="te", fp16=False) # fp16=False for CPU compatibility
78
+ return result["text"]
79
+ except Exception as e:
80
+ st.error(f"Error during audio transcription: {e}")
81
+ return ""
82
+ finally:
83
+ # Clean up the temporary file
84
+ if os.path.exists(path):
85
+ os.remove(path)
86
+
87
+ # --- MarianMT Model for Translation ---
88
+ @st.cache_resource # Caches the model to avoid reloading on rerun
89
  def load_translation_model():
90
+ """Loads the MarianMT English-Telugu translation model."""
91
+ st.info("Loading translation model (this may take a moment)...")
92
  model_name = "Helsinki-NLP/opus-mt-en-te"
93
  tokenizer = MarianTokenizer.from_pretrained(model_name)
94
  model = MarianMTModel.from_pretrained(model_name)
95
+ st.success("Translation model loaded!")
96
  return tokenizer, model
97
 
98
  def translate_to_telugu(text):
99
+ """Translates English text to Telugu."""
100
+ try:
101
+ tokenizer, model = load_translation_model()
102
+ # Prepare input for the model, ensuring truncation for long texts
103
+ tokens = tokenizer.prepare_seq2seq_batch([text], return_tensors="pt", truncation=True, max_length=512)
104
+ translated = model.generate(**tokens)
105
+ return tokenizer.decode(translated[0], skip_special_tokens=True)
106
+ except Exception as e:
107
+ st.error(f"Error during translation: {e}")
108
+ return "Translation failed."
109
+
110
+ # --- Article Content Extraction ---
111
  def extract_article_content(url):
112
+ """Extracts the first few paragraphs from an article URL."""
113
  try:
114
+ # Add headers to mimic a browser, as some sites block direct requests
115
+ headers = {
116
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
117
+ }
118
+ response = requests.get(url, timeout=10, headers=headers) # Increased timeout
119
+ response.raise_for_status() # Raise an HTTPError for bad responses (4xx or 5xx)
120
  soup = BeautifulSoup(response.text, "html.parser")
121
  paragraphs = soup.find_all('p')
122
+ # Join the text of the first 5 paragraphs, or fewer if not available
123
  content = ' '.join([p.get_text() for p in paragraphs[:5]])
124
+ if not content: # If no paragraphs found, try to get text from body
125
+ content = soup.body.get_text(separator=' ', strip=True)[:1000] # Get first 1000 chars
126
+ return content if content else "Could not extract article content."
127
+ except requests.exceptions.RequestException as e:
128
+ return f"Network or HTTP error: {e}"
129
  except Exception as e:
130
+ return f"Error extracting content: {e}"
131
 
132
+ # --- Authentication UI ---
133
+ # Initialize session state for login status
 
134
  if "logged_in" not in st.session_state:
135
  st.session_state.logged_in = False
136
+ if "user" not in st.session_state:
137
+ st.session_state.user = None
138
 
139
  if not st.session_state.logged_in:
140
  st.title("🔐 Login to BharatPulse")
 
142
  tab1, tab2 = st.tabs(["🔑 Login", "📝 Register"])
143
 
144
  with tab1:
145
+ st.subheader("Existing User Login")
146
+ email = st.text_input("Email", key="login_email")
147
+ password = st.text_input("Password", type="password", key="login_password")
148
+ if st.button("Login", key="login_button"):
149
  success, user_or_msg = login_user(email, password)
150
  if success:
151
  st.session_state.logged_in = True
152
  st.session_state.user = user_or_msg
153
+ st.success(f"Welcome, {st.session_state.user['username']}!")
154
+ st.rerun() # Rerun to switch to the main app view
155
  else:
156
  st.error(user_or_msg)
157
 
158
  with tab2:
159
+ st.subheader("New User Registration")
160
+ new_username = st.text_input("Username", key="register_username")
161
+ new_email = st.text_input("New Email", key="register_email")
162
+ new_password = st.text_input("New Password", type="password", key="register_password")
163
+ new_location = st.text_input("Location (e.g., Hyderabad)", key="register_location")
164
+ if st.button("Register", key="register_button"):
165
+ if new_username and new_email and new_password and new_location:
166
+ success, message = register_user(new_username, new_email, new_password, new_location)
167
+ if success:
168
+ st.success(message)
169
+ else:
170
+ st.error(message)
171
  else:
172
+ st.warning("Please fill in all registration fields.")
173
+ st.stop() # Stop execution if not logged in
174
 
175
+ # --- Main News UI (only runs if logged in) ---
176
+ st.sidebar.header("User Info")
 
177
  st.sidebar.success(f"Logged in as: {st.session_state.user['username']}")
178
+ st.sidebar.info(f"Your registered location: {st.session_state.user['location']}")
179
+
180
  if st.sidebar.button("🚪 Logout"):
181
  st.session_state.logged_in = False
182
  st.session_state.user = None
183
+ st.rerun() # Rerun to go back to login screen
184
 
185
+ st.title("📍 BharatPulse - Local News & Sentiment")
 
186
 
187
+ # Define RSS feeds
188
+ # You can expand this dictionary with more RSS feeds for different regions/newspapers
189
  feeds = {
190
  "Sakshi - Medak": "https://www.sakshi.com/rss/district-news/medak.xml",
191
+ "Eenadu - AP": "https://www.eenadu.net/rss/andhra-pradesh.xml",
192
+ # Add more feeds here, e.g., "Eenadu - Telangana": "https://www.eenadu.net/rss/telangana.xml"
193
  }
194
 
195
+ tabs = st.tabs(["📰 Headlines", "🔍 Search by City", "🎤 Voice Search"])
196
+
197
+ # --- 📰 Headlines Tab ---
198
  with tabs[0]:
199
+ st.header("📰 Latest Local Telugu News Headlines")
200
  for name, url in feeds.items():
201
  st.subheader(name)
202
+ try:
203
+ feed = feedparser.parse(url)
204
+ if not feed.entries:
205
+ st.info(f"No entries found for {name}.")
206
+ continue
207
+
208
+ for entry in feed.entries[:3]: # Display top 3 articles per feed
209
+ st.markdown(f"### [{entry.title}]({entry.link})") # Make title a clickable link
210
+ st.write(f"Published: {entry.published}")
211
+
212
+ # Extract and summarize article content
213
+ summary = extract_article_content(entry.link)
214
+ st.markdown(f"**English Summary:** {summary}")
215
+
216
+ # Translate summary to Telugu
217
+ telugu_summary = translate_to_telugu(summary)
218
+ st.markdown(f"**తెలుగు అనువాదం:** {telugu_summary}")
219
+ st.markdown("---")
220
+ except Exception as e:
221
+ st.error(f"Could not fetch news from {name}: {e}")
222
+
223
+ # --- 🔍 Search by City Tab ---
224
  with tabs[1]:
225
+ st.header("🔎 Search News by City / District")
226
+ # Pre-fill with user's registered location if available
227
+ default_city = st.session_state.user['location'] if st.session_state.user else ""
228
+ city_search_input = st.text_input("Enter your city/district name (e.g., Medak, Hyderabad)", value=default_city)
229
 
230
+ if city_search_input:
231
+ st.success(f"Searching news for: **{city_search_input}**")
232
+ found_news = False
233
  for name, url in feeds.items():
234
+ # Basic matching: checks if the city name is in the feed's name (case-insensitive)
235
+ if city_search_input.lower() in name.lower():
236
+ st.subheader(f"News from {name}")
237
+ try:
238
+ feed = feedparser.parse(url)
239
+ if not feed.entries:
240
+ st.info(f"No entries found for {name} matching '{city_search_input}'.")
241
+ continue
242
+
243
+ for entry in feed.entries[:2]: # Display top 2 articles per matching feed
244
+ st.markdown(f"**🗞️ [{entry.title}]({entry.link})**")
245
+ summary = extract_article_content(entry.link)
246
+ telugu_summary = translate_to_telugu(summary)
247
+ st.markdown(f"**తెలుగు అనువాదం:** {telugu_summary}")
248
+ st.markdown("---")
249
+ found_news = True
250
+ except Exception as e:
251
+ st.error(f"Could not fetch news from {name}: {e}")
252
+ if not found_news:
253
+ st.info(f"No news found for '{city_search_input}' in the available feeds. Try a different city or check our 'Headlines' tab.")
254
+
255
+ # --- 🎤 Voice Search Tab ---
256
  with tabs[2]:
257
  st.header("🎤 Search by Telugu Voice")
258
+ st.info("Click 'Record' to start, 'Stop' when done. Speak the city/district name in Telugu.")
259
+
260
+ # Use the audio recorder component
261
+ audio_bytes = st_audiorec()
262
+
263
+ if audio_bytes:
264
+ with st.spinner("Transcribing audio..."):
265
+ transcribed_text = transcribe_audio(audio_bytes)
266
+
267
+ if transcribed_text:
268
+ st.success(f"Voice Input Recognized: **{transcribed_text}**")
269
+ city_from_voice = transcribed_text.strip() # Use the transcribed text as the city
270
+
271
+ st.subheader(f"Searching news for: **{city_from_voice}**")
272
+ found_news_voice = False
273
+ for name, url in feeds.items():
274
+ # Basic matching: checks if the transcribed city name is in the feed's name
275
+ if city_from_voice.lower() in name.lower():
276
+ st.subheader(f"News from {name}")
277
+ try:
278
+ feed = feedparser.parse(url)
279
+ if not feed.entries:
280
+ st.info(f"No entries found for {name} matching '{city_from_voice}'.")
281
+ continue
282
+
283
+ for entry in feed.entries[:2]: # Display top 2 articles per matching feed
284
+ st.markdown(f"**🗞️ [{entry.title}]({entry.link})**")
285
+ summary = extract_article_content(entry.link)
286
+ telugu_summary = translate_to_telugu(summary)
287
+ st.markdown(f"**తెలుగు అనువాదం:** {telugu_summary}")
288
+ st.markdown("---")
289
+ found_news_voice = True
290
+ except Exception as e:
291
+ st.error(f"Could not fetch news from {name}: {e}")
292
+ if not found_news_voice:
293
+ st.info(f"No news found for '{city_from_voice}' in the available feeds. Try speaking clearly or check our 'Headlines' tab.")
294
+ else:
295
+ st.warning("Could not transcribe audio. Please try again.")
296
+