| | """ |
| | AI News Sentiment Analyzer - Streamlit Web Application |
| | Interactive dashboard for analyzing sentiment of AI-related news |
| | """ |
| |
|
| | import streamlit as st |
| | import pandas as pd |
| | import plotly.express as px |
| | import json |
| | from api_handler import AINewsAnalyzer |
| | import io |
| |
|
| |
|
| | |
| | st.set_page_config( |
| | page_title="AI News Sentiment Analyzer", |
| | page_icon="๐ค", |
| | layout="wide", |
| | initial_sidebar_state="expanded" |
| | ) |
| |
|
| | |
| | st.markdown(""" |
| | <style> |
| | .main-header { |
| | font-size: 2.5rem; |
| | font-weight: bold; |
| | color: #1f77b4; |
| | text-align: center; |
| | margin-bottom: 2rem; |
| | } |
| | .metric-card { |
| | background-color: #f0f2f6; |
| | padding: 1rem; |
| | border-radius: 0.5rem; |
| | border-left: 5px solid #1f77b4; |
| | } |
| | .positive { color: #28a745; } |
| | .negative { color: #dc3545; } |
| | .neutral { color: #6c757d; } |
| | </style> |
| | """, unsafe_allow_html=True) |
| |
|
| | |
| | def load_config(): |
| | """Load configuration from config.json""" |
| | with open('config.json', 'r') as f: |
| | return json.load(f) |
| |
|
| | |
| | def load_news_data(query, days, sources=None, model="TextBlob"): |
| | """Load and cache news data""" |
| | try: |
| | analyzer = AINewsAnalyzer() |
| | df = analyzer.get_ai_news_with_sentiment(query=query, days=days, sources=sources, model=model) |
| | return df, None |
| | except Exception as e: |
| | return pd.DataFrame(), str(e) |
| |
|
| |
|
| | def create_sentiment_distribution(df): |
| | """Create sentiment distribution pie chart""" |
| | if df.empty: |
| | return None |
| | |
| | sentiment_counts = df['sentiment_label'].value_counts() |
| | print("sentiment counts", sentiment_counts) |
| | fig = px.pie( |
| | values=sentiment_counts.values, |
| | names=sentiment_counts.index, |
| | title="๐ฏ Sentiment Distribution", |
| | color_discrete_map={ |
| | 'positive': '#28a745', |
| | 'negative': '#dc3545', |
| | 'neutral': '#6c757d' |
| | } |
| | ) |
| | |
| | fig.update_traces(textposition='inside', textinfo='percent+label') |
| | return fig |
| |
|
| | def create_source_analysis(df): |
| | """Create source analysis chart""" |
| | if df.empty: |
| | return None |
| | |
| | source_sentiment = df.groupby(['source', 'sentiment_label']).size().unstack(fill_value=0) |
| | source_sentiment = source_sentiment.loc[source_sentiment.sum(axis=1).nlargest(10).index] |
| | print("source Sentiment", source_sentiment) |
| | fig = px.bar( |
| | source_sentiment.reset_index(), |
| | x='source', |
| | y=['positive', 'negative', 'neutral'], |
| | title="๐ฐ Sentiment by News Source (Top 10)", |
| | color_discrete_map={ |
| | 'positive': '#28a745', |
| | 'negative': '#dc3545', |
| | 'neutral': '#6c757d' |
| | } |
| | ) |
| | |
| | fig.update_layout( |
| | xaxis_title="News Source", |
| | yaxis_title="Number of Articles", |
| | xaxis_tickangle=-45 |
| | ) |
| | |
| | return fig |
| |
|
| | def create_polarity_distribution(df, thresh: float): |
| | """Create sentiment polarity distribution""" |
| | if df.empty: |
| | return None |
| | |
| | fig = px.histogram( |
| | df, |
| | x='sentiment_polarity', |
| | nbins=30, |
| | title="๐ Sentiment Polarity Distribution", |
| | labels={'sentiment_polarity': 'Sentiment Polarity', 'count': 'Number of Articles'} |
| | ) |
| | |
| | |
| | fig.add_vline(x=thresh, line_dash="dash", line_color="green", annotation_text="Positive Threshold", annotation_position="top right") |
| | fig.add_vline(x=-thresh, line_dash="dash", line_color="red", annotation_text="Negative Threshold", annotation_position="top left") |
| | fig.add_vline(x=0, line_dash="dash", line_color="gray", annotation_text="Neutral", annotation_position="top") |
| | return fig |
| |
|
| |
|
| | def main(): |
| | |
| | st.markdown("<h1 class='main-header'>๐ค AI News Sentiment Analyzer</h1>", unsafe_allow_html=True) |
| | st.markdown("### Discover the sentiment trends in AI-related news from around the world") |
| | |
| | |
| | config = load_config() |
| | |
| | |
| | st.sidebar.header("๐ง Analysis Settings") |
| | |
| | |
| | query_options = config["search_queries"] |
| | |
| | selected_query = st.sidebar.selectbox( |
| | "๐ Search Topic:", |
| | options=query_options, |
| | index=0 |
| | ) |
| | |
| | custom_query = st.sidebar.text_input( |
| | "Or enter custom search:", |
| | placeholder="e.g., 'generative AI'" |
| | ) |
| |
|
| | model_query = st.sidebar.selectbox( |
| | "๐ Search a Sentiment Model:", |
| | options=config["model_options"], |
| | index=0 |
| | ) |
| | |
| | |
| | final_query = custom_query if custom_query else selected_query |
| | |
| | |
| | days = st.sidebar.slider( |
| | "๐
Days to analyze:", |
| | min_value=1, |
| | max_value=30, |
| | value=(7,14), |
| | help="How many days back to search for news" |
| | ) |
| |
|
| | |
| | |
| | |
| |
|
| | |
| | |
| | news_sources = config["news_sources"] |
| | |
| | source_option = st.sidebar.selectbox( |
| | "๐ฐ Source Category:", |
| | options=config["source_categories"], |
| | index=0 |
| | ) |
| | |
| | if source_option == "Tech Media": |
| | sources = news_sources["tech_media"] |
| | elif source_option == "General News": |
| | sources = news_sources["general_news"] |
| | elif source_option == "US News": |
| | sources = news_sources["us_news"] |
| | elif source_option == "Financial News": |
| | sources = news_sources["financial_news"] |
| | else: |
| | sources = None |
| | |
| | |
| | if st.sidebar.button("๐ Analyze News", type="primary"): |
| | with st.spinner(f"Fetching and analyzing news about '{final_query}'..."): |
| | df, error = load_news_data(final_query, days=days, sources=sources, model=model_query) |
| | |
| | if error: |
| | st.error(f"Error loading data: {error}") |
| | st.stop() |
| | |
| | if df.empty: |
| | st.warning("No articles found. Try adjusting your search parameters.") |
| | st.stop() |
| | |
| | |
| | st.session_state.df = df |
| | st.session_state.query = final_query |
| | st.session_state.days = days |
| |
|
| | |
| | if 'df' in st.session_state and not st.session_state.df.empty: |
| | df = st.session_state.df |
| | print(df.info) |
| | |
| | st.markdown("### ๐ Analysis Summary") |
| | col1, col2, col3, col4 = st.columns(4) |
| |
|
| | with col1: |
| | st.metric("๐ฐ Total Articles", len(df)) |
| | with col2: |
| | avg_polarity = df['sentiment_polarity'].mean() |
| | delta_polarity = f"{avg_polarity:+.3f}" |
| | st.metric("๐ญ Avg Sentiment", f"{avg_polarity:.3f}", delta_polarity) |
| | with col3: |
| | positive_pct = (len(df[df['sentiment_label'] == 'positive']) / len(df) * 100) |
| | st.metric("๐ Positive %", f"{positive_pct:.1f}%") |
| | with col4: |
| | unique_sources = df['source'].nunique() |
| | st.metric("๐บ News Sources", unique_sources) |
| |
|
| |
|
| | |
| | st.markdown("### ๐ Visual Analysis") |
| | col1, col2 = st.columns(2) |
| |
|
| | |
| | dist_fig = create_sentiment_distribution(df) |
| | if dist_fig: |
| | st.plotly_chart(dist_fig, use_container_width=True, key="dist_fig") |
| | |
| | buf = io.BytesIO() |
| | dist_fig.update_layout(template="plotly_white") |
| | dist_fig.update_layout(plot_bgcolor='white', paper_bgcolor='white') |
| | try: |
| | dist_fig.write_image(buf, format="png", engine="kaleido") |
| | except RuntimeError: |
| | |
| | html_buf = io.StringIO() |
| | dist_fig.write_html(html_buf) |
| | buf = io.BytesIO(html_buf.getvalue().encode('utf-8')) |
| | st.download_button("๐ท Download Distribution Chart as PNG", buf.getvalue(), |
| | "distribution_chart.png", mime="image/png") |
| | st.download_button("๐ Download Distribution Chart as HTML", |
| | dist_fig.to_html().encode("utf-8"), "distribution_chart.html", |
| | mime="text/html") |
| |
|
| | |
| | source_fig = create_source_analysis(df) |
| | if source_fig: |
| | st.plotly_chart(source_fig, use_container_width=True, key="source_fig") |
| | buf = io.BytesIO() |
| | source_fig.update_layout(template="plotly_white") |
| | source_fig.update_layout(plot_bgcolor='white', paper_bgcolor='white') |
| | try: |
| | source_fig.write_image(buf, format="png", engine="kaleido") |
| | except RuntimeError: |
| | |
| | html_buf = io.StringIO() |
| | source_fig.write_html(html_buf) |
| | buf = io.BytesIO(html_buf.getvalue().encode('utf-8')) |
| | st.download_button("๐ท Download Source Chart as PNG", buf.getvalue(), |
| | "source_chart.png", mime="image/png") |
| | st.download_button("๐ Download Source Chart as HTML", |
| | source_fig.to_html().encode("utf-8"), "source_chart.html", |
| | mime="text/html") |
| |
|
| | |
| | polarity_fig = create_polarity_distribution(df, 0.1) |
| | if polarity_fig: |
| | st.plotly_chart(polarity_fig, use_container_width=True, key="polarity_fig") |
| | buf = io.BytesIO() |
| | polarity_fig.update_layout(template="plotly_white") |
| | polarity_fig.update_layout(plot_bgcolor='white', paper_bgcolor='white') |
| | try: |
| | polarity_fig.write_image(buf, format="png", engine="kaleido") |
| | except RuntimeError: |
| | |
| | html_buf = io.StringIO() |
| | polarity_fig.write_html(html_buf) |
| | buf = io.BytesIO(html_buf.getvalue().encode('utf-8')) |
| | st.download_button("๐ท Download Polarity Chart as PNG", buf.getvalue(), |
| | "polarity_chart.png", mime="image/png") |
| | st.download_button("๐ Download Polarity Chart as HTML", |
| | polarity_fig.to_html().encode("utf-8"), "polarity_chart.html", |
| | mime="text/html") |
| |
|
| | |
| | |
| | csv_data = df.to_csv(index=False).encode('utf-8') |
| | st.download_button( |
| | label="๐พ Export Analysis as CSV", |
| | data=csv_data, |
| | file_name=f"ai_news_analysis_{st.session_state.query.replace(' ', '_')}.csv", |
| | mime='text/csv' |
| | ) |
| | |
| | else: |
| | |
| | st.info("๐ Welcome! Configure your analysis settings in the sidebar and click 'Analyze News' to get started.") |
| | |
| | |
| | st.markdown(""" |
| | ### ๐ How to Use: |
| | |
| | 1. **Choose a topic** from the dropdown or enter your own search term |
| | 2. **Select time range** (1-30 days) to analyze recent news |
| | 3. **Pick news sources** or leave as 'All Sources' for comprehensive coverage |
| | 4. **Click 'Analyze News'** to fetch and analyze articles |
| | |
| | ### ๐ What You'll Get: |
| | |
| | - **Sentiment Analysis** of headlines and descriptions |
| | - **Interactive Charts** showing trends over time |
| | - **Source Breakdown** to see which outlets cover your topic |
| | """) |
| | pass |
| |
|
| |
|
| |
|
| |
|
| | if __name__ == "__main__": |
| | main() |