""" Main Streamlit Dashboard Application MySpace Ooty Data Analytics - Enhanced Interactive Dashboard """ import streamlit as st import pandas as pd import numpy as np import plotly.express as px import plotly.graph_objects as go from datetime import datetime, timedelta import sys from pathlib import Path import warnings warnings.filterwarnings('ignore') # Add project root to path project_root = Path(__file__).parent.parent sys.path.insert(0, str(project_root)) LOGO_B64 = """ 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 """ from config.settings import * from src.generate_powerpoint_report import PowerPointReportGenerator # ═══════════════════════════════════════════════════════════════════════════ # PAGE CONFIGURATION & STYLING # ═══════════════════════════════════════════════════════════════════════════ # Load logo for page icon logo_file = project_root / "mypace-logo.png" st.set_page_config( page_title="🏨 MySpace Ooty Analytics", page_icon=str(logo_file) if logo_file.exists() else "🏨", layout="wide", initial_sidebar_state="expanded", ) # Custom CSS for enhanced UI and responsive design st.markdown(""" """, unsafe_allow_html=True) # ═══════════════════════════════════════════════════════════════════════════ # DATA LOADING FUNCTION # ═══════════════════════════════════════════════════════════════════════════ @st.cache_data(ttl=3600) def load_data(): """Load processed data from CSV""" try: file_path = Path(project_root) / "data" / "processed" / "data_cleaned_with_kpi.csv" if file_path.exists(): df = pd.read_csv(file_path) # Convert date columns date_cols = ['Year', 'Month', 'Quarter', 'Week', 'Day'] for col in date_cols: if col in df.columns: if col == 'Month_Name': df[col] = df[col].astype(str) return df else: return None except Exception as e: st.error(f"Error loading data: {e}") return None @st.cache_data def load_kpi_summary(): """Load KPI summary""" try: file_path = Path(project_root) / "data" / "processed" / "kpi_summary.csv" if file_path.exists(): return pd.read_csv(file_path) return None except: return None # ═══════════════════════════════════════════════════════════════════════════ # SIDEBAR - FILTERS & NAVIGATION # ═══════════════════════════════════════════════════════════════════════════ with st.sidebar: # Display logo try: from PIL import Image # Try to load logo from streamlit_app directory first (most direct path) logo_direct = Path(__file__).parent / "mypace-logo.png" if logo_direct.exists(): # Load directly from streamlit_app folder logo_img = Image.open(logo_direct) st.image(logo_img, use_container_width=True) else: # Fallback: Try other paths logo_candidates = [ Path(project_root) / "mypace-logo.png", Path.cwd() / "mypace-logo.png", ] logo_path = None for candidate in logo_candidates: if candidate.exists(): logo_path = candidate break if logo_path: logo_img = Image.open(logo_path) st.image(logo_img, use_container_width=True) else: # Try HF URL hf_logo_url = "https://huggingface.co/spaces/abrahamcbe/myspace-ooty-analytics/resolve/main/mypace-logo.png" try: st.image(hf_logo_url, use_container_width=True) except Exception: # Use base64 try: logo_b64 = "".join(LOGO_B64.split()) st.image(f"data:image/png;base64,{logo_b64}", use_container_width=True) except Exception: st.markdown("

🏨

", unsafe_allow_html=True) except Exception as e: st.markdown("

🏨

", unsafe_allow_html=True) st.title("🏨 MySpace Ooty Holiday Inn") st.markdown("📊 Data Analytics Dashboard") st.markdown("---") # Navigation page = st.radio( "📍 Navigation", ["📊 Overview", "📈 KPIs & Metrics", "🔍 Data Exploration", "📉 Trends & Analysis", "🎯 Custom Reports"] ) st.markdown("---") # Load data df = load_data() if df is not None: st.success("✓ Data Loaded Successfully!") st.metric("Records", f"{len(df):,}") st.markdown("---") st.subheader("🔧 Filters") # Date range filter if 'Year' in df.columns and 'Month' in df.columns: year_options = sorted([y for y in df['Year'].unique() if pd.notna(y)]) selected_year = st.multiselect( "📅 Select Year(s)", year_options, default=year_options if year_options else [], help="Filter by booking year" ) month_options = sorted([m for m in df['Month'].unique() if pd.notna(m)]) selected_month = st.multiselect( "📆 Select Month(s)", month_options, default=month_options if month_options else [], help="Filter by booking month (1-12)" ) else: selected_year = [] selected_month = [] # Filter by booking status if 'Booking_Status' in df.columns: status_options = [s for s in df['Booking_Status'].unique() if pd.notna(s)] selected_status = st.multiselect( "✅ Select Booking Status", status_options, default=status_options if status_options else [], help="Filter by booking status" ) else: selected_status = [None] # Apply filters df_filtered = df.copy() if 'Year' in df.columns and selected_year: df_filtered = df_filtered[df_filtered['Year'].isin(selected_year)] if 'Month' in df.columns and selected_month: df_filtered = df_filtered[df_filtered['Month'].isin(selected_month)] if 'Booking_Status' in df_filtered.columns and selected_status and len(selected_status) > 0: df_filtered = df_filtered[df_filtered['Booking_Status'].isin(selected_status)] st.metric("Filtered Records", f"{len(df_filtered):,}") else: st.warning("⚠ No data found. Please run the EDA notebook first.") df_filtered = None st.markdown("---") st.info("💡 Tip: Use filters to customize your analysis") # ═══════════════════════════════════════════════════════════════════════════ # MAIN CONTENT - PAGE ROUTING # ═══════════════════════════════════════════════════════════════════════════ if df_filtered is None or len(df_filtered) == 0: st.error("🚨 No data available. Please ensure the data file exists at `data/processed/data_cleaned_with_kpi.csv`") st.stop() # PAGE 1: OVERVIEW if page == "📊 Overview": st.title("📊 Dashboard Overview") st.markdown("Get a quick summary of your business metrics") # Expandable filters section with st.expander("🔧 Active Filters", expanded=True): col1, col2, col3 = st.columns(3) with col1: st.metric("Years Selected", len(selected_year)) with col2: st.metric("Months Selected", len(selected_month)) with col3: st.metric("Status Selected", len(selected_status)) st.info(f"📊 Showing {len(df_filtered):,} records out of {len(df):,} total") # Load KPI summary kpi_summary = load_kpi_summary() if kpi_summary is not None: kpis_dict = dict(zip(kpi_summary['Metric'], kpi_summary['Value'])) else: kpis_dict = {} # KPI Cards with Custom Styling st.markdown("### 📈 Key Performance Indicators") # Calculate KPI values total_bookings = len(df_filtered) total_revenue = df_filtered[[col for col in df_filtered.columns if 'amount' in col.lower() or 'revenue' in col.lower()]].sum().sum() # Calculate average length of stay if 'No. Nights' in df_filtered.columns: avg_los = df_filtered['No. Nights'].fillna(0).astype(float).mean() elif 'Room_Nights' in df_filtered.columns: avg_los = df_filtered['Room_Nights'].fillna(0).astype(float).mean() else: nights_col = [col for col in df_filtered.columns if 'night' in col.lower()] avg_los = df_filtered[nights_col[0]].fillna(0).astype(float).mean() if nights_col else 0 avg_revenue = total_revenue / total_bookings if total_bookings > 0 else 0 # Display KPI cards using columns kpi_col1, kpi_col2, kpi_col3, kpi_col4 = st.columns([0.8, 1.1, 1.1, 1.1], gap="small") with kpi_col1: st.markdown(f"""
TOTAL RECORDS
{total_bookings:,}
""", unsafe_allow_html=True) with kpi_col2: st.markdown(f"""
TOTAL REVENUE
₹{total_revenue:,.0f}
""", unsafe_allow_html=True) with kpi_col3: st.markdown(f"""
AVG. LENGTH OF STAY
{avg_los:.1f} nights
""", unsafe_allow_html=True) with kpi_col4: st.markdown(f"""
REVENUE PER BOOKING
₹{avg_revenue:,.0f}
""", unsafe_allow_html=True) # Charts Row 1 st.markdown("---") st.markdown("### 📊 Analytics") col1, col2 = st.columns(2) with col1: st.subheader("Bookings by Month") month_series = None if 'Month' in df_filtered.columns: month_series = df_filtered['Month'] else: date_col = next((c for c in df_filtered.columns if 'date' in c.lower()), None) if date_col: month_series = pd.to_datetime(df_filtered[date_col], errors='coerce').dt.month if month_series is not None: month_series = pd.to_numeric(month_series, errors='coerce').dropna().astype(int) if not month_series.empty: month_order = list(range(1, 13)) monthly_data = month_series.value_counts().reindex(month_order, fill_value=0).reset_index() monthly_data.columns = ['MonthNum', 'Bookings'] monthly_data['Month'] = pd.to_datetime(monthly_data['MonthNum'], format='%m').dt.strftime('%b') fig = px.bar( monthly_data, x='Month', y='Bookings', title="Monthly Booking Distribution", color='Bookings', color_continuous_scale='Blues' ) fig.update_layout(height=400, showlegend=False) st.plotly_chart(fig, use_container_width=True) else: st.info("No month data available for the current filters.") else: st.info("Month data not found. Add a Month or Date column to plot this chart.") with col2: st.subheader("Revenue Distribution") # Prioritize certain columns for revenue priority_cols = ['Total Amount', 'Booking Amount', 'Received Amount'] revenue_candidates = [ col for col in df_filtered.columns if any(k in col.lower() for k in ['amount', 'revenue', 'total', 'rate', 'price', 'cost']) ] # Sort by priority revenue_candidates = sorted( revenue_candidates, key=lambda x: (priority_cols.index(x) if x in priority_cols else len(priority_cols)) ) best_col = None best_score = 0 for col in revenue_candidates: cleaned = ( df_filtered[col] .astype(str) .str.replace(r'[^0-9.-]', '', regex=True) ) vals = pd.to_numeric(cleaned, errors='coerce') # Filter to positive values only (revenue should be > 0) positive_vals = vals[vals > 0] score = len(positive_vals) if score > best_score and positive_vals.sum() > 0: best_score = score best_col = col if best_col: cleaned = ( df_filtered[best_col] .astype(str) .str.replace(r'[^0-9.-]', '', regex=True) ) revenue_values = pd.to_numeric(cleaned, errors='coerce') # Filter to positive values only revenue_values = revenue_values[revenue_values > 0].dropna() if revenue_values.empty: st.info(f"No positive revenue values found in {best_col}") elif len(revenue_values) <= 5: st.info("Insufficient data for distribution chart") else: try: # Create histogram using DataFrame format for better Streamlit compatibility nbins = min(30, max(5, len(revenue_values) // 10)) df_hist = pd.DataFrame({'Revenue': revenue_values}) fig = px.histogram( df_hist, x='Revenue', nbins=nbins, title=f"Revenue Distribution - {best_col}", color_discrete_sequence=['#636EFA'] ) fig.update_layout( height=400, xaxis_title=best_col, yaxis_title="Count", hovermode='x unified', showlegend=False ) fig.update_traces(marker=dict(line=dict(width=0))) st.plotly_chart(fig, use_container_width=True) except Exception as e: st.error(f"Error creating histogram: {str(e)}") # Fallback: show summary stats in columns col_stats1, col_stats2, col_stats3 = st.columns(3) with col_stats1: st.metric("Average", f"₹{revenue_values.mean():,.0f}") with col_stats2: st.metric("Median", f"₹{revenue_values.median():,.0f}") with col_stats3: st.metric("Max", f"₹{revenue_values.max():,.0f}") else: st.info("Revenue column not found. Add a revenue/amount column to plot this chart.") # Charts Row 2 col1, col2 = st.columns(2) with col1: st.subheader("Bookings by Day of Week") if 'Day_of_Week' in df_filtered.columns: dow_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] dow_data = df_filtered['Day_of_Week'].value_counts().reindex( [d for d in dow_order if d in df_filtered['Day_of_Week'].values] ).reset_index(name='Bookings') dow_data.columns = ['Day', 'Bookings'] fig = px.bar(dow_data, x='Day', y='Bookings', title="Bookings by Day of Week", color='Bookings', color_continuous_scale='Greens') fig.update_layout(height=400) st.plotly_chart(fig, use_container_width=True) with col2: st.subheader("Holiday vs Regular Season") if 'Is_Holiday_Season' in df_filtered.columns: season_data = df_filtered['Is_Holiday_Season'].map({1: 'Holiday Season', 0: 'Regular Season'}).value_counts() fig = px.pie(values=season_data.values, names=season_data.index, title="Booking Distribution by Season", color_discrete_sequence=['#FF6B6B', '#4ECDC4']) fig.update_layout(height=400) st.plotly_chart(fig, use_container_width=True) # PAGE 2: KPIs & METRICS elif page == "📈 KPIs & Metrics": st.title("📈 Key Performance Indicators") st.markdown("Detailed business metrics and performance indicators") kpi_summary = load_kpi_summary() if kpi_summary is not None: # Display KPI table st.subheader("Summary Metrics") st.dataframe( kpi_summary.head(15), use_container_width=True, height=400 ) # Calculate additional metrics st.markdown("---") st.subheader("Performance Analysis") col1, col2, col3 = st.columns(3) with col1: total_bookings = len(df_filtered) st.info(f"📊 Total Bookings\n\n**{total_bookings:,}** bookings") with col2: if 'Is_Weekend' in df_filtered.columns: weekend_bookings = (df_filtered['Is_Weekend'] == 1).sum() pct = (weekend_bookings / len(df_filtered) * 100) if len(df_filtered) > 0 else 0 st.info(f"🎉 Weekend Bookings\n\n**{pct:.1f}%** of total") with col3: if 'Is_Holiday_Season' in df_filtered.columns: holiday_bookings = (df_filtered['Is_Holiday_Season'] == 1).sum() pct = (holiday_bookings / len(df_filtered) * 100) if len(df_filtered) > 0 else 0 st.info(f"🎄 Holiday Season\n\n**{pct:.1f}%** of bookings") # PAGE 3: DATA EXPLORATION elif page == "🔍 Data Exploration": st.title("🔍 Exploratory Data Analysis") # Data Overview st.subheader("Dataset Overview") col1, col2, col3, col4 = st.columns(4) with col1: st.metric("Rows", f"{len(df_filtered):,}") with col2: st.metric("Columns", df_filtered.shape[1]) with col3: st.metric("Missing Values", f"{df_filtered.isnull().sum().sum():,}") with col4: st.metric("Duplicates", "0") # Display data st.subheader("Data Sample") st.dataframe(df_filtered.head(10), use_container_width=True) # Column statistics st.subheader("Column Statistics") numeric_cols = df_filtered.select_dtypes(include=[np.number]).columns.tolist() if numeric_cols: selected_cols = st.multiselect("Select columns to analyze", numeric_cols, default=numeric_cols[:5]) st.dataframe( df_filtered[selected_cols].describe().round(2), use_container_width=True ) # PAGE 4: TRENDS & ANALYSIS elif page == "📉 Trends & Analysis": st.title("📉 Trends & Statistical Analysis") # Monthly Trend st.subheader("Monthly Trends") if 'Month' in df_filtered.columns: monthly_bookings = df_filtered.groupby('Month').size() fig = px.line(x=monthly_bookings.index, y=monthly_bookings.values, labels={'x': 'Month', 'y': 'Bookings'}, title="Booking Trend Over Months", markers=True) fig.update_traces(line=dict(color='#FF6B6B', width=3), marker=dict(size=10)) st.plotly_chart(fig, use_container_width=True) # Revenue Trends st.markdown("---") st.subheader("Revenue Trends") revenue_cols = [col for col in df_filtered.columns if any(kw in col.lower() for kw in ['amount', 'revenue', 'total'])] if revenue_cols and 'Month' in df_filtered.columns: revenue_col = revenue_cols[0] monthly_revenue = df_filtered.groupby('Month')[revenue_col].sum() fig = px.line(x=monthly_revenue.index, y=monthly_revenue.values, labels={'x': 'Month', 'y': 'Revenue (₹)'}, title="Revenue Trend Over Months", markers=True) fig.update_traces(line=dict(color='#00CC96', width=3), marker=dict(size=10)) st.plotly_chart(fig, use_container_width=True) # PAGE 5: CUSTOM REPORTS elif page == "🎯 Custom Reports": st.title("🎯 Custom Reports & Export") st.markdown("Generate personalized reports with selected filters") # Report options st.subheader("Report Configuration") col1, col2 = st.columns(2) with col1: report_type = st.selectbox( "Report Type", ["Summary Report", "Detailed Analysis", "Executive Summary", "Seasonal Analysis"] ) with col2: export_format = st.selectbox( "Export Format", ["CSV", "Excel", "PDF (Coming Soon)"] ) # Report preview st.subheader("Report Preview") st.info(f"📄 {report_type} - {len(df_filtered):,} records") st.dataframe(df_filtered.head(20), use_container_width=True) # Export button col1, col2, col3 = st.columns(3) with col1: if st.button("📥 Download CSV", key="csv"): csv = df_filtered.to_csv(index=False) st.download_button( label="CSV Report", data=csv, file_name=f"myspace_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv", mime="text/csv" ) with col2: if st.button("📊 Download Excel", key="excel"): excel_buffer = pd.ExcelWriter('/tmp/report.xlsx', engine='openpyxl') df_filtered.to_excel(excel_buffer, index=False) excel_buffer.close() with open('/tmp/report.xlsx', 'rb') as f: st.download_button( label="Excel Report", data=f.read(), file_name=f"myspace_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx", mime="application/vnd.ms-excel" ) with col3: if st.button("📊 Generate PowerPoint", key="ppt"): with st.spinner("🔄 Generating PowerPoint presentation..."): try: # Generate PowerPoint report generator = PowerPointReportGenerator() ppt_path = generator.generate_report() # Read the file and provide download with open(ppt_path, 'rb') as f: st.download_button( label="📥 Download PowerPoint", data=f.read(), file_name=f"MySpace_Ooty_Report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pptx", mime="application/vnd.openxmlformats-officedocument.presentationml.presentation" ) st.success("✅ PowerPoint report generated successfully!") except Exception as e: st.error(f"❌ Error generating PowerPoint: {str(e)}") # Footer st.markdown("---") st.markdown("") with st.container(): st.markdown("

🏨 MySpace Holiday Inn - Ooty

", unsafe_allow_html=True) col1, col2, col3 = st.columns([1, 2, 1]) with col2: st.markdown("""

📍 HEAD OFFICE
Kotagiri – 643217

📞 CONTACT US
+91 82206 62206 | +91-6369052954 | +91-6369973006
📧 myspaceholidayinn@gmail.com
📱 +916381911228

🕐 TIMINGS
Check-In: 12:00 PM | Check-Out: 10:00 AM

""", unsafe_allow_html=True) st.caption(f"📊 Data Analytics Dashboard | Last Updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")