PD03 commited on
Commit
26c7bcc
·
verified ·
1 Parent(s): a550e54

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +558 -256
src/streamlit_app.py CHANGED
@@ -8,7 +8,7 @@ import random
8
 
9
  # Page configuration
10
  st.set_page_config(
11
- page_title="Rane Group - Ecosystem Supply Chain Hub",
12
  page_icon="🌐",
13
  layout="wide",
14
  initial_sidebar_state="expanded"
@@ -25,6 +25,20 @@ st.markdown("""
25
  text-align: center;
26
  margin-bottom: 2rem;
27
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  .ecosystem-alert {
29
  background: #fef2f2;
30
  padding: 1rem;
@@ -32,6 +46,28 @@ st.markdown("""
32
  border-left: 6px solid #dc2626;
33
  margin: 0.5rem 0;
34
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  .tier-impact {
36
  background: #fff7ed;
37
  padding: 0.8rem;
@@ -54,19 +90,19 @@ st.markdown("""
54
  margin: 0.3rem 0;
55
  border-left: 4px solid #3b82f6;
56
  }
57
- .supplier-card {
58
- background: #f8fafc;
59
- padding: 1rem;
60
- border-radius: 8px;
61
- border: 1px solid #e2e8f0;
62
- margin: 0.5rem 0;
63
  }
64
- .impact-timeline {
65
- background: #fefce8;
66
  padding: 0.6rem;
67
  border-radius: 6px;
 
68
  margin: 0.2rem 0;
69
- border-left: 3px solid #eab308;
70
  }
71
  </style>
72
  """, unsafe_allow_html=True)
@@ -74,8 +110,78 @@ st.markdown("""
74
  # Initialize session state
75
  if 'executed_mitigations' not in st.session_state:
76
  st.session_state.executed_mitigations = []
77
- if 'supplier_disruptions' not in st.session_state:
78
- st.session_state.supplier_disruptions = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
  # Tier 2 Supplier Data
81
  @st.cache_data
@@ -184,7 +290,68 @@ def generate_ecosystem_data():
184
 
185
  return pd.DataFrame(all_data)
186
 
187
- # Generate mitigation strategies
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  def generate_mitigation_strategies(supplier, material, impact_amount, impact_days):
189
  base_strategies = [
190
  {
@@ -194,7 +361,6 @@ def generate_mitigation_strategies(supplier, material, impact_amount, impact_day
194
  'cost': 'High (+15% unit cost)',
195
  'effectiveness': '90%',
196
  'capacity': f'+{impact_amount * 0.9:.0f} units/day',
197
- 'implementation_time': 2
198
  },
199
  {
200
  'strategy': 'Emergency Air Freight',
@@ -203,7 +369,6 @@ def generate_mitigation_strategies(supplier, material, impact_amount, impact_day
203
  'cost': 'Very High (+40% logistics cost)',
204
  'effectiveness': '75%',
205
  'capacity': f'+{impact_amount * 0.75:.0f} units/day',
206
- 'implementation_time': 0.5
207
  },
208
  {
209
  'strategy': 'Inventory Reallocation',
@@ -212,301 +377,438 @@ def generate_mitigation_strategies(supplier, material, impact_amount, impact_day
212
  'cost': 'Medium (+5% handling cost)',
213
  'effectiveness': '60%',
214
  'capacity': f'+{impact_amount * 0.6:.0f} units/day',
215
- 'implementation_time': 1
216
- },
217
- {
218
- 'strategy': 'Production Rescheduling',
219
- 'description': f'Reschedule production to prioritize {material}',
220
- 'timeline': '4-8 hours',
221
- 'cost': 'Low (internal reorg)',
222
- 'effectiveness': '45%',
223
- 'capacity': f'+{impact_amount * 0.45:.0f} units/day',
224
- 'implementation_time': 0.3
225
- },
226
- {
227
- 'strategy': 'Customer Communication',
228
- 'description': 'Proactive communication with OEM customers',
229
- 'timeline': '1-2 hours',
230
- 'cost': 'Very Low',
231
- 'effectiveness': '30% (delay mitigation)',
232
- 'capacity': 'Manages expectations',
233
- 'implementation_time': 0.1
234
  }
235
  ]
236
 
237
  # AI recommendation logic
238
  if impact_amount > 100: # Critical impact
239
- recommended = [0, 1, 4] # Alternate supplier + Air freight + Communication
240
  elif impact_amount > 50: # High impact
241
- recommended = [0, 2, 4] # Alternate supplier + Reallocation + Communication
242
  else: # Medium impact
243
- recommended = [2, 3, 4] # Reallocation + Rescheduling + Communication
244
 
245
  return base_strategies, recommended
246
 
247
  # Load data
248
- df = generate_ecosystem_data()
 
 
249
  suppliers = get_tier2_suppliers()
250
 
251
  # Header
252
  st.markdown("""
253
  <div class="main-header">
254
- <h1>🌐 Rane Group - Ecosystem Supply Chain Command Center</h1>
255
- <h3>Tier 2 Supplier Impact Analysis & Agentic AI Mitigation</h3>
256
- <p>Real-time Ecosystem Monitoring | Supply Chain Resilience | Automated Response</p>
257
  </div>
258
  """, unsafe_allow_html=True)
259
 
260
- # Sidebar controls
261
- st.sidebar.title("🎯 Ecosystem Controls")
262
-
263
- # Supplier filter
264
- selected_suppliers = st.sidebar.multiselect(
265
- "Monitor Suppliers:",
266
- list(suppliers.keys()),
267
- default=list(suppliers.keys())
268
  )
269
 
270
- # Disruption simulation
271
- st.sidebar.subheader("🎮 Disruption Simulation")
272
- disruption_scenario = st.sidebar.selectbox(
273
- "Inject Additional Disruption:",
274
- ["None", "Multi-supplier Strike", "Regional Power Outage", "Transportation Blockade", "Raw Material Shortage"]
275
- )
276
-
277
- if st.sidebar.button("🚨 Activate Disruption"):
278
- st.sidebar.error(f"Simulating: {disruption_scenario}")
279
-
280
- # Main Dashboard - Ecosystem Alerts
281
- st.subheader("🚨 Live Ecosystem Supply Chain Alerts")
282
-
283
- # Generate real-time alerts
284
- ecosystem_alerts = []
285
- for supplier in selected_suppliers:
286
- supplier_data = df[df['Supplier'] == supplier]
287
- disrupted_data = supplier_data[supplier_data['Is_Disrupted'] == True]
288
-
289
- if not disrupted_data.empty:
290
- for material in disrupted_data['Material'].unique():
291
- material_disruptions = disrupted_data[disrupted_data['Material'] == material]
292
-
293
- total_impact = material_disruptions['Tier2_Impact'].sum()
294
- impact_days = len(material_disruptions)
295
- first_impact_date = material_disruptions['Date'].min()
 
 
 
 
 
 
296
 
297
- # Calculate Rane impact with lead time
298
- rane_impacted = supplier_data[
299
- (supplier_data['Material'] == material) &
300
- (supplier_data['Is_Rane_Impacted'] == True)
301
- ]
 
 
 
302
 
303
- if not rane_impacted.empty:
304
- rane_impact_start = rane_impacted['Date'].min()
305
- rane_impact_days = len(rane_impacted)
306
- rane_total_impact = rane_impacted['Rane_Impact'].sum()
 
 
307
 
308
- ecosystem_alerts.append({
309
- 'supplier': supplier,
310
- 'material': material,
311
- 'disruption_cause': material_disruptions.iloc[0]['Disruption_Cause'],
312
- 'tier2_impact_start': first_impact_date,
313
- 'tier2_impact_days': impact_days,
314
- 'tier2_total_impact': total_impact,
315
- 'rane_impact_start': rane_impact_start,
316
- 'rane_impact_days': rane_impact_days,
317
- 'rane_total_impact': rane_total_impact,
318
- 'lead_time': material_disruptions.iloc[0]['Lead_Time_Days']
319
- })
320
-
321
- # Display ecosystem alerts
322
- if ecosystem_alerts:
323
- for alert in ecosystem_alerts:
324
- st.markdown(f"""
325
- <div class="ecosystem-alert">
326
- <h4>⚠️ Tier 2 Supplier Disruption Alert</h4>
327
- <p><b>Supplier:</b> {alert['supplier']} | <b>Material:</b> {alert['material']}</p>
328
- <p><b>Root Cause:</b> {alert['disruption_cause']}</p>
329
  </div>
330
  """, unsafe_allow_html=True)
 
 
 
 
 
 
331
 
332
- # Detailed impact analysis
333
- col1, col2 = st.columns(2)
334
 
335
- with col1:
336
- st.markdown("**🏭 Tier 2 Supplier Impact:**")
337
- st.markdown(f"""
338
- <div class="tier-impact">
339
- 📅 <b>Impact Period:</b> {alert['tier2_impact_start'].strftime('%Y-%m-%d')} ({alert['tier2_impact_days']} days)<br>
340
- 📉 <b>Total Supply Lost:</b> {alert['tier2_total_impact']} units<br>
341
- 🎯 <b>Daily Impact:</b> {alert['tier2_total_impact'] // alert['tier2_impact_days']} units/day
342
- </div>
343
- """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
 
345
- with col2:
346
- st.markdown("**⚙️ Rane Group Impact (with Lead Time):**")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
347
  st.markdown(f"""
348
- <div class="tier-impact">
349
- 📅 <b>Impact Period:</b> {alert['rane_impact_start'].strftime('%Y-%m-%d')} ({alert['rane_impact_days']} days)<br>
350
- 📉 <b>Total Supply Lost:</b> {alert['rane_total_impact']} units<br>
351
- ⏱️ <b>Lead Time Delay:</b> {alert['lead_time']} days
352
  </div>
353
  """, unsafe_allow_html=True)
 
 
 
354
 
355
- # Generate and display mitigation strategies
356
- strategies, recommended_indices = generate_mitigation_strategies(
357
- alert['supplier'],
358
- alert['material'],
359
- alert['rane_total_impact'] // alert['rane_impact_days'],
360
- alert['rane_impact_days']
361
- )
362
 
363
- st.markdown("**🤖 Agentic AI Mitigation Strategies:**")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
 
365
- for i, strategy in enumerate(strategies):
366
- is_recommended = i in recommended_indices
367
- is_executed = f"{alert['supplier']}_{alert['material']}_{i}" in st.session_state.executed_mitigations
368
-
369
- if is_executed:
370
- card_class = "mitigation-executed"
371
- status_prefix = "✅ **EXECUTED** "
372
- elif is_recommended:
373
- card_class = "mitigation-recommended"
374
- status_prefix = "🏆 **AI RECOMMENDED** "
375
- else:
376
- card_class = "mitigation-recommended"
377
- status_prefix = ""
378
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  st.markdown(f"""
380
- <div class="{card_class}">
381
- {status_prefix}<b>{strategy['strategy']}</b><br>
382
- 📋 {strategy['description']}<br>
383
- ⏱️ <b>Timeline:</b> {strategy['timeline']} | 💰 <b>Cost:</b> {strategy['cost']}<br>
384
- 📈 <b>Effectiveness:</b> {strategy['effectiveness']} | 🚀 <b>Capacity:</b> {strategy['capacity']}
385
  </div>
386
  """, unsafe_allow_html=True)
387
 
388
- # Action buttons
389
- col1, col2, col3 = st.columns([2, 1, 1])
390
-
391
- strategy_key = f"{alert['supplier']}_{alert['material']}_{i}"
392
 
393
  with col1:
394
- if not is_executed:
395
- if st.button(f"🚀 Execute Strategy", key=f"execute_{strategy_key}"):
396
- st.session_state.executed_mitigations.append(strategy_key)
397
- st.success(f"Executing: {strategy['strategy']}")
398
- st.rerun()
399
- else:
400
- st.success("Strategy Active")
 
401
 
402
  with col2:
403
- if st.button(f"📊 Impact Analysis", key=f"analysis_{strategy_key}"):
404
- st.info(f"Detailed analysis for {strategy['strategy']}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
 
406
- with col3:
407
- if is_recommended:
408
- st.button("🏆 Recommended", key=f"rec_{strategy_key}", disabled=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
 
410
- st.markdown("---")
411
-
412
- # Ecosystem visualization
413
- st.subheader("📊 Ecosystem Supply Chain Flow Visualization")
414
-
415
- # Create ecosystem impact chart
416
- fig = go.Figure()
417
-
418
- for supplier in selected_suppliers:
419
- supplier_data = df[df['Supplier'] == supplier]
420
-
421
- # Sample one material per supplier for clarity
422
- sample_material = supplier_data['Material'].iloc[0]
423
- material_data = supplier_data[supplier_data['Material'] == sample_material]
424
-
425
- # Tier 2 supply line
426
- fig.add_trace(go.Scatter(
427
- x=material_data['Date'],
428
- y=material_data['Tier2_Disrupted_Supply'],
429
- mode='lines+markers',
430
- name=f'{supplier} (Tier 2)',
431
- line=dict(width=2, dash='dash'),
432
- marker=dict(size=6)
433
- ))
434
-
435
- # Rane impact line
436
- fig.add_trace(go.Scatter(
437
- x=material_data['Date'],
438
- y=material_data['Rane_Impacted_Supply'],
439
- mode='lines+markers',
440
- name=f'Rane Impact from {supplier}',
441
- line=dict(width=3),
442
- marker=dict(size=8)
443
- ))
444
-
445
- fig.update_layout(
446
- title='Tier 2 Supplier Disruptions → Rane Group Supply Impact',
447
- xaxis_title='Date',
448
- yaxis_title='Supply Units',
449
- height=500,
450
- showlegend=True,
451
- hovermode='x unified'
452
- )
453
-
454
- st.plotly_chart(fig, use_container_width=True)
455
-
456
- # Detailed ecosystem table
457
- st.subheader("📋 Detailed Ecosystem Impact Timeline")
458
-
459
- # Create comprehensive timeline table
460
- timeline_data = []
461
- for supplier in selected_suppliers:
462
- supplier_data = df[df['Supplier'] == supplier]
463
-
464
- for _, row in supplier_data.iterrows():
465
- if row['Is_Disrupted'] or row['Is_Rane_Impacted']:
466
- timeline_data.append({
467
- 'Date': row['Date'].strftime('%Y-%m-%d'),
468
- 'Supplier': row['Supplier'],
469
- 'Material': row['Material'].split('-')[1],
470
- 'Tier2_Impact': f"-{row['Tier2_Impact']}" if row['Tier2_Impact'] > 0 else "Normal",
471
- 'Rane_Impact': f"-{row['Rane_Impact']}" if row['Rane_Impact'] > 0 else "Normal",
472
- 'Lead_Time': f"{row['Lead_Time_Days']} days",
473
- 'Disruption': row['Disruption_Cause'],
474
- 'Status': "🔴 Active" if row['Is_Disrupted'] else "🟡 Downstream Impact"
475
- })
476
-
477
- if timeline_data:
478
- timeline_df = pd.DataFrame(timeline_data)
479
- st.dataframe(timeline_df, use_container_width=True, height=400)
480
- else:
481
- st.info("🟢 No ecosystem disruptions detected in the current timeframe")
482
 
483
- # Summary metrics
484
- st.subheader("📊 Ecosystem Resilience Dashboard")
485
 
486
  col1, col2, col3, col4 = st.columns(4)
487
 
488
- total_suppliers_disrupted = len(df[df['Is_Disrupted'] == True]['Supplier'].unique())
489
- total_rane_impact_days = len(df[df['Is_Rane_Impacted'] == True])
490
- total_mitigation_strategies = len([s for s in st.session_state.executed_mitigations])
491
- avg_lead_time = df['Lead_Time_Days'].mean()
492
-
493
- with col1:
494
- st.metric("Suppliers Disrupted", f"{total_suppliers_disrupted}")
495
-
496
- with col2:
497
- st.metric("Rane Impact Days", f"{total_rane_impact_days}")
498
-
499
- with col3:
500
- st.metric("Active Mitigations", f"{total_mitigation_strategies}")
 
 
 
 
 
 
501
 
502
- with col4:
503
- st.metric("Avg Lead Time", f"{avg_lead_time:.1f} days")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
504
 
505
  # Footer
506
  st.markdown("---")
507
  st.markdown("""
508
  <div style='text-align: center; color: #666;'>
509
- <p>🌐 <b>Rane Group Ecosystem Command Center</b> | Tier 2 Impact Analysis | Agentic AI Response<br>
510
- Supply Chain Resilience | Automated Mitigation | Real-time Ecosystem Monitoring</p>
511
  </div>
512
  """, unsafe_allow_html=True)
 
8
 
9
  # Page configuration
10
  st.set_page_config(
11
+ page_title="Rane Group - Complete Supply Chain Hub",
12
  page_icon="🌐",
13
  layout="wide",
14
  initial_sidebar_state="expanded"
 
25
  text-align: center;
26
  margin-bottom: 2rem;
27
  }
28
+ .tab-header {
29
+ background: linear-gradient(90deg, #059669, #10b981);
30
+ padding: 0.8rem;
31
+ border-radius: 8px;
32
+ color: white;
33
+ margin-bottom: 1rem;
34
+ }
35
+ .alert-card {
36
+ background: #fff5f5;
37
+ padding: 1rem;
38
+ border-radius: 8px;
39
+ border-left: 6px solid #e53e3e;
40
+ margin: 0.5rem 0;
41
+ }
42
  .ecosystem-alert {
43
  background: #fef2f2;
44
  padding: 1rem;
 
46
  border-left: 6px solid #dc2626;
47
  margin: 0.5rem 0;
48
  }
49
+ .root-cause {
50
+ background: #fef7e7;
51
+ padding: 0.8rem;
52
+ border-radius: 6px;
53
+ margin: 0.3rem 0;
54
+ border-left: 3px solid #f6ad55;
55
+ }
56
+ .mitigation {
57
+ background: #e6fffa;
58
+ padding: 0.8rem;
59
+ border-radius: 6px;
60
+ margin: 0.3rem 0;
61
+ border-left: 3px solid #4fd1c7;
62
+ }
63
+ .best-option {
64
+ background: #f0fff4;
65
+ padding: 0.8rem;
66
+ border-radius: 6px;
67
+ margin: 0.3rem 0;
68
+ border-left: 4px solid #48bb78;
69
+ border: 2px solid #48bb78;
70
+ }
71
  .tier-impact {
72
  background: #fff7ed;
73
  padding: 0.8rem;
 
90
  margin: 0.3rem 0;
91
  border-left: 4px solid #3b82f6;
92
  }
93
+ .normal-status {
94
+ background: #f0fff4;
95
+ padding: 0.6rem;
96
+ border-radius: 6px;
97
+ border-left: 4px solid #48bb78;
98
+ margin: 0.2rem 0;
99
  }
100
+ .external-signal {
101
+ background: #f3e5f5;
102
  padding: 0.6rem;
103
  border-radius: 6px;
104
+ border-left: 4px solid #9c27b0;
105
  margin: 0.2rem 0;
 
106
  }
107
  </style>
108
  """, unsafe_allow_html=True)
 
110
  # Initialize session state
111
  if 'executed_mitigations' not in st.session_state:
112
  st.session_state.executed_mitigations = []
113
+ if 'external_signals' not in st.session_state:
114
+ st.session_state.external_signals = []
115
+
116
+ # Generate forward-looking demand data
117
+ @st.cache_data
118
+ def generate_forward_demand_data():
119
+ today = datetime(2025, 8, 4)
120
+ dates = [today + timedelta(days=x) for x in range(14)]
121
+
122
+ materials = [
123
+ 'STG001-Steering Gear',
124
+ 'STG002-Steering Column',
125
+ 'STG003-Power Steering',
126
+ 'BRK001-Brake Pads',
127
+ 'SUS001-Shock Absorber'
128
+ ]
129
+
130
+ all_data = []
131
+
132
+ for material in materials:
133
+ np.random.seed(hash(material) % 1000)
134
+
135
+ # Generate base demand
136
+ base_demand = np.random.normal(150, 20, 14)
137
+ demand_forecast = np.clip(base_demand, 80, 250).astype(int)
138
+
139
+ # Generate supply capacity
140
+ supply_capacity = np.random.normal(160, 15, 14)
141
+ supply_plan = np.clip(supply_capacity, 100, 280).astype(int)
142
+
143
+ # External impacts (as integers)
144
+ weather_impact = np.zeros(14, dtype=int)
145
+ policy_impact = np.zeros(14, dtype=int)
146
+ market_impact = np.zeros(14, dtype=int)
147
+
148
+ # Weather impact (Chennai rains days 1-3)
149
+ weather_impact[1:4] = -25
150
+
151
+ # EV policy impact (from day 7)
152
+ if 'STG' in material:
153
+ policy_impact[7:] = 15
154
+
155
+ # Market sentiment (festive season days 5-9)
156
+ market_impact[5:9] = [8, 10, 12, 6]
157
+
158
+ # Calculate adjusted demand and supply
159
+ adjusted_demand = demand_forecast + weather_impact + policy_impact + market_impact
160
+ adjusted_demand = np.clip(adjusted_demand, 50, 300).astype(int)
161
+
162
+ # Supply with weather constraints
163
+ supply_actual = supply_plan.copy()
164
+ for i in range(1, 4): # Weather impact on supply
165
+ supply_actual[i] = int(supply_actual[i] * 0.7)
166
+
167
+ # Calculate gaps
168
+ supply_gap = supply_actual - adjusted_demand
169
+
170
+ for i, date in enumerate(dates):
171
+ all_data.append({
172
+ 'Date': date,
173
+ 'Material': material,
174
+ 'Demand_Forecast': int(demand_forecast[i]),
175
+ 'Demand_Adjusted': int(adjusted_demand[i]),
176
+ 'Supply_Plan': int(supply_plan[i]),
177
+ 'Supply_Projected': int(supply_actual[i]),
178
+ 'Gap': int(supply_gap[i]),
179
+ 'Weather_Impact': int(weather_impact[i]),
180
+ 'Policy_Impact': int(policy_impact[i]),
181
+ 'Market_Impact': int(market_impact[i])
182
+ })
183
+
184
+ return pd.DataFrame(all_data)
185
 
186
  # Tier 2 Supplier Data
187
  @st.cache_data
 
290
 
291
  return pd.DataFrame(all_data)
292
 
293
+ # External signals data
294
+ @st.cache_data
295
+ def get_external_signals():
296
+ return [
297
+ {'Source': 'Weather API', 'Signal': 'Heavy rains forecasted in Chennai for next 3 days', 'Impact': 'Supply Risk', 'Confidence': 95},
298
+ {'Source': 'Market Intelligence', 'Signal': 'EV sales up 25% this quarter', 'Impact': 'Demand Increase', 'Confidence': 88},
299
+ {'Source': 'News Analytics', 'Signal': 'Upcoming festive season - historically 15% demand spike', 'Impact': 'Demand Surge', 'Confidence': 92},
300
+ {'Source': 'Supplier Network', 'Signal': 'Tier-2 supplier capacity increased by 20%', 'Impact': 'Supply Boost', 'Confidence': 98},
301
+ {'Source': 'Social Media', 'Signal': 'Positive sentiment around new Maruti EV model', 'Impact': 'Demand Growth', 'Confidence': 75},
302
+ {'Source': 'Government Portal', 'Signal': 'New EV subsidy policy effective next week', 'Impact': 'Market Expansion', 'Confidence': 100}
303
+ ]
304
+
305
+ # Generate detailed alerts with root cause analysis
306
+ def generate_detailed_alerts(df):
307
+ alerts = []
308
+
309
+ for material in df['Material'].unique():
310
+ material_data = df[df['Material'] == material]
311
+ shortage_days = material_data[material_data['Gap'] < -5]
312
+
313
+ if not shortage_days.empty:
314
+ for _, row in shortage_days.iterrows():
315
+ # Root cause analysis
316
+ root_causes = []
317
+ if row['Weather_Impact'] < -10:
318
+ root_causes.append("Chennai plant disruption due to heavy rains (-25 units)")
319
+ if row['Policy_Impact'] > 10:
320
+ root_causes.append(f"EV policy driving increased demand (+{row['Policy_Impact']} units)")
321
+ if row['Market_Impact'] > 5:
322
+ root_causes.append(f"Festive season demand surge (+{row['Market_Impact']} units)")
323
+ if not root_causes:
324
+ root_causes.append("Base demand exceeding current supply capacity")
325
+
326
+ # Mitigation options
327
+ mitigation_options = [
328
+ {"option": "Activate Pune backup production", "impact": "+30 units/day", "cost": "High", "timeline": "24 hours"},
329
+ {"option": "Expedite Tier-2 supplier shipments", "impact": "+15 units/day", "cost": "Medium", "timeline": "12 hours"},
330
+ {"option": "Emergency air freight from backup suppliers", "impact": "+40 units/day", "cost": "Very High", "timeline": "6 hours"},
331
+ {"option": "Reallocate inventory from other plants", "impact": "+20 units/day", "cost": "Low", "timeline": "18 hours"}
332
+ ]
333
+
334
+ # Determine best option based on severity
335
+ if row['Gap'] < -20: # Critical shortage
336
+ best_option = mitigation_options[2] # Air freight
337
+ elif row['Gap'] < -10: # High shortage
338
+ best_option = mitigation_options[0] # Pune backup
339
+ else: # Medium shortage
340
+ best_option = mitigation_options[1] # Expedite suppliers
341
+
342
+ alerts.append({
343
+ 'material': material,
344
+ 'date': row['Date'].strftime('%Y-%m-%d'),
345
+ 'shortage': abs(row['Gap']),
346
+ 'severity': 'Critical' if row['Gap'] < -20 else 'High' if row['Gap'] < -10 else 'Medium',
347
+ 'root_causes': root_causes,
348
+ 'mitigation_options': mitigation_options,
349
+ 'best_option': best_option
350
+ })
351
+
352
+ return alerts
353
+
354
+ # Generate mitigation strategies for ecosystem
355
  def generate_mitigation_strategies(supplier, material, impact_amount, impact_days):
356
  base_strategies = [
357
  {
 
361
  'cost': 'High (+15% unit cost)',
362
  'effectiveness': '90%',
363
  'capacity': f'+{impact_amount * 0.9:.0f} units/day',
 
364
  },
365
  {
366
  'strategy': 'Emergency Air Freight',
 
369
  'cost': 'Very High (+40% logistics cost)',
370
  'effectiveness': '75%',
371
  'capacity': f'+{impact_amount * 0.75:.0f} units/day',
 
372
  },
373
  {
374
  'strategy': 'Inventory Reallocation',
 
377
  'cost': 'Medium (+5% handling cost)',
378
  'effectiveness': '60%',
379
  'capacity': f'+{impact_amount * 0.6:.0f} units/day',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
  }
381
  ]
382
 
383
  # AI recommendation logic
384
  if impact_amount > 100: # Critical impact
385
+ recommended = [0, 1] # Alternate supplier + Air freight
386
  elif impact_amount > 50: # High impact
387
+ recommended = [0, 2] # Alternate supplier + Reallocation
388
  else: # Medium impact
389
+ recommended = [2] # Reallocation
390
 
391
  return base_strategies, recommended
392
 
393
  # Load data
394
+ df_demand = generate_forward_demand_data()
395
+ df_ecosystem = generate_ecosystem_data()
396
+ external_signals = get_external_signals()
397
  suppliers = get_tier2_suppliers()
398
 
399
  # Header
400
  st.markdown("""
401
  <div class="main-header">
402
+ <h1>🌐 Rane Group - Complete Supply Chain Command Center</h1>
403
+ <h3>Forward-Looking Demand Sensing | Ecosystem Impact Analysis | Agentic AI Optimization</h3>
404
+ <p>Comprehensive Supply Chain Intelligence | Real-time Decision Support</p>
405
  </div>
406
  """, unsafe_allow_html=True)
407
 
408
+ # Tab Navigation
409
+ st.sidebar.title("🎯 Dashboard Navigation")
410
+ dashboard_tab = st.sidebar.radio(
411
+ "Select Dashboard:",
412
+ ["📊 Demand & Supply Forecast", "🌐 Ecosystem Supplier Impact"],
413
+ index=0
 
 
414
  )
415
 
416
+ # TAB 1: DEMAND & SUPPLY FORECAST
417
+ if dashboard_tab == "📊 Demand & Supply Forecast":
418
+ st.markdown("""
419
+ <div class="tab-header">
420
+ <h2>📊 Demand & Supply Forecast Dashboard</h2>
421
+ <p>Forward-Looking 14-Day Demand Sensing | External Signal Integration | AI-Driven Optimization</p>
422
+ </div>
423
+ """, unsafe_allow_html=True)
424
+
425
+ # Material selection
426
+ selected_materials_demand = st.sidebar.multiselect(
427
+ "Focus Materials:",
428
+ df_demand['Material'].unique(),
429
+ default=df_demand['Material'].unique()[:3]
430
+ )
431
+
432
+ # Filter data
433
+ filtered_df_demand = df_demand[df_demand['Material'].isin(selected_materials_demand)]
434
+
435
+ # Generate and display alerts
436
+ st.subheader("🚨 Detailed Supply Chain Alerts")
437
+
438
+ alerts = generate_detailed_alerts(filtered_df_demand)
439
+
440
+ if alerts:
441
+ for i, alert in enumerate(alerts[:3]): # Show top 3 alerts
442
+ st.markdown(f"""
443
+ <div class="alert-card">
444
+ <h4>⚠️ {alert['material']} - {alert['severity']} Shortage Alert</h4>
445
+ <p><b>Date:</b> {alert['date']} | <b>Shortage:</b> {alert['shortage']} units</p>
446
+ </div>
447
+ """, unsafe_allow_html=True)
448
 
449
+ # Root cause analysis
450
+ st.markdown("**🔍 Root Cause Analysis:**")
451
+ for cause in alert['root_causes']:
452
+ st.markdown(f"""
453
+ <div class="root-cause">
454
+ 🎯 {cause}
455
+ </div>
456
+ """, unsafe_allow_html=True)
457
 
458
+ # Mitigation options
459
+ st.markdown("**⚡ Mitigation Options:**")
460
+ for option in alert['mitigation_options']:
461
+ is_best = option == alert['best_option']
462
+ option_class = "best-option" if is_best else "mitigation"
463
+ best_indicator = "🏆 **RECOMMENDED** " if is_best else ""
464
 
465
+ st.markdown(f"""
466
+ <div class="{option_class}">
467
+ {best_indicator}<b>{option['option']}</b><br>
468
+ 📈 Impact: {option['impact']} | 💰 Cost: {option['cost']} | ⏱️ Timeline: {option['timeline']}
469
+ </div>
470
+ """, unsafe_allow_html=True)
471
+
472
+ # Action buttons
473
+ col1, col2, col3 = st.columns([2, 1, 1])
474
+ with col1:
475
+ if st.button(f"✅ Implement Solution", key=f"demand_implement_{i}"):
476
+ st.success(f"Implementing: {alert['best_option']['option']}")
477
+
478
+ st.markdown("---")
479
+ else:
480
+ st.markdown("""
481
+ <div class="normal-status">
482
+ <b>All Good!</b> No critical supply shortages detected in the next 14 days.
 
 
 
483
  </div>
484
  """, unsafe_allow_html=True)
485
+
486
+ # Enhanced visualization
487
+ st.subheader("📊 14-Day Supply vs Demand Outlook")
488
+
489
+ for material in selected_materials_demand:
490
+ material_data = filtered_df_demand[filtered_df_demand['Material'] == material]
491
 
492
+ st.markdown(f"**{material}**")
 
493
 
494
+ # Create clear chart
495
+ fig = go.Figure()
496
+
497
+ # Add demand line
498
+ fig.add_trace(go.Scatter(
499
+ x=material_data['Date'],
500
+ y=material_data['Demand_Adjusted'],
501
+ mode='lines+markers',
502
+ name='Adjusted Demand',
503
+ line=dict(color='red', width=3),
504
+ marker=dict(size=8)
505
+ ))
506
+
507
+ # Add supply line
508
+ fig.add_trace(go.Scatter(
509
+ x=material_data['Date'],
510
+ y=material_data['Supply_Projected'],
511
+ mode='lines+markers',
512
+ name='Projected Supply',
513
+ line=dict(color='green', width=3),
514
+ marker=dict(size=8)
515
+ ))
516
+
517
+ # Highlight shortage areas
518
+ shortage_data = material_data[material_data['Gap'] < 0]
519
+ if not shortage_data.empty:
520
+ fig.add_trace(go.Scatter(
521
+ x=shortage_data['Date'],
522
+ y=shortage_data['Supply_Projected'],
523
+ mode='markers',
524
+ name='Shortage Days',
525
+ marker=dict(color='orange', size=12, symbol='x'),
526
+ ))
527
 
528
+ fig.update_layout(
529
+ title=f'{material} - Supply vs Demand Forecast',
530
+ xaxis_title='Date',
531
+ yaxis_title='Units',
532
+ height=350,
533
+ showlegend=True,
534
+ hovermode='x unified'
535
+ )
536
+
537
+ st.plotly_chart(fig, use_container_width=True)
538
+
539
+ # External demand sensing
540
+ st.subheader("📡 Real-time External Demand Sensing")
541
+
542
+ col1, col2 = st.columns(2)
543
+
544
+ with col1:
545
+ st.write("**Active External Signals:**")
546
+ for signal in external_signals:
547
+ confidence_color = "🟢" if signal['Confidence'] > 90 else "🟡" if signal['Confidence'] > 80 else "🟠"
548
  st.markdown(f"""
549
+ <div class="external-signal">
550
+ <b>{confidence_color} {signal['Source']}</b><br>
551
+ {signal['Signal']}<br>
552
+ <small>Impact: {signal['Impact']} | Confidence: {signal['Confidence']}%</small>
553
  </div>
554
  """, unsafe_allow_html=True)
555
+
556
+ with col2:
557
+ st.write("**Scenario Planning:**")
558
 
559
+ scenario = st.selectbox("Select Scenario to Test:",
560
+ ["Base Case", "Heavy Monsoon", "Festive Surge", "EV Policy Boost"])
 
 
 
 
 
561
 
562
+ if st.button("🎮 Run Scenario", key="demand_scenario"):
563
+ if scenario == "Heavy Monsoon":
564
+ st.error("Scenario: 40% supply reduction for 5 days. Activating contingency plans...")
565
+ elif scenario == "Festive Surge":
566
+ st.warning("Scenario: 35% demand increase for 7 days. Scaling production...")
567
+ elif scenario == "EV Policy Boost":
568
+ st.info("Scenario: 25% demand boost for EV components. Optimizing product mix...")
569
+
570
+ # TAB 2: ECOSYSTEM SUPPLIER IMPACT
571
+ elif dashboard_tab == "🌐 Ecosystem Supplier Impact":
572
+ st.markdown("""
573
+ <div class="tab-header">
574
+ <h2>🌐 Ecosystem Supplier Impact Dashboard</h2>
575
+ <p>Tier 2 Supplier Disruption Analysis | Cascading Impact Modeling | Automated Mitigation Response</p>
576
+ </div>
577
+ """, unsafe_allow_html=True)
578
+
579
+ # Supplier selection
580
+ selected_suppliers = st.sidebar.multiselect(
581
+ "Monitor Suppliers:",
582
+ list(suppliers.keys()),
583
+ default=list(suppliers.keys())
584
+ )
585
+
586
+ # Main Dashboard - Ecosystem Alerts
587
+ st.subheader("🚨 Live Ecosystem Supply Chain Alerts")
588
+
589
+ # Generate real-time alerts
590
+ ecosystem_alerts = []
591
+ for supplier in selected_suppliers:
592
+ supplier_data = df_ecosystem[df_ecosystem['Supplier'] == supplier]
593
+ disrupted_data = supplier_data[supplier_data['Is_Disrupted'] == True]
594
 
595
+ if not disrupted_data.empty:
596
+ for material in disrupted_data['Material'].unique():
597
+ material_disruptions = disrupted_data[disrupted_data['Material'] == material]
598
+
599
+ total_impact = material_disruptions['Tier2_Impact'].sum()
600
+ impact_days = len(material_disruptions)
601
+ first_impact_date = material_disruptions['Date'].min()
602
+
603
+ # Calculate Rane impact with lead time
604
+ rane_impacted = supplier_data[
605
+ (supplier_data['Material'] == material) &
606
+ (supplier_data['Is_Rane_Impacted'] == True)
607
+ ]
608
+
609
+ if not rane_impacted.empty:
610
+ rane_impact_start = rane_impacted['Date'].min()
611
+ rane_impact_days = len(rane_impacted)
612
+ rane_total_impact = rane_impacted['Rane_Impact'].sum()
613
+
614
+ ecosystem_alerts.append({
615
+ 'supplier': supplier,
616
+ 'material': material,
617
+ 'disruption_cause': material_disruptions.iloc[0]['Disruption_Cause'],
618
+ 'tier2_impact_start': first_impact_date,
619
+ 'tier2_impact_days': impact_days,
620
+ 'tier2_total_impact': total_impact,
621
+ 'rane_impact_start': rane_impact_start,
622
+ 'rane_impact_days': rane_impact_days,
623
+ 'rane_total_impact': rane_total_impact,
624
+ 'lead_time': material_disruptions.iloc[0]['Lead_Time_Days']
625
+ })
626
+
627
+ # Display ecosystem alerts
628
+ if ecosystem_alerts:
629
+ for alert in ecosystem_alerts:
630
  st.markdown(f"""
631
+ <div class="ecosystem-alert">
632
+ <h4>⚠️ Tier 2 Supplier Disruption Alert</h4>
633
+ <p><b>Supplier:</b> {alert['supplier']} | <b>Material:</b> {alert['material']}</p>
634
+ <p><b>Root Cause:</b> {alert['disruption_cause']}</p>
 
635
  </div>
636
  """, unsafe_allow_html=True)
637
 
638
+ # Detailed impact analysis
639
+ col1, col2 = st.columns(2)
 
 
640
 
641
  with col1:
642
+ st.markdown("**🏭 Tier 2 Supplier Impact:**")
643
+ st.markdown(f"""
644
+ <div class="tier-impact">
645
+ 📅 <b>Impact Period:</b> {alert['tier2_impact_start'].strftime('%Y-%m-%d')} ({alert['tier2_impact_days']} days)<br>
646
+ 📉 <b>Total Supply Lost:</b> {alert['tier2_total_impact']} units<br>
647
+ 🎯 <b>Daily Impact:</b> {alert['tier2_total_impact'] // alert['tier2_impact_days']} units/day
648
+ </div>
649
+ """, unsafe_allow_html=True)
650
 
651
  with col2:
652
+ st.markdown("**⚙️ Rane Group Impact (with Lead Time):**")
653
+ st.markdown(f"""
654
+ <div class="tier-impact">
655
+ 📅 <b>Impact Period:</b> {alert['rane_impact_start'].strftime('%Y-%m-%d')} ({alert['rane_impact_days']} days)<br>
656
+ 📉 <b>Total Supply Lost:</b> {alert['rane_total_impact']} units<br>
657
+ ⏱️ <b>Lead Time Delay:</b> {alert['lead_time']} days
658
+ </div>
659
+ """, unsafe_allow_html=True)
660
+
661
+ # Generate and display mitigation strategies
662
+ strategies, recommended_indices = generate_mitigation_strategies(
663
+ alert['supplier'],
664
+ alert['material'],
665
+ alert['rane_total_impact'] // alert['rane_impact_days'],
666
+ alert['rane_impact_days']
667
+ )
668
+
669
+ st.markdown("**🤖 Agentic AI Mitigation Strategies:**")
670
 
671
+ for i, strategy in enumerate(strategies):
672
+ is_recommended = i in recommended_indices
673
+ is_executed = f"eco_{alert['supplier']}_{alert['material']}_{i}" in st.session_state.executed_mitigations
674
+
675
+ if is_executed:
676
+ card_class = "mitigation-executed"
677
+ status_prefix = "✅ **EXECUTED** "
678
+ elif is_recommended:
679
+ card_class = "mitigation-recommended"
680
+ status_prefix = "🏆 **AI RECOMMENDED** "
681
+ else:
682
+ card_class = "mitigation-recommended"
683
+ status_prefix = ""
684
+
685
+ st.markdown(f"""
686
+ <div class="{card_class}">
687
+ {status_prefix}<b>{strategy['strategy']}</b><br>
688
+ 📋 {strategy['description']}<br>
689
+ ⏱️ <b>Timeline:</b> {strategy['timeline']} | 💰 <b>Cost:</b> {strategy['cost']}<br>
690
+ 📈 <b>Effectiveness:</b> {strategy['effectiveness']} | 🚀 <b>Capacity:</b> {strategy['capacity']}
691
+ </div>
692
+ """, unsafe_allow_html=True)
693
+
694
+ # Action buttons
695
+ strategy_key = f"eco_{alert['supplier']}_{alert['material']}_{i}"
696
+
697
+ col1, col2 = st.columns([2, 1])
698
+
699
+ with col1:
700
+ if not is_executed:
701
+ if st.button(f"🚀 Execute Strategy", key=f"execute_{strategy_key}"):
702
+ st.session_state.executed_mitigations.append(strategy_key)
703
+ st.success(f"Executing: {strategy['strategy']}")
704
+ st.rerun()
705
+ else:
706
+ st.success("Strategy Active")
707
+
708
+ with col2:
709
+ if is_recommended:
710
+ st.button("🏆 Recommended", key=f"rec_{strategy_key}", disabled=True)
711
+
712
+ st.markdown("---")
713
+ else:
714
+ st.markdown("""
715
+ <div class="normal-status">
716
+ ✅ <b>Ecosystem Healthy!</b> No supplier disruptions detected in the current timeframe.
717
+ </div>
718
+ """, unsafe_allow_html=True)
719
+
720
+ # Ecosystem visualization
721
+ st.subheader("📊 Ecosystem Supply Chain Flow Visualization")
722
+
723
+ # Create ecosystem impact chart
724
+ fig = go.Figure()
725
+
726
+ for supplier in selected_suppliers:
727
+ supplier_data = df_ecosystem[df_ecosystem['Supplier'] == supplier]
728
 
729
+ # Sample one material per supplier for clarity
730
+ sample_material = supplier_data['Material'].iloc[0]
731
+ material_data = supplier_data[supplier_data['Material'] == sample_material]
732
+
733
+ # Tier 2 supply line
734
+ fig.add_trace(go.Scatter(
735
+ x=material_data['Date'],
736
+ y=material_data['Tier2_Disrupted_Supply'],
737
+ mode='lines+markers',
738
+ name=f'{supplier} (Tier 2)',
739
+ line=dict(width=2, dash='dash'),
740
+ marker=dict(size=6)
741
+ ))
742
+
743
+ # Rane impact line
744
+ fig.add_trace(go.Scatter(
745
+ x=material_data['Date'],
746
+ y=material_data['Rane_Impacted_Supply'],
747
+ mode='lines+markers',
748
+ name=f'Rane Impact from {supplier}',
749
+ line=dict(width=3),
750
+ marker=dict(size=8)
751
+ ))
752
+
753
+ fig.update_layout(
754
+ title='Tier 2 Supplier Disruptions → Rane Group Supply Impact',
755
+ xaxis_title='Date',
756
+ yaxis_title='Supply Units',
757
+ height=500,
758
+ showlegend=True,
759
+ hovermode='x unified'
760
+ )
761
+
762
+ st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
763
 
764
+ # Performance summary (common for both tabs)
765
+ st.subheader("📊 Performance Summary")
766
 
767
  col1, col2, col3, col4 = st.columns(4)
768
 
769
+ if dashboard_tab == "📊 Demand & Supply Forecast":
770
+ filtered_df = filtered_df_demand if 'filtered_df_demand' in locals() else df_demand
771
+
772
+ total_shortage_days = len(filtered_df[filtered_df['Gap'] < 0])
773
+ critical_shortage_days = len(filtered_df[filtered_df['Gap'] < -20])
774
+ materials_at_risk = len(filtered_df[filtered_df['Gap'] < -5]['Material'].unique())
775
+ avg_gap = filtered_df['Gap'].mean()
776
+
777
+ with col1:
778
+ st.metric("Days with Shortages", f"{total_shortage_days}")
779
+
780
+ with col2:
781
+ st.metric("Critical Days", f"{critical_shortage_days}")
782
+
783
+ with col3:
784
+ st.metric("Materials at Risk", f"{materials_at_risk}")
785
+
786
+ with col4:
787
+ st.metric("Avg Supply Gap", f"{avg_gap:.0f} units")
788
 
789
+ else: # Ecosystem tab
790
+ total_suppliers_disrupted = len(df_ecosystem[df_ecosystem['Is_Disrupted'] == True]['Supplier'].unique())
791
+ total_rane_impact_days = len(df_ecosystem[df_ecosystem['Is_Rane_Impacted'] == True])
792
+ total_mitigation_strategies = len([s for s in st.session_state.executed_mitigations if 'eco_' in s])
793
+ avg_lead_time = df_ecosystem['Lead_Time_Days'].mean()
794
+
795
+ with col1:
796
+ st.metric("Suppliers Disrupted", f"{total_suppliers_disrupted}")
797
+
798
+ with col2:
799
+ st.metric("Rane Impact Days", f"{total_rane_impact_days}")
800
+
801
+ with col3:
802
+ st.metric("Active Mitigations", f"{total_mitigation_strategies}")
803
+
804
+ with col4:
805
+ st.metric("Avg Lead Time", f"{avg_lead_time:.1f} days")
806
 
807
  # Footer
808
  st.markdown("---")
809
  st.markdown("""
810
  <div style='text-align: center; color: #666;'>
811
+ <p>🌐 <b>Rane Group Complete Supply Chain Command Center</b> | Demand Sensing + Ecosystem Intelligence<br>
812
+ Powered by Agentic AI | Real-time Decision Support | Comprehensive Supply Chain Resilience</p>
813
  </div>
814
  """, unsafe_allow_html=True)