Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import feedparser | |
| import requests | |
| import json | |
| import os | |
| from bs4 import BeautifulSoup | |
| from transformers import MarianMTModel, MarianTokenizer | |
| # Configure the Streamlit page | |
| st.set_page_config(page_title="📍 BharatPulse - Local News & Sentiment", layout="wide") | |
| # --- User Authentication Functions --- | |
| # IMPORTANT: For production, this 'users.json' file will not persist across deployments | |
| # on cloud platforms like Streamlit Community Cloud. For persistent user data, | |
| # a proper database (e.g., Firebase Firestore, PostgreSQL) is REQUIRED. | |
| # If you are seeing PermissionError even with /tmp, your environment does not allow file writes. | |
| USERS_FILE = "/tmp/users.json" # Attempt to use /tmp for write permissions | |
| def load_users(): | |
| """Loads user data from the JSON file.""" | |
| users_data = {} | |
| if os.path.exists(USERS_FILE): | |
| try: | |
| with open(USERS_FILE, "r") as f: | |
| users_data = json.load(f) | |
| except json.JSONDecodeError: | |
| st.error("Error: users.json file is corrupted. Starting with an empty user database.") | |
| except PermissionError: | |
| st.error("Permission Error: Cannot read users.json. Your environment might restrict file access. For persistent user data, a database is required.") | |
| except Exception as e: | |
| st.error(f"An unexpected error occurred while loading users: {e}. For persistent user data, a database is required.") | |
| return users_data | |
| def save_users(users): | |
| """Saves user data to the JSON file.""" | |
| try: | |
| with open(USERS_FILE, "w") as f: | |
| json.dump(users, f, indent=2) | |
| except PermissionError: | |
| st.error("Permission Error: Cannot write to users.json. Your environment might restrict file access. For persistent user data, a database is required.") | |
| except Exception as e: | |
| st.error(f"An unexpected error occurred while saving users: {e}. For persistent user data, a database is required.") | |
| def register_user(username, email, password, location): | |
| """Registers a new user.""" | |
| users = load_users() | |
| if email in users: | |
| return False, "User already exists with this email." | |
| users[email] = { | |
| "username": username, | |
| "email": email, | |
| "password": password, # In a real app, hash passwords! | |
| "location": location | |
| } | |
| save_users(users) # Attempt to save | |
| # The success of saving will be indicated by the absence of an error from save_users | |
| return True, "Registration successful. You can now log in." | |
| def login_user(email, password): | |
| """Logs in an existing user.""" | |
| users = load_users() | |
| user = users.get(email) | |
| if user and user["password"] == password: # In a real app, compare hashed hashed passwords! | |
| return True, user | |
| return False, "Invalid email or password. If you don't have an account, please go to the 'Register' tab to create one." | |
| # --- MarianMT Model for Translation --- | |
| # Caches the model to avoid reloading on rerun | |
| def load_translation_model(): | |
| """Loads the MarianMT English-Telugu translation model.""" | |
| st.info("Loading translation model (this may take a moment)...") | |
| model_name = "Helsinki-NLP/opus-mt-en-te" | |
| tokenizer = MarianTokenizer.from_pretrained(model_name) | |
| model = MarianMTModel.from_pretrained(model_name) | |
| st.success("Translation model loaded!") | |
| return tokenizer, model | |
| def translate_to_telugu(text): | |
| """Translates English text to Telugu.""" | |
| try: | |
| tokenizer, model = load_translation_model() | |
| # Prepare input for the model, ensuring truncation for long texts | |
| tokens = tokenizer.prepare_seq2seq_batch([text], return_tensors="pt", truncation=True, max_length=512) | |
| translated = model.generate(**tokens) | |
| return tokenizer.decode(translated[0], skip_special_tokens=True) | |
| except Exception as e: | |
| st.error(f"Error during translation: {e}") | |
| return "Translation failed." | |
| # --- Article Content Extraction --- | |
| def extract_article_content(url): | |
| """Extracts the first few paragraphs from an article URL.""" | |
| try: | |
| # Add headers to mimic a browser, as some sites block direct requests | |
| headers = { | |
| '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' | |
| } | |
| response = requests.get(url, timeout=10, headers=headers) # Increased timeout | |
| response.raise_for_status() # Raise an HTTPError for bad responses (4xx or 5xx) | |
| soup = BeautifulSoup(response.text, "html.parser") | |
| paragraphs = soup.find_all('p') | |
| # Join the text of the first 5 paragraphs, or fewer if not available | |
| content = ' '.join([p.get_text() for p in paragraphs[:5]]) | |
| if not content: # If no paragraphs found, try to get text from body | |
| content = soup.body.get_text(separator=' ', strip=True)[:1000] # Get first 1000 chars | |
| return content if content else "Could not extract article content." | |
| except requests.exceptions.RequestException as e: | |
| return f"Network or HTTP error: {e}" | |
| except Exception as e: | |
| return f"Error extracting content: {e}" | |
| # --- Weather Functionality --- | |
| # IMPORTANT: Replace 'YOUR_OPENWEATHER_API_KEY' with your actual OpenWeatherMap API key. | |
| # You can get one from https://openweathermap.org/api | |
| # For production, store this securely (e.g., Streamlit secrets or environment variables). | |
| OPENWEATHER_API_KEY = "YOUR_OPENWEATHER_API_KEY" | |
| WEATHER_API_URL = "http://api.openweathermap.org/data/2.5/weather" | |
| def get_weather_data(city): | |
| """Fetches weather data for a given city from OpenWeatherMap.""" | |
| if OPENWEATHER_API_KEY == "YOUR_OPENWEATHER_API_KEY": | |
| st.warning("Please replace 'YOUR_OPENWEATHER_API_KEY' with your actual OpenWeatherMap API key to fetch weather data.") | |
| return None | |
| params = { | |
| "q": city, | |
| "appid": OPENWEATHER_API_KEY, | |
| "units": "metric" # Get temperature in Celsius | |
| } | |
| try: | |
| response = requests.get(WEATHER_API_URL, params=params, timeout=10) | |
| response.raise_for_status() # Raise an HTTPError for bad responses (4xx or 5xx) | |
| data = response.json() | |
| return data | |
| except requests.exceptions.RequestException as e: | |
| st.error(f"Error fetching weather data for {city}: {e}. Please check the city name or your internet connection.") | |
| return None | |
| except json.JSONDecodeError: | |
| st.error(f"Error decoding weather data for {city}. Unexpected response from API.") | |
| return None | |
| except Exception as e: | |
| st.error(f"An unexpected error occurred while fetching weather data: {e}") | |
| return None | |
| # --- Authentication UI --- | |
| # Initialize session state for login status | |
| if "logged_in" not in st.session_state: | |
| st.session_state.logged_in = False | |
| if "user" not in st.session_state: | |
| st.session_state.user = None | |
| if not st.session_state.logged_in: | |
| st.title("🔐 Login to BharatPulse") | |
| tab1, tab2 = st.tabs(["🔑 Login", "📝 Register"]) | |
| with tab1: | |
| st.subheader("Existing User Login") | |
| email = st.text_input("Email", key="login_email") | |
| password = st.text_input("Password", type="password", key="login_password") | |
| if st.button("Login", key="login_button"): | |
| success, user_or_msg = login_user(email, password) | |
| if success: | |
| st.session_state.logged_in = True | |
| st.session_state.user = user_or_msg | |
| st.success(f"Welcome, {st.session_state.user['username']}!") | |
| st.rerun() # Rerun to switch to the main app view | |
| else: | |
| st.error(user_or_msg) # This error message now includes the suggestion to register | |
| with tab2: | |
| st.subheader("New User Registration") | |
| new_username = st.text_input("Username", key="register_username") | |
| new_email = st.text_input("New Email", key="register_email") | |
| new_password = st.text_input("New Password", type="password", key="register_password") | |
| new_location = st.text_input("Location (e.g., Hyderabad)", key="register_location") | |
| if st.button("Register", key="register_button"): | |
| if new_username and new_email and new_password and new_location: | |
| success, message = register_user(new_username, new_email, new_password, new_location) | |
| if success: | |
| st.success(message) | |
| else: | |
| st.error(message) | |
| else: | |
| st.warning("Please fill in all registration fields.") | |
| st.stop() # Stop execution if not logged in | |
| # --- Main News UI (only runs if logged in) --- | |
| st.sidebar.header("User Info") | |
| st.sidebar.success(f"Logged in as: {st.session_state.user['username']}") | |
| st.sidebar.info(f"Your registered location: {st.session_state.user['location']}") | |
| if st.sidebar.button("🚪 Logout"): | |
| st.session_state.logged_in = False | |
| st.session_state.user = None | |
| st.rerun() # Rerun to go back to login screen | |
| st.title("📍 BharatPulse - Local News & Sentiment") | |
| # Define RSS feeds | |
| # You can expand this dictionary with more RSS feeds for different regions/newspapers | |
| feeds = { | |
| "Sakshi - Medak": "https://www.sakshi.com/rss/district-news/medak.xml", | |
| "Eenadu - AP": "https://www.eenadu.net/rss/andhra-pradesh.xml", | |
| # Add more feeds here, e.g., "Eenadu - Telangana": "https://www.eenadu.net/rss/telangana.xml" | |
| } | |
| # Added "☁️ Weather" tab | |
| tabs = st.tabs(["📰 Headlines", "🔍 Search by City", "☁️ Weather"]) | |
| # --- 📰 Headlines Tab --- | |
| with tabs[0]: | |
| st.header("📰 Latest Local Telugu News Headlines") | |
| for name, url in feeds.items(): | |
| st.subheader(name) | |
| try: | |
| feed = feedparser.parse(url) | |
| if not feed.entries: | |
| st.info(f"No entries found for {name}.") | |
| continue | |
| for entry in feed.entries[:3]: # Display top 3 articles per feed | |
| st.markdown(f"### [{entry.title}]({entry.link})") # Make title a clickable link | |
| st.write(f"Published: {entry.published}") | |
| # Extract and summarize article content | |
| summary = extract_article_content(entry.link) | |
| st.markdown(f"**English Summary:** {summary}") | |
| # Translate summary to Telugu | |
| telugu_summary = translate_to_telugu(summary) | |
| st.markdown(f"**తెలుగు అనువాదం:** {telugu_summary}") | |
| st.markdown("---") | |
| except Exception as e: | |
| st.error(f"Could not fetch news from {name}: {e}") | |
| # --- 🔍 Search by City Tab --- | |
| with tabs[1]: | |
| st.header("🔎 Search News by City / District") | |
| # Pre-fill with user's registered location if available | |
| default_city = st.session_state.user['location'] if st.session_state.user else "" | |
| city_search_input = st.text_input("Enter your city/district name (e.g., Medak, Hyderabad)", value=default_city) | |
| if city_search_input: | |
| st.success(f"Searching news for: **{city_search_input}**") | |
| found_news = False | |
| for name, url in feeds.items(): | |
| # Basic matching: checks if the city name is in the feed's name (case-insensitive) | |
| if city_search_input.lower() in name.lower(): | |
| st.subheader(f"News from {name}") | |
| try: | |
| feed = feedparser.parse(url) | |
| if not feed.entries: | |
| st.info(f"No entries found for {name} matching '{city_search_input}'.") | |
| continue | |
| for entry in feed.entries[:2]: # Display top 2 articles per matching feed | |
| st.markdown(f"**🗞️ [{entry.title}]({entry.link})**") | |
| summary = extract_article_content(entry.link) | |
| telugu_summary = translate_to_telugu(summary) | |
| st.markdown(f"**తెలుగు అనువాదం:** {telugu_summary}") | |
| st.markdown("---") | |
| found_news = True | |
| except Exception as e: | |
| st.error(f"Could not fetch news from {name}: {e}") | |
| if not found_news: | |
| st.info(f"No news found for '{city_search_input}' in the available feeds. Try a different city or check our 'Headlines' tab.") | |
| # --- ☁️ Weather Tab --- | |
| with tabs[2]: | |
| st.header("☁️ Weather Conditions") | |
| # Pre-fill with user's registered location if available | |
| default_weather_city = st.session_state.user['location'] if st.session_state.user else "" | |
| weather_city_input = st.text_input("Enter city name for weather (e.g., Hyderabad, Delhi)", value=default_weather_city, key="weather_city_input") | |
| if st.button("Get Weather", key="get_weather_button"): | |
| if weather_city_input: | |
| with st.spinner(f"Fetching weather for {weather_city_input}..."): | |
| weather_data = get_weather_data(weather_city_input) | |
| if weather_data: | |
| st.subheader(f"Current Weather in {weather_data['name']}, {weather_data['sys']['country']}") | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.metric("Temperature", f"{weather_data['main']['temp']:.1f}°C") | |
| with col2: | |
| st.metric("Feels Like", f"{weather_data['main']['feels_like']:.1f}°C") | |
| with col3: | |
| st.metric("Humidity", f"{weather_data['main']['humidity']}%") | |
| st.write(f"**Description:** {weather_data['weather'][0]['description'].capitalize()}") | |
| st.write(f"**Wind Speed:** {weather_data['wind']['speed']} m/s") | |
| st.write(f"**Pressure:** {weather_data['main']['pressure']} hPa") | |
| else: | |
| st.info("Could not retrieve weather data. Please check the city name or your API key.") | |
| else: | |
| st.warning("Please enter a city name to get weather information.") | |