Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import os | |
| import time | |
| from utils import fetch_news, analyze_sentiment, extract_topics, generate_tts | |
| import plotly.express as px | |
| import warnings | |
| warnings.filterwarnings("ignore", category=UserWarning) | |
| # Add custom CSS | |
| st.markdown(""" | |
| <style> | |
| h1, .stTitle {color: #2E86C1; font-size: 2.5em; font-weight: bold;} | |
| h2, .stSubheader {color: #1A5276; font-weight: bold;} | |
| .stButton>button {background-color: #3498DB; color: white; border-radius: 5px; padding: 0.5em 1em;} | |
| .stButton>button:hover {background-color: #2E86C1;} | |
| .sentiment-positive {color: green; font-weight: bold;} | |
| .sentiment-negative {color: red; font-weight: bold;} | |
| .sentiment-neutral {color: gray; font-weight: bold;} | |
| .sidebar .sidebar-content {position: sticky; top: 50%; padding: 10px;} | |
| </style> | |
| """, unsafe_allow_html=True) | |
| st.title("News Summarizer and Sentiment Analyzer") | |
| company_name = st.text_input("Enter a company name to get a sentiment report of its recent news.", placeholder="e.g., Google, Meta", value="") | |
| if st.button("Analyze"): | |
| with st.spinner("Fetching and analyzing news articles..."): | |
| time.sleep(1) | |
| articles_data = fetch_news(company_name) | |
| if not articles_data: | |
| st.error(f"No articles found for {company_name}. Check logs for details.") | |
| else: | |
| articles = [] | |
| sentiments = {"Positive": 0, "Negative": 0, "Neutral": 0} | |
| positive_articles = [] | |
| negative_articles = [] | |
| neutral_articles = [] | |
| for article in articles_data: | |
| summary = article["summary"].strip() or article["title"].split(" - ")[0].strip() | |
| source = article["title"].split(" - ")[-1].strip() if " - " in article["title"] else "" | |
| if source in summary: | |
| summary = summary.replace(source, "").strip() | |
| summary = f"{summary.rstrip(' -')} - {source}" | |
| sentiment = analyze_sentiment(summary) | |
| topics = extract_topics(summary) | |
| sentiments[sentiment] += 1 | |
| title = article["title"].split(" - ")[0].strip() | |
| if sentiment == "Positive": | |
| positive_articles.append(title) | |
| elif sentiment == "Negative": | |
| negative_articles.append(title) | |
| else: | |
| neutral_articles.append(title) | |
| articles.append({ | |
| "Title": article["title"], | |
| "Summary": summary, | |
| "Sentiment": sentiment, | |
| "Topics": topics, | |
| "Link": article["link"], | |
| "PubDate": article["pub_date"] | |
| }) | |
| import random | |
| detailed_comparisons = [f"- News {i + 1} {article['Sentiment'].lower()}ly discusses {', '.join(article['Topics'])}" | |
| for i, article in enumerate(articles)] | |
| dominant_sentiment = max(sentiments, key=sentiments.get) | |
| trends = f"{company_name} News Trends: {dominant_sentiment}" | |
| total_articles = sum(sentiments.values()) | |
| sentiment_count = f"{sentiments['Positive']} positive, {sentiments['Negative']} negative, {sentiments['Neutral']} neutral" | |
| intro_phrases = [ | |
| f"Spanning {total_articles} recent reports, the narrative surrounding {company_name} tilts {dominant_sentiment.lower()}, with {sentiment_count}.", | |
| f"Across {total_articles} articles in recent coverage, {company_name}’s story emerges as predominantly {dominant_sentiment.lower()}, reflecting {sentiment_count}.", | |
| f"Drawing from {total_articles} latest publications, {company_name}’s news landscape leans {dominant_sentiment.lower()}, underscored by {sentiment_count}." | |
| ] | |
| positive_phrases = [ | |
| 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'}'.", | |
| 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'}'.", | |
| 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." | |
| ] | |
| negative_phrases = [ | |
| f"However, {len(negative_articles)} troubling narratives raise concerns, including '{random.choice(negative_articles) if negative_articles else 'no specific concerns noted'}'.", | |
| f"Yet, {len(negative_articles)} adverse reports signal challenges, such as '{random.choice(negative_articles) if negative_articles else 'no highlighted issues'}'.", | |
| f"Nevertheless, {len(negative_articles)} concerning stories cast a shadow, notably '{random.choice(negative_articles) if negative_articles else 'no notable setbacks'}'." | |
| ] | |
| neutral_phrases = [ | |
| f"Additionally, {len(neutral_articles)} impartial updates provide context, such as '{random.choice(neutral_articles) if neutral_articles else 'no neutral updates available'}'.", | |
| f"Meanwhile, {len(neutral_articles)} balanced accounts offer insight, including '{random.choice(neutral_articles) if neutral_articles else 'no balanced reports'}'.", | |
| f"Furthermore, {len(neutral_articles)} objective pieces contribute details, like '{random.choice(neutral_articles) if neutral_articles else 'no objective details'}'." | |
| ] | |
| outlook_phrases_positive = [ | |
| f"In summary, {company_name} appears poised for a favorable trajectory.", | |
| f"All told, {company_name} stands on the cusp of a promising future.", | |
| f"Ultimately, {company_name} is positioned for an optimistic course ahead." | |
| ] | |
| outlook_phrases_negative = [ | |
| f"In conclusion, {company_name} confronts a challenging path forward.", | |
| f"Overall, {company_name} navigates a formidable road ahead.", | |
| f"To conclude, {company_name} faces a demanding horizon." | |
| ] | |
| outlook_phrases_mixed = [ | |
| f"In the final analysis, {company_name} balances opportunity and uncertainty.", | |
| f"On balance, {company_name} presents a complex outlook moving forward.", | |
| f"Ultimately, {company_name} reflects a blend of prospects and hurdles." | |
| ] | |
| final_text = random.choice(intro_phrases) + " " | |
| if positive_articles: | |
| final_text += random.choice(positive_phrases) + " " | |
| if negative_articles: | |
| final_text += random.choice(negative_phrases) + " " | |
| if neutral_articles: | |
| final_text += random.choice(neutral_phrases) + " " | |
| if sentiments["Positive"] > sentiments["Negative"]: | |
| final_text += random.choice(outlook_phrases_positive) | |
| elif sentiments["Negative"] > sentiments["Positive"]: | |
| final_text += random.choice(outlook_phrases_negative) | |
| else: | |
| final_text += random.choice(outlook_phrases_mixed) | |
| st.session_state.result = { | |
| "Company": company_name, | |
| "Articles": articles, | |
| "Comparative Sentiment Score": { | |
| "Sentiment Distribution": f"Positive: {sentiments['Positive']}, Negative: {sentiments['Negative']}, Neutral: {sentiments['Neutral']}", | |
| "Trends": trends, | |
| "Detailed Comparisons": "\n".join(detailed_comparisons) | |
| }, | |
| "Final Sentiment Analysis": final_text.strip() | |
| } | |
| if "result" in st.session_state: | |
| result = st.session_state.result | |
| if "error" in result: | |
| st.error(result["error"]) | |
| else: | |
| dist = result['Comparative Sentiment Score']['Sentiment Distribution'] | |
| sentiment_counts = { | |
| "Positive": int(dist.split("Positive: ")[1].split(",")[0]), | |
| "Negative": int(dist.split("Negative: ")[1].split(",")[0]), | |
| "Neutral": int(dist.split("Neutral: ")[1]) | |
| } | |
| fig = px.pie( | |
| values=list(sentiment_counts.values()), | |
| names=list(sentiment_counts.keys()), | |
| title="Sentiment Distribution", | |
| color_discrete_map={"Positive": "green", "Negative": "red", "Neutral": "gray"}, | |
| width=300, | |
| height=300 | |
| ) | |
| fig.update_layout(margin=dict(t=40, b=0, l=0, r=0)) | |
| st.sidebar.plotly_chart(fig, use_container_width=True) | |
| st.subheader(f"Analysis for {result['Company']}") | |
| for i, article in enumerate(result["Articles"], 1): | |
| st.write(f"**News {i}:** {article['PubDate']} [Read full article]({article['Link']})") | |
| st.write(f"Summary: {article['Summary']}") | |
| sentiment_class = f"sentiment-{article['Sentiment'].lower()}" | |
| st.markdown(f"Sentiment: <span class='{sentiment_class}'>{article['Sentiment']}</span>", unsafe_allow_html=True) | |
| st.write("") | |
| st.subheader("Comparative Sentiment Analysis") | |
| st.write("Detailed Comparisons:") | |
| st.write(f"Sentiment Distribution: {result['Comparative Sentiment Score']['Sentiment Distribution']}") | |
| st.markdown(f"**{result['Comparative Sentiment Score']['Trends']}**", unsafe_allow_html=True) | |
| st.markdown(result["Comparative Sentiment Score"]["Detailed Comparisons"], unsafe_allow_html=True) | |
| st.subheader("Final Sentiment Analysis") | |
| st.write(result["Final Sentiment Analysis"]) | |
| language = st.selectbox("Select Audio Language", ["Hindi", "English"]) | |
| if st.button("Generate News Audio"): | |
| with st.spinner("Generating audio..."): | |
| audio_buffer = generate_tts(result["Final Sentiment Analysis"], 'hi' if language == "Hindi" else 'en') | |
| if audio_buffer: | |
| st.audio(audio_buffer, format="audio/mp3") | |
| else: | |
| st.error("Failed to generate audio. Check terminal logs.") | |
| st.markdown(""" | |
| <p style="font-size: small; color: grey; text-align: center;"> | |
| Developed By: Krishna Prakash | |
| <a href="https://www.linkedin.com/in/krishnaprakash-profile/" target="_blank"> | |
| <img src="https://img.icons8.com/ios-filled/30/0077b5/linkedin.png" alt="LinkedIn" style="vertical-align: middle; margin: 0 5px;"/> | |
| </a> | |
| </p> | |
| """, unsafe_allow_html=True) |