Why this app doesn’t work

import streamlit as st
import pandas as pd
import plotly.express as px
import folium
from folium.plugins import MarkerCluster
from streamlit_folium import st_folium
from datetime import datetime, timedelta
import random

==========================================

1. PAGE CONFIGURATION

==========================================

st.set_page_config(layout=“wide”, page_title=“Capital Projects Dashboard”)

==========================================

2. DATA GENERATION (Simulating your real data)

==========================================

@st.cache_data
def load_data():

Configuration

NUM_STORES = 1000
DATE_START = datetime(2020, 1, 1)
DATE_END = datetime(2024, 12, 31)
REGIONS = [‘North East’, ‘South East’, ‘Midwest’, ‘South West’, ‘West’,
‘North Central’, ‘South Central’, ‘Pacific’, ‘Mountain’]
GROUPS = [‘Flagship’, ‘Outlet’, ‘Mall’, ‘Street’, ‘Kiosk’,
‘Drive-Thru’, ‘Airport’, ‘Campus’, ‘Pop-up’, ‘Reserve’]
VENDORS = [‘Acme Const.’, ‘Global Tech’, ‘City Services’, ‘BuildRight’,
‘Apex Solutions’, ‘Zenith HVAC’, ‘Prime Electric’, ‘Safety First’]

# Generate Stores
stores_data = []
random.seed(42) # Fixed seed so data looks the same every time you run it

for i in range(1, NUM_STORES + 1):
    store_id = f"S-{i:04d}"
    region = random.choice(REGIONS)
    group = random.choice(GROUPS)
    # Lat/Lon mostly within US bounds
    lat = round(random.uniform(25.0, 49.0), 4)
    lon = round(random.uniform(-125.0, -67.0), 4)
    stores_data.append({'Store_ID': store_id, 'Region': region, 'Store_Group': group, 'Latitude': lat, 'Longitude': lon})

df_stores = pd.DataFrame(stores_data)

# Generate Transactions (One-to-Many logic)
transactions = []
for index, store in df_stores.iterrows():
    num_projects = random.randint(1, 6) # 1 to 6 projects per store
    for p in range(num_projects):
        project_id = f"PROJ-{store['Store_ID']}-{p+1}"
        num_vendors = random.randint(1, 2) # 1 or 2 vendors per project
        for v in range(num_vendors):
            days_diff = (DATE_END - DATE_START).days
            r_date = DATE_START + timedelta(days=random.randint(0, days_diff))
            cost = round(random.uniform(1000, 50000), 2)
            transactions.append({
                'Date': r_date,
                'Project_ID': project_id,
                'Store_ID': store['Store_ID'],
                'Region': store['Region'],
                'Store_Group': store['Store_Group'],
                'Latitude': store['Latitude'],
                'Longitude': store['Longitude'],
                'Vendor': random.choice(VENDORS),
                'Cost': cost
            })

df = pd.DataFrame(transactions)
return df

Load the data

df = load_data()

==========================================

3. SIDEBAR FILTERS

==========================================

st.sidebar.header(“Filter Projects”)

Region Filter

selected_regions = st.sidebar.multiselect(
“Select Region”,
options=df[‘Region’].unique(),
default=df[‘Region’].unique()
)

Store Group Filter

selected_groups = st.sidebar.multiselect(
“Select Store Group”,
options=df[‘Store_Group’].unique(),
default=df[‘Store_Group’].unique()
)

Vendor Filter (Important for Drill-down)

selected_vendors = st.sidebar.multiselect(
“Select Vendor”,
options=df[‘Vendor’].unique(),
default=df[‘Vendor’].unique()
)

Apply Filters

df_filtered = df[
(df[‘Region’].isin(selected_regions)) &
(df[‘Store_Group’].isin(selected_groups)) &
(df[‘Vendor’].isin(selected_vendors))
]

==========================================

4. KPI METRICS (Top Row)

==========================================

st.title(“:building_construction: Capital Projects Interactive Dashboard”)
st.markdown(“—”)

c1, c2, c3, c4 = st.columns(4)
c1.metric(“Total Spend”, f"${df_filtered[‘Cost’].sum()/1e6:.1f}M")
c2.metric(“Total Projects”, f"{df_filtered[‘Project_ID’].nunique()}“)
c3.metric(“Active Stores”, f”{df_filtered[‘Store_ID’].nunique()}“)
c4.metric(“Vendors Engaged”, f”{df_filtered[‘Vendor’].nunique()}")

st.markdown(“—”)

==========================================

5. CHARTS ROW (Sunburst & Bar)

==========================================

col1, col2 = st.columns(2)

with col1:
st.subheader(“Spend by Region & Group”)
fig_sun = px.sunburst(
df_filtered,
path=[‘Region’, ‘Store_Group’],
values=‘Cost’,
color=‘Cost’,
color_continuous_scale=‘Blues’
)
st.plotly_chart(fig_sun, use_container_width=True)

with col2:
st.subheader(“Top Vendors by Spend”)
vendor_group = df_filtered.groupby(‘Vendor’)[‘Cost’].sum().reset_index().sort_values(‘Cost’, ascending=True)
fig_bar = px.bar(
vendor_group,
x=‘Cost’,
y=‘Vendor’,
orientation=‘h’,
text_auto=‘.2s’
)
fig_bar.update_layout(xaxis_title=“Total Spend”, yaxis_title=“”)
st.plotly_chart(fig_bar, use_container_width=True)

==========================================

6. DETAILED DATA & TIME SERIES

==========================================

st.markdown(“—”)
st.subheader(“:date: Spending Trends & Details”)

tab1, tab2 = st.tabs([“Spending Over Time”, “Detailed Project List”])

with tab1:

Aggregating by Month for cleaner line chart

df_filtered[‘Month_Year’] = df_filtered[‘Date’].dt.to_period(‘M’).astype(str)
time_data = df_filtered.groupby(‘Month_Year’)[‘Cost’].sum().reset_index()
fig_line = px.line(time_data, x=‘Month_Year’, y=‘Cost’, markers=True)
fig_line.update_xaxes(type=‘category’) # Ensures dates don’t get squashed
st.plotly_chart(fig_line, use_container_width=True)

with tab2:

The detailed table

st.dataframe(
df_filtered[[‘Date’, ‘Store_ID’, ‘Project_ID’, ‘Vendor’, ‘Cost’, ‘Region’, ‘Store_Group’]].sort_values(‘Date’, ascending=False),
use_container_width=True
)

==========================================

7. GEOSPATIAL MAP (At the bottom)

==========================================

st.markdown(“—”)
st.subheader(“:round_pushpin: Geospatial Spend Map”)
st.caption(“Map shows stores matching your filters. Zoom in to see individual locations.”)

Aggregate data for the map (1 bubble = 1 Store)

map_data = df_filtered.groupby([‘Store_ID’, ‘Latitude’, ‘Longitude’, ‘Region’]).agg({
‘Cost’: ‘sum’,
‘Project_ID’: ‘count’,
‘Vendor’: lambda x: x.mode()[0] if not x.mode().empty else ‘Mixed’
}).reset_index()

Only render map if there is data

if not map_data.empty:
m = folium.Map(location=[39.8283, -98.5795], zoom_start=4)
marker_cluster = MarkerCluster().add_to(m)

for idx, row in map_data.iterrows():
    # Create HTML for the tooltip
    tooltip_html = f"""
    <b>Store:</b> {row['Store_ID']}<br>
    <b>Region:</b> {row['Region']}<br>
    <b>Total Spend:</b> ${row['Cost']:,.0f}<br>
    <b>Projects:</b> {row['Project_ID']}
    """
    
    folium.CircleMarker(
        location=[row['Latitude'], row['Longitude']],
        radius=6,
        tooltip=f"Store {row['Store_ID']} (${row['Cost']/1000:.0f}k)",
        popup=folium.Popup(tooltip_html, max_width=300),
        color='#3186cc',
        fill=True,
        fill_color='#3186cc'
    ).add_to(marker_cluster)

st_folium(m, width=1200, height=500)

else:
st.warning(“No stores match your current filters.”)