SamadhiDBS commited on
Commit
6e5c7f6
Β·
verified Β·
1 Parent(s): 6ab17dd

Update app/main.py

Browse files
Files changed (1) hide show
  1. app/main.py +643 -645
app/main.py CHANGED
@@ -1,646 +1,644 @@
1
- """
2
- Smart Analytics Copilot - Complete Version
3
- With Export, OpenAI, Save/Load, Chart Customization, Power BI Export
4
- """
5
-
6
- import streamlit as st
7
- import pandas as pd
8
- import os
9
- from datetime import datetime
10
- from dotenv import load_dotenv
11
-
12
- # Load environment variables
13
- load_dotenv()
14
-
15
- from data_processor import DataProcessor
16
- from analyzer import Analyzer
17
- from insight_generator import InsightGenerator
18
- from dashboard import DashboardGenerator
19
- from query_engine import QueryEngine
20
- from export_utils import ExportUtils
21
- from session_manager import SessionManager
22
- from chart_customizer import ChartCustomizer
23
-
24
- # Page config
25
- st.set_page_config(
26
- page_title="Smart Analytics Copilot",
27
- page_icon="πŸš€",
28
- layout="wide",
29
- initial_sidebar_state="expanded"
30
- )
31
-
32
- # ============ DARK THEME CSS ============
33
- st.markdown("""
34
- <style>
35
- /* Main background */
36
- .stApp {
37
- background-color: #0a0e17 !important;
38
- }
39
-
40
- /* All text - light color */
41
- .stMarkdown, .stMarkdown p, .stMarkdown div, .stMarkdown span,
42
- .stText, p, div, span, label {
43
- color: #e8e8e8 !important;
44
- }
45
-
46
- /* Headers */
47
- h1, h2, h3, h4, h5, h6 {
48
- color: #00ff9d !important;
49
- font-weight: 600 !important;
50
- }
51
-
52
- /* Main header */
53
- .main-header {
54
- font-size: 2.8rem;
55
- font-weight: bold;
56
- background: linear-gradient(135deg, #00ff9d 0%, #00d4ff 100%);
57
- -webkit-background-clip: text;
58
- -webkit-text-fill-color: transparent;
59
- margin-bottom: 1rem;
60
- text-align: center;
61
- }
62
-
63
- /* Sidebar */
64
- .css-1d391kg, .stSidebar, .sidebar-content {
65
- background-color: #111827 !important;
66
- }
67
-
68
- /* Metrics */
69
- div[data-testid="stMetricValue"] {
70
- color: #00ff9d !important;
71
- font-size: 2rem !important;
72
- font-weight: bold !important;
73
- }
74
-
75
- div[data-testid="stMetricLabel"] {
76
- color: #a0aec0 !important;
77
- font-size: 0.9rem !important;
78
- }
79
-
80
- /* Tabs */
81
- .stTabs [data-baseweb="tab-list"] {
82
- gap: 4px;
83
- background-color: #111827;
84
- border-radius: 10px;
85
- padding: 6px;
86
- }
87
-
88
- .stTabs [data-baseweb="tab"] {
89
- background-color: #1f2937;
90
- border-radius: 8px;
91
- padding: 8px 24px;
92
- color: #e8e8e8 !important;
93
- }
94
-
95
- .stTabs [aria-selected="true"] {
96
- background: linear-gradient(135deg, #00ff9d 0%, #00d4ff 100%) !important;
97
- color: #0a0e17 !important;
98
- font-weight: bold;
99
- }
100
-
101
- /* Buttons */
102
- .stButton button {
103
- background: linear-gradient(135deg, #00ff9d 0%, #00d4ff 100%) !important;
104
- color: #0a0e17 !important;
105
- font-weight: bold !important;
106
- border: none !important;
107
- border-radius: 8px !important;
108
- }
109
-
110
- /* File uploader */
111
- .stFileUploader {
112
- background-color: #1f2937 !important;
113
- border: 2px dashed #374151 !important;
114
- border-radius: 12px !important;
115
- }
116
-
117
- /* Expander */
118
- .streamlit-expanderHeader {
119
- background-color: #1f2937 !important;
120
- color: #00ff9d !important;
121
- border-radius: 8px;
122
- }
123
-
124
- /* Success/Info/Warning boxes */
125
- .stAlert {
126
- background-color: #1f2937 !important;
127
- border: 1px solid #374151 !important;
128
- border-radius: 10px !important;
129
- }
130
-
131
- .stAlert p, .stAlert div {
132
- color: #e8e8e8 !important;
133
- }
134
-
135
- /* Dataframe */
136
- .stDataFrame {
137
- background-color: #111827 !important;
138
- }
139
-
140
- .stDataFrame thead th {
141
- background-color: #1f2937 !important;
142
- color: #00ff9d !important;
143
- }
144
-
145
- /* Text input */
146
- .stTextInput input {
147
- background-color: #1f2937 !important;
148
- color: #e8e8e8 !important;
149
- border: 1px solid #374151 !important;
150
- border-radius: 8px !important;
151
- }
152
-
153
- /* Select box */
154
- .stSelectbox div[data-baseweb="select"] {
155
- background-color: #1f2937 !important;
156
- border-color: #374151 !important;
157
- }
158
-
159
- /* Download button */
160
- .stDownloadButton button {
161
- background: linear-gradient(135deg, #00ff9d 0%, #00d4ff 100%) !important;
162
- color: #0a0e17 !important;
163
- }
164
- </style>
165
- """, unsafe_allow_html=True)
166
-
167
- # Initialize session state
168
- if 'data_loaded' not in st.session_state:
169
- st.session_state.data_loaded = False
170
- if 'df' not in st.session_state:
171
- st.session_state.df = None
172
- if 'schema' not in st.session_state:
173
- st.session_state.schema = None
174
- if 'analysis' not in st.session_state:
175
- st.session_state.analysis = None
176
- if 'insights' not in st.session_state:
177
- st.session_state.insights = None
178
- if 'charts' not in st.session_state:
179
- st.session_state.charts = None
180
- if 'use_openai' not in st.session_state:
181
- st.session_state.use_openai = False
182
-
183
- # Initialize managers
184
- session_mgr = SessionManager()
185
-
186
-
187
- def main():
188
- st.markdown('<div class="main-header">πŸš€ Smart Analytics Copilot</div>', unsafe_allow_html=True)
189
- st.caption("✨ Upload any CSV/JSON - AI analyzes, visualizes, and answers questions")
190
- st.markdown("---")
191
-
192
- # Sidebar
193
- with st.sidebar:
194
- st.markdown("### πŸ“ Data Source")
195
-
196
- # Data source selection
197
- source = st.radio("Choose data source:", ["πŸ“€ Upload File", "πŸ’Ύ Load Saved Session"])
198
-
199
- if source == "πŸ“€ Upload File":
200
- uploaded_file = st.file_uploader("Choose CSV or JSON", type=['csv', 'json'])
201
- if uploaded_file and not st.session_state.data_loaded:
202
- with st.spinner("πŸ”„ Processing your data..."):
203
- process_data(uploaded_file)
204
- else:
205
- # Load saved sessions
206
- sessions = session_mgr.list_sessions()
207
- if sessions:
208
- session_names = [s['name'] for s in sessions]
209
- selected_session = st.selectbox("Select saved session:", session_names)
210
- if st.button("πŸ“‚ Load Session"):
211
- with st.spinner("Loading..."):
212
- load_session(selected_session)
213
- else:
214
- st.info("No saved sessions found")
215
-
216
- st.markdown("---")
217
-
218
- # Settings
219
- with st.expander("βš™οΈ Settings"):
220
- st.session_state.use_openai = st.checkbox("Use OpenAI (better insights)",
221
- value=st.session_state.use_openai)
222
- if st.session_state.use_openai:
223
- api_key = st.text_input("OpenAI API Key:", type="password")
224
- if api_key:
225
- os.environ['OPENAI_API_KEY'] = api_key
226
- st.success("API Key set!")
227
-
228
- st.markdown("---")
229
-
230
- # Export section (only if data loaded)
231
- if st.session_state.data_loaded:
232
- st.markdown("### πŸ’Ύ Export Options")
233
- export_utils = ExportUtils(st.session_state.df)
234
-
235
- export_format = st.selectbox("Export format:",
236
- ["CSV", "Excel", "JSON", "Power BI CSV", "Power BI ZIP (Complete)"])
237
-
238
- if st.button("πŸ“₯ Download"):
239
- if export_format == "CSV":
240
- data = export_utils.to_csv()
241
- mime = "text/csv"
242
- ext = "csv"
243
- elif export_format == "Excel":
244
- data = export_utils.to_excel()
245
- mime = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
246
- ext = "xlsx"
247
- elif export_format == "JSON":
248
- data = export_utils.to_json()
249
- mime = "application/json"
250
- ext = "json"
251
- elif export_format == "Power BI CSV":
252
- data = export_utils.to_powerbi_ready()
253
- mime = "text/csv"
254
- ext = "csv"
255
- else: # Power BI ZIP (Complete)
256
- data = export_utils.to_powerbi_zip()
257
- mime = "application/zip"
258
- ext = "zip"
259
-
260
- st.download_button(
261
- label="βœ… Click to Download",
262
- data=data,
263
- file_name=f"export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.{ext}",
264
- mime=mime
265
- )
266
-
267
- # Save session button
268
- st.markdown("---")
269
- if st.button("πŸ’Ύ Save Current Session"):
270
- name, path = session_mgr.save_session(st.session_state.df, st.session_state.schema)
271
- st.success(f"βœ… Session saved as: {name}")
272
-
273
- # Main content
274
- if st.session_state.data_loaded:
275
- tab1, tab2, tab3, tab4, tab5 = st.tabs([
276
- "πŸ“Š Dashboard", "πŸ’‘ AI Insights", "🎨 Custom Charts", "πŸ” Query", "πŸ“‹ Data"
277
- ])
278
-
279
- with tab1:
280
- show_dashboard()
281
-
282
- with tab2:
283
- show_insights()
284
-
285
- with tab3:
286
- show_chart_customizer()
287
-
288
- with tab4:
289
- show_query_interface()
290
-
291
- with tab5:
292
- show_data_preview()
293
-
294
- else:
295
- show_welcome()
296
-
297
-
298
- def process_data(uploaded_file):
299
- """Process uploaded data"""
300
- try:
301
- processor = DataProcessor()
302
- st.session_state.df = processor.load_from_upload(uploaded_file)
303
- st.session_state.df = processor.preprocess()
304
- st.session_state.schema = processor.detect_schema()
305
-
306
- analyzer = Analyzer(st.session_state.df, st.session_state.schema)
307
- st.session_state.analysis = analyzer.run_full_analysis()
308
-
309
- # Use OpenAI if enabled
310
- api_key = os.environ.get('OPENAI_API_KEY')
311
- insight_gen = InsightGenerator(use_openai=st.session_state.use_openai, api_key=api_key)
312
- st.session_state.insights = insight_gen.generate_insights(
313
- st.session_state.df,
314
- st.session_state.schema,
315
- st.session_state.analysis
316
- )
317
-
318
- dashboard_gen = DashboardGenerator(st.session_state.df, st.session_state.schema)
319
- st.session_state.charts = dashboard_gen.generate_all_charts()
320
-
321
- st.session_state.data_loaded = True
322
- st.success(f"βœ… Successfully loaded {len(st.session_state.df):,} rows with {len(st.session_state.df.columns)} columns")
323
- st.balloons()
324
- st.rerun()
325
- except Exception as e:
326
- st.error(f"Error: {e}")
327
-
328
-
329
- def load_session(session_name):
330
- """Load saved session and regenerate insights"""
331
- session = session_mgr.load_session(session_name)
332
- if session:
333
- st.session_state.df = session['df']
334
- st.session_state.schema = session['schema']
335
-
336
- # Regenerate analysis and insights for loaded session
337
- with st.spinner("πŸ”„ Regenerating analysis..."):
338
- analyzer = Analyzer(st.session_state.df, st.session_state.schema)
339
- st.session_state.analysis = analyzer.run_full_analysis()
340
-
341
- # Regenerate insights
342
- api_key = os.environ.get('OPENAI_API_KEY')
343
- insight_gen = InsightGenerator(use_openai=st.session_state.use_openai, api_key=api_key)
344
- st.session_state.insights = insight_gen.generate_insights(
345
- st.session_state.df,
346
- st.session_state.schema,
347
- st.session_state.analysis
348
- )
349
-
350
- # Regenerate charts
351
- dashboard_gen = DashboardGenerator(st.session_state.df, st.session_state.schema)
352
- st.session_state.charts = dashboard_gen.generate_all_charts()
353
-
354
- st.session_state.data_loaded = True
355
- st.success(f"βœ… Loaded session: {session_name}")
356
- st.rerun()
357
- else:
358
- st.error("Failed to load session")
359
-
360
-
361
- def show_dashboard():
362
- """Display dashboard"""
363
- st.markdown("### πŸ“ˆ Key Metrics")
364
- st.markdown("---")
365
-
366
- # Check if data exists
367
- if st.session_state.df is None:
368
- st.warning("No data loaded. Please upload a file first.")
369
- return
370
-
371
- # Display metrics
372
- if st.session_state.schema['numeric']:
373
- cols = st.columns(min(4, len(st.session_state.schema['numeric'])))
374
- for idx, col in enumerate(st.session_state.schema['numeric'][:4]):
375
- with cols[idx]:
376
- total = st.session_state.df[col].sum()
377
- avg = st.session_state.df[col].mean()
378
- st.metric(
379
- label=f"πŸ’° {col.upper()}",
380
- value=f"{total:,.0f}",
381
- delta=f"Avg: {avg:,.0f}"
382
- )
383
-
384
- st.markdown("---")
385
- st.markdown("### πŸ“Š Visualizations")
386
-
387
- if st.session_state.charts:
388
- for chart in st.session_state.charts[:4]:
389
- st.plotly_chart(chart['figure'], use_container_width=True)
390
- else:
391
- st.info("No charts available. Try uploading data first.")
392
-
393
- st.markdown("---")
394
- st.markdown("### πŸ“‹ Summary Statistics")
395
- if st.session_state.schema['numeric']:
396
- summary = st.session_state.df[st.session_state.schema['numeric']].describe()
397
- st.dataframe(summary, use_container_width=True)
398
-
399
-
400
- def show_insights():
401
- """Display AI insights"""
402
- st.markdown("### 🧠 AI-Powered Insights")
403
- st.markdown("Here's what we discovered in your data:")
404
- st.markdown("---")
405
-
406
- # Check if insights exist
407
- if st.session_state.insights is None:
408
- st.info("πŸ’‘ Insights will appear after data is analyzed.")
409
- return
410
-
411
- for insight in st.session_state.insights:
412
- if "Dataset" in insight:
413
- st.info(f"πŸ“Š {insight}")
414
- elif "correlation" in insight.lower():
415
- st.success(f"βœ… {insight}")
416
- elif "skewed" in insight.lower():
417
- st.warning(f"πŸ“ˆ {insight}")
418
- elif "Recommendation" in insight:
419
- st.info(f"πŸ’‘ {insight}")
420
- else:
421
- st.markdown(f"β€’ {insight}")
422
-
423
- # Power BI template section
424
- st.markdown("---")
425
- with st.expander("πŸ“Š Power BI Resources"):
426
- export_utils = ExportUtils(st.session_state.df)
427
-
428
- col1, col2 = st.columns(2)
429
-
430
- with col1:
431
- # Show DAX template
432
- template = export_utils.get_powerbi_template()
433
- st.code(template, language="dax")
434
-
435
- st.download_button(
436
- label="πŸ“₯ Download DAX Template",
437
- data=template,
438
- file_name="powerbi_measures.dax",
439
- mime="text/plain"
440
- )
441
-
442
- with col2:
443
- # Show instructions
444
- instructions = """
445
- **Power BI Import Steps:**
446
-
447
- 1. **Export Data**: Use sidebar to export as "Power BI CSV"
448
- 2. **Open Power BI Desktop**
449
- 3. **Get Data** β†’ **Text/CSV**
450
- 4. **Select your exported CSV**
451
- 5. **Click Load**
452
- 6. **Copy DAX measures** from above
453
- 7. **Create visuals** using the measures
454
- """
455
- st.info(instructions)
456
-
457
-
458
- def show_chart_customizer():
459
- """Show chart customization interface"""
460
- st.markdown("### 🎨 Custom Chart Builder")
461
- st.markdown("Create your own custom visualizations")
462
- st.markdown("---")
463
-
464
- customizer = ChartCustomizer(st.session_state.df)
465
- available_charts = customizer.get_available_charts()
466
-
467
- col1, col2, col3 = st.columns([1, 1, 1])
468
-
469
- with col1:
470
- chart_type = st.selectbox("Chart Type:", available_charts)
471
-
472
- with col2:
473
- # Get appropriate columns
474
- if 'Histogram' in chart_type or 'Box' in chart_type:
475
- columns = st.session_state.schema['numeric']
476
- if not columns:
477
- columns = list(st.session_state.df.select_dtypes(include=['number']).columns)
478
- elif 'Pie' in chart_type or 'Bar' in chart_type:
479
- columns = st.session_state.schema['categorical']
480
- if not columns:
481
- columns = list(st.session_state.df.select_dtypes(include=['object']).columns)
482
- else:
483
- columns = list(st.session_state.df.columns)
484
-
485
- if columns:
486
- x_col = st.selectbox("X-Axis / Category:", columns)
487
- else:
488
- x_col = None
489
- st.warning("No suitable columns found")
490
-
491
- with col3:
492
- # For charts that need Y-axis
493
- if any(t in chart_type for t in ['Line', 'Scatter', 'Bar']) and 'Histogram' not in chart_type:
494
- y_cols = ['None'] + st.session_state.schema['numeric']
495
- y_col = st.selectbox("Y-Axis / Value:", y_cols)
496
- y_col = None if y_col == 'None' else y_col
497
- else:
498
- y_col = None
499
-
500
- # Color column (optional)
501
- color_cols = ['None'] + st.session_state.schema['categorical']
502
- color_col = st.selectbox("Color By (optional):", color_cols)
503
- color_col = None if color_col == 'None' else color_col
504
-
505
- # Title
506
- title = st.text_input("Chart Title:", value=f"{chart_type} of {x_col if x_col else 'data'}")
507
-
508
- if st.button("🎨 Generate Chart", use_container_width=True):
509
- if x_col:
510
- with st.spinner("Creating chart..."):
511
- fig = customizer.create_chart(chart_type, x_col, y_col, color_col, title)
512
- if fig:
513
- st.plotly_chart(fig, use_container_width=True)
514
-
515
- # Download chart button
516
- try:
517
- st.download_button(
518
- label="πŸ“Έ Download as PNG",
519
- data=fig.to_image(format="png"),
520
- file_name="custom_chart.png",
521
- mime="image/png"
522
- )
523
- except:
524
- st.info("πŸ’‘ Install kaleido for PNG export: `pip install kaleido`")
525
- else:
526
- st.error("Could not create chart. Try different settings.")
527
- else:
528
- st.error("Please select a column for X-Axis")
529
-
530
-
531
- def show_query_interface():
532
- """Natural language query interface"""
533
- st.markdown("### πŸ’¬ Natural Language Query")
534
- st.markdown("Ask any question about your data in plain English:")
535
- st.markdown("---")
536
-
537
- query_engine = QueryEngine(st.session_state.df, st.session_state.schema)
538
-
539
- # Example questions
540
- with st.expander("πŸ” View Example Questions"):
541
- if st.session_state.schema['numeric']:
542
- example_col = st.session_state.schema['numeric'][0]
543
- st.markdown(f"β€’ 'Statistics {example_col}'")
544
- st.markdown(f"β€’ 'Total {example_col}'")
545
- st.markdown(f"β€’ 'Average {example_col}'")
546
-
547
- if st.session_state.schema['categorical'] and st.session_state.schema['numeric']:
548
- st.markdown(f"β€’ 'Top 5 {st.session_state.schema['categorical'][0]} by {st.session_state.schema['numeric'][0]}'")
549
-
550
- st.markdown("β€’ 'Summary statistics'")
551
- st.markdown("β€’ 'Show me the data'")
552
-
553
- st.markdown("---")
554
-
555
- question = st.text_input("Ask a question:", placeholder="e.g., What is the average of time_in_hospital?")
556
-
557
- if question:
558
- with st.spinner("πŸ€” Analyzing your question..."):
559
- answer = query_engine.answer_question(question)
560
- st.markdown("### βœ… Answer")
561
- st.success(answer)
562
-
563
-
564
- def show_data_preview():
565
- """Show data preview and info with better formatting"""
566
- st.markdown("### πŸ“‹ Data Preview")
567
- st.markdown("---")
568
-
569
- col1, col2, col3 = st.columns(3)
570
- with col1:
571
- st.metric("πŸ“Š Total Rows", f"{len(st.session_state.df):,}")
572
- with col2:
573
- st.metric("πŸ“‹ Total Columns", len(st.session_state.df.columns))
574
- with col3:
575
- memory = st.session_state.df.memory_usage(deep=True).sum() / 1024**2
576
- st.metric("πŸ’Ύ Memory Usage", f"{memory:.2f} MB")
577
-
578
- st.markdown("---")
579
- st.markdown("### πŸ“„ Data Sample (First 100 rows)")
580
-
581
- # Create a copy for display
582
- display_df = st.session_state.df.head(100).copy()
583
-
584
- # Clean datetime columns for better display
585
- for col in display_df.columns:
586
- if 'datetime' in col.lower() or 'date' in col.lower() or 'time' in col.lower():
587
- try:
588
- display_df[col] = pd.to_datetime(display_df[col]).dt.strftime('%Y-%m-%d %H:%M:%S')
589
- except:
590
- pass
591
-
592
- st.dataframe(display_df, use_container_width=True)
593
-
594
- st.markdown("---")
595
- st.markdown("### πŸ“Š Column Information")
596
-
597
- col_info = pd.DataFrame({
598
- 'Column': st.session_state.df.columns,
599
- 'Type': st.session_state.df.dtypes.astype(str),
600
- 'Non-Null': st.session_state.df.count().values,
601
- 'Nulls': st.session_state.df.isnull().sum().values,
602
- 'Unique': st.session_state.df.nunique().values
603
- })
604
- st.dataframe(col_info, use_container_width=True)
605
-
606
-
607
- def show_welcome():
608
- """Welcome screen"""
609
- st.markdown("""
610
- <div style="text-align: center; padding: 2rem; background: linear-gradient(135deg, #111827 0%, #0a0e17 100%); border-radius: 20px; margin: 2rem 0;">
611
- <h2 style="color: #00ff9d;">πŸš€ Welcome to Smart Analytics Copilot</h2>
612
- <p style="font-size: 1.1rem;">Upload any CSV or JSON file and let AI analyze it instantly</p>
613
- <hr>
614
- <p>πŸ‘ˆ <strong>Get Started</strong>: Upload a file or load a saved session from the sidebar</p>
615
- </div>
616
- """, unsafe_allow_html=True)
617
-
618
- col1, col2, col3 = st.columns(3)
619
-
620
- with col1:
621
- st.markdown("""
622
- <div style="background: linear-gradient(135deg, #1f2937 0%, #111827 100%); padding: 1.5rem; border-radius: 15px; text-align: center;">
623
- <h3 style="color: #00ff9d;">πŸ“Š Auto Dashboard</h3>
624
- <p>Smart charts based on your data</p>
625
- </div>
626
- """, unsafe_allow_html=True)
627
-
628
- with col2:
629
- st.markdown("""
630
- <div style="background: linear-gradient(135deg, #1f2937 0%, #111827 100%); padding: 1.5rem; border-radius: 15px; text-align: center;">
631
- <h3 style="color: #00ff9d;">πŸ’‘ AI Insights</h3>
632
- <p>Natural language explanations</p>
633
- </div>
634
- """, unsafe_allow_html=True)
635
-
636
- with col3:
637
- st.markdown("""
638
- <div style="background: linear-gradient(135deg, #1f2937 0%, #111827 100%); padding: 1.5rem; border-radius: 15px; text-align: center;">
639
- <h3 style="color: #00ff9d;">🎨 Custom Charts</h3>
640
- <p>Build your own visualizations</p>
641
- </div>
642
- """, unsafe_allow_html=True)
643
-
644
-
645
- if __name__ == "__main__":
646
  main()
 
1
+ """Smart Analytics Copilot - Complete Version
2
+ With Export, OpenAI, Save/Load, Chart Customization, Power BI Export"""
3
+
4
+ import streamlit as st
5
+ import pandas as pd
6
+ import os
7
+ from datetime import datetime
8
+ from dotenv import load_dotenv
9
+
10
+ #Load environment variables
11
+ load_dotenv()
12
+
13
+ from data_processor import DataProcessor
14
+ from analyzer import Analyzer
15
+ from insight_generator import InsightGenerator
16
+ from dashboard import DashboardGenerator
17
+ from query_engine import QueryEngine
18
+ from export_utils import ExportUtils
19
+ from session_manager import SessionManager
20
+ from chart_customizer import ChartCustomizer
21
+
22
+ #Page config
23
+ st.set_page_config(
24
+ page_title="Smart Analytics Copilot",
25
+ page_icon="πŸš€",
26
+ layout="wide",
27
+ initial_sidebar_state="expanded"
28
+ )
29
+
30
+ # ============ DARK THEME CSS ============
31
+ st.markdown("""
32
+ <style>
33
+ /* Main background */
34
+ .stApp {
35
+ background-color: #0a0e17 !important;
36
+ }
37
+
38
+ /* All text - light color */
39
+ .stMarkdown, .stMarkdown p, .stMarkdown div, .stMarkdown span,
40
+ .stText, p, div, span, label {
41
+ color: #e8e8e8 !important;
42
+ }
43
+
44
+ /* Headers */
45
+ h1, h2, h3, h4, h5, h6 {
46
+ color: #00ff9d !important;
47
+ font-weight: 600 !important;
48
+ }
49
+
50
+ /* Main header */
51
+ .main-header {
52
+ font-size: 2.8rem;
53
+ font-weight: bold;
54
+ background: linear-gradient(135deg, #00ff9d 0%, #00d4ff 100%);
55
+ -webkit-background-clip: text;
56
+ -webkit-text-fill-color: transparent;
57
+ margin-bottom: 1rem;
58
+ text-align: center;
59
+ }
60
+
61
+ /* Sidebar */
62
+ .css-1d391kg, .stSidebar, .sidebar-content {
63
+ background-color: #111827 !important;
64
+ }
65
+
66
+ /* Metrics */
67
+ div[data-testid="stMetricValue"] {
68
+ color: #00ff9d !important;
69
+ font-size: 2rem !important;
70
+ font-weight: bold !important;
71
+ }
72
+
73
+ div[data-testid="stMetricLabel"] {
74
+ color: #a0aec0 !important;
75
+ font-size: 0.9rem !important;
76
+ }
77
+
78
+ /* Tabs */
79
+ .stTabs [data-baseweb="tab-list"] {
80
+ gap: 4px;
81
+ background-color: #111827;
82
+ border-radius: 10px;
83
+ padding: 6px;
84
+ }
85
+
86
+ .stTabs [data-baseweb="tab"] {
87
+ background-color: #1f2937;
88
+ border-radius: 8px;
89
+ padding: 8px 24px;
90
+ color: #e8e8e8 !important;
91
+ }
92
+
93
+ .stTabs [aria-selected="true"] {
94
+ background: linear-gradient(135deg, #00ff9d 0%, #00d4ff 100%) !important;
95
+ color: #0a0e17 !important;
96
+ font-weight: bold;
97
+ }
98
+
99
+ /* Buttons */
100
+ .stButton button {
101
+ background: linear-gradient(135deg, #00ff9d 0%, #00d4ff 100%) !important;
102
+ color: #0a0e17 !important;
103
+ font-weight: bold !important;
104
+ border: none !important;
105
+ border-radius: 8px !important;
106
+ }
107
+
108
+ /* File uploader */
109
+ .stFileUploader {
110
+ background-color: #1f2937 !important;
111
+ border: 2px dashed #374151 !important;
112
+ border-radius: 12px !important;
113
+ }
114
+
115
+ /* Expander */
116
+ .streamlit-expanderHeader {
117
+ background-color: #1f2937 !important;
118
+ color: #00ff9d !important;
119
+ border-radius: 8px;
120
+ }
121
+
122
+ /* Success/Info/Warning boxes */
123
+ .stAlert {
124
+ background-color: #1f2937 !important;
125
+ border: 1px solid #374151 !important;
126
+ border-radius: 10px !important;
127
+ }
128
+
129
+ .stAlert p, .stAlert div {
130
+ color: #e8e8e8 !important;
131
+ }
132
+
133
+ /* Dataframe */
134
+ .stDataFrame {
135
+ background-color: #111827 !important;
136
+ }
137
+
138
+ .stDataFrame thead th {
139
+ background-color: #1f2937 !important;
140
+ color: #00ff9d !important;
141
+ }
142
+
143
+ /* Text input */
144
+ .stTextInput input {
145
+ background-color: #1f2937 !important;
146
+ color: #e8e8e8 !important;
147
+ border: 1px solid #374151 !important;
148
+ border-radius: 8px !important;
149
+ }
150
+
151
+ /* Select box */
152
+ .stSelectbox div[data-baseweb="select"] {
153
+ background-color: #1f2937 !important;
154
+ border-color: #374151 !important;
155
+ }
156
+
157
+ /* Download button */
158
+ .stDownloadButton button {
159
+ background: linear-gradient(135deg, #00ff9d 0%, #00d4ff 100%) !important;
160
+ color: #0a0e17 !important;
161
+ }
162
+ </style>
163
+ """, unsafe_allow_html=True)
164
+
165
+ # Initialize session state
166
+ if 'data_loaded' not in st.session_state:
167
+ st.session_state.data_loaded = False
168
+ if 'df' not in st.session_state:
169
+ st.session_state.df = None
170
+ if 'schema' not in st.session_state:
171
+ st.session_state.schema = None
172
+ if 'analysis' not in st.session_state:
173
+ st.session_state.analysis = None
174
+ if 'insights' not in st.session_state:
175
+ st.session_state.insights = None
176
+ if 'charts' not in st.session_state:
177
+ st.session_state.charts = None
178
+ if 'use_openai' not in st.session_state:
179
+ st.session_state.use_openai = False
180
+
181
+ # Initialize managers
182
+ session_mgr = SessionManager()
183
+
184
+
185
+ def main():
186
+ st.markdown('<div class="main-header">πŸš€ Smart Analytics Copilot</div>', unsafe_allow_html=True)
187
+ st.caption("✨ Upload any CSV/JSON - AI analyzes, visualizes, and answers questions")
188
+ st.markdown("---")
189
+
190
+ # Sidebar
191
+ with st.sidebar:
192
+ st.markdown("### πŸ“ Data Source")
193
+
194
+ # Data source selection
195
+ source = st.radio("Choose data source:", ["πŸ“€ Upload File", "πŸ’Ύ Load Saved Session"])
196
+
197
+ if source == "πŸ“€ Upload File":
198
+ uploaded_file = st.file_uploader("Choose CSV or JSON", type=['csv', 'json'])
199
+ if uploaded_file and not st.session_state.data_loaded:
200
+ with st.spinner("πŸ”„ Processing your data..."):
201
+ process_data(uploaded_file)
202
+ else:
203
+ # Load saved sessions
204
+ sessions = session_mgr.list_sessions()
205
+ if sessions:
206
+ session_names = [s['name'] for s in sessions]
207
+ selected_session = st.selectbox("Select saved session:", session_names)
208
+ if st.button("πŸ“‚ Load Session"):
209
+ with st.spinner("Loading..."):
210
+ load_session(selected_session)
211
+ else:
212
+ st.info("No saved sessions found")
213
+
214
+ st.markdown("---")
215
+
216
+ # Settings
217
+ with st.expander("βš™οΈ Settings"):
218
+ st.session_state.use_openai = st.checkbox("Use OpenAI (better insights)",
219
+ value=st.session_state.use_openai)
220
+ if st.session_state.use_openai:
221
+ api_key = st.text_input("OpenAI API Key:", type="password")
222
+ if api_key:
223
+ os.environ['OPENAI_API_KEY'] = api_key
224
+ st.success("API Key set!")
225
+
226
+ st.markdown("---")
227
+
228
+ # Export section (only if data loaded)
229
+ if st.session_state.data_loaded:
230
+ st.markdown("### πŸ’Ύ Export Options")
231
+ export_utils = ExportUtils(st.session_state.df)
232
+
233
+ export_format = st.selectbox("Export format:",
234
+ ["CSV", "Excel", "JSON", "Power BI CSV", "Power BI ZIP (Complete)"])
235
+
236
+ if st.button("πŸ“₯ Download"):
237
+ if export_format == "CSV":
238
+ data = export_utils.to_csv()
239
+ mime = "text/csv"
240
+ ext = "csv"
241
+ elif export_format == "Excel":
242
+ data = export_utils.to_excel()
243
+ mime = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
244
+ ext = "xlsx"
245
+ elif export_format == "JSON":
246
+ data = export_utils.to_json()
247
+ mime = "application/json"
248
+ ext = "json"
249
+ elif export_format == "Power BI CSV":
250
+ data = export_utils.to_powerbi_ready()
251
+ mime = "text/csv"
252
+ ext = "csv"
253
+ else: # Power BI ZIP (Complete)
254
+ data = export_utils.to_powerbi_zip()
255
+ mime = "application/zip"
256
+ ext = "zip"
257
+
258
+ st.download_button(
259
+ label="βœ… Click to Download",
260
+ data=data,
261
+ file_name=f"export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.{ext}",
262
+ mime=mime
263
+ )
264
+
265
+ # Save session button
266
+ st.markdown("---")
267
+ if st.button("πŸ’Ύ Save Current Session"):
268
+ name, path = session_mgr.save_session(st.session_state.df, st.session_state.schema)
269
+ st.success(f"βœ… Session saved as: {name}")
270
+
271
+ # Main content
272
+ if st.session_state.data_loaded:
273
+ tab1, tab2, tab3, tab4, tab5 = st.tabs([
274
+ "πŸ“Š Dashboard", "πŸ’‘ AI Insights", "🎨 Custom Charts", "πŸ” Query", "πŸ“‹ Data"
275
+ ])
276
+
277
+ with tab1:
278
+ show_dashboard()
279
+
280
+ with tab2:
281
+ show_insights()
282
+
283
+ with tab3:
284
+ show_chart_customizer()
285
+
286
+ with tab4:
287
+ show_query_interface()
288
+
289
+ with tab5:
290
+ show_data_preview()
291
+
292
+ else:
293
+ show_welcome()
294
+
295
+
296
+ def process_data(uploaded_file):
297
+ """Process uploaded data"""
298
+ try:
299
+ processor = DataProcessor()
300
+ st.session_state.df = processor.load_from_upload(uploaded_file)
301
+ st.session_state.df = processor.preprocess()
302
+ st.session_state.schema = processor.detect_schema()
303
+
304
+ analyzer = Analyzer(st.session_state.df, st.session_state.schema)
305
+ st.session_state.analysis = analyzer.run_full_analysis()
306
+
307
+ # Use OpenAI if enabled
308
+ api_key = os.environ.get('OPENAI_API_KEY')
309
+ insight_gen = InsightGenerator(use_openai=st.session_state.use_openai, api_key=api_key)
310
+ st.session_state.insights = insight_gen.generate_insights(
311
+ st.session_state.df,
312
+ st.session_state.schema,
313
+ st.session_state.analysis
314
+ )
315
+
316
+ dashboard_gen = DashboardGenerator(st.session_state.df, st.session_state.schema)
317
+ st.session_state.charts = dashboard_gen.generate_all_charts()
318
+
319
+ st.session_state.data_loaded = True
320
+ st.success(f"βœ… Successfully loaded {len(st.session_state.df):,} rows with {len(st.session_state.df.columns)} columns")
321
+ st.balloons()
322
+ st.rerun()
323
+ except Exception as e:
324
+ st.error(f"Error: {e}")
325
+
326
+
327
+ def load_session(session_name):
328
+ """Load saved session and regenerate insights"""
329
+ session = session_mgr.load_session(session_name)
330
+ if session:
331
+ st.session_state.df = session['df']
332
+ st.session_state.schema = session['schema']
333
+
334
+ # Regenerate analysis and insights for loaded session
335
+ with st.spinner("πŸ”„ Regenerating analysis..."):
336
+ analyzer = Analyzer(st.session_state.df, st.session_state.schema)
337
+ st.session_state.analysis = analyzer.run_full_analysis()
338
+
339
+ # Regenerate insights
340
+ api_key = os.environ.get('OPENAI_API_KEY')
341
+ insight_gen = InsightGenerator(use_openai=st.session_state.use_openai, api_key=api_key)
342
+ st.session_state.insights = insight_gen.generate_insights(
343
+ st.session_state.df,
344
+ st.session_state.schema,
345
+ st.session_state.analysis
346
+ )
347
+
348
+ # Regenerate charts
349
+ dashboard_gen = DashboardGenerator(st.session_state.df, st.session_state.schema)
350
+ st.session_state.charts = dashboard_gen.generate_all_charts()
351
+
352
+ st.session_state.data_loaded = True
353
+ st.success(f"βœ… Loaded session: {session_name}")
354
+ st.rerun()
355
+ else:
356
+ st.error("Failed to load session")
357
+
358
+
359
+ def show_dashboard():
360
+ """Display dashboard"""
361
+ st.markdown("### πŸ“ˆ Key Metrics")
362
+ st.markdown("---")
363
+
364
+ # Check if data exists
365
+ if st.session_state.df is None:
366
+ st.warning("No data loaded. Please upload a file first.")
367
+ return
368
+
369
+ # Display metrics
370
+ if st.session_state.schema['numeric']:
371
+ cols = st.columns(min(4, len(st.session_state.schema['numeric'])))
372
+ for idx, col in enumerate(st.session_state.schema['numeric'][:4]):
373
+ with cols[idx]:
374
+ total = st.session_state.df[col].sum()
375
+ avg = st.session_state.df[col].mean()
376
+ st.metric(
377
+ label=f"πŸ’° {col.upper()}",
378
+ value=f"{total:,.0f}",
379
+ delta=f"Avg: {avg:,.0f}"
380
+ )
381
+
382
+ st.markdown("---")
383
+ st.markdown("### πŸ“Š Visualizations")
384
+
385
+ if st.session_state.charts:
386
+ for chart in st.session_state.charts[:4]:
387
+ st.plotly_chart(chart['figure'], use_container_width=True)
388
+ else:
389
+ st.info("No charts available. Try uploading data first.")
390
+
391
+ st.markdown("---")
392
+ st.markdown("### πŸ“‹ Summary Statistics")
393
+ if st.session_state.schema['numeric']:
394
+ summary = st.session_state.df[st.session_state.schema['numeric']].describe()
395
+ st.dataframe(summary, use_container_width=True)
396
+
397
+
398
+ def show_insights():
399
+ """Display AI insights"""
400
+ st.markdown("### 🧠 AI-Powered Insights")
401
+ st.markdown("Here's what we discovered in your data:")
402
+ st.markdown("---")
403
+
404
+ # Check if insights exist
405
+ if st.session_state.insights is None:
406
+ st.info("πŸ’‘ Insights will appear after data is analyzed.")
407
+ return
408
+
409
+ for insight in st.session_state.insights:
410
+ if "Dataset" in insight:
411
+ st.info(f"πŸ“Š {insight}")
412
+ elif "correlation" in insight.lower():
413
+ st.success(f"βœ… {insight}")
414
+ elif "skewed" in insight.lower():
415
+ st.warning(f"πŸ“ˆ {insight}")
416
+ elif "Recommendation" in insight:
417
+ st.info(f"πŸ’‘ {insight}")
418
+ else:
419
+ st.markdown(f"β€’ {insight}")
420
+
421
+ # Power BI template section
422
+ st.markdown("---")
423
+ with st.expander("πŸ“Š Power BI Resources"):
424
+ export_utils = ExportUtils(st.session_state.df)
425
+
426
+ col1, col2 = st.columns(2)
427
+
428
+ with col1:
429
+ # Show DAX template
430
+ template = export_utils.get_powerbi_template()
431
+ st.code(template, language="dax")
432
+
433
+ st.download_button(
434
+ label="πŸ“₯ Download DAX Template",
435
+ data=template,
436
+ file_name="powerbi_measures.dax",
437
+ mime="text/plain"
438
+ )
439
+
440
+ with col2:
441
+ # Show instructions
442
+ instructions = """
443
+ **Power BI Import Steps:**
444
+
445
+ 1. **Export Data**: Use sidebar to export as "Power BI CSV"
446
+ 2. **Open Power BI Desktop**
447
+ 3. **Get Data** β†’ **Text/CSV**
448
+ 4. **Select your exported CSV**
449
+ 5. **Click Load**
450
+ 6. **Copy DAX measures** from above
451
+ 7. **Create visuals** using the measures
452
+ """
453
+ st.info(instructions)
454
+
455
+
456
+ def show_chart_customizer():
457
+ """Show chart customization interface"""
458
+ st.markdown("### 🎨 Custom Chart Builder")
459
+ st.markdown("Create your own custom visualizations")
460
+ st.markdown("---")
461
+
462
+ customizer = ChartCustomizer(st.session_state.df)
463
+ available_charts = customizer.get_available_charts()
464
+
465
+ col1, col2, col3 = st.columns([1, 1, 1])
466
+
467
+ with col1:
468
+ chart_type = st.selectbox("Chart Type:", available_charts)
469
+
470
+ with col2:
471
+ # Get appropriate columns
472
+ if 'Histogram' in chart_type or 'Box' in chart_type:
473
+ columns = st.session_state.schema['numeric']
474
+ if not columns:
475
+ columns = list(st.session_state.df.select_dtypes(include=['number']).columns)
476
+ elif 'Pie' in chart_type or 'Bar' in chart_type:
477
+ columns = st.session_state.schema['categorical']
478
+ if not columns:
479
+ columns = list(st.session_state.df.select_dtypes(include=['object']).columns)
480
+ else:
481
+ columns = list(st.session_state.df.columns)
482
+
483
+ if columns:
484
+ x_col = st.selectbox("X-Axis / Category:", columns)
485
+ else:
486
+ x_col = None
487
+ st.warning("No suitable columns found")
488
+
489
+ with col3:
490
+ # For charts that need Y-axis
491
+ if any(t in chart_type for t in ['Line', 'Scatter', 'Bar']) and 'Histogram' not in chart_type:
492
+ y_cols = ['None'] + st.session_state.schema['numeric']
493
+ y_col = st.selectbox("Y-Axis / Value:", y_cols)
494
+ y_col = None if y_col == 'None' else y_col
495
+ else:
496
+ y_col = None
497
+
498
+ # Color column (optional)
499
+ color_cols = ['None'] + st.session_state.schema['categorical']
500
+ color_col = st.selectbox("Color By (optional):", color_cols)
501
+ color_col = None if color_col == 'None' else color_col
502
+
503
+ # Title
504
+ title = st.text_input("Chart Title:", value=f"{chart_type} of {x_col if x_col else 'data'}")
505
+
506
+ if st.button("🎨 Generate Chart", use_container_width=True):
507
+ if x_col:
508
+ with st.spinner("Creating chart..."):
509
+ fig = customizer.create_chart(chart_type, x_col, y_col, color_col, title)
510
+ if fig:
511
+ st.plotly_chart(fig, use_container_width=True)
512
+
513
+ # Download chart button
514
+ try:
515
+ st.download_button(
516
+ label="πŸ“Έ Download as PNG",
517
+ data=fig.to_image(format="png"),
518
+ file_name="custom_chart.png",
519
+ mime="image/png"
520
+ )
521
+ except:
522
+ st.info("πŸ’‘ Install kaleido for PNG export: `pip install kaleido`")
523
+ else:
524
+ st.error("Could not create chart. Try different settings.")
525
+ else:
526
+ st.error("Please select a column for X-Axis")
527
+
528
+
529
+ def show_query_interface():
530
+ """Natural language query interface"""
531
+ st.markdown("### πŸ’¬ Natural Language Query")
532
+ st.markdown("Ask any question about your data in plain English:")
533
+ st.markdown("---")
534
+
535
+ query_engine = QueryEngine(st.session_state.df, st.session_state.schema)
536
+
537
+ # Example questions
538
+ with st.expander("πŸ” View Example Questions"):
539
+ if st.session_state.schema['numeric']:
540
+ example_col = st.session_state.schema['numeric'][0]
541
+ st.markdown(f"β€’ 'Statistics {example_col}'")
542
+ st.markdown(f"β€’ 'Total {example_col}'")
543
+ st.markdown(f"β€’ 'Average {example_col}'")
544
+
545
+ if st.session_state.schema['categorical'] and st.session_state.schema['numeric']:
546
+ st.markdown(f"β€’ 'Top 5 {st.session_state.schema['categorical'][0]} by {st.session_state.schema['numeric'][0]}'")
547
+
548
+ st.markdown("β€’ 'Summary statistics'")
549
+ st.markdown("β€’ 'Show me the data'")
550
+
551
+ st.markdown("---")
552
+
553
+ question = st.text_input("Ask a question:", placeholder="e.g., What is the average of time_in_hospital?")
554
+
555
+ if question:
556
+ with st.spinner("πŸ€” Analyzing your question..."):
557
+ answer = query_engine.answer_question(question)
558
+ st.markdown("### βœ… Answer")
559
+ st.success(answer)
560
+
561
+
562
+ def show_data_preview():
563
+ """Show data preview and info with better formatting"""
564
+ st.markdown("### πŸ“‹ Data Preview")
565
+ st.markdown("---")
566
+
567
+ col1, col2, col3 = st.columns(3)
568
+ with col1:
569
+ st.metric("πŸ“Š Total Rows", f"{len(st.session_state.df):,}")
570
+ with col2:
571
+ st.metric("πŸ“‹ Total Columns", len(st.session_state.df.columns))
572
+ with col3:
573
+ memory = st.session_state.df.memory_usage(deep=True).sum() / 1024**2
574
+ st.metric("πŸ’Ύ Memory Usage", f"{memory:.2f} MB")
575
+
576
+ st.markdown("---")
577
+ st.markdown("### πŸ“„ Data Sample (First 100 rows)")
578
+
579
+ # Create a copy for display
580
+ display_df = st.session_state.df.head(100).copy()
581
+
582
+ # Clean datetime columns for better display
583
+ for col in display_df.columns:
584
+ if 'datetime' in col.lower() or 'date' in col.lower() or 'time' in col.lower():
585
+ try:
586
+ display_df[col] = pd.to_datetime(display_df[col]).dt.strftime('%Y-%m-%d %H:%M:%S')
587
+ except:
588
+ pass
589
+
590
+ st.dataframe(display_df, use_container_width=True)
591
+
592
+ st.markdown("---")
593
+ st.markdown("### πŸ“Š Column Information")
594
+
595
+ col_info = pd.DataFrame({
596
+ 'Column': st.session_state.df.columns,
597
+ 'Type': st.session_state.df.dtypes.astype(str),
598
+ 'Non-Null': st.session_state.df.count().values,
599
+ 'Nulls': st.session_state.df.isnull().sum().values,
600
+ 'Unique': st.session_state.df.nunique().values
601
+ })
602
+ st.dataframe(col_info, use_container_width=True)
603
+
604
+
605
+ def show_welcome():
606
+ """Welcome screen"""
607
+ st.markdown("""
608
+ <div style="text-align: center; padding: 2rem; background: linear-gradient(135deg, #111827 0%, #0a0e17 100%); border-radius: 20px; margin: 2rem 0;">
609
+ <h2 style="color: #00ff9d;">πŸš€ Welcome to Smart Analytics Copilot</h2>
610
+ <p style="font-size: 1.1rem;">Upload any CSV or JSON file and let AI analyze it instantly</p>
611
+ <hr>
612
+ <p>πŸ‘ˆ <strong>Get Started</strong>: Upload a file or load a saved session from the sidebar</p>
613
+ </div>
614
+ """, unsafe_allow_html=True)
615
+
616
+ col1, col2, col3 = st.columns(3)
617
+
618
+ with col1:
619
+ st.markdown("""
620
+ <div style="background: linear-gradient(135deg, #1f2937 0%, #111827 100%); padding: 1.5rem; border-radius: 15px; text-align: center;">
621
+ <h3 style="color: #00ff9d;">πŸ“Š Auto Dashboard</h3>
622
+ <p>Smart charts based on your data</p>
623
+ </div>
624
+ """, unsafe_allow_html=True)
625
+
626
+ with col2:
627
+ st.markdown("""
628
+ <div style="background: linear-gradient(135deg, #1f2937 0%, #111827 100%); padding: 1.5rem; border-radius: 15px; text-align: center;">
629
+ <h3 style="color: #00ff9d;">πŸ’‘ AI Insights</h3>
630
+ <p>Natural language explanations</p>
631
+ </div>
632
+ """, unsafe_allow_html=True)
633
+
634
+ with col3:
635
+ st.markdown("""
636
+ <div style="background: linear-gradient(135deg, #1f2937 0%, #111827 100%); padding: 1.5rem; border-radius: 15px; text-align: center;">
637
+ <h3 style="color: #00ff9d;">🎨 Custom Charts</h3>
638
+ <p>Build your own visualizations</p>
639
+ </div>
640
+ """, unsafe_allow_html=True)
641
+
642
+
643
+ if __name__ == "__main__":
 
 
644
  main()