Sirapatrwan commited on
Commit
8bf526a
·
verified ·
1 Parent(s): 940ba87

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +193 -0
app.py ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import os
3
+ import time
4
+ from utils import fetch_news, analyze_sentiment, extract_topics, generate_tts
5
+ import plotly.express as px
6
+ import warnings
7
+ warnings.filterwarnings("ignore", category=UserWarning)
8
+
9
+ # Add custom CSS
10
+ st.markdown("""
11
+ <style>
12
+ h1, .stTitle {color: #2E86C1; font-size: 2.5em; font-weight: bold;}
13
+ h2, .stSubheader {color: #1A5276; font-weight: bold;}
14
+ .stButton>button {background-color: #3498DB; color: white; border-radius: 5px; padding: 0.5em 1em;}
15
+ .stButton>button:hover {background-color: #2E86C1;}
16
+ .sentiment-positive {color: green; font-weight: bold;}
17
+ .sentiment-negative {color: red; font-weight: bold;}
18
+ .sentiment-neutral {color: gray; font-weight: bold;}
19
+ .sidebar .sidebar-content {position: sticky; top: 50%; padding: 10px;}
20
+ </style>
21
+ """, unsafe_allow_html=True)
22
+
23
+ st.title("News Summarizer and Sentiment Analyzer")
24
+
25
+ company_name = st.text_input("Enter a company name to get a sentiment report of its recent news.", placeholder="e.g., Google, Meta", value="")
26
+
27
+ if st.button("Analyze"):
28
+ with st.spinner("Fetching and analyzing news articles..."):
29
+ time.sleep(1)
30
+ articles_data = fetch_news(company_name)
31
+ if not articles_data:
32
+ st.error(f"No articles found for {company_name}. Check logs for details.")
33
+ else:
34
+ articles = []
35
+ sentiments = {"Positive": 0, "Negative": 0, "Neutral": 0}
36
+ positive_articles = []
37
+ negative_articles = []
38
+ neutral_articles = []
39
+
40
+ for article in articles_data:
41
+ summary = article["summary"].strip() or article["title"].split(" - ")[0].strip()
42
+ source = article["title"].split(" - ")[-1].strip() if " - " in article["title"] else ""
43
+ if source in summary:
44
+ summary = summary.replace(source, "").strip()
45
+ summary = f"{summary.rstrip(' -')} - {source}"
46
+
47
+ sentiment = analyze_sentiment(summary)
48
+ topics = extract_topics(summary)
49
+ sentiments[sentiment] += 1
50
+
51
+ title = article["title"].split(" - ")[0].strip()
52
+ if sentiment == "Positive":
53
+ positive_articles.append(title)
54
+ elif sentiment == "Negative":
55
+ negative_articles.append(title)
56
+ else:
57
+ neutral_articles.append(title)
58
+
59
+ articles.append({
60
+ "Title": article["title"],
61
+ "Summary": summary,
62
+ "Sentiment": sentiment,
63
+ "Topics": topics,
64
+ "Link": article["link"],
65
+ "PubDate": article["pub_date"]
66
+ })
67
+
68
+ import random
69
+ detailed_comparisons = [f"- News {i + 1} {article['Sentiment'].lower()}ly discusses {', '.join(article['Topics'])}"
70
+ for i, article in enumerate(articles)]
71
+ dominant_sentiment = max(sentiments, key=sentiments.get)
72
+ trends = f"{company_name} News Trends: {dominant_sentiment}"
73
+
74
+ total_articles = sum(sentiments.values())
75
+ sentiment_count = f"{sentiments['Positive']} positive, {sentiments['Negative']} negative, {sentiments['Neutral']} neutral"
76
+
77
+ intro_phrases = [
78
+ f"Spanning {total_articles} recent reports, the narrative surrounding {company_name} tilts {dominant_sentiment.lower()}, with {sentiment_count}.",
79
+ f"Across {total_articles} articles in recent coverage, {company_name}’s story emerges as predominantly {dominant_sentiment.lower()}, reflecting {sentiment_count}.",
80
+ f"Drawing from {total_articles} latest publications, {company_name}’s news landscape leans {dominant_sentiment.lower()}, underscored by {sentiment_count}."
81
+ ]
82
+ positive_phrases = [
83
+ f"With {len(positive_articles)} favorable accounts, {company_name} demonstrates notable progress, exemplified by '{random.choice(positive_articles) if positive_articles else 'no specific examples available'}'.",
84
+ f"Boasting {len(positive_articles)} positive developments, {company_name} showcases strength, as evidenced in '{random.choice(positive_articles) if positive_articles else 'no notable instances'}'.",
85
+ f"Highlighted by {len(positive_articles)} encouraging reports, {company_name} is forging ahead, with '{random.choice(positive_articles) if positive_articles else 'no standout reports'}' standing out."
86
+ ]
87
+ negative_phrases = [
88
+ f"However, {len(negative_articles)} troubling narratives raise concerns, including '{random.choice(negative_articles) if negative_articles else 'no specific concerns noted'}'.",
89
+ f"Yet, {len(negative_articles)} adverse reports signal challenges, such as '{random.choice(negative_articles) if negative_articles else 'no highlighted issues'}'.",
90
+ f"Nevertheless, {len(negative_articles)} concerning stories cast a shadow, notably '{random.choice(negative_articles) if negative_articles else 'no notable setbacks'}'."
91
+ ]
92
+ neutral_phrases = [
93
+ f"Additionally, {len(neutral_articles)} impartial updates provide context, such as '{random.choice(neutral_articles) if neutral_articles else 'no neutral updates available'}'.",
94
+ f"Meanwhile, {len(neutral_articles)} balanced accounts offer insight, including '{random.choice(neutral_articles) if neutral_articles else 'no balanced reports'}'.",
95
+ f"Furthermore, {len(neutral_articles)} objective pieces contribute details, like '{random.choice(neutral_articles) if neutral_articles else 'no objective details'}'."
96
+ ]
97
+ outlook_phrases_positive = [
98
+ f"In summary, {company_name} appears poised for a favorable trajectory.",
99
+ f"All told, {company_name} stands on the cusp of a promising future.",
100
+ f"Ultimately, {company_name} is positioned for an optimistic course ahead."
101
+ ]
102
+ outlook_phrases_negative = [
103
+ f"In conclusion, {company_name} confronts a challenging path forward.",
104
+ f"Overall, {company_name} navigates a formidable road ahead.",
105
+ f"To conclude, {company_name} faces a demanding horizon."
106
+ ]
107
+ outlook_phrases_mixed = [
108
+ f"In the final analysis, {company_name} balances opportunity and uncertainty.",
109
+ f"On balance, {company_name} presents a complex outlook moving forward.",
110
+ f"Ultimately, {company_name} reflects a blend of prospects and hurdles."
111
+ ]
112
+
113
+ final_text = random.choice(intro_phrases) + " "
114
+ if positive_articles:
115
+ final_text += random.choice(positive_phrases) + " "
116
+ if negative_articles:
117
+ final_text += random.choice(negative_phrases) + " "
118
+ if neutral_articles:
119
+ final_text += random.choice(neutral_phrases) + " "
120
+ if sentiments["Positive"] > sentiments["Negative"]:
121
+ final_text += random.choice(outlook_phrases_positive)
122
+ elif sentiments["Negative"] > sentiments["Positive"]:
123
+ final_text += random.choice(outlook_phrases_negative)
124
+ else:
125
+ final_text += random.choice(outlook_phrases_mixed)
126
+
127
+ st.session_state.result = {
128
+ "Company": company_name,
129
+ "Articles": articles,
130
+ "Comparative Sentiment Score": {
131
+ "Sentiment Distribution": f"Positive: {sentiments['Positive']}, Negative: {sentiments['Negative']}, Neutral: {sentiments['Neutral']}",
132
+ "Trends": trends,
133
+ "Detailed Comparisons": "\n".join(detailed_comparisons)
134
+ },
135
+ "Final Sentiment Analysis": final_text.strip()
136
+ }
137
+
138
+ if "result" in st.session_state:
139
+ result = st.session_state.result
140
+ if "error" in result:
141
+ st.error(result["error"])
142
+ else:
143
+ dist = result['Comparative Sentiment Score']['Sentiment Distribution']
144
+ sentiment_counts = {
145
+ "Positive": int(dist.split("Positive: ")[1].split(",")[0]),
146
+ "Negative": int(dist.split("Negative: ")[1].split(",")[0]),
147
+ "Neutral": int(dist.split("Neutral: ")[1])
148
+ }
149
+ fig = px.pie(
150
+ values=list(sentiment_counts.values()),
151
+ names=list(sentiment_counts.keys()),
152
+ title="Sentiment Distribution",
153
+ color_discrete_map={"Positive": "green", "Negative": "red", "Neutral": "gray"},
154
+ width=300,
155
+ height=300
156
+ )
157
+ fig.update_layout(margin=dict(t=40, b=0, l=0, r=0))
158
+ st.sidebar.plotly_chart(fig, use_container_width=True)
159
+
160
+ st.subheader(f"Analysis for {result['Company']}")
161
+ for i, article in enumerate(result["Articles"], 1):
162
+ st.write(f"**News {i}:** {article['PubDate']} [Read full article]({article['Link']})")
163
+ st.write(f"Summary: {article['Summary']}")
164
+ sentiment_class = f"sentiment-{article['Sentiment'].lower()}"
165
+ st.markdown(f"Sentiment: <span class='{sentiment_class}'>{article['Sentiment']}</span>", unsafe_allow_html=True)
166
+ st.write("")
167
+
168
+ st.subheader("Comparative Sentiment Analysis")
169
+ st.write("Detailed Comparisons:")
170
+ st.write(f"Sentiment Distribution: {result['Comparative Sentiment Score']['Sentiment Distribution']}")
171
+ st.markdown(f"**{result['Comparative Sentiment Score']['Trends']}**", unsafe_allow_html=True)
172
+ st.markdown(result["Comparative Sentiment Score"]["Detailed Comparisons"], unsafe_allow_html=True)
173
+
174
+ st.subheader("Final Sentiment Analysis")
175
+ st.write(result["Final Sentiment Analysis"])
176
+
177
+ language = st.selectbox("Select Audio Language", ["Hindi", "English"])
178
+ if st.button("Generate News Audio"):
179
+ with st.spinner("Generating audio..."):
180
+ audio_buffer = generate_tts(result["Final Sentiment Analysis"], 'hi' if language == "Hindi" else 'en')
181
+ if audio_buffer:
182
+ st.audio(audio_buffer, format="audio/mp3")
183
+ else:
184
+ st.error("Failed to generate audio. Check terminal logs.")
185
+
186
+ st.markdown("""
187
+ <p style="font-size: small; color: grey; text-align: center;">
188
+ Developed By: Krishna Prakash
189
+ <a href="https://www.linkedin.com/in/krishnaprakash-profile/" target="_blank">
190
+ <img src="https://img.icons8.com/ios-filled/30/0077b5/linkedin.png" alt="LinkedIn" style="vertical-align: middle; margin: 0 5px;"/>
191
+ </a>
192
+ </p>
193
+ """, unsafe_allow_html=True)