TTS / app.py
Subham629's picture
Update app.py
618fb05 verified
import streamlit as st
import pandas as pd
import requests
import json
import base64
import plotly.express as px
import plotly.graph_objects as go
import os
from io import BytesIO
from datetime import datetime
# Set page configuration
st.set_page_config(
page_title="News Summarization & Analysis",
page_icon="πŸ“°",
layout="wide",
initial_sidebar_state="expanded"
)
import logging
from flask import Flask
logging.basicConfig(level=logging.INFO)
app = Flask(__name__)
@app.route('/')
def home():
logging.info("Home endpoint was reached.")
return "Hello, World!"
if __name__ == '__main__':
logging.info("Starting the Flask application.")
app.run(host='0.0.0.0', port=7860)
# API endpoint (Flask backend)
API_URL = "http://0.0.0.0:7860"
def get_company_news(company_name):
"""Fetch news articles for a given company via API"""
try:
response = requests.get(f"{API_URL}/news/{company_name}")
if response.status_code == 200:
return response.json()
else:
st.error(f"Error fetching news: {response.text}")
return None
except Exception as e:
st.error(f"API connection error: {str(e)}")
return None
def get_analysis(company_name, articles):
"""Get sentiment analysis and comparative analysis via API"""
try:
response = requests.post(
f"{API_URL}/analyze",
json={
"company": company_name,
"articles": articles
}
)
if response.status_code == 200:
return response.json()
else:
st.error(f"Error analyzing content: {response.text}")
return None
except Exception as e:
st.error(f"API connection error: {str(e)}")
return None
def get_tts(text, language='hi'):
"""Get TTS audio in the specified language via API"""
try:
response = requests.post(
f"{API_URL}/tts",
json={
"text": text,
"language": language
}
)
if response.status_code == 200:
return response.content, language
else:
st.error(f"Error generating speech: {response.text}")
return None, language
except Exception as e:
st.error(f"API connection error: {str(e)}")
return None, language
def create_audio_player(audio_bytes):
"""Create an HTML audio player for the TTS audio"""
audio_base64 = base64.b64encode(audio_bytes).decode()
audio_html = f"""
<audio controls>
<source src="data:audio/mp3;base64,{audio_base64}" type="audio/mp3">
Your browser does not support the audio element.
</audio>
"""
return audio_html
def display_article_details(articles):
"""Display detailed information about each article in a card layout"""
st.markdown("""
<style>
.article-card {
background-color: #f9f9f9;
border-radius: 10px;
padding: 20px;
margin-bottom: 20px;
border-left: 5px solid #4CAF50;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.article-negative {
border-left: 5px solid #F44336;
}
.article-neutral {
border-left: 5px solid #9E9E9E;
}
.article-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
}
.article-meta {
color: #666;
font-size: 14px;
margin-bottom: 10px;
}
.article-summary {
margin-bottom: 15px;
}
.article-sentiment {
display: inline-block;
padding: 5px 10px;
border-radius: 20px;
font-size: 14px;
margin-right: 10px;
}
.sentiment-positive {
background-color: rgba(76, 175, 80, 0.2);
color: #2E7D32;
}
.sentiment-negative {
background-color: rgba(244, 67, 54, 0.2);
color: #C62828;
}
.sentiment-neutral {
background-color: rgba(158, 158, 158, 0.2);
color: #616161;
}
.topic-tag {
display: inline-block;
background-color: #E0E0E0;
padding: 3px 10px;
border-radius: 15px;
margin-right: 8px;
margin-bottom: 8px;
font-size: 12px;
}
</style>
""", unsafe_allow_html=True)
cols = st.columns(1)
for i, article in enumerate(articles):
sentiment = article['Sentiment']
sentiment_class = ""
tag_class = ""
if sentiment == "Positive":
sentiment_class = "article-positive"
tag_class = "sentiment-positive"
elif sentiment == "Negative":
sentiment_class = "article-negative"
tag_class = "sentiment-negative"
else:
sentiment_class = "article-neutral"
tag_class = "sentiment-neutral"
article_html = f"""
<div class="article-card {sentiment_class}">
<div class="article-title">{article['Title']}</div>
<div class="article-meta">
Source: {article.get('Source', 'Unknown')} |
Date: {article.get('Date', 'N/A')}
</div>
<div class="article-summary">{article['Summary']}</div>
<div class="article-sentiment {tag_class}">{sentiment}</div>
"""
# Add topics as tags
if 'Topics' in article and article['Topics']:
article_html += '<div class="article-topics">'
for topic in article['Topics']:
article_html += f'<span class="topic-tag">{topic}</span>'
article_html += '</div>'
article_html += f"""
<div style="margin-top: 10px;">
<a href="{article.get('URL', '#')}" target="_blank" style="text-decoration: none;">
<span style="color: #1E88E5;">Read original article β†’</span>
</a>
</div>
</div>
"""
cols[0].markdown(article_html, unsafe_allow_html=True)
def display_sentiment_distribution(analysis):
"""Display sentiment distribution chart with enhanced styling"""
if 'Comparative Sentiment Score' in analysis and 'Sentiment Distribution' in analysis['Comparative Sentiment Score']:
dist = analysis['Comparative Sentiment Score']['Sentiment Distribution']
data = {
'Sentiment': list(dist.keys()),
'Count': list(dist.values())
}
df = pd.DataFrame(data)
# Create color map
color_map = {
'Positive': '#4CAF50',
'Negative': '#F44336',
'Neutral': '#9E9E9E'
}
# Create a card container for the chart
st.markdown("""
<div style="background-color:white; padding:20px; border-radius:10px; box-shadow:0 4px 6px rgba(0,0,0,0.1); margin-bottom:20px;">
<h3 style="margin-bottom:15px; border-bottom:1px solid #eee; padding-bottom:10px;">Sentiment Distribution</h3>
</div>
""", unsafe_allow_html=True)
# Create pie chart for sentiment distribution
labels = list(dist.keys())
values = list(dist.values())
colors = [color_map[label] for label in labels]
# Create two columns for different chart types
col1, col2 = st.columns(2)
with col1:
# Bar chart
fig_bar = px.bar(
df,
x='Sentiment',
y='Count',
color='Sentiment',
color_discrete_map=color_map,
title="Sentiment Distribution (Bar Chart)"
)
fig_bar.update_layout(
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)',
font=dict(size=14),
margin=dict(l=20, r=20, t=40, b=20),
height=350
)
st.plotly_chart(fig_bar, use_container_width=True)
with col2:
# Pie chart
fig_pie = go.Figure(data=[go.Pie(
labels=labels,
values=values,
marker=dict(colors=colors),
textinfo='percent+label',
hole=.4
)])
fig_pie.update_layout(
title_text="Sentiment Distribution (Pie Chart)",
annotations=[dict(text='Sentiment', x=0.5, y=0.5, font_size=14, showarrow=False)],
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)',
font=dict(size=14),
margin=dict(l=20, r=20, t=40, b=20),
height=350
)
st.plotly_chart(fig_pie, use_container_width=True)
# Add a summary of the sentiment distribution
total = sum(values)
if total > 0:
percentages = {label: (count/total*100) for label, count in zip(labels, values)}
# Create a summary card
summary_html = """
<div style="background-color:#f8f9fa; padding:15px; border-radius:8px; margin-top:10px;">
<h4 style="margin-bottom:10px;">Summary</h4>
<p style="font-size:15px; line-height:1.5;">
"""
for label in labels:
if label in percentages:
color = color_map[label]
summary_html += f'<span style="color:{color}; font-weight:bold;">{label}</span>: {percentages[label]:.1f}% | '
summary_html = summary_html.rstrip(' | ') + '</p></div>'
st.markdown(summary_html, unsafe_allow_html=True)
def display_topic_analysis(analysis):
"""Display topic analysis visualization"""
if 'Comparative Sentiment Score' in analysis and 'Topic Overlap' in analysis['Comparative Sentiment Score']:
topic_data = analysis['Comparative Sentiment Score']['Topic Overlap']
# Prepare data for visualization
all_topics = set()
if 'Common Topics' in topic_data:
all_topics.update(topic_data['Common Topics'])
for i in range(1, 11): # Check for unique topics in each article
key = f"Unique Topics in Article {i}"
if key in topic_data and topic_data[key]:
all_topics.update(topic_data[key])
# Count topic occurrences across articles
topic_counts = {}
for topic in all_topics:
count = 0
if 'Common Topics' in topic_data and topic in topic_data['Common Topics']:
count += len(analysis['Articles']) # All articles have common topics
for i in range(1, 11):
key = f"Unique Topics in Article {i}"
if key in topic_data and topic in topic_data[key]:
count += 1
topic_counts[topic] = count
# Create DataFrame and visualization
topic_df = pd.DataFrame({
'Topic': list(topic_counts.keys()),
'Occurrence': list(topic_counts.values())
}).sort_values('Occurrence', ascending=False)
fig = px.bar(
topic_df,
x='Topic',
y='Occurrence',
title="Topic Distribution Across Articles",
color='Occurrence',
color_continuous_scale=px.colors.sequential.Viridis
)
st.plotly_chart(fig, use_container_width=True)
def display_comparative_analysis(analysis):
"""Display comparative analysis details"""
if 'Comparative Sentiment Score' in analysis and 'Coverage Differences' in analysis['Comparative Sentiment Score']:
differences = analysis['Comparative Sentiment Score']['Coverage Differences']
st.subheader("Comparative Analysis")
for i, diff in enumerate(differences):
with st.expander(f"Comparison {i+1}"):
st.write(f"**Comparison**: {diff['Comparison']}")
st.write(f"**Impact**: {diff['Impact']}")
# Main app layout with enhanced design and better readability
st.markdown("""
<style>
.main-header {
text-align: center;
padding: 2rem 0;
background: linear-gradient(to right, #2E7D32, #1565C0);
color: white;
border-radius: 10px;
margin-bottom: 30px;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
}
.app-title {
font-size: 36px;
font-weight: bold;
text-shadow: 1px 1px 3px rgba(0,0,0,0.3);
margin-bottom: 15px;
}
.app-description {
font-size: 18px;
color: white;
max-width: 800px;
margin: 0 auto;
line-height: 1.6;
text-shadow: 0px 1px 2px rgba(0,0,0,0.2);
}
.benefits-container {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 20px;
flex-wrap: wrap;
}
.benefit-item {
background-color: rgba(255,255,255,0.25);
padding: 8px 15px;
border-radius: 20px;
font-size: 14px;
font-weight: 500;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
text-shadow: 0px 1px 1px rgba(0,0,0,0.1);
}
</style>
<div class="main-header">
<div class="app-title">πŸ“° News Summarization & Sentiment Analysis</div>
<p class="app-description">
Analyze recent news articles about any company. Get sentiment analysis, topic extraction,
and multilingual text-to-speech summaries instantly in 10 different languages.
</p>
<div class="benefits-container">
<div class="benefit-item">βœ… Real-time News Analysis</div>
<div class="benefit-item">πŸ“Š Sentiment Visualization</div>
<div class="benefit-item">πŸ” Topic Extraction</div>
<div class="benefit-item">🎧 Multilingual Text-to-Speech</div>
</div>
</div>
""", unsafe_allow_html=True)
# Input form with enhanced styling
st.markdown("""
<style>
.search-container {
background-color: white;
padding: 25px;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
margin-bottom: 30px;
}
.search-title {
font-size: 20px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.search-description {
color: #666;
margin-bottom: 20px;
font-size: 16px;
}
</style>
<div class="search-container">
<div class="search-title">πŸ” Search for Company News</div>
<div class="search-description">
Enter a company name below to analyze its recent news coverage.
Try companies like Tesla, Apple, Microsoft, Google, or Amazon.
</div>
</div>
""", unsafe_allow_html=True)
with st.form("search_form"):
col1, col2 = st.columns([3, 1])
with col1:
company_name = st.text_input("Company Name", placeholder="Enter company name (e.g., Tesla)", label_visibility="collapsed")
with col2:
submit_button = st.form_submit_button("πŸ” Analyze News")
# Add some example buttons below the form
st.markdown("""
<style>
.example-row {
display: flex;
gap: 10px;
margin-top: 10px;
flex-wrap: wrap;
justify-content: center;
}
.example-chip {
background-color: #f0f2f6;
border-radius: 20px;
padding: 5px 15px;
font-size: 12px;
cursor: pointer;
transition: all 0.2s;
}
.example-chip:hover {
background-color: #4CAF50;
color: white;
}
</style>
<div style="text-align: center; margin-top: 10px; font-size: 12px; color: #666;">
Try analyzing news for:
<div class="example-row">
<div class="example-chip">Tesla</div>
<div class="example-chip">Apple</div>
<div class="example-chip">Microsoft</div>
<div class="example-chip">Google</div>
<div class="example-chip">Amazon</div>
</div>
</div>
""", unsafe_allow_html=True)
# Process form submission
if submit_button and company_name:
with st.spinner(f"Fetching news articles about {company_name}..."):
articles_data = get_company_news(company_name)
if articles_data and 'articles' in articles_data and len(articles_data['articles']) > 0:
articles = articles_data['articles']
with st.spinner("Performing sentiment analysis..."):
analysis_result = get_analysis(company_name, articles)
if analysis_result:
# Store complete analysis in session state
st.session_state.analysis = analysis_result
# Display summary and stats
st.header(f"Analysis Results for {company_name}")
# Create a nice header with company logo or icon
company_icon = "🏒" # Default company icon
if company_name.lower() == "tesla":
company_icon = "πŸš—"
elif company_name.lower() == "apple":
company_icon = "🍎"
elif company_name.lower() == "microsoft":
company_icon = "πŸ’»"
elif company_name.lower() == "amazon":
company_icon = "πŸ“¦"
elif company_name.lower() == "google":
company_icon = "πŸ”"
st.markdown(f"""
<div style="background-color:#f0f2f6; padding:20px; border-radius:10px; margin-bottom:20px;">
<h1 style="text-align:center; margin-bottom:20px;">{company_icon} {company_name} News Analysis</h1>
<p style="text-align:center; font-size:16px; color:#666;">
Analysis of {len(analysis_result['Articles'])} news articles | Generated on {datetime.now().strftime('%B %d, %Y')}
</p>
</div>
""", unsafe_allow_html=True)
# Display visualization tabs with custom styling
st.markdown("""
<style>
.stTabs [data-baseweb="tab-list"] {
gap: 8px;
}
.stTabs [data-baseweb="tab"] {
border-radius: 4px 4px 0px 0px;
padding: 10px 16px;
background-color: #f0f2f6;
}
.stTabs [aria-selected="true"] {
background-color: #4CAF50 !important;
color: white !important;
}
</style>
""", unsafe_allow_html=True)
tab1, tab2, tab3, tab4 = st.tabs(["πŸ“Š Overview", "😊 Sentiment Analysis", "πŸ” Topic Analysis", "πŸ“° Article Details"])
with tab1:
# Create a card-style container for the summary
st.markdown("""
<div style="background-color:white; padding:25px; border-radius:10px; box-shadow:0 4px 6px rgba(0,0,0,0.1); margin-bottom:20px;">
<h3 style="margin-bottom:15px; border-bottom:1px solid #eee; padding-bottom:10px;">Executive Summary</h3>
""", unsafe_allow_html=True)
summary_text = analysis_result.get("Final Sentiment Analysis", "No summary available")
st.markdown(f"<p style='font-size:16px; line-height:1.6;'>{summary_text}</p>", unsafe_allow_html=True)
st.markdown("</div>", unsafe_allow_html=True)
if "Final Sentiment Analysis" in analysis_result:
# Language selection for TTS
language_options = {
"hi": "Hindi",
"en": "English",
"es": "Spanish",
"fr": "French",
"de": "German",
"ja": "Japanese",
"zh-CN": "Chinese",
"ru": "Russian",
"ar": "Arabic",
"it": "Italian"
}
selected_language = st.selectbox(
"Select Language for Text-to-Speech",
options=list(language_options.keys()),
format_func=lambda x: language_options[x],
index=0 # Default to Hindi
)
with st.spinner(f"Generating {language_options[selected_language]} text-to-speech..."):
audio_bytes, language = get_tts(analysis_result["Final Sentiment Analysis"], selected_language)
if audio_bytes:
st.markdown(f"""
<div style="background-color:white; padding:25px; border-radius:10px; box-shadow:0 4px 6px rgba(0,0,0,0.1);">
<h3 style="margin-bottom:15px; border-bottom:1px solid #eee; padding-bottom:10px;">
<span style="vertical-align:middle;">πŸ”Š</span> {language_options[language]} Text-to-Speech Summary
</h3>
""", unsafe_allow_html=True)
st.markdown(create_audio_player(audio_bytes), unsafe_allow_html=True)
st.markdown("</div>", unsafe_allow_html=True)
with tab2:
st.subheader("Sentiment Distribution")
display_sentiment_distribution(analysis_result)
display_comparative_analysis(analysis_result)
with tab3:
st.subheader("Topic Analysis")
display_topic_analysis(analysis_result)
with tab4:
st.subheader("Article Details")
display_article_details(analysis_result['Articles'])
# Display JSON output option
st.subheader("Raw JSON Output")
with st.expander("Show JSON"):
st.json(analysis_result)
else:
st.error("Failed to perform analysis. Please try again.")
else:
st.warning(f"No news articles found for {company_name}. Please try another company name.")
# Footer with enhanced design and improved readability
st.markdown("""
<style>
.footer {
background: linear-gradient(to right, #2E7D32, #1565C0);
padding: 30px 10px;
color: white;
border-radius: 10px;
text-align: center;
margin-top: 40px;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
}
.footer-content {
max-width: 800px;
margin: 0 auto;
}
.footer-title {
font-size: 22px;
margin-bottom: 15px;
font-weight: bold;
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
}
.footer-text {
font-size: 15px;
line-height: 1.6;
margin-bottom: 15px;
text-shadow: 0px 1px 1px rgba(0,0,0,0.2);
}
.footer-features {
display: flex;
justify-content: center;
gap: 15px;
margin: 20px 0;
flex-wrap: wrap;
}
.footer-feature {
background-color: rgba(255,255,255,0.25);
padding: 8px 15px;
border-radius: 20px;
font-size: 14px;
font-weight: 500;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
text-shadow: 0px 1px 1px rgba(0,0,0,0.1);
}
.footer-copyright {
margin-top: 15px;
font-size: 14px;
opacity: 0.9;
text-shadow: 0px 1px 1px rgba(0,0,0,0.1);
}
</style>
<div class="footer">
<div class="footer-content">
<div class="footer-title">πŸ“° News Summarization & Analysis Application</div>
<div class="footer-text">
A powerful tool for analyzing news content, extracting sentiments, and generating insights.
Get comprehensive analysis of any company's news coverage within seconds.
</div>
<div class="footer-features">
<div class="footer-feature">⚑ Real-time News Extraction</div>
<div class="footer-feature">πŸ“Š Sentiment Analysis</div>
<div class="footer-feature">πŸ” Topic Analysis</div>
<div class="footer-feature">🎧 Multilingual Text-to-Speech</div>
</div>
<div class="footer-copyright">Created with Streamlit β€’ {datetime.now().year}</div>
</div>
</div>
""", unsafe_allow_html=True)