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 --- @st.cache_resource # 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.")