PD03 commited on
Commit
a8d5e0a
Β·
verified Β·
1 Parent(s): a5eb38c

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +439 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,441 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
1
  import streamlit as st
2
+ import pandas as pd
3
+ import plotly.express as px
4
+ from streamlit_option_menu import option_menu
5
+ import numpy as np
6
+ from datetime import datetime, timedelta
7
+
8
+ # Import our modules
9
+ from data.synthetic_data import SAPDataGenerator
10
+ from agents.procurement_agent import ProcurementAgent
11
+ from utils.charts import ProcurementCharts
12
+
13
+ # Page configuration
14
+ st.set_page_config(
15
+ page_title="πŸš€ SAP S/4HANA Procurement AI",
16
+ page_icon="πŸš€",
17
+ layout="wide",
18
+ initial_sidebar_state="expanded"
19
+ )
20
+
21
+ # Custom CSS for beautiful UI
22
+ st.markdown("""
23
+ <style>
24
+ .main-header {
25
+ font-size: 3rem;
26
+ font-weight: bold;
27
+ text-align: center;
28
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
29
+ -webkit-background-clip: text;
30
+ -webkit-text-fill-color: transparent;
31
+ margin-bottom: 2rem;
32
+ }
33
+
34
+ .metric-card {
35
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
36
+ padding: 1rem;
37
+ border-radius: 10px;
38
+ color: white;
39
+ text-align: center;
40
+ margin: 0.5rem 0;
41
+ }
42
+
43
+ .insight-box {
44
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
45
+ padding: 1.5rem;
46
+ border-radius: 15px;
47
+ color: white;
48
+ margin: 1rem 0;
49
+ }
50
+
51
+ .recommendation-box {
52
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
53
+ padding: 1rem;
54
+ border-radius: 10px;
55
+ color: white;
56
+ margin: 0.5rem 0;
57
+ }
58
+
59
+ .sidebar .sidebar-content {
60
+ background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
61
+ }
62
+ </style>
63
+ """, unsafe_allow_html=True)
64
+
65
+ # Initialize session state
66
+ if 'data_loaded' not in st.session_state:
67
+ st.session_state.data_loaded = False
68
+ st.session_state.po_data = None
69
+ st.session_state.supplier_data = None
70
+ st.session_state.spend_data = None
71
+
72
+ @st.cache_data
73
+ def load_synthetic_data():
74
+ """Load and cache synthetic data"""
75
+ generator = SAPDataGenerator()
76
+ po_data = generator.generate_purchase_orders(1000)
77
+ supplier_data = generator.generate_supplier_performance()
78
+ spend_data = generator.generate_spend_analysis()
79
+ return po_data, supplier_data, spend_data
80
+
81
+ def main():
82
+ # Main header
83
+ st.markdown('<h1 class="main-header">πŸš€ SAP S/4HANA Procurement AI Assistant</h1>', unsafe_allow_html=True)
84
+ st.markdown('<p style="text-align: center; font-size: 1.2rem; color: #666;">Intelligent Procurement Analytics with AI-Powered Insights</p>', unsafe_allow_html=True)
85
+
86
+ # Load data
87
+ if not st.session_state.data_loaded:
88
+ with st.spinner("πŸ”„ Loading SAP S/4HANA Data..."):
89
+ po_data, supplier_data, spend_data = load_synthetic_data()
90
+ st.session_state.po_data = po_data
91
+ st.session_state.supplier_data = supplier_data
92
+ st.session_state.spend_data = spend_data
93
+ st.session_state.data_loaded = True
94
+
95
+ # Sidebar navigation
96
+ with st.sidebar:
97
+ st.image("https://via.placeholder.com/200x80/667eea/white?text=SAP+S/4HANA", width=200)
98
+
99
+ selected = option_menu(
100
+ menu_title="Navigation",
101
+ options=["🏠 Dashboard", "πŸ“Š Analytics", "πŸ€– AI Insights", "πŸ” Deep Dive", "βš™οΈ Settings"],
102
+ icons=["house", "graph-up", "robot", "search", "gear"],
103
+ menu_icon="cast",
104
+ default_index=0,
105
+ styles={
106
+ "container": {"padding": "0!important", "background-color": "#fafafa"},
107
+ "icon": {"color": "#667eea", "font-size": "18px"},
108
+ "nav-link": {"font-size": "16px", "text-align": "left", "margin": "0px", "--hover-color": "#eee"},
109
+ "nav-link-selected": {"background-color": "#667eea"},
110
+ }
111
+ )
112
+
113
+ # Initialize AI agent
114
+ agent = ProcurementAgent()
115
+ charts = ProcurementCharts()
116
+
117
+ # Main content based on selection
118
+ if selected == "🏠 Dashboard":
119
+ show_dashboard(st.session_state.po_data, st.session_state.supplier_data, charts)
120
+ elif selected == "πŸ“Š Analytics":
121
+ show_analytics(st.session_state.po_data, st.session_state.spend_data, charts)
122
+ elif selected == "πŸ€– AI Insights":
123
+ show_ai_insights(st.session_state.po_data, st.session_state.supplier_data, agent)
124
+ elif selected == "πŸ” Deep Dive":
125
+ show_deep_dive(st.session_state.po_data, st.session_state.supplier_data)
126
+ elif selected == "βš™οΈ Settings":
127
+ show_settings()
128
+
129
+ def show_dashboard(po_data, supplier_data, charts):
130
+ st.subheader("πŸ“ˆ Executive Dashboard")
131
+
132
+ # KPI Metrics
133
+ col1, col2, col3, col4 = st.columns(4)
134
+
135
+ total_spend = po_data['Total_Value'].sum()
136
+ total_pos = len(po_data)
137
+ avg_delivery = po_data['Delivery_Performance'].mean()
138
+ top_supplier = po_data.groupby('Supplier')['Total_Value'].sum().idxmax()
139
+
140
+ with col1:
141
+ st.markdown(f"""
142
+ <div class="metric-card">
143
+ <h3>πŸ’° Total Spend</h3>
144
+ <h2>${total_spend:,.0f}</h2>
145
+ </div>
146
+ """, unsafe_allow_html=True)
147
+
148
+ with col2:
149
+ st.markdown(f"""
150
+ <div class="metric-card">
151
+ <h3>πŸ“‹ Purchase Orders</h3>
152
+ <h2>{total_pos:,}</h2>
153
+ </div>
154
+ """, unsafe_allow_html=True)
155
+
156
+ with col3:
157
+ st.markdown(f"""
158
+ <div class="metric-card">
159
+ <h3>🎯 Avg Delivery</h3>
160
+ <h2>{avg_delivery:.1f}%</h2>
161
+ </div>
162
+ """, unsafe_allow_html=True)
163
+
164
+ with col4:
165
+ st.markdown(f"""
166
+ <div class="metric-card">
167
+ <h3>πŸ† Top Supplier</h3>
168
+ <h2>{top_supplier}</h2>
169
+ </div>
170
+ """, unsafe_allow_html=True)
171
+
172
+ st.markdown("---")
173
+
174
+ # Charts
175
+ col1, col2 = st.columns(2)
176
+
177
+ with col1:
178
+ fig_trend = charts.create_spend_trend_chart(po_data)
179
+ st.plotly_chart(fig_trend, use_container_width=True)
180
+
181
+ with col2:
182
+ fig_category = charts.create_category_pie_chart(po_data)
183
+ st.plotly_chart(fig_category, use_container_width=True)
184
+
185
+ # Status and Performance
186
+ col1, col2 = st.columns(2)
187
+
188
+ with col1:
189
+ fig_status = charts.create_status_donut_chart(po_data)
190
+ st.plotly_chart(fig_status, use_container_width=True)
191
+
192
+ with col2:
193
+ fig_supplier = charts.create_supplier_performance_chart(po_data)
194
+ st.plotly_chart(fig_supplier, use_container_width=True)
195
+
196
+ def show_analytics(po_data, spend_data, charts):
197
+ st.subheader("πŸ“Š Advanced Analytics")
198
+
199
+ # Filter controls
200
+ col1, col2, col3 = st.columns(3)
201
+
202
+ with col1:
203
+ selected_suppliers = st.multiselect(
204
+ "Select Suppliers:",
205
+ options=po_data['Supplier'].unique(),
206
+ default=po_data['Supplier'].unique()[:5]
207
+ )
208
+
209
+ with col2:
210
+ selected_categories = st.multiselect(
211
+ "Select Categories:",
212
+ options=po_data['Category'].unique(),
213
+ default=po_data['Category'].unique()[:5]
214
+ )
215
+
216
+ with col3:
217
+ date_range = st.date_input(
218
+ "Date Range:",
219
+ value=(po_data['PO_Date'].min(), po_data['PO_Date'].max()),
220
+ min_value=po_data['PO_Date'].min(),
221
+ max_value=po_data['PO_Date'].max()
222
+ )
223
+
224
+ # Filter data
225
+ filtered_data = po_data[
226
+ (po_data['Supplier'].isin(selected_suppliers)) &
227
+ (po_data['Category'].isin(selected_categories))
228
+ ]
229
+
230
+ st.markdown("---")
231
+
232
+ # Advanced Charts
233
+ tab1, tab2, tab3 = st.tabs(["πŸ“ˆ Trends", "🏒 Suppliers", "πŸ“¦ Categories"])
234
+
235
+ with tab1:
236
+ col1, col2 = st.columns(2)
237
+ with col1:
238
+ # Monthly trend
239
+ fig_trend = charts.create_spend_trend_chart(filtered_data)
240
+ st.plotly_chart(fig_trend, use_container_width=True)
241
+
242
+ with col2:
243
+ # Delivery performance over time
244
+ monthly_delivery = filtered_data.groupby(filtered_data['PO_Date'].dt.to_period('M'))['Delivery_Performance'].mean().reset_index()
245
+ monthly_delivery['PO_Date'] = monthly_delivery['PO_Date'].astype(str)
246
+
247
+ fig = px.bar(monthly_delivery, x='PO_Date', y='Delivery_Performance',
248
+ title='🚚 Monthly Delivery Performance',
249
+ color='Delivery_Performance',
250
+ color_continuous_scale='RdYlGn')
251
+ fig.update_layout(height=400, plot_bgcolor='rgba(0,0,0,0)')
252
+ st.plotly_chart(fig, use_container_width=True)
253
+
254
+ with tab2:
255
+ # Supplier analysis
256
+ supplier_summary = filtered_data.groupby('Supplier').agg({
257
+ 'Total_Value': ['sum', 'mean', 'count'],
258
+ 'Delivery_Performance': 'mean'
259
+ }).round(2)
260
+
261
+ supplier_summary.columns = ['Total Spend', 'Avg PO Value', 'PO Count', 'Delivery %']
262
+ supplier_summary = supplier_summary.reset_index()
263
+
264
+ st.dataframe(
265
+ supplier_summary.style.highlight_max(axis=0),
266
+ use_container_width=True,
267
+ height=400
268
+ )
269
+
270
+ with tab3:
271
+ # Category deep dive
272
+ category_analysis = filtered_data.groupby('Category').agg({
273
+ 'Total_Value': 'sum',
274
+ 'Quantity': 'sum',
275
+ 'Unit_Price': 'mean',
276
+ 'Delivery_Performance': 'mean'
277
+ }).round(2)
278
+
279
+ st.dataframe(
280
+ category_analysis.style.highlight_max(axis=0),
281
+ use_container_width=True,
282
+ height=400
283
+ )
284
+
285
+ def show_ai_insights(po_data, supplier_data, agent):
286
+ st.subheader("πŸ€– AI-Powered Procurement Insights")
287
+
288
+ # Generate insights
289
+ with st.spinner("🧠 AI Agent is analyzing your procurement data..."):
290
+ insights = agent.generate_insights(po_data, supplier_data)
291
+
292
+ # Executive Summary
293
+ st.markdown(f"""
294
+ <div class="insight-box">
295
+ <h3>πŸ“‹ Executive Summary</h3>
296
+ {insights['summary']}
297
+ </div>
298
+ """, unsafe_allow_html=True)
299
+
300
+ # Tabs for different insights
301
+ tab1, tab2, tab3 = st.tabs(["πŸ’° Spend Analysis", "🏒 Supplier Intelligence", "⚠️ Risk Alerts"])
302
+
303
+ with tab1:
304
+ spend_insights = insights['spend_analysis']
305
+
306
+ col1, col2 = st.columns(2)
307
+ with col1:
308
+ st.metric("Total Spend", f"${spend_insights.get('total_spend', 0):,.2f}")
309
+ st.metric("Avg PO Value", f"${spend_insights.get('avg_po_value', 0):,.2f}")
310
+
311
+ with col2:
312
+ st.metric("Spending Trend", spend_insights.get('monthly_trend', 'N/A').title())
313
+ st.metric("Top Category", spend_insights.get('top_category', 'N/A'))
314
+
315
+ st.subheader("🎯 AI Recommendations")
316
+ for recommendation in spend_insights.get('recommendations', []):
317
+ st.markdown(f"""
318
+ <div class="recommendation-box">
319
+ {recommendation}
320
+ </div>
321
+ """, unsafe_allow_html=True)
322
+
323
+ with tab2:
324
+ supplier_insights = insights['supplier_analysis']
325
+
326
+ col1, col2 = st.columns(2)
327
+ with col1:
328
+ best = supplier_insights.get('best_performer', {})
329
+ st.success(f"πŸ† Best Performer: {best.get('name', 'N/A')} ({best.get('performance', 0):.1f}%)")
330
+
331
+ with col2:
332
+ worst = supplier_insights.get('worst_performer', {})
333
+ st.error(f"⚠️ Needs Improvement: {worst.get('name', 'N/A')} ({worst.get('performance', 0):.1f}%)")
334
+
335
+ st.subheader("πŸ“ˆ Supplier Recommendations")
336
+ for recommendation in supplier_insights.get('recommendations', []):
337
+ st.markdown(f"""
338
+ <div class="recommendation-box">
339
+ {recommendation}
340
+ </div>
341
+ """, unsafe_allow_html=True)
342
+
343
+ with tab3:
344
+ anomalies = insights['anomalies']
345
+
346
+ if anomalies:
347
+ st.subheader(f"🚨 {len(anomalies)} Critical Issues Detected")
348
+
349
+ for anomaly in anomalies:
350
+ risk_color = {"High": "πŸ”΄", "Medium": "🟑", "Low": "🟒"}
351
+
352
+ st.markdown(f"""
353
+ <div class="recommendation-box">
354
+ <strong>{risk_color.get(anomaly.get('risk_level', 'Medium'), '🟑')} {anomaly['type']}</strong><br>
355
+ PO: {anomaly.get('po_number', 'N/A')} | Supplier: {anomaly.get('supplier', 'N/A')}<br>
356
+ Risk Level: {anomaly.get('risk_level', 'Unknown')}
357
+ </div>
358
+ """, unsafe_allow_html=True)
359
+ else:
360
+ st.success("πŸŽ‰ No critical issues detected in your procurement data!")
361
+
362
+ def show_deep_dive(po_data, supplier_data):
363
+ st.subheader("πŸ” Deep Dive Analysis")
364
+
365
+ # Data explorer
366
+ st.subheader("πŸ“Š Purchase Orders Data Explorer")
367
+
368
+ # Search and filter
369
+ col1, col2, col3 = st.columns(3)
370
+ with col1:
371
+ search_po = st.text_input("πŸ” Search PO Number:")
372
+ with col2:
373
+ filter_status = st.selectbox("Filter by Status:", ['All'] + list(po_data['Status'].unique()))
374
+ with col3:
375
+ min_value = st.number_input("Min PO Value:", min_value=0, value=0)
376
+
377
+ # Apply filters
378
+ filtered_po = po_data.copy()
379
+
380
+ if search_po:
381
+ filtered_po = filtered_po[filtered_po['PO_Number'].str.contains(search_po, case=False)]
382
+
383
+ if filter_status != 'All':
384
+ filtered_po = filtered_po[filtered_po['Status'] == filter_status]
385
+
386
+ if min_value > 0:
387
+ filtered_po = filtered_po[filtered_po['Total_Value'] >= min_value]
388
+
389
+ # Display filtered data
390
+ st.dataframe(
391
+ filtered_po.style.highlight_max(axis=0),
392
+ use_container_width=True,
393
+ height=400
394
+ )
395
+
396
+ # Download data
397
+ csv = filtered_po.to_csv(index=False)
398
+ st.download_button(
399
+ label="πŸ“₯ Download Filtered Data",
400
+ data=csv,
401
+ file_name=f"procurement_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
402
+ mime="text/csv"
403
+ )
404
+
405
+ def show_settings():
406
+ st.subheader("βš™οΈ Application Settings")
407
+
408
+ st.info("πŸš€ **Demo Configuration**")
409
+
410
+ col1, col2 = st.columns(2)
411
+
412
+ with col1:
413
+ st.markdown("### πŸ“Š Data Settings")
414
+ data_refresh = st.button("πŸ”„ Refresh Synthetic Data")
415
+ if data_refresh:
416
+ st.session_state.data_loaded = False
417
+ st.rerun()
418
+
419
+ st.markdown("### 🎨 Theme Settings")
420
+ theme = st.selectbox("Choose Theme:", ["Default", "Dark", "Light"])
421
+
422
+ with col2:
423
+ st.markdown("### πŸ€– AI Settings")
424
+ ai_model = st.selectbox("AI Model:", ["GPT-4", "Claude", "Local Model"])
425
+ confidence = st.slider("Confidence Threshold:", 0.0, 1.0, 0.8)
426
+
427
+ st.markdown("### πŸ“ˆ Chart Settings")
428
+ chart_style = st.selectbox("Chart Style:", ["Modern", "Classic", "Minimal"])
429
+
430
+ st.markdown("---")
431
+ st.markdown("### πŸ“‹ Application Info")
432
+ st.json({
433
+ "version": "1.0.0",
434
+ "framework": "Streamlit",
435
+ "data_source": "Synthetic SAP S/4HANA",
436
+ "ai_agent": "Custom Procurement Agent",
437
+ "last_updated": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
438
+ })
439
 
440
+ if __name__ == "__main__":
441
+ main()