Danialebrat's picture
updating names
24b804f
"""
Musora Sentiment Analysis Dashboard
Main Streamlit Application
Run with: streamlit run app.py
"""
import streamlit as st
import sys
from pathlib import Path
import json
# Add parent directory to path
parent_dir = Path(__file__).resolve().parent
sys.path.append(str(parent_dir))
from data.data_loader import SentimentDataLoader
from components.dashboard import render_dashboard
from components.sentiment_analysis import render_sentiment_analysis
from components.reply_required import render_reply_required
from utils.auth import check_authentication, render_login_page, logout, get_current_user
# ── Load configuration ────────────────────────────────────────────────────────
config_path = parent_dir / "config" / "viz_config.json"
with open(config_path, 'r') as f:
config = json.load(f)
# ── Page configuration ────────────────────────────────────────────────────────
st.set_page_config(
page_title=config['page_config']['page_title'],
page_icon=config['page_config']['page_icon'],
layout=config['page_config']['layout'],
initial_sidebar_state=config['page_config']['initial_sidebar_state']
)
# ── Authentication gate ───────────────────────────────────────────────────────
# render_login_page() calls st.stop() when the user is not authenticated,
# so nothing below this point executes until login succeeds.
if not check_authentication():
render_login_page()
# ── Single data-loader instance (cheap: just reads config) ────────────────────
data_loader = SentimentDataLoader()
def _ensure_dashboard_data():
"""
Load dashboard data once and store in session_state.
Subsequent calls within the same session (or until cache expires) are free.
"""
if 'dashboard_df' not in st.session_state or st.session_state['dashboard_df'] is None:
with st.spinner("Loading dashboard data…"):
df = data_loader.load_dashboard_data()
st.session_state['dashboard_df'] = df
return st.session_state['dashboard_df']
def main():
# ── Sidebar ───────────────────────────────────────────────────────────────
with st.sidebar:
st.image("visualization/img/musora.png", use_container_width=True)
# User info + logout
current_user = get_current_user()
if current_user:
st.caption(f"Logged in as **{current_user}**")
if st.button("πŸ”“ Logout", use_container_width=True):
logout()
st.rerun()
st.markdown("---")
st.title("Navigation")
page = st.radio(
"Select Page",
["πŸ“Š Sentiment Dashboard", "πŸ” Custom Sentiment Queries", "πŸ’¬ Reply Required"],
index=0
)
st.markdown("---")
st.markdown("### πŸ” Global Filters")
# Load / retrieve dashboard data for filter options
dashboard_df = _ensure_dashboard_data()
if dashboard_df.empty:
st.error("No data available. Please check your Snowflake connection.")
return
filter_options = data_loader.get_filter_options(dashboard_df)
# Restore previous filter values from session_state so widgets keep state
prev = st.session_state.get('global_filters', {})
selected_platforms = st.multiselect(
"Platforms",
options=filter_options['platforms'],
default=prev.get('platforms', [])
)
selected_brands = st.multiselect(
"Brands",
options=filter_options['brands'],
default=prev.get('brands', [])
)
selected_sentiments = st.multiselect(
"Sentiments",
options=filter_options['sentiments'],
default=prev.get('sentiments', [])
)
# Date range filter
if 'comment_timestamp' in dashboard_df.columns and not dashboard_df.empty:
min_date = dashboard_df['comment_timestamp'].min().date()
max_date = dashboard_df['comment_timestamp'].max().date()
prev_range = prev.get('date_range')
default_range = (
(prev_range[0], prev_range[1]) if prev_range and len(prev_range) == 2
else (min_date, max_date)
)
date_range = st.date_input(
"Date Range",
value=default_range,
min_value=min_date,
max_value=max_date
)
else:
date_range = None
# Apply / Reset
if st.button("πŸ” Apply Filters", use_container_width=True):
st.session_state['global_filters'] = {
'platforms': selected_platforms,
'brands': selected_brands,
'sentiments': selected_sentiments,
'date_range': date_range if date_range and len(date_range) == 2 else None,
}
st.session_state['filters_applied'] = True
if st.button("πŸ”„ Reset Filters", use_container_width=True):
st.session_state['global_filters'] = {}
st.session_state['filters_applied'] = False
st.rerun()
st.markdown("---")
# Data management
st.markdown("### πŸ”„ Data Management")
if st.button("♻️ Reload Data", use_container_width=True):
st.cache_data.clear()
st.session_state.pop('dashboard_df', None)
st.rerun()
# Data info
st.markdown("---")
st.markdown("### ℹ️ Data Info")
st.info(f"**Total Records:** {len(dashboard_df):,}")
if 'processed_at' in dashboard_df.columns and not dashboard_df.empty:
last_update = dashboard_df['processed_at'].max()
if hasattr(last_update, 'strftime'):
st.info(f"**Last Updated:** {last_update.strftime('%Y-%m-%d %H:%M')}")
# ── Build filtered dashboard_df for the Dashboard page ───────────────────
filters_applied = st.session_state.get('filters_applied', False)
global_filters = st.session_state.get('global_filters', {})
if filters_applied and global_filters:
filtered_df = data_loader.apply_filters(
dashboard_df,
platforms=global_filters.get('platforms') or None,
brands=global_filters.get('brands') or None,
sentiments=global_filters.get('sentiments') or None,
date_range=global_filters.get('date_range') or None,
)
if filtered_df.empty:
st.warning("No data matches the selected filters. Please adjust your filters.")
return
st.info(f"Showing **{len(filtered_df):,}** records after applying filters")
else:
filtered_df = dashboard_df
# ── Render selected page ──────────────────────────────────────────────────
if page == "πŸ“Š Sentiment Dashboard":
render_dashboard(filtered_df)
elif page == "πŸ” Custom Sentiment Queries":
# SA page fetches its own data on demand; receives only data_loader
render_sentiment_analysis(data_loader)
elif page == "πŸ’¬ Reply Required":
# RR page fetches its own data on demand; receives only data_loader
render_reply_required(data_loader)
# ── Footer ────────────────────────────────────────────────────────────────
st.markdown("---")
st.markdown(
"""
<div style='text-align: center; color: gray; padding: 20px;'>
<p>Musora Sentiment Analysis Dashboard v1.0</p>
<p>Powered by Streamlit | Data from Snowflake</p>
</div>
""",
unsafe_allow_html=True
)
if __name__ == "__main__":
try:
main()
except Exception as e:
st.error(f"An error occurred: {str(e)}")
st.exception(e)