dimoZ commited on
Commit
6a7d8ad
Β·
verified Β·
1 Parent(s): b22e788

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +661 -0
app.py ADDED
@@ -0,0 +1,661 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+ import seaborn as sns
6
+ from google import genai
7
+ from google.genai import types
8
+ import json
9
+ import streamlit.components.v1 as components
10
+ from datetime import datetime, date
11
+ import io
12
+ import base64
13
+
14
+ # ------------------------------
15
+ # Custom JSON Encoder for Timestamps
16
+ # ------------------------------
17
+ class CustomJSONEncoder(json.JSONEncoder):
18
+ def default(self, obj):
19
+ if isinstance(obj, (datetime, date, pd.Timestamp)):
20
+ return obj.isoformat()
21
+ if isinstance(obj, np.integer):
22
+ return int(obj)
23
+ if isinstance(obj, np.floating):
24
+ return float(obj)
25
+ if isinstance(obj, np.ndarray):
26
+ return obj.tolist()
27
+ if pd.isna(obj):
28
+ return None
29
+ return super().default(obj)
30
+
31
+ # ------------------------------
32
+ # Page Configuration
33
+ # ------------------------------
34
+ st.set_page_config(
35
+ page_title="AI Excel BI Dashboard",
36
+ page_icon="πŸ“Š",
37
+ layout="wide",
38
+ initial_sidebar_state="expanded"
39
+ )
40
+
41
+ # Initialize session state
42
+ if 'api_configured' not in st.session_state:
43
+ st.session_state['api_configured'] = False
44
+ if 'dark_mode' not in st.session_state:
45
+ st.session_state['dark_mode'] = True # Default to dark mode
46
+
47
+ # ------------------------------
48
+ # Sidebar: API Key Setup
49
+ # ------------------------------
50
+ with st.sidebar:
51
+ st.header("βš™οΈ Configuration")
52
+ st.markdown("---")
53
+
54
+ api_key = st.text_input(
55
+ "πŸ”‘ Gemini API Key",
56
+ type="password",
57
+ help="Enter your Google Gemini API key"
58
+ )
59
+
60
+ if api_key:
61
+ try:
62
+ client = genai.Client(api_key=api_key)
63
+ st.success("βœ… API Key Configured")
64
+ st.session_state['api_configured'] = True
65
+ except Exception as e:
66
+ st.error(f"❌ Invalid API Key: {e}")
67
+ client = None
68
+ st.session_state['api_configured'] = False
69
+ else:
70
+ client = None
71
+
72
+ st.markdown("---")
73
+
74
+ st.subheader("ℹ️ About")
75
+ st.info("""
76
+ This AI-powered dashboard:
77
+ - Analyzes Excel/CSV data
78
+ - Generates intelligent visualizations
79
+ - Creates interactive HTML dashboards
80
+ - Provides business insights
81
+ - Detects company/brand data
82
+ """)
83
+
84
+ st.markdown("---")
85
+ st.caption("Powered by Google Gemini AI")
86
+
87
+ # Apply dark mode styling (always on by default)
88
+ st.markdown("""
89
+ <style>
90
+ .stApp {
91
+ background-color: #0e1117;
92
+ color: #fafafa;
93
+ }
94
+ </style>
95
+ """, unsafe_allow_html=True)
96
+
97
+ # ------------------------------
98
+ # Main Area: Dashboard
99
+ # ------------------------------
100
+ st.title("πŸ“Š AI-Powered Business Intelligence Dashboard")
101
+ st.markdown("Upload your data file and let AI create professional insights!")
102
+
103
+ # Check if API key is configured
104
+ if not api_key or not client:
105
+ st.warning("⚠️ Please enter your Gemini API Key in the sidebar to continue.")
106
+ st.stop()
107
+
108
+ # ------------------------------
109
+ # File Upload Section
110
+ # ------------------------------
111
+ st.markdown("---")
112
+ uploaded_file = st.file_uploader(
113
+ "πŸ“‚ Upload Your Data File",
114
+ type=["csv", "xlsx"],
115
+ help="Supports CSV and Excel files"
116
+ )
117
+
118
+ if uploaded_file:
119
+ try:
120
+ # Load dataset
121
+ with st.spinner("Loading data..."):
122
+ if uploaded_file.name.endswith(".csv"):
123
+ df = pd.read_csv(uploaded_file)
124
+ else:
125
+ df = pd.read_excel(uploaded_file)
126
+
127
+ st.success(f"βœ… File '{uploaded_file.name}' uploaded successfully!")
128
+
129
+ # ------------------------------
130
+ # Enhanced Data Overview Section
131
+ # ------------------------------
132
+ st.markdown("---")
133
+ st.subheader("πŸ“‹ Comprehensive Data Overview")
134
+
135
+ # Basic Metrics
136
+ col1, col2, col3, col4, col5 = st.columns(5)
137
+ with col1:
138
+ st.metric("Total Rows", f"{df.shape[0]:,}")
139
+ with col2:
140
+ st.metric("Total Columns", df.shape[1])
141
+ with col3:
142
+ st.metric("Numeric Columns", len(df.select_dtypes(include=['number']).columns))
143
+ with col4:
144
+ st.metric("Categorical Columns", len(df.select_dtypes(include=['object']).columns))
145
+ with col5:
146
+ missing_pct = (df.isnull().sum().sum() / (df.shape[0] * df.shape[1]) * 100)
147
+ st.metric("Missing Data", f"{missing_pct:.1f}%")
148
+
149
+ # Detailed Data Analysis
150
+ with st.expander("πŸ” View Detailed Data Analysis", expanded=True):
151
+ tab1, tab2, tab3 = st.tabs(["πŸ“Š Data Preview", "πŸ“ˆ Statistics", "⚠️ Data Quality"])
152
+
153
+ with tab1:
154
+ st.dataframe(df.head(15), use_container_width=True)
155
+
156
+ with tab2:
157
+ # Statistical Summary
158
+ st.markdown("**Statistical Summary**")
159
+ numeric_cols = df.select_dtypes(include=['number']).columns
160
+ if len(numeric_cols) > 0:
161
+ stats_df = df[numeric_cols].describe()
162
+ st.dataframe(stats_df, use_container_width=True)
163
+ else:
164
+ st.info("No numeric columns found for statistical analysis")
165
+
166
+ # Categorical Summary
167
+ cat_cols = df.select_dtypes(include=['object']).columns
168
+ if len(cat_cols) > 0:
169
+ st.markdown("**Categorical Summary**")
170
+ cat_summary = pd.DataFrame({
171
+ 'Column': cat_cols,
172
+ 'Unique Values': [df[col].nunique() for col in cat_cols],
173
+ 'Most Frequent': [df[col].mode()[0] if len(df[col].mode()) > 0 else 'N/A' for col in cat_cols],
174
+ 'Frequency': [df[col].value_counts().iloc[0] if len(df[col]) > 0 else 0 for col in cat_cols]
175
+ })
176
+ st.dataframe(cat_summary, use_container_width=True)
177
+
178
+ with tab3:
179
+ # Data Quality Metrics
180
+ quality_data = []
181
+ for col in df.columns:
182
+ missing = df[col].isnull().sum()
183
+ missing_pct = (missing / len(df)) * 100
184
+
185
+ # Check for blank spaces in string columns
186
+ blank_spaces = 0
187
+ if df[col].dtype == 'object':
188
+ blank_spaces = df[col].astype(str).str.strip().eq('').sum()
189
+
190
+ # Standard deviation for numeric columns
191
+ std_dev = df[col].std() if df[col].dtype in ['int64', 'float64'] else None
192
+
193
+ quality_data.append({
194
+ 'Column': col,
195
+ 'Data Type': str(df[col].dtype),
196
+ 'Missing Values': missing,
197
+ 'Missing %': f"{missing_pct:.2f}%",
198
+ 'Blank Spaces': blank_spaces,
199
+ 'Std Deviation': f"{std_dev:.2f}" if std_dev is not None else 'N/A',
200
+ 'Unique Values': df[col].nunique()
201
+ })
202
+
203
+ quality_df = pd.DataFrame(quality_data)
204
+ st.dataframe(quality_df, use_container_width=True)
205
+
206
+ # Highlight issues
207
+ total_missing = df.isnull().sum().sum()
208
+ if total_missing > 0:
209
+ st.warning(f"⚠️ Found {total_missing:,} missing values across the dataset")
210
+ else:
211
+ st.success("βœ… No missing values detected")
212
+
213
+ # ------------------------------
214
+ # AI Analysis Section
215
+ # ------------------------------
216
+ st.markdown("---")
217
+ st.subheader("πŸ€– AI-Generated Dashboard")
218
+
219
+ col_btn1, col_btn2 = st.columns(2)
220
+
221
+ with col_btn1:
222
+ generate_charts = st.button("πŸ“ˆ Generate Charts & Insights", type="primary", use_container_width=True)
223
+
224
+ with col_btn2:
225
+ generate_interactive = st.button("🎨 Generate Interactive HTML Dashboard", type="secondary", use_container_width=True)
226
+
227
+ # Add Presentation Maker Button
228
+ st.markdown("")
229
+ generate_presentation = st.button("🎀 Generate AI Presentation (PPT)", use_container_width=True)
230
+
231
+ # ------------------------------
232
+ # Generate Charts and Insights (Collage View)
233
+ # ------------------------------
234
+ if generate_charts:
235
+ with st.spinner("AI is analyzing your data..."):
236
+ try:
237
+ # Prepare schema with proper serialization
238
+ sample_data = df.head(3).copy()
239
+ for col in sample_data.columns:
240
+ if sample_data[col].dtype == 'datetime64[ns]' or isinstance(sample_data[col].iloc[0], pd.Timestamp):
241
+ sample_data[col] = sample_data[col].astype(str)
242
+
243
+ schema = {
244
+ "columns": {col: str(df[col].dtype) for col in df.columns},
245
+ "sample": sample_data.to_dict(),
246
+ "shape": {"rows": int(df.shape[0]), "columns": int(df.shape[1])},
247
+ "numeric_columns": [col for col in df.select_dtypes(include=['number']).columns.tolist()],
248
+ "categorical_columns": [col for col in df.select_dtypes(include=['object']).columns.tolist()]
249
+ }
250
+
251
+ prompt = f"""
252
+ You are a business intelligence and data visualization expert.
253
+
254
+ Dataset Information:
255
+ {json.dumps(schema, indent=2, cls=CustomJSONEncoder)}
256
+
257
+ Analyze this dataset and determine:
258
+ 1. Is this company/business data? (sales, revenue, employees, products, etc.)
259
+ 2. What industry or domain does it belong to? (retail, finance, healthcare, entertainment, etc.)
260
+ 3. What are the key metrics and KPIs?
261
+
262
+ Then respond with ONLY a valid JSON object (no markdown, no explanations) with this exact structure:
263
+ {{
264
+ "domain": "industry name (e.g., retail, finance, entertainment, generic)",
265
+ "is_company_data": true/false,
266
+ "charts": [
267
+ {{"type": "bar", "x": "column_name", "y": "column_name", "title": "Descriptive Chart Title"}},
268
+ {{"type": "line", "x": "column_name", "y": "column_name", "title": "Descriptive Chart Title"}},
269
+ {{"type": "scatter", "x": "column_name", "y": "column_name", "title": "Descriptive Chart Title"}},
270
+ {{"type": "pie", "column": "column_name", "title": "Descriptive Chart Title"}}
271
+ ],
272
+ "insights": [
273
+ "First business insight about the data",
274
+ "Second business insight about the data",
275
+ "Third business insight about the data"
276
+ ]
277
+ }}
278
+
279
+ Chart types available: bar, line, scatter, histogram, pie
280
+ Generate 4-6 charts that would be most insightful for this data domain.
281
+ """
282
+
283
+ # Call Gemini API
284
+ response = client.models.generate_content(
285
+ model="gemini-2.0-flash-exp",
286
+ contents=[prompt]
287
+ )
288
+
289
+ # Parse response
290
+ response_text = response.text.strip()
291
+ if response_text.startswith("```"):
292
+ response_text = response_text.split("```")[1]
293
+ if response_text.startswith("json"):
294
+ response_text = response_text[4:]
295
+
296
+ chart_plan = json.loads(response_text)
297
+
298
+ # Store in session state
299
+ st.session_state['chart_plan'] = chart_plan
300
+ st.session_state['df'] = df
301
+
302
+ except Exception as e:
303
+ st.error(f"❌ Error generating dashboard: {e}")
304
+ st.exception(e)
305
+
306
+ # ------------------------------
307
+ # Display Charts in Collage View
308
+ # ------------------------------
309
+ if 'chart_plan' in st.session_state:
310
+ chart_plan = st.session_state['chart_plan']
311
+ df = st.session_state['df']
312
+
313
+ st.markdown("---")
314
+ st.markdown("### πŸ“ˆ Visualizations Collage")
315
+ st.markdown(f"**Dashboard Title:** {uploaded_file.name.split('.')[0].replace('_', ' ').title()}")
316
+ st.markdown("**Detailed Charts & Graphs** - Comprehensive visual analysis with proper labels and insights")
317
+
318
+ charts = chart_plan.get("charts", [])
319
+
320
+ # Create matplotlib figure with all charts
321
+ num_charts = len(charts)
322
+ cols_per_row = 3
323
+ rows = (num_charts + cols_per_row - 1) // cols_per_row
324
+
325
+ fig = plt.figure(figsize=(20, 5 * rows))
326
+
327
+ for idx, chart in enumerate(charts, 1):
328
+ try:
329
+ chart_type = chart.get("type")
330
+ title = chart.get("title", f"Chart {idx}")
331
+
332
+ ax = fig.add_subplot(rows, cols_per_row, idx)
333
+
334
+ if chart_type == "bar" and "x" in chart and "y" in chart:
335
+ grouped_data = df.groupby(chart["x"])[chart["y"]].sum()
336
+ # Limit to top 15 categories for readability
337
+ if len(grouped_data) > 15:
338
+ grouped_data = grouped_data.nlargest(15)
339
+ sns.barplot(x=grouped_data.values, y=grouped_data.index, ax=ax, palette='Blues_d')
340
+ ax.set_xlabel(chart["y"], fontsize=10)
341
+ ax.set_ylabel(chart["x"], fontsize=10)
342
+
343
+ elif chart_type == "line" and "x" in chart and "y" in chart:
344
+ # Sample data if too many points
345
+ plot_df = df.copy()
346
+ if len(plot_df) > 100:
347
+ plot_df = plot_df.sample(100).sort_values(by=chart["x"])
348
+ sns.lineplot(data=plot_df, x=chart["x"], y=chart["y"], ax=ax, marker='o', color='green', linewidth=2)
349
+ ax.set_xlabel(chart["x"], fontsize=10)
350
+ ax.set_ylabel(chart["y"], fontsize=10)
351
+ plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right', fontsize=8)
352
+
353
+ elif chart_type == "scatter" and "x" in chart and "y" in chart:
354
+ sns.scatterplot(data=df, x=chart["x"], y=chart["y"], ax=ax, color='coral', s=50, alpha=0.6)
355
+ ax.set_xlabel(chart["x"], fontsize=10)
356
+ ax.set_ylabel(chart["y"], fontsize=10)
357
+
358
+ elif chart_type == "histogram" and "x" in chart:
359
+ sns.histplot(df[chart["x"]].dropna(), bins=20, kde=True, ax=ax, color='purple', alpha=0.7)
360
+ ax.set_xlabel(chart["x"], fontsize=10)
361
+ ax.set_ylabel("Frequency", fontsize=10)
362
+
363
+ elif chart_type == "pie" and "column" in chart:
364
+ data = df[chart["column"]].value_counts().head(5)
365
+ colors = sns.color_palette("pastel")
366
+ ax.pie(data.values, labels=data.index, autopct='%1.1f%%', startangle=90, colors=colors)
367
+
368
+ ax.set_title(title, fontsize=11, fontweight='bold', pad=10)
369
+
370
+ except Exception as chart_error:
371
+ ax.text(0.5, 0.5, f'Error: {str(chart_error)}', ha='center', va='center')
372
+ ax.set_title(title, fontsize=11)
373
+
374
+ plt.tight_layout()
375
+ st.pyplot(fig)
376
+ plt.close()
377
+
378
+ # Display Insights
379
+ st.markdown("---")
380
+ st.markdown("### πŸ’‘ Business Insights")
381
+
382
+ insights = chart_plan.get("insights", [])
383
+ for idx, insight in enumerate(insights, 1):
384
+ st.markdown(f"**{idx}.** {insight}")
385
+
386
+ # ------------------------------
387
+ # Generate Interactive HTML Dashboard (Professional Power BI Style)
388
+ # ------------------------------
389
+ if generate_interactive:
390
+ with st.spinner("Generating professional interactive dashboard..."):
391
+ try:
392
+ # Detect domain and company info
393
+ domain = st.session_state.get('chart_plan', {}).get('domain', 'general')
394
+ is_company = st.session_state.get('chart_plan', {}).get('is_company_data', False)
395
+
396
+ # Get file name for dashboard title
397
+ dashboard_title = uploaded_file.name.split('.')[0].replace('_', ' ').title()
398
+
399
+ # Prepare data with proper serialization
400
+ sample_data = df.head(20).copy()
401
+ for col in sample_data.columns:
402
+ if sample_data[col].dtype == 'datetime64[ns]' or isinstance(sample_data[col].iloc[0], pd.Timestamp):
403
+ sample_data[col] = sample_data[col].astype(str)
404
+
405
+ stats_dict = {}
406
+ for col in df.select_dtypes(include=['number']).columns:
407
+ stats_dict[col] = {
408
+ 'mean': float(df[col].mean()),
409
+ 'median': float(df[col].median()),
410
+ 'std': float(df[col].std()),
411
+ 'min': float(df[col].min()),
412
+ 'max': float(df[col].max())
413
+ }
414
+
415
+ html_prompt = f"""
416
+ Create a COMPLETE, self-contained, professional Power BI-style HTML dashboard.
417
+
418
+ Dataset Context:
419
+ - Dashboard Title: {dashboard_title}
420
+ - Domain: {domain}
421
+ - Is Company Data: {is_company}
422
+ - Columns: {', '.join(df.columns.tolist())}
423
+ - Rows: {df.shape[0]}
424
+ - Sample Data: {json.dumps(sample_data.to_dict('records')[:10], cls=CustomJSONEncoder)}
425
+ - Statistics: {json.dumps(stats_dict, cls=CustomJSONEncoder)}
426
+
427
+ CRITICAL Requirements for Handling Large Data:
428
+ 1. For bar charts with many categories (>15), show only TOP 15 values and add "...and X more" text
429
+ 2. For time series/date data, aggregate by week or month, never show individual dates
430
+ 3. Use responsive chart heights (max 300px per chart)
431
+ 4. Implement proper overflow handling with max-height and scrolling only if necessary
432
+ 5. For dates on x-axis: rotate labels 45deg, use abbreviated format (MMM-YY), show every Nth label
433
+
434
+ Dashboard Design:
435
+ 1. Use Chart.js from CDN: https://cdn.jsdelivr.net/npm/chart.js
436
+ 2. Dynamic color scheme based on domain/data characteristics:
437
+ - Finance: Blue (#1e3a8a) to Navy gradient with gold accents
438
+ - Retail/Sales: Orange (#ea580c) to Green (#16a34a) gradient
439
+ - Healthcare: Teal (#0d9488) to Blue (#0284c7) gradient
440
+ - Entertainment/Movies: Purple (#7c3aed) to Magenta (#db2777) gradient
441
+ - Technology: Cyan (#06b6d4) to Blue (#3b82f6) gradient
442
+ - Generic: Professional Blue (#2563eb) to Gray (#64748b) gradient
443
+ 3. Layout: Responsive grid with 2-3 columns, cards with shadows
444
+ 4. Include:
445
+ - Top banner with "{dashboard_title}" as main title
446
+ - 4-6 KPI cards with key metrics (large numbers, trend indicators)
447
+ - 6-8 charts in grid layout (bar, line, pie, doughnut, area charts)
448
+ - Each chart in a card with title, proper spacing
449
+ - All charts must be USEFUL for Business Intelligence and KPI tracking
450
+ - Focus on metrics that show: trends, comparisons, distributions, performance
451
+ 5. If company data, add company logo placeholder at top
452
+ 6. Footer: "{datetime.now().strftime('%B %d, %Y')} | {dashboard_title} Analytics Dashboard"
453
+ 7. Make charts interactive: hover tooltips, legend toggle
454
+ 8. Use actual data values, aggregate large datasets intelligently
455
+ 9. Add smooth animations (fade-in, scale effects)
456
+ 10. Ensure dates are always visible, accurate & readable
457
+
458
+ Chart Configuration Best Practices:
459
+ - Bar charts: Horizontal for many categories.
460
+ - Line charts: Aggregate time data, show trends not noise
461
+ - Pie/Doughnut: Limit to top 10 categories, group "Others"
462
+ - Use appropriate scales and formatting (K, M, B for large numbers)
463
+
464
+ Return ONLY complete HTML code starting with <!DOCTYPE html>
465
+ NO markdown, NO explanations, just pure HTML that looks like a professional BI tool.
466
+ """
467
+
468
+ response = client.models.generate_content(
469
+ model="gemini-2.0-flash-exp",
470
+ contents=[html_prompt]
471
+ )
472
+
473
+ html_code = response.text.strip()
474
+
475
+ if html_code.startswith("```"):
476
+ html_code = html_code.split("```")[1]
477
+ if html_code.startswith("html"):
478
+ html_code = html_code[4:]
479
+ html_code = html_code.strip()
480
+
481
+ st.session_state['html_dashboard'] = html_code
482
+ st.success("βœ… Professional dashboard generated!")
483
+
484
+ except Exception as e:
485
+ st.error(f"❌ Error generating HTML dashboard: {e}")
486
+ st.exception(e)
487
+
488
+ # ------------------------------
489
+ # Display Interactive HTML Dashboard
490
+ # ------------------------------
491
+ if 'html_dashboard' in st.session_state:
492
+ st.markdown("---")
493
+ st.markdown("### 🎨 Professional Interactive Dashboard")
494
+
495
+ html_code = st.session_state['html_dashboard']
496
+
497
+ # Display the interactive HTML
498
+ components.html(html_code, height=1000, scrolling=True)
499
+
500
+ col1, col2 = st.columns(2)
501
+ with col1:
502
+ st.download_button(
503
+ label="πŸ“₯ Download HTML Dashboard",
504
+ data=html_code,
505
+ file_name=f"dashboard_{uploaded_file.name.split('.')[0]}.html",
506
+ mime="text/html",
507
+ use_container_width=True
508
+ )
509
+
510
+ with col2:
511
+ with st.expander("πŸ’» View HTML Source Code"):
512
+ st.code(html_code, language="html")
513
+
514
+ # ------------------------------
515
+ # Generate AI Presentation (PPT-style HTML)
516
+ # ------------------------------
517
+ if generate_presentation:
518
+ with st.spinner("Creating professional presentation..."):
519
+ try:
520
+ # Get insights and domain info
521
+ chart_plan = st.session_state.get('chart_plan', {})
522
+ domain = chart_plan.get('domain', 'general')
523
+ insights = chart_plan.get('insights', [])
524
+
525
+ dashboard_title = uploaded_file.name.split('.')[0].replace('_', ' ').title()
526
+
527
+ # Prepare data summary
528
+ key_metrics = []
529
+ for col in df.select_dtypes(include=['number']).columns[:4]:
530
+ key_metrics.append({
531
+ 'metric': col,
532
+ 'value': float(df[col].sum()),
533
+ 'avg': float(df[col].mean()),
534
+ 'trend': 'up' if df[col].mean() > df[col].median() else 'down'
535
+ })
536
+
537
+ presentation_prompt = f"""
538
+ Create a professional HTML presentation (PowerPoint-style) with slide navigation.
539
+
540
+ Presentation Context:
541
+ - Title: {dashboard_title} - Business Intelligence Analysis
542
+ - Domain: {domain}
543
+ - Dataset: {df.shape[0]} rows, {df.shape[1]} columns
544
+ - Key Insights: {json.dumps(insights, cls=CustomJSONEncoder)}
545
+ - Key Metrics: {json.dumps(key_metrics, cls=CustomJSONEncoder)}
546
+
547
+ Create EXACTLY 5 slides with this structure:
548
+
549
+ SLIDE 1 - Title & Introduction:
550
+ - Large title: "{dashboard_title}"
551
+ - Subtitle: "Business Intelligence Dashboard Analysis"
552
+ - Brief introduction about the data and purpose
553
+ - Beautiful gradient background matching {domain} theme
554
+ - Company logo placeholder if applicable
555
+
556
+ SLIDE 2 - Key Objectives & Questions:
557
+ - Title: "Business Objectives"
558
+ - List 3-4 core business questions this analysis answers
559
+ - Use bullet points with icons
560
+ - Examples: "What drives revenue growth?", "Which segments perform best?", etc.
561
+
562
+ SLIDE 3 - Data & Analysis:
563
+ - Title: "Key Findings & Visualizations"
564
+ - Include 2-3 mini chart visualizations using Chart.js
565
+ - Show the most important metrics and trends
566
+ - Use actual data from the metrics provided
567
+ - Keep charts simple and clear
568
+
569
+ SLIDE 4 - Insights & Recommendations:
570
+ - Title: "Strategic Insights"
571
+ - Present the top 3 insights from the data
572
+ - Add actionable recommendations for each insight
573
+ - Use cards/boxes for visual separation
574
+ - Include trend indicators (↑↓→)
575
+
576
+ SLIDE 5 - Conclusion & Next Steps:
577
+ - Title: "Conclusion & Action Plan"
578
+ - Recap key takeaways (3-4 points)
579
+ - Suggest 2-3 concrete next steps
580
+ - Add a "Questions?" section
581
+ - Thank you message
582
+
583
+ Technical Requirements:
584
+ 1. Full-screen slides (100vh height, 100vw width)
585
+ 2. Slide navigation: Previous/Next buttons + keyboard arrows
586
+ 3. Slide counter: "Slide X of 5"
587
+ 4. Smooth transitions between slides (slide/fade effect)
588
+ 5. Professional design matching {domain} color scheme:
589
+ - Finance: Navy blue with gold accents
590
+ - Retail: Orange and green tones
591
+ - Healthcare: Teal and blue
592
+ - Entertainment: Purple and magenta
593
+ - Technology: Cyan and blue
594
+ - Generic: Professional blue-gray
595
+ 6. Use Chart.js for any charts (CDN: https://cdn.jsdelivr.net/npm/chart.js)
596
+ 7. Responsive typography and spacing
597
+ 8. Each slide should be self-contained and visually appealing
598
+ 9. Add subtle animations (fade-in effects for content)
599
+ 10. Footer on each slide with page number and date
600
+
601
+ Return ONLY complete HTML code starting with <!DOCTYPE html>
602
+ NO markdown, NO explanations.
603
+ The presentation should look like a professional PowerPoint/Keynote presentation.
604
+ """
605
+
606
+ response = client.models.generate_content(
607
+ model="gemini-2.0-flash-exp",
608
+ contents=[presentation_prompt]
609
+ )
610
+
611
+ ppt_html = response.text.strip()
612
+
613
+ if ppt_html.startswith("```"):
614
+ ppt_html = ppt_html.split("```")[1]
615
+ if ppt_html.startswith("html"):
616
+ ppt_html = ppt_html[4:]
617
+ ppt_html = ppt_html.strip()
618
+
619
+ st.session_state['presentation'] = ppt_html
620
+ st.success("βœ… Presentation generated!")
621
+
622
+ except Exception as e:
623
+ st.error(f"❌ Error generating presentation: {e}")
624
+ st.exception(e)
625
+
626
+ # ------------------------------
627
+ # Display Presentation
628
+ # ------------------------------
629
+ if 'presentation' in st.session_state:
630
+ st.markdown("---")
631
+ st.markdown("### 🎀 AI-Generated Business Presentation")
632
+ st.info("Use arrow keys or navigation buttons to move between slides")
633
+
634
+ ppt_html = st.session_state['presentation']
635
+
636
+ # Display the presentation
637
+ components.html(ppt_html, height=700, scrolling=False)
638
+
639
+ st.download_button(
640
+ label="πŸ“₯ Download Presentation (HTML)",
641
+ data=ppt_html,
642
+ file_name=f"presentation_{uploaded_file.name.split('.')[0]}.html",
643
+ mime="text/html",
644
+ use_container_width=True
645
+ )
646
+
647
+ except Exception as e:
648
+ st.error(f"❌ Error loading file: {e}")
649
+ st.exception(e)
650
+
651
+ else:
652
+ st.info("πŸ‘† Please upload a CSV or Excel file to get started.")
653
+
654
+ # ------------------------------
655
+ # Footer
656
+ # ------------------------------
657
+ st.markdown("---")
658
+ st.markdown(
659
+ f"<div style='text-align: center; color: gray;'>Built with Streamlit & Google Gemini AI</div>",
660
+ unsafe_allow_html=True
661
+ )