prernajeet14 commited on
Commit
cc893db
·
verified ·
1 Parent(s): dc2e351

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +233 -46
app.py CHANGED
@@ -70,7 +70,7 @@ class SupplyChainOptimizer:
70
  def call_claude_api(self, prompt, system_message=""):
71
  """Call Claude via AWS Bedrock"""
72
  if self.demo_mode:
73
- return "Demo mode response"
74
 
75
  try:
76
  body = {
@@ -184,8 +184,17 @@ class SupplyChainOptimizer:
184
  return processed_data
185
 
186
  def _process_csv_data(self, df):
187
- """Process CSV data"""
188
- return {'data': df.to_dict('records')}
 
 
 
 
 
 
 
 
 
189
 
190
  def _process_pdf_data(self, file_path):
191
  """Extract text from PDF"""
@@ -223,8 +232,180 @@ class SupplyChainOptimizer:
223
  except Exception as e:
224
  return f"Error reading PowerPoint: {str(e)}"
225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  def create_forecast_visualization(self, forecast_data):
227
  """Create interactive forecast visualization with vibrant colors"""
 
 
 
228
  df = pd.DataFrame(forecast_data)
229
 
230
  fig = go.Figure()
@@ -266,6 +447,11 @@ class SupplyChainOptimizer:
266
 
267
  def create_inventory_chart(self, inventory_data, forecast_data):
268
  """Create inventory vs demand comparison with vibrant styling"""
 
 
 
 
 
269
  inv_df = pd.DataFrame(inventory_data)
270
  fore_df = pd.DataFrame(forecast_data)
271
 
@@ -320,6 +506,9 @@ class SupplyChainOptimizer:
320
 
321
  def create_route_network(self, route_data):
322
  """Create route network visualization with vibrant colors"""
 
 
 
323
  df = pd.DataFrame(route_data)
324
 
325
  fig = go.Figure()
@@ -527,6 +716,12 @@ except Exception as e:
527
 
528
  def parse_file_content(self, path, file_type):
529
  return "Demo mode"
 
 
 
 
 
 
530
 
531
  optimizer = DemoOptimizer()
532
  startup_message = "Running in minimal demo mode due to initialization error."
@@ -560,52 +755,46 @@ DEFAULT_ROUTES = [
560
  def process_files_and_optimize(forecast_file, inventory_file, routes_file, text_input, search_query):
561
  """Process uploaded files and text input for optimization"""
562
  try:
563
- forecast_data = DEFAULT_FORECAST
564
- inventory_data = DEFAULT_INVENTORY
565
- route_data = DEFAULT_ROUTES
566
-
567
- # Process uploaded files
568
  file_contents = []
569
 
 
570
  if forecast_file:
571
- file_ext = forecast_file.name.split('.')[-1].lower()
572
- if file_ext in ['xlsx', 'xls']:
573
- content = optimizer.parse_file_content(forecast_file.name, 'excel')
574
- if 'forecast' in content:
575
- forecast_data = content['forecast']
576
- file_contents.append(f"Forecast file processed: {forecast_file.name}")
577
- elif file_ext == 'csv':
578
- df = pd.read_csv(forecast_file.name)
579
- forecast_data = df.to_dict('records')
580
- file_contents.append(f"Forecast CSV processed: {forecast_file.name}")
581
 
582
  if inventory_file:
583
- file_ext = inventory_file.name.split('.')[-1].lower()
584
- if file_ext in ['xlsx', 'xls']:
585
- content = optimizer.parse_file_content(inventory_file.name, 'excel')
586
- if 'inventory' in content:
587
- inventory_data = content['inventory']
588
- file_contents.append(f"Inventory file processed: {inventory_file.name}")
589
- elif file_ext == 'csv':
590
- df = pd.read_csv(inventory_file.name)
591
- inventory_data = df.to_dict('records')
592
- file_contents.append(f"Inventory CSV processed: {inventory_file.name}")
593
 
594
  if routes_file:
595
- file_ext = routes_file.name.split('.')[-1].lower()
596
- if file_ext in ['xlsx', 'xls']:
597
- content = optimizer.parse_file_content(routes_file.name, 'excel')
598
- if 'routes' in content:
599
- route_data = content['routes']
600
- file_contents.append(f"Routes file processed: {routes_file.name}")
601
- elif file_ext == 'csv':
602
- df = pd.read_csv(routes_file.name)
603
- route_data = df.to_dict('records')
604
- file_contents.append(f"Routes CSV processed: {routes_file.name}")
 
 
 
 
 
 
 
 
605
 
606
  # Process text input if provided
607
  if text_input and text_input.strip():
608
- file_contents.append(f"Text input processed: {len(text_input)} characters")
609
 
610
  # Create visualizations
611
  forecast_chart = optimizer.create_forecast_visualization(forecast_data)
@@ -617,8 +806,7 @@ def process_files_and_optimize(forecast_file, inventory_file, routes_file, text_
617
  forecast_data, inventory_data, route_data, search_query
618
  )
619
 
620
- # Processing summary
621
- processing_summary = "Files processed:\n" + "\n".join(file_contents) if file_contents else "Using default data"
622
 
623
  return (
624
  forecast_chart,
@@ -632,7 +820,8 @@ def process_files_and_optimize(forecast_file, inventory_file, routes_file, text_
632
 
633
  except Exception as e:
634
  error_msg = f"Processing error: {str(e)}"
635
- return None, None, None, error_msg, error_msg, error_msg, error_msg
 
636
 
637
  # Create Gradio interface with updated warm color scheme
638
  custom_css = """
@@ -717,7 +906,6 @@ custom_css = """
717
  transform: translateY(-2px);
718
  }
719
 
720
- /* Button styling */
721
  .gradio-button {
722
  background: linear-gradient(135deg, #FF4757 0%, #FFA502 100%) !important;
723
  color: white !important;
@@ -725,7 +913,7 @@ custom_css = """
725
  border: 2px solid #B8860B !important;
726
  border-radius: 8px !important;
727
  padding: 12px 24px !important;
728
- font-size: 1rem !important;
729
  transition: all 0.3s ease !important;
730
  box-shadow: 0 4px 15px rgba(255, 71, 87, 0.2) !important;
731
  }
@@ -736,7 +924,6 @@ custom_css = """
736
  box-shadow: 0 6px 20px rgba(255, 71, 87, 0.3) !important;
737
  }
738
 
739
- /* Input styling */
740
  .gradio-textbox, .gradio-dropdown {
741
  border: 2px solid #DAA520 !important;
742
  border-radius: 8px !important;
@@ -864,7 +1051,7 @@ with gr.Blocks(css=custom_css, title="AI-Powered Supply Chain Optimizer") as int
864
  gr.HTML("""
865
  <div class="footer">
866
  <p><strong>AI-Powered Supply Chain Optimizer</strong> | Advanced Analytics & Real-Time Intelligence</p>
867
- <p>🔧 Built with AutoGen, Tavily API, and Gradio | 🚀 Powered by OpenAI GPT</p>
868
  </div>
869
  """)
870
 
 
70
  def call_claude_api(self, prompt, system_message=""):
71
  """Call Claude via AWS Bedrock"""
72
  if self.demo_mode:
73
+ return "Demo mode response - AI analysis would appear here with real API keys"
74
 
75
  try:
76
  body = {
 
184
  return processed_data
185
 
186
  def _process_csv_data(self, df):
187
+ # Clean column names first
188
+ df.columns = df.columns.str.strip().str.lower()
189
+
190
+ # Let AI analyze the structure and map columns
191
+ column_analysis = self._analyze_columns_with_ai(df)
192
+
193
+ return {
194
+ 'data': df.to_dict('records'),
195
+ 'column_mapping': column_analysis,
196
+ 'original_columns': df.columns.tolist()
197
+ }
198
 
199
  def _process_pdf_data(self, file_path):
200
  """Extract text from PDF"""
 
232
  except Exception as e:
233
  return f"Error reading PowerPoint: {str(e)}"
234
 
235
+ def _analyze_columns_with_ai(self, df):
236
+ """Use AI to understand column structure and map to standard format"""
237
+ sample_data = df.head(3).to_string()
238
+ columns = df.columns.tolist()
239
+
240
+ prompt = f"""
241
+ Analyze this data structure and map columns to standard supply chain format:
242
+
243
+ Columns: {columns}
244
+ Sample Data:
245
+ {sample_data}
246
+
247
+ Map these columns to:
248
+ - city/location: (identify city/location column)
249
+ - product: (identify product column)
250
+ - demand/forecast: (identify demand/forecast column)
251
+ - stock/inventory: (identify stock/inventory column)
252
+ - cost: (identify cost column)
253
+ - distance: (identify distance column)
254
+
255
+ Return JSON mapping like: {{"city": "actual_column_name", "product": "actual_column_name", ...}}
256
+ """
257
+
258
+ if self.demo_mode:
259
+ # Return best guess mapping
260
+ mapping = {}
261
+ for col in columns:
262
+ col_lower = col.lower()
263
+ if any(word in col_lower for word in ['city', 'location', 'destination', 'source']):
264
+ mapping['city'] = col
265
+ elif any(word in col_lower for word in ['product', 'item', 'sku']):
266
+ mapping['product'] = col
267
+ elif any(word in col_lower for word in ['demand', 'forecast', 'required']):
268
+ mapping['demand'] = col
269
+ elif any(word in col_lower for word in ['stock', 'inventory', 'level']):
270
+ mapping['stock'] = col
271
+ return mapping
272
+ else:
273
+ response = self.call_claude_api(prompt, "You are a data analyst expert at understanding file structures.")
274
+ # Parse JSON response
275
+ try:
276
+ return json.loads(response)
277
+ except:
278
+ return self._fallback_column_mapping(columns)
279
+
280
+ def _fallback_column_mapping(self, columns):
281
+ """Fallback column mapping if AI parsing fails"""
282
+ mapping = {}
283
+ for col in columns:
284
+ col_lower = col.lower()
285
+ if any(word in col_lower for word in ['city', 'location', 'destination', 'source']):
286
+ mapping['city'] = col
287
+ elif any(word in col_lower for word in ['product', 'item', 'sku']):
288
+ mapping['product'] = col
289
+ elif any(word in col_lower for word in ['demand', 'forecast', 'required']):
290
+ mapping['demand'] = col
291
+ elif any(word in col_lower for word in ['stock', 'inventory', 'level']):
292
+ mapping['stock'] = col
293
+ elif any(word in col_lower for word in ['cost', 'price']):
294
+ mapping['cost'] = col
295
+ elif any(word in col_lower for word in ['distance', 'km', 'miles']):
296
+ mapping['distance'] = col
297
+ return mapping
298
+
299
+ def analyze_file_with_ai(self, file_obj, data_type):
300
+ """Analyze uploaded file and standardize data format"""
301
+ try:
302
+ # Get file extension
303
+ file_name = file_obj.name
304
+ if file_name.endswith('.csv'):
305
+ df = pd.read_csv(file_obj.name)
306
+ elif file_name.endswith(('.xlsx', '.xls')):
307
+ df = pd.read_excel(file_obj.name)
308
+ else:
309
+ return {'standardized_data': [], 'detected_columns': [], 'error': 'Unsupported file format'}
310
+
311
+ # Clean column names
312
+ df.columns = df.columns.str.strip()
313
+ detected_columns = df.columns.tolist()
314
+
315
+ # Map columns based on data type
316
+ column_mapping = self._analyze_columns_with_ai(df)
317
+
318
+ # Standardize data based on type
319
+ standardized_data = self._standardize_data(df, column_mapping, data_type)
320
+
321
+ return {
322
+ 'standardized_data': standardized_data,
323
+ 'detected_columns': detected_columns,
324
+ 'column_mapping': column_mapping
325
+ }
326
+ except Exception as e:
327
+ return {'standardized_data': [], 'detected_columns': [], 'error': str(e)}
328
+
329
+ def _standardize_data(self, df, column_mapping, data_type):
330
+ """Standardize data format based on type"""
331
+ standardized = []
332
+
333
+ try:
334
+ if data_type == 'forecast':
335
+ for _, row in df.iterrows():
336
+ item = {
337
+ 'City': row.get(column_mapping.get('city', ''), 'Unknown'),
338
+ 'Product': row.get(column_mapping.get('product', ''), 'Unknown'),
339
+ 'Forecasted_Demand': int(row.get(column_mapping.get('demand', ''), 0)),
340
+ 'Month': 'December' # Default month
341
+ }
342
+ standardized.append(item)
343
+
344
+ elif data_type == 'inventory':
345
+ for _, row in df.iterrows():
346
+ item = {
347
+ 'City': row.get(column_mapping.get('city', ''), 'Unknown'),
348
+ 'Product': row.get(column_mapping.get('product', ''), 'Unknown'),
349
+ 'Stock_Level': int(row.get(column_mapping.get('stock', ''), 0))
350
+ }
351
+ standardized.append(item)
352
+
353
+ elif data_type == 'routes':
354
+ for _, row in df.iterrows():
355
+ item = {
356
+ 'Source': row.get(column_mapping.get('source', ''), 'Unknown'),
357
+ 'Destination': row.get(column_mapping.get('destination', ''), 'Unknown'),
358
+ 'Distance_km': float(row.get(column_mapping.get('distance', ''), 0)),
359
+ 'Cost_per_km': float(row.get(column_mapping.get('cost', ''), 0)),
360
+ 'Average_Travel_Time_hrs': float(row.get(column_mapping.get('time', ''), 0))
361
+ }
362
+ standardized.append(item)
363
+
364
+ except Exception as e:
365
+ print(f"Error standardizing data: {e}")
366
+ return []
367
+
368
+ return standardized
369
+
370
+ def generate_data_from_text(self, text_input):
371
+ """Generate sample data based on text description"""
372
+ prompt = f"""
373
+ Based on this business description, generate sample supply chain data:
374
+
375
+ Text: {text_input}
376
+
377
+ Generate realistic data for:
378
+ 1. Forecast data (cities, products, demand)
379
+ 2. Inventory data (cities, products, stock levels)
380
+ 3. Route data (source, destination, distance, cost, travel time)
381
+
382
+ Return as JSON with keys: forecast, inventory, routes
383
+ Each should be a list of dictionaries with appropriate fields.
384
+ """
385
+
386
+ if self.demo_mode:
387
+ # Return default data
388
+ return {
389
+ 'forecast': DEFAULT_FORECAST,
390
+ 'inventory': DEFAULT_INVENTORY,
391
+ 'routes': DEFAULT_ROUTES
392
+ }
393
+ else:
394
+ try:
395
+ response = self.call_claude_api(prompt, "You are a supply chain data expert.")
396
+ return json.loads(response)
397
+ except:
398
+ return {
399
+ 'forecast': DEFAULT_FORECAST,
400
+ 'inventory': DEFAULT_INVENTORY,
401
+ 'routes': DEFAULT_ROUTES
402
+ }
403
+
404
  def create_forecast_visualization(self, forecast_data):
405
  """Create interactive forecast visualization with vibrant colors"""
406
+ if not forecast_data:
407
+ forecast_data = DEFAULT_FORECAST
408
+
409
  df = pd.DataFrame(forecast_data)
410
 
411
  fig = go.Figure()
 
447
 
448
  def create_inventory_chart(self, inventory_data, forecast_data):
449
  """Create inventory vs demand comparison with vibrant styling"""
450
+ if not inventory_data:
451
+ inventory_data = DEFAULT_INVENTORY
452
+ if not forecast_data:
453
+ forecast_data = DEFAULT_FORECAST
454
+
455
  inv_df = pd.DataFrame(inventory_data)
456
  fore_df = pd.DataFrame(forecast_data)
457
 
 
506
 
507
  def create_route_network(self, route_data):
508
  """Create route network visualization with vibrant colors"""
509
+ if not route_data:
510
+ route_data = DEFAULT_ROUTES
511
+
512
  df = pd.DataFrame(route_data)
513
 
514
  fig = go.Figure()
 
716
 
717
  def parse_file_content(self, path, file_type):
718
  return "Demo mode"
719
+
720
+ def analyze_file_with_ai(self, file_obj, data_type):
721
+ return {'standardized_data': DEFAULT_FORECAST if data_type == 'forecast' else DEFAULT_INVENTORY if data_type == 'inventory' else DEFAULT_ROUTES, 'detected_columns': [], 'error': None}
722
+
723
+ def generate_data_from_text(self, text):
724
+ return {'forecast': DEFAULT_FORECAST, 'inventory': DEFAULT_INVENTORY, 'routes': DEFAULT_ROUTES}
725
 
726
  optimizer = DemoOptimizer()
727
  startup_message = "Running in minimal demo mode due to initialization error."
 
755
  def process_files_and_optimize(forecast_file, inventory_file, routes_file, text_input, search_query):
756
  """Process uploaded files and text input for optimization"""
757
  try:
758
+ # Let AI analyze files instead of using defaults
759
+ forecast_data = []
760
+ inventory_data = []
761
+ route_data = []
 
762
  file_contents = []
763
 
764
+ # AI-powered file processing
765
  if forecast_file:
766
+ analyzed_data = optimizer.analyze_file_with_ai(forecast_file, 'forecast')
767
+ forecast_data = analyzed_data['standardized_data']
768
+ file_contents.append(f"Forecast file analyzed: {forecast_file.name} - Found columns: {analyzed_data.get('detected_columns', 'N/A')}")
 
 
 
 
 
 
 
769
 
770
  if inventory_file:
771
+ analyzed_data = optimizer.analyze_file_with_ai(inventory_file, 'inventory')
772
+ inventory_data = analyzed_data['standardized_data']
773
+ file_contents.append(f"Inventory file analyzed: {inventory_file.name} - Found columns: {analyzed_data.get('detected_columns', 'N/A')}")
 
 
 
 
 
 
 
774
 
775
  if routes_file:
776
+ analyzed_data = optimizer.analyze_file_with_ai(routes_file, 'routes')
777
+ route_data = analyzed_data['standardized_data']
778
+ file_contents.append(f"Routes file analyzed: {routes_file.name} - Found columns: {analyzed_data.get('detected_columns', 'N/A')}")
779
+
780
+ # If no files uploaded, use defaults or generate from text
781
+ if not any([forecast_file, inventory_file, routes_file]):
782
+ if text_input and text_input.strip():
783
+ ai_generated_data = optimizer.generate_data_from_text(text_input)
784
+ forecast_data = ai_generated_data.get('forecast', DEFAULT_FORECAST)
785
+ inventory_data = ai_generated_data.get('inventory', DEFAULT_INVENTORY)
786
+ route_data = ai_generated_data.get('routes', DEFAULT_ROUTES)
787
+ file_contents.append("AI generated data from text description")
788
+ else:
789
+ # Use default data
790
+ forecast_data = DEFAULT_FORECAST
791
+ inventory_data = DEFAULT_INVENTORY
792
+ route_data = DEFAULT_ROUTES
793
+ file_contents.append("Using default sample data")
794
 
795
  # Process text input if provided
796
  if text_input and text_input.strip():
797
+ file_contents.append(f"Text context processed: {len(text_input)} characters")
798
 
799
  # Create visualizations
800
  forecast_chart = optimizer.create_forecast_visualization(forecast_data)
 
806
  forecast_data, inventory_data, route_data, search_query
807
  )
808
 
809
+ processing_summary = "Files processed:\n" + "\n".join(file_contents) if file_contents else "No data provided"
 
810
 
811
  return (
812
  forecast_chart,
 
820
 
821
  except Exception as e:
822
  error_msg = f"Processing error: {str(e)}"
823
+ empty_fig = go.Figure().add_annotation(text=f"Error: {str(e)}", x=0.5, y=0.5, showarrow=False)
824
+ return empty_fig, empty_fig, empty_fig, error_msg, error_msg, error_msg, error_msg
825
 
826
  # Create Gradio interface with updated warm color scheme
827
  custom_css = """
 
906
  transform: translateY(-2px);
907
  }
908
 
 
909
  .gradio-button {
910
  background: linear-gradient(135deg, #FF4757 0%, #FFA502 100%) !important;
911
  color: white !important;
 
913
  border: 2px solid #B8860B !important;
914
  border-radius: 8px !important;
915
  padding: 12px 24px !important;
916
+ font-size: 1rem !important;
917
  transition: all 0.3s ease !important;
918
  box-shadow: 0 4px 15px rgba(255, 71, 87, 0.2) !important;
919
  }
 
924
  box-shadow: 0 6px 20px rgba(255, 71, 87, 0.3) !important;
925
  }
926
 
 
927
  .gradio-textbox, .gradio-dropdown {
928
  border: 2px solid #DAA520 !important;
929
  border-radius: 8px !important;
 
1051
  gr.HTML("""
1052
  <div class="footer">
1053
  <p><strong>AI-Powered Supply Chain Optimizer</strong> | Advanced Analytics & Real-Time Intelligence</p>
1054
+ <p>🔧 Built with AutoGen, Tavily API, and Claude | 🚀 Powered by AWS Bedrock</p>
1055
  </div>
1056
  """)
1057