import streamlit as st
import requests
import pandas as pd
import json
import os
import matplotlib.pyplot as plt
import seaborn as sns
import base64
from io import BytesIO
from PIL import Image, ImageEnhance
import time
import threading
import subprocess
from typing import Dict, Any, List
# Configure page settings for Hugging Face Spaces
st.set_page_config(
page_title="News Summarization & TTS",
page_icon="📰",
layout="wide",
initial_sidebar_state="expanded"
)
# Start the API in the background
def start_api():
process = subprocess.Popen(["python", "api.py"])
print(f"Started API server with PID {process.pid}")
return process
# Check if the API is already running, if not start it
@st.cache_resource
def ensure_api_running():
try:
# Try to connect to the API
response = requests.get("http://localhost:8000/docs", timeout=2)
if response.status_code == 200:
st.sidebar.success("✅ API server is running")
print("API already running")
return True
except Exception as e:
print(f"API not running: {str(e)}")
pass
# API not running, start it
print("Starting API server...")
st.sidebar.info("Starting API server...")
# Start API in a separate thread
api_process = start_api()
# Wait for API to start
api_started = False
retries = 0
max_retries = 15
while not api_started and retries < max_retries:
try:
time.sleep(2)
response = requests.get("http://localhost:8000/docs", timeout=2)
if response.status_code == 200:
api_started = True
st.sidebar.success("✅ API server is running")
print("API server started successfully")
return True
except:
retries += 1
print(f"Waiting for API to start... (attempt {retries}/{max_retries})")
if not api_started:
st.sidebar.error("❌ Failed to start API server")
print("Failed to start API server")
return False
# API Base URL for Spaces deployment
API_BASE_URL = "http://localhost:8000"
# Function to create plot for sentiment distribution
def plot_sentiment_distribution(sentiment_data):
# Extract and combine sentiment categories
categories = []
counts = []
# Process all sentiment categories
for category, count in sentiment_data.items():
if count > 0: # Only include non-zero categories
categories.append(category)
counts.append(count)
# Create a DataFrame for easier plotting
df = pd.DataFrame({
'Sentiment': categories,
'Count': counts
})
# Set up colors based on sentiment
colors = []
for sentiment in df['Sentiment']:
if sentiment == 'Positive' or sentiment == 'Slightly Positive':
colors.append('#10B981') # Green
elif sentiment == 'Negative' or sentiment == 'Slightly Negative':
colors.append('#EF4444') # Red
else:
colors.append('#6B7280') # Gray
# Create matplotlib figure
fig, ax = plt.subplots(figsize=(6, 4))
bars = ax.bar(df['Sentiment'], df['Count'], color=colors)
# Add count labels on top of bars
for bar in bars:
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2., height + 0.1,
str(int(height)), ha='center', va='bottom')
# Add labels and title
ax.set_xlabel('Sentiment')
ax.set_ylabel('Number of Articles')
ax.set_title('Sentiment Distribution')
# Improve aesthetics
plt.xticks(rotation=45)
plt.tight_layout()
return fig
# Function to create word cloud
def display_word_cloud(topics):
from wordcloud import WordCloud
# Convert topics to text with frequency
text = " ".join(topics)
# Generate word cloud
wordcloud = WordCloud(
width=400,
height=200,
background_color='white',
colormap='viridis',
max_words=100,
contour_width=1
).generate(text)
# Display word cloud
fig, ax = plt.subplots(figsize=(10, 5))
ax.imshow(wordcloud, interpolation='bilinear')
ax.axis('off')
return fig
# Function to generate the example output format
def generate_example_output(company_name: str) -> str:
"""
Generate output in the example format for the given company.
Returns the formatted JSON as a string.
"""
try:
# Make API request to get the analysis data
url = f"{API_BASE_URL}/api/complete_analysis"
response = requests.post(url, json={"company_name": company_name})
response.raise_for_status()
data = response.json()
# Format the data to match the example output format exactly
formatted_output = {
"Company": data["Company"],
"Articles": data["Articles"],
"Comparative Sentiment Score": {
"Sentiment Distribution": data["Comparative Sentiment Score"]["Sentiment Distribution"],
"Coverage Differences": data["Comparative Sentiment Score"]["Coverage Differences"],
"Topic Overlap": data["Comparative Sentiment Score"]["Topic Overlap"]
},
"Final Sentiment Analysis": data["Final Sentiment Analysis"],
"Audio": "[Play Hindi Speech]" if data.get("Audio") else "No audio available"
}
# Convert to JSON string with proper formatting
return json.dumps(formatted_output, indent=2)
except Exception as e:
return json.dumps({
"error": str(e),
"message": "Failed to generate example output"
}, indent=2)
# Custom CSS for better styling
st.markdown("""
""", unsafe_allow_html=True)
# App header
st.markdown("
📰 News Summarization & Text-to-Speech
", unsafe_allow_html=True)
st.markdown("This application extracts news articles about a company, performs sentiment analysis, conducts comparative analysis, and generates a text-to-speech output in Hindi. Enter a company name to get started.", unsafe_allow_html=True)
# Start the API server when the app loads
api_running = ensure_api_running()
# Sidebar
st.sidebar.markdown("## Input Settings")
company_name = st.sidebar.text_input("Company Name", value="Tesla")
# Audio playback settings
st.sidebar.markdown("## Audio Settings")
audio_speed = st.sidebar.select_slider("TTS Speech Speed:", options=["Slow", "Normal", "Fast"], value="Normal")
st.sidebar.markdown("---")
# Add option to see JSON in example format
st.sidebar.markdown("## Developer Options")
show_json = st.sidebar.checkbox("Show JSON output in example format")
st.sidebar.markdown("---")
# About section
st.sidebar.markdown("## About")
st.sidebar.info("This application was developed for news analysis and translation. It uses web scraping, NLP, and TTS technologies to provide insights about companies.")
# Analyze button
analyze_button = st.sidebar.button("Analyze Company News", disabled=not api_running)
# Main content
if analyze_button and company_name and api_running:
with st.spinner(f"Analyzing news for {company_name}. This may take a moment..."):
try:
# Call the API to get results (with longer timeout for Spaces)
response = requests.post(f"{API_BASE_URL}/api/complete_analysis",
json={"company_name": company_name},
timeout=180) # 3 minutes timeout
response.raise_for_status() # Raise exception for HTTP errors
# Parse JSON response
response = response.json()
# Display results
st.markdown(f"", unsafe_allow_html=True)
# Display sentiment overview
st.markdown("", unsafe_allow_html=True)
# Get sentiment counts
sentiment_data = response["Comparative Sentiment Score"]["Sentiment Distribution"]
# Create columns for visualization
col1, col2 = st.columns([3, 2])
with col1:
# Extract total counts
positive_count = sentiment_data.get("Positive", 0) + sentiment_data.get("Slightly Positive", 0)
negative_count = sentiment_data.get("Negative", 0) + sentiment_data.get("Slightly Negative", 0)
neutral_count = sentiment_data.get("Neutral", 0)
total_count = positive_count + negative_count + neutral_count
# Show summary text
sentiment_text = f"The company has "
if positive_count > negative_count and positive_count > neutral_count:
sentiment_text += f"mostly positive coverage ({positive_count}/{total_count} positive, {negative_count}/{total_count} negative, {neutral_count}/{total_count} neutral)."
elif negative_count > positive_count and negative_count > neutral_count:
sentiment_text += f"mostly negative coverage ({positive_count}/{total_count} positive, {negative_count}/{total_count} negative, {neutral_count}/{total_count} neutral)."
else:
sentiment_text += f"balanced coverage ({positive_count}/{total_count} positive, {negative_count}/{total_count} negative, {neutral_count}/{total_count} neutral)."
st.write(sentiment_text)
# Plot sentiment distribution
try:
fig = plot_sentiment_distribution(sentiment_data)
st.pyplot(fig)
except Exception as e:
st.warning(f"Could not create sentiment chart: {str(e)}")
with col2:
# Summary of key points
st.markdown("Key Insights
", unsafe_allow_html=True)
# Show final sentiment analysis
st.write(response["Final Sentiment Analysis"])
# Show common topics
common_topics = response["Comparative Sentiment Score"]["Topic Overlap"].get("Common Topics", [])
if common_topics:
st.markdown("Common Topics
", unsafe_allow_html=True)
for topic in common_topics:
st.markdown(f"{topic}", unsafe_allow_html=True)
# Display Hindi TTS audio
if "Audio" in response and response["Audio"]:
st.markdown("", unsafe_allow_html=True)
audio_message = response["Audio"]
if audio_message == "Failed to generate audio":
st.warning("Hindi audio could not be generated. However, you can still read the Hindi text below.")
else:
try:
# Check if the response contains the actual audio file path
audio_file_path = response.get("_audio_file_path")
if audio_file_path:
# Extract the filename
audio_filename = os.path.basename(audio_file_path)
audio_url = f"{API_BASE_URL}/api/audio/{audio_filename}"
else:
# If no path is provided, just display a message
st.info("Audio is available but the path was not provided.")
audio_url = None
if audio_url:
# Attempt to download the audio file
audio_response = requests.get(audio_url)
if audio_response.status_code == 200:
# Save temporarily
temp_audio_path = f"temp_audio_{os.path.basename(audio_url)}"
with open(temp_audio_path, "wb") as f:
f.write(audio_response.content)
# Play from local file
st.markdown("", unsafe_allow_html=True)
st.audio(temp_audio_path, format="audio/mp3")
# Display audio download link
st.markdown(f"
Download Hindi Audio", unsafe_allow_html=True)
# Clean up temp file (optional)
# os.remove(temp_audio_path) # Uncomment to delete after use
else:
st.warning(f"Unable to load audio file (HTTP {audio_response.status_code}). You can still read the Hindi text below.")
else:
st.info("Hindi audio summary would be available here.")
except Exception as e:
st.warning(f"Error playing audio: {str(e)}. You can still read the Hindi text below.")
# Display the Hindi text with better formatting
with st.expander("Show Hindi Text"):
hindi_text = response.get("Hindi Summary", "Hindi text not available.")
# Format the text for better readability
paragraphs = hindi_text.split("। ")
for paragraph in paragraphs:
if paragraph.strip():
# Add a period if it doesn't end with one
if not paragraph.strip().endswith("।"):
paragraph += "।"
st.markdown(f"
{paragraph}
", unsafe_allow_html=True)
st.markdown("
", unsafe_allow_html=True)
st.markdown("", unsafe_allow_html=True)
# Display news articles
st.markdown("", unsafe_allow_html=True)
# Show each article in a card
articles = response["Articles"]
for i, article in enumerate(articles):
with st.container():
st.markdown(f"", unsafe_allow_html=True)
# Article title and sentiment
sentiment_class = "sentiment-neutral"
if article["Sentiment"] == "Positive" or article["Sentiment"] == "Slightly Positive":
sentiment_class = "sentiment-positive"
elif article["Sentiment"] == "Negative" or article["Sentiment"] == "Slightly Negative":
sentiment_class = "sentiment-negative"
st.markdown(f"
{i+1}. {article['Title']}
", unsafe_allow_html=True)
st.markdown(f"{article['Sentiment']}", unsafe_allow_html=True)
# Article summary
st.write(article["Summary"])
# Article topics
for topic in article["Topics"]:
st.markdown(f"{topic}", unsafe_allow_html=True)
st.markdown("", unsafe_allow_html=True)
# Display comparative analysis
st.markdown("", unsafe_allow_html=True)
# Common topics
st.markdown("Common Topics
", unsafe_allow_html=True)
common_topics = response["Comparative Sentiment Score"]["Topic Overlap"].get("Common Topics", [])
if common_topics:
for topic in common_topics:
st.markdown(f"{topic}", unsafe_allow_html=True)
else:
st.write("No common topics found across articles.")
# Coverage comparison
st.markdown("Coverage Comparison
", unsafe_allow_html=True)
comparisons = response["Comparative Sentiment Score"].get("Coverage Differences", [])
if comparisons:
# Show first comparison inline
first_comparison = comparisons[0]
st.write(first_comparison.get("Comparison", ""))
st.markdown(f"{first_comparison.get('Impact', '')}
", unsafe_allow_html=True)
else:
st.write("No comparative insights available.")
# Display full comparison in expander
with st.expander("View All Comparisons"):
comparisons = response["Comparative Sentiment Score"].get("Coverage Differences", [])
for i, comparison in enumerate(comparisons):
st.markdown(f"{i+1}. {comparison.get('Comparison', '')}
", unsafe_allow_html=True)
st.markdown(f"{comparison.get('Impact', '')}
", unsafe_allow_html=True)
st.markdown("
", unsafe_allow_html=True)
# Show JSON in example format if requested
if show_json:
st.markdown("", unsafe_allow_html=True)
st.markdown("", unsafe_allow_html=True)
# Get the formatted JSON
json_output = generate_example_output(company_name)
# Display the JSON in a code block
st.code(json_output, language="json")
except requests.exceptions.HTTPError as http_err:
if http_err.response.status_code == 404:
st.error(f"No news articles found for {company_name}. Please try another company name.")
elif http_err.response.status_code == 500:
error_detail = "Unknown server error"
try:
error_data = http_err.response.json()
if "detail" in error_data:
error_detail = error_data["detail"]
except:
pass
st.error(f"Server error: {error_detail}")
else:
st.error(f"HTTP error occurred: {http_err}")
except requests.exceptions.ConnectionError:
st.error("Failed to connect to the server. Please make sure the API is running.")
except requests.exceptions.Timeout:
st.error("Request timed out. The analysis might be taking too long to complete.")
except Exception as e:
st.error(f"An error occurred: {str(e)}")
elif analyze_button and not api_running:
st.error("Cannot perform analysis because the API server is not running. Please check the logs.")
else:
# Display placeholder
st.info("Enter a company name and click 'Analyze Company News' to get started.")
# Example of what the application does
with st.expander("See Example Analysis"):
st.write("""
This application will provide:
1. Sentiment analysis of news articles about the company
2. Key topics mentioned in the articles
3. Comparative analysis of different articles
4. Hindi audio summary of the findings
Companies you can try: Apple, Microsoft, Google, Amazon, Tesla, etc.
""")