Update pages/dashboard.py
Browse files- pages/dashboard.py +280 -271
pages/dashboard.py
CHANGED
|
@@ -1,317 +1,326 @@
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
import pandas as pd
|
| 3 |
-
import numpy as np
|
| 4 |
import plotly.express as px
|
| 5 |
import plotly.graph_objects as go
|
| 6 |
from datetime import datetime, timedelta
|
| 7 |
-
|
| 8 |
from utils.storage import load_data, save_data, load_dataframe, save_dataframe
|
| 9 |
-
from utils.
|
| 10 |
-
from utils.error_handling import handle_ui_exceptions
|
| 11 |
-
from utils.logging import get_logger
|
| 12 |
|
| 13 |
-
logger = get_logger(__name__)
|
| 14 |
|
| 15 |
-
@
|
| 16 |
def create_dashboard_page():
|
| 17 |
-
"""
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
with
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
with tab3:
|
| 32 |
-
create_data_upload_section()
|
| 33 |
-
|
| 34 |
-
with tab4:
|
| 35 |
-
create_realtime_section()
|
| 36 |
-
|
| 37 |
-
@handle_ui_exceptions
|
| 38 |
-
def create_overview_section():
|
| 39 |
-
"""Create the overview section of the dashboard"""
|
| 40 |
-
st.header("π Overview")
|
| 41 |
-
|
| 42 |
-
# Create sample metrics
|
| 43 |
-
col1, col2, col3, col4 = st.columns(4)
|
| 44 |
-
|
| 45 |
-
with col1:
|
| 46 |
-
st.metric("Total Records", "1,234", "12%")
|
| 47 |
-
|
| 48 |
-
with col2:
|
| 49 |
-
st.metric("Active Users", "567", "8%")
|
| 50 |
-
|
| 51 |
-
with col3:
|
| 52 |
-
st.metric("Processing Time", "2.3s", "-0.5s")
|
| 53 |
-
|
| 54 |
-
with col4:
|
| 55 |
-
st.metric("Success Rate", "98.5%", "1.2%")
|
| 56 |
-
|
| 57 |
-
# Create sample charts
|
| 58 |
-
col1, col2 = st.columns(2)
|
| 59 |
-
|
| 60 |
-
with col1:
|
| 61 |
-
st.subheader("π Trend Analysis")
|
| 62 |
-
# Generate sample data
|
| 63 |
-
dates = pd.date_range(start='2024-01-01', end='2024-12-31', freq='D')
|
| 64 |
-
values = np.random.randn(len(dates)).cumsum() + 100
|
| 65 |
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
|
| 71 |
-
|
| 72 |
-
st.
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
st.subheader("π¦ Category Distribution")
|
| 76 |
-
categories = ['A', 'B', 'C', 'D', 'E']
|
| 77 |
-
values = np.random.randint(10, 100, size=len(categories))
|
| 78 |
|
| 79 |
-
|
| 80 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
-
|
| 83 |
-
def
|
| 84 |
-
"""
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
-
|
| 88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
with col1:
|
| 91 |
-
|
| 92 |
-
"
|
| 93 |
-
value=
|
| 94 |
-
|
| 95 |
)
|
| 96 |
|
| 97 |
with col2:
|
| 98 |
-
|
| 99 |
-
"
|
| 100 |
-
|
|
|
|
| 101 |
)
|
| 102 |
|
| 103 |
with col3:
|
| 104 |
-
|
| 105 |
-
"
|
| 106 |
-
|
|
|
|
| 107 |
)
|
| 108 |
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
dates = pd.date_range(start=start_date, end=end_date, freq='D')
|
| 116 |
-
values = np.random.randn(len(dates)).cumsum() + np.random.randint(50, 200)
|
| 117 |
-
|
| 118 |
-
df = pd.DataFrame({
|
| 119 |
-
'Date': dates,
|
| 120 |
-
metric: values
|
| 121 |
-
})
|
| 122 |
-
|
| 123 |
-
# Create chart based on selection
|
| 124 |
-
if chart_type == "Line":
|
| 125 |
-
fig = px.line(df, x='Date', y=metric, title=f'{metric} Over Time')
|
| 126 |
-
elif chart_type == "Bar":
|
| 127 |
-
fig = px.bar(df, x='Date', y=metric, title=f'{metric} by Day')
|
| 128 |
-
elif chart_type == "Scatter":
|
| 129 |
-
df['Random'] = np.random.randn(len(df))
|
| 130 |
-
fig = px.scatter(df, x='Random', y=metric, title=f'{metric} Scatter Plot')
|
| 131 |
-
elif chart_type == "Area":
|
| 132 |
-
fig = px.area(df, x='Date', y=metric, title=f'{metric} Area Chart')
|
| 133 |
-
elif chart_type == "Histogram":
|
| 134 |
-
fig = px.histogram(df, x=metric, title=f'{metric} Distribution')
|
| 135 |
-
|
| 136 |
-
st.plotly_chart(fig, use_container_width=True)
|
| 137 |
-
|
| 138 |
-
# Show data table
|
| 139 |
-
with st.expander("View Raw Data"):
|
| 140 |
-
st.dataframe(df)
|
| 141 |
|
| 142 |
-
@handle_ui_exceptions
|
| 143 |
-
def create_data_upload_section():
|
| 144 |
-
"""Create the data upload section"""
|
| 145 |
-
st.header("π€ Data Upload")
|
| 146 |
-
|
| 147 |
-
# File upload
|
| 148 |
-
uploaded_file = st.file_uploader(
|
| 149 |
-
"Upload your data file",
|
| 150 |
-
type=['csv', 'xlsx', 'json'],
|
| 151 |
-
help="Supported formats: CSV, Excel, JSON"
|
| 152 |
-
)
|
| 153 |
-
|
| 154 |
-
if uploaded_file is not None:
|
| 155 |
-
try:
|
| 156 |
-
# Read the uploaded file
|
| 157 |
-
if uploaded_file.name.endswith('.csv'):
|
| 158 |
-
df = pd.read_csv(uploaded_file)
|
| 159 |
-
elif uploaded_file.name.endswith('.xlsx'):
|
| 160 |
-
df = pd.read_excel(uploaded_file)
|
| 161 |
-
elif uploaded_file.name.endswith('.json'):
|
| 162 |
-
df = pd.read_json(uploaded_file)
|
| 163 |
-
|
| 164 |
-
st.success(f"File uploaded successfully! Shape: {df.shape}")
|
| 165 |
-
|
| 166 |
-
# Display data preview
|
| 167 |
-
st.subheader("Data Preview")
|
| 168 |
-
st.dataframe(df.head())
|
| 169 |
-
|
| 170 |
-
# Data summary
|
| 171 |
-
col1, col2 = st.columns(2)
|
| 172 |
-
|
| 173 |
-
with col1:
|
| 174 |
-
st.subheader("Data Info")
|
| 175 |
-
st.write(f"**Rows:** {len(df)}")
|
| 176 |
-
st.write(f"**Columns:** {len(df.columns)}")
|
| 177 |
-
st.write(f"**File Size:** {uploaded_file.size} bytes")
|
| 178 |
-
|
| 179 |
-
with col2:
|
| 180 |
-
st.subheader("Column Types")
|
| 181 |
-
for col, dtype in df.dtypes.items():
|
| 182 |
-
st.write(f"**{col}:** {dtype}")
|
| 183 |
-
|
| 184 |
-
# Save data option
|
| 185 |
-
if st.button("Save Data"):
|
| 186 |
-
save_dataframe(df, f"uploaded_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}")
|
| 187 |
-
st.success("Data saved successfully!")
|
| 188 |
-
|
| 189 |
-
# Basic analytics on uploaded data
|
| 190 |
-
if len(df.select_dtypes(include=[np.number]).columns) > 0:
|
| 191 |
-
st.subheader("Quick Analytics")
|
| 192 |
-
numeric_cols = df.select_dtypes(include=[np.number]).columns
|
| 193 |
-
|
| 194 |
-
selected_col = st.selectbox("Select column for analysis", numeric_cols)
|
| 195 |
-
|
| 196 |
-
if selected_col:
|
| 197 |
-
col1, col2 = st.columns(2)
|
| 198 |
-
|
| 199 |
-
with col1:
|
| 200 |
-
fig = px.histogram(df, x=selected_col, title=f'Distribution of {selected_col}')
|
| 201 |
-
st.plotly_chart(fig, use_container_width=True)
|
| 202 |
-
|
| 203 |
-
with col2:
|
| 204 |
-
fig = px.box(df, y=selected_col, title=f'Box Plot of {selected_col}')
|
| 205 |
-
st.plotly_chart(fig, use_container_width=True)
|
| 206 |
-
|
| 207 |
-
except Exception as e:
|
| 208 |
-
st.error(f"Error processing file: {str(e)}")
|
| 209 |
-
logger.error(f"File processing error: {str(e)}")
|
| 210 |
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
# Auto-refresh toggle
|
| 217 |
-
auto_refresh = st.checkbox("Enable Auto-refresh", value=False)
|
| 218 |
|
| 219 |
-
|
| 220 |
-
refresh_interval = st.slider("Refresh Interval (seconds)", 1, 60, 5)
|
| 221 |
-
st.info(f"Data will refresh every {refresh_interval} seconds")
|
| 222 |
|
| 223 |
-
#
|
| 224 |
-
col1, col2
|
| 225 |
-
|
| 226 |
-
# Generate real-time data
|
| 227 |
-
current_time = datetime.now()
|
| 228 |
|
| 229 |
with col1:
|
| 230 |
-
|
| 231 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
|
| 233 |
with col2:
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
))
|
| 266 |
-
|
| 267 |
-
fig.update_layout(
|
| 268 |
-
title='System Performance Over Time',
|
| 269 |
-
xaxis_title='Time',
|
| 270 |
-
yaxis_title='Usage (%)',
|
| 271 |
-
hovermode='x unified'
|
| 272 |
)
|
| 273 |
|
| 274 |
-
st.plotly_chart(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 275 |
|
| 276 |
-
|
| 277 |
-
st.subheader("π¨ System Status")
|
| 278 |
|
| 279 |
-
|
|
|
|
| 280 |
|
| 281 |
with col1:
|
| 282 |
-
|
| 283 |
-
st.write(f"**Database:** {status}")
|
| 284 |
|
| 285 |
with col2:
|
| 286 |
-
|
| 287 |
-
st.write(f"**API:** {status}")
|
| 288 |
|
| 289 |
with col3:
|
| 290 |
-
|
| 291 |
-
st.write(f"**Services:** {status}")
|
| 292 |
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
st.subheader("π Recent Events")
|
| 299 |
-
|
| 300 |
-
events = [
|
| 301 |
-
{"time": current_time - timedelta(minutes=2), "event": "System backup completed", "type": "info"},
|
| 302 |
-
{"time": current_time - timedelta(minutes=5), "event": "High CPU usage detected", "type": "warning"},
|
| 303 |
-
{"time": current_time - timedelta(minutes=8), "event": "New user registration", "type": "info"},
|
| 304 |
-
{"time": current_time - timedelta(minutes=12), "event": "Database connection restored", "type": "success"},
|
| 305 |
-
{"time": current_time - timedelta(minutes=15), "event": "Scheduled maintenance started", "type": "info"},
|
| 306 |
]
|
| 307 |
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 311 |
|
| 312 |
-
#
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Dashboard page for the application
|
| 3 |
+
"""
|
| 4 |
import streamlit as st
|
| 5 |
import pandas as pd
|
|
|
|
| 6 |
import plotly.express as px
|
| 7 |
import plotly.graph_objects as go
|
| 8 |
from datetime import datetime, timedelta
|
|
|
|
| 9 |
from utils.storage import load_data, save_data, load_dataframe, save_dataframe
|
| 10 |
+
from utils.error_handling import handle_data_exceptions, log_error, display_error
|
|
|
|
|
|
|
| 11 |
|
|
|
|
| 12 |
|
| 13 |
+
@handle_data_exceptions
|
| 14 |
def create_dashboard_page():
|
| 15 |
+
"""
|
| 16 |
+
Create the main dashboard page
|
| 17 |
+
"""
|
| 18 |
+
st.title("π Dashboard")
|
| 19 |
+
st.markdown("---")
|
| 20 |
+
|
| 21 |
+
# Initialize session state
|
| 22 |
+
if 'dashboard_data' not in st.session_state:
|
| 23 |
+
st.session_state.dashboard_data = generate_sample_data()
|
| 24 |
+
|
| 25 |
+
# Sidebar controls
|
| 26 |
+
with st.sidebar:
|
| 27 |
+
st.header("Dashboard Controls")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
+
# Data refresh button
|
| 30 |
+
if st.button("π Refresh Data"):
|
| 31 |
+
st.session_state.dashboard_data = generate_sample_data()
|
| 32 |
+
st.success("Data refreshed!")
|
| 33 |
|
| 34 |
+
# Date range selector
|
| 35 |
+
st.subheader("Date Range")
|
| 36 |
+
start_date = st.date_input("Start Date", value=datetime.now() - timedelta(days=30))
|
| 37 |
+
end_date = st.date_input("End Date", value=datetime.now())
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
+
# Chart type selector
|
| 40 |
+
chart_type = st.selectbox(
|
| 41 |
+
"Chart Type",
|
| 42 |
+
["Line Chart", "Bar Chart", "Area Chart", "Scatter Plot"]
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
# Main dashboard content
|
| 46 |
+
create_metrics_section()
|
| 47 |
+
create_charts_section(chart_type)
|
| 48 |
+
create_data_table_section()
|
| 49 |
|
| 50 |
+
|
| 51 |
+
def generate_sample_data():
|
| 52 |
+
"""
|
| 53 |
+
Generate sample data for the dashboard
|
| 54 |
+
"""
|
| 55 |
+
import random
|
| 56 |
+
import numpy as np
|
| 57 |
+
|
| 58 |
+
# Generate date range
|
| 59 |
+
dates = pd.date_range(start='2024-01-01', end='2024-12-31', freq='D')
|
| 60 |
+
|
| 61 |
+
# Generate sample metrics
|
| 62 |
+
data = {
|
| 63 |
+
'date': dates,
|
| 64 |
+
'sales': [random.randint(1000, 5000) + random.randint(-500, 500) for _ in dates],
|
| 65 |
+
'users': [random.randint(100, 1000) + random.randint(-100, 100) for _ in dates],
|
| 66 |
+
'revenue': [random.randint(5000, 25000) + random.randint(-2000, 2000) for _ in dates],
|
| 67 |
+
'conversion_rate': [round(random.uniform(0.02, 0.08), 4) for _ in dates]
|
| 68 |
+
}
|
| 69 |
|
| 70 |
+
return pd.DataFrame(data)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def create_metrics_section():
|
| 74 |
+
"""
|
| 75 |
+
Create metrics cards section
|
| 76 |
+
"""
|
| 77 |
+
st.subheader("π Key Metrics")
|
| 78 |
+
|
| 79 |
+
data = st.session_state.dashboard_data
|
| 80 |
+
|
| 81 |
+
# Calculate metrics
|
| 82 |
+
total_sales = data['sales'].sum()
|
| 83 |
+
total_users = data['users'].sum()
|
| 84 |
+
total_revenue = data['revenue'].sum()
|
| 85 |
+
avg_conversion = data['conversion_rate'].mean()
|
| 86 |
+
|
| 87 |
+
# Previous period comparison (last 30 days vs previous 30 days)
|
| 88 |
+
recent_data = data.tail(30)
|
| 89 |
+
previous_data = data.iloc[-60:-30] if len(data) >= 60 else data.head(30)
|
| 90 |
+
|
| 91 |
+
sales_change = ((recent_data['sales'].sum() - previous_data['sales'].sum()) / previous_data['sales'].sum()) * 100
|
| 92 |
+
users_change = ((recent_data['users'].sum() - previous_data['users'].sum()) / previous_data['users'].sum()) * 100
|
| 93 |
+
revenue_change = ((recent_data['revenue'].sum() - previous_data['revenue'].sum()) / previous_data['revenue'].sum()) * 100
|
| 94 |
+
conversion_change = ((recent_data['conversion_rate'].mean() - previous_data['conversion_rate'].mean()) / previous_data['conversion_rate'].mean()) * 100
|
| 95 |
+
|
| 96 |
+
# Display metrics in columns
|
| 97 |
+
col1, col2, col3, col4 = st.columns(4)
|
| 98 |
|
| 99 |
with col1:
|
| 100 |
+
st.metric(
|
| 101 |
+
label="Total Sales",
|
| 102 |
+
value=f"{total_sales:,}",
|
| 103 |
+
delta=f"{sales_change:.1f}%"
|
| 104 |
)
|
| 105 |
|
| 106 |
with col2:
|
| 107 |
+
st.metric(
|
| 108 |
+
label="Total Users",
|
| 109 |
+
value=f"{total_users:,}",
|
| 110 |
+
delta=f"{users_change:.1f}%"
|
| 111 |
)
|
| 112 |
|
| 113 |
with col3:
|
| 114 |
+
st.metric(
|
| 115 |
+
label="Total Revenue",
|
| 116 |
+
value=f"${total_revenue:,}",
|
| 117 |
+
delta=f"{revenue_change:.1f}%"
|
| 118 |
)
|
| 119 |
|
| 120 |
+
with col4:
|
| 121 |
+
st.metric(
|
| 122 |
+
label="Avg Conversion Rate",
|
| 123 |
+
value=f"{avg_conversion:.2%}",
|
| 124 |
+
delta=f"{conversion_change:.1f}%"
|
| 125 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
+
def create_charts_section(chart_type):
|
| 129 |
+
"""
|
| 130 |
+
Create charts section
|
| 131 |
+
"""
|
| 132 |
+
st.subheader("π Analytics Charts")
|
|
|
|
|
|
|
| 133 |
|
| 134 |
+
data = st.session_state.dashboard_data
|
|
|
|
|
|
|
| 135 |
|
| 136 |
+
# Create two columns for charts
|
| 137 |
+
col1, col2 = st.columns(2)
|
|
|
|
|
|
|
|
|
|
| 138 |
|
| 139 |
with col1:
|
| 140 |
+
st.write("**Sales Over Time**")
|
| 141 |
+
if chart_type == "Line Chart":
|
| 142 |
+
fig = px.line(data, x='date', y='sales', title='Daily Sales')
|
| 143 |
+
elif chart_type == "Bar Chart":
|
| 144 |
+
# Group by month for bar chart
|
| 145 |
+
monthly_data = data.groupby(data['date'].dt.to_period('M'))['sales'].sum().reset_index()
|
| 146 |
+
monthly_data['date'] = monthly_data['date'].astype(str)
|
| 147 |
+
fig = px.bar(monthly_data, x='date', y='sales', title='Monthly Sales')
|
| 148 |
+
elif chart_type == "Area Chart":
|
| 149 |
+
fig = px.area(data, x='date', y='sales', title='Sales Area Chart')
|
| 150 |
+
else: # Scatter Plot
|
| 151 |
+
fig = px.scatter(data, x='date', y='sales', title='Sales Scatter Plot')
|
| 152 |
+
|
| 153 |
+
fig.update_layout(height=400)
|
| 154 |
+
st.plotly_chart(fig, use_container_width=True)
|
| 155 |
|
| 156 |
with col2:
|
| 157 |
+
st.write("**Revenue vs Users**")
|
| 158 |
+
fig2 = px.scatter(
|
| 159 |
+
data,
|
| 160 |
+
x='users',
|
| 161 |
+
y='revenue',
|
| 162 |
+
size='sales',
|
| 163 |
+
color='conversion_rate',
|
| 164 |
+
title='Revenue vs Users (sized by Sales)',
|
| 165 |
+
color_continuous_scale='Viridis'
|
| 166 |
+
)
|
| 167 |
+
fig2.update_layout(height=400)
|
| 168 |
+
st.plotly_chart(fig2, use_container_width=True)
|
| 169 |
+
|
| 170 |
+
# Full width chart
|
| 171 |
+
st.write("**Multi-Metric Trend Analysis**")
|
| 172 |
+
|
| 173 |
+
# Normalize data for comparison
|
| 174 |
+
normalized_data = data.copy()
|
| 175 |
+
for col in ['sales', 'users', 'revenue']:
|
| 176 |
+
normalized_data[f'{col}_normalized'] = (normalized_data[col] - normalized_data[col].min()) / (normalized_data[col].max() - normalized_data[col].min())
|
| 177 |
+
|
| 178 |
+
fig3 = go.Figure()
|
| 179 |
+
fig3.add_trace(go.Scatter(x=normalized_data['date'], y=normalized_data['sales_normalized'], name='Sales (Normalized)'))
|
| 180 |
+
fig3.add_trace(go.Scatter(x=normalized_data['date'], y=normalized_data['users_normalized'], name='Users (Normalized)'))
|
| 181 |
+
fig3.add_trace(go.Scatter(x=normalized_data['date'], y=normalized_data['revenue_normalized'], name='Revenue (Normalized)'))
|
| 182 |
+
|
| 183 |
+
fig3.update_layout(
|
| 184 |
+
title='Normalized Trends Comparison',
|
| 185 |
+
xaxis_title='Date',
|
| 186 |
+
yaxis_title='Normalized Value (0-1)',
|
| 187 |
+
height=400
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
)
|
| 189 |
|
| 190 |
+
st.plotly_chart(fig3, use_container_width=True)
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
def create_data_table_section():
|
| 194 |
+
"""
|
| 195 |
+
Create data table section
|
| 196 |
+
"""
|
| 197 |
+
st.subheader("π Data Table")
|
| 198 |
|
| 199 |
+
data = st.session_state.dashboard_data
|
|
|
|
| 200 |
|
| 201 |
+
# Add filters
|
| 202 |
+
col1, col2, col3 = st.columns(3)
|
| 203 |
|
| 204 |
with col1:
|
| 205 |
+
min_sales = st.number_input("Min Sales", value=0, max_value=int(data['sales'].max()))
|
|
|
|
| 206 |
|
| 207 |
with col2:
|
| 208 |
+
min_users = st.number_input("Min Users", value=0, max_value=int(data['users'].max()))
|
|
|
|
| 209 |
|
| 210 |
with col3:
|
| 211 |
+
min_revenue = st.number_input("Min Revenue", value=0, max_value=int(data['revenue'].max()))
|
|
|
|
| 212 |
|
| 213 |
+
# Filter data
|
| 214 |
+
filtered_data = data[
|
| 215 |
+
(data['sales'] >= min_sales) &
|
| 216 |
+
(data['users'] >= min_users) &
|
| 217 |
+
(data['revenue'] >= min_revenue)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
]
|
| 219 |
|
| 220 |
+
# Display filtered data
|
| 221 |
+
st.write(f"Showing {len(filtered_data)} of {len(data)} records")
|
| 222 |
+
|
| 223 |
+
# Format data for display
|
| 224 |
+
display_data = filtered_data.copy()
|
| 225 |
+
display_data['date'] = display_data['date'].dt.strftime('%Y-%m-%d')
|
| 226 |
+
display_data['revenue'] = display_data['revenue'].apply(lambda x: f"${x:,}")
|
| 227 |
+
display_data['conversion_rate'] = display_data['conversion_rate'].apply(lambda x: f"{x:.2%}")
|
| 228 |
+
|
| 229 |
+
st.dataframe(
|
| 230 |
+
display_data,
|
| 231 |
+
use_container_width=True,
|
| 232 |
+
hide_index=True,
|
| 233 |
+
column_config={
|
| 234 |
+
"date": "Date",
|
| 235 |
+
"sales": st.column_config.NumberColumn("Sales", format="%d"),
|
| 236 |
+
"users": st.column_config.NumberColumn("Users", format="%d"),
|
| 237 |
+
"revenue": "Revenue",
|
| 238 |
+
"conversion_rate": "Conversion Rate"
|
| 239 |
+
}
|
| 240 |
+
)
|
| 241 |
|
| 242 |
+
# Download button
|
| 243 |
+
csv = data.to_csv(index=False)
|
| 244 |
+
st.download_button(
|
| 245 |
+
label="π₯ Download Data as CSV",
|
| 246 |
+
data=csv,
|
| 247 |
+
file_name=f"dashboard_data_{datetime.now().strftime('%Y%m%d')}.csv",
|
| 248 |
+
mime="text/csv"
|
| 249 |
+
)
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
def create_summary_statistics():
|
| 253 |
+
"""
|
| 254 |
+
Create summary statistics section
|
| 255 |
+
"""
|
| 256 |
+
data = st.session_state.dashboard_data
|
| 257 |
+
|
| 258 |
+
st.subheader("π Summary Statistics")
|
| 259 |
+
|
| 260 |
+
# Calculate statistics
|
| 261 |
+
stats = data[['sales', 'users', 'revenue', 'conversion_rate']].describe()
|
| 262 |
+
|
| 263 |
+
# Display statistics table
|
| 264 |
+
st.dataframe(
|
| 265 |
+
stats,
|
| 266 |
+
use_container_width=True,
|
| 267 |
+
column_config={
|
| 268 |
+
"sales": st.column_config.NumberColumn("Sales", format="%.0f"),
|
| 269 |
+
"users": st.column_config.NumberColumn("Users", format="%.0f"),
|
| 270 |
+
"revenue": st.column_config.NumberColumn("Revenue", format="%.0f"),
|
| 271 |
+
"conversion_rate": st.column_config.NumberColumn("Conversion Rate", format="%.4f")
|
| 272 |
+
}
|
| 273 |
+
)
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
# Additional utility functions
|
| 277 |
+
@handle_data_exceptions
|
| 278 |
+
def export_dashboard_data(format_type='csv'):
|
| 279 |
+
"""
|
| 280 |
+
Export dashboard data in specified format
|
| 281 |
+
"""
|
| 282 |
+
data = st.session_state.dashboard_data
|
| 283 |
+
|
| 284 |
+
if format_type == 'csv':
|
| 285 |
+
return data.to_csv(index=False)
|
| 286 |
+
elif format_type == 'json':
|
| 287 |
+
return data.to_json(orient='records', date_format='iso')
|
| 288 |
+
elif format_type == 'excel':
|
| 289 |
+
# This would require openpyxl
|
| 290 |
+
return data.to_excel(index=False)
|
| 291 |
+
else:
|
| 292 |
+
raise ValueError(f"Unsupported format: {format_type}")
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
@handle_data_exceptions
|
| 296 |
+
def load_dashboard_config():
|
| 297 |
+
"""
|
| 298 |
+
Load dashboard configuration
|
| 299 |
+
"""
|
| 300 |
+
try:
|
| 301 |
+
config = load_data('dashboard_config.json')
|
| 302 |
+
return config if config else get_default_config()
|
| 303 |
+
except:
|
| 304 |
+
return get_default_config()
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
def get_default_config():
|
| 308 |
+
"""
|
| 309 |
+
Get default dashboard configuration
|
| 310 |
+
"""
|
| 311 |
+
return {
|
| 312 |
+
'theme': 'light',
|
| 313 |
+
'default_chart_type': 'Line Chart',
|
| 314 |
+
'refresh_interval': 300, # 5 minutes
|
| 315 |
+
'show_metrics': True,
|
| 316 |
+
'show_charts': True,
|
| 317 |
+
'show_table': True
|
| 318 |
+
}
|
| 319 |
+
|
| 320 |
+
|
| 321 |
+
@handle_data_exceptions
|
| 322 |
+
def save_dashboard_config(config):
|
| 323 |
+
"""
|
| 324 |
+
Save dashboard configuration
|
| 325 |
+
"""
|
| 326 |
+
return save_data(config, 'dashboard_config.json')
|