Spaces:
Sleeping
Sleeping
| import os | |
| import sys | |
| from dotenv import load_dotenv | |
| import gradio as gr | |
| from typing import Optional, Dict, List, Union | |
| import logging | |
| # Custom CSS | |
| CUSTOM_CSS = """ | |
| .footer { | |
| text-align: center !important; | |
| padding: 20px !important; | |
| margin-top: 40px !important; | |
| border-top: 1px solid #404040 !important; | |
| color: #89CFF0 !important; | |
| font-size: 1.1em !important; | |
| } | |
| .gradio-container { | |
| max-width: 1200px !important; | |
| margin: auto !important; | |
| padding: 20px !important; | |
| background-color: #1a1a1a !important; | |
| color: #ffffff !important; | |
| } | |
| .main-header { | |
| background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%) !important; | |
| color: white !important; | |
| padding: 30px !important; | |
| border-radius: 15px !important; | |
| margin-bottom: 30px !important; | |
| text-align: center !important; | |
| box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2) !important; | |
| } | |
| .app-title { | |
| font-size: 2.5em !important; | |
| font-weight: bold !important; | |
| margin-bottom: 10px !important; | |
| background: linear-gradient(90deg, #ffffff, #3498DB) !important; | |
| -webkit-background-clip: text !important; | |
| -webkit-text-fill-color: transparent !important; | |
| text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3) !important; | |
| } | |
| .app-subtitle { | |
| font-size: 1.3em !important; | |
| color: #89CFF0 !important; | |
| margin-bottom: 15px !important; | |
| font-weight: 500 !important; | |
| } | |
| .app-description { | |
| font-size: 1.1em !important; | |
| color: #B0C4DE !important; | |
| margin-bottom: 20px !important; | |
| line-height: 1.5 !important; | |
| } | |
| .gr-checkbox-group { | |
| background: #363636 !important; | |
| padding: 15px !important; | |
| border-radius: 10px !important; | |
| margin: 10px 0 !important; | |
| } | |
| .gr-slider { | |
| margin-top: 10px !important; | |
| } | |
| .status-message { | |
| margin-top: 10px !important; | |
| padding: 8px !important; | |
| border-radius: 4px !important; | |
| background-color: #2d2d2d !important; | |
| } | |
| .result-box { | |
| background: #363636 !important; | |
| border: 1px solid #404040 !important; | |
| border-radius: 10px !important; | |
| padding: 20px !important; | |
| margin-top: 15px !important; | |
| color: #ffffff !important; | |
| } | |
| .chart-container { | |
| background: #2d2d2d !important; | |
| padding: 20px !important; | |
| border-radius: 10px !important; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.2) !important; | |
| color: #ffffff !important; | |
| } | |
| .action-button { | |
| background: #3498DB !important; | |
| color: white !important; | |
| border: none !important; | |
| padding: 10px 20px !important; | |
| border-radius: 5px !important; | |
| cursor: pointer !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| .action-button:hover { | |
| background: #2980B9 !important; | |
| transform: translateY(-2px) !important; | |
| box-shadow: 0 4px 8px rgba(0,0,0,0.2) !important; | |
| } | |
| """ | |
| # Configure logging | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' | |
| ) | |
| logger = logging.getLogger(__name__) | |
| # Load environment variables | |
| load_dotenv() | |
| # Constants | |
| MAX_FILE_SIZE = 50 * 1024 * 1024 # 50MB | |
| ALLOWED_EXTENSIONS = {'.xlsx', '.xls', '.csv'} | |
| import pandas as pd | |
| import google.generativeai as genai | |
| import joblib | |
| from reportlab.lib import colors | |
| from reportlab.lib.pagesizes import letter | |
| from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image | |
| from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| import tempfile | |
| from datetime import datetime | |
| import numpy as np | |
| from xgboost import XGBRegressor | |
| # Configure Gemini API | |
| GEMINI_API_KEY = os.getenv("gemini_api") | |
| genai.configure(api_key=GEMINI_API_KEY) | |
| generation_config = { | |
| "temperature": 1, | |
| "top_p": 0.95, | |
| "top_k": 64, | |
| "max_output_tokens": 8192, | |
| "response_mime_type": "text/plain", | |
| } | |
| model = genai.GenerativeModel( | |
| model_name="gemini-2.0-pro-exp-02-05", | |
| generation_config=generation_config, | |
| ) | |
| chat_model = genai.GenerativeModel('"gemini-2.0-pro-exp-02-05"') | |
| class SupplyChainState: | |
| def __init__(self): | |
| self.sales_df = None | |
| self.supplier_df = None | |
| self.text_data = None | |
| self.chat_history = [] | |
| self.analysis_results = {} | |
| self.freight_predictions = [] | |
| try: | |
| self.freight_model = create_initial_model() | |
| except Exception as e: | |
| print(f"Warning: Could not create freight prediction model: {e}") | |
| self.freight_model = None | |
| def create_initial_model(): | |
| n_samples = 1000 | |
| np.random.seed(42) | |
| data = { | |
| 'weight (kilograms)': np.random.uniform(100, 10000, n_samples), | |
| 'line item value': np.random.uniform(1000, 1000000, n_samples), | |
| 'cost per kilogram': np.random.uniform(1, 500, n_samples), | |
| 'shipment mode_Air Charter_weight': np.zeros(n_samples), | |
| 'shipment mode_Ocean_weight': np.zeros(n_samples), | |
| 'shipment mode_Truck_weight': np.zeros(n_samples), | |
| 'shipment mode_Air Charter_line_item_value': np.zeros(n_samples), | |
| 'shipment mode_Ocean_line_item_value': np.zeros(n_samples), | |
| 'shipment mode_Truck_line_item_value': np.zeros(n_samples) | |
| } | |
| modes = ['Air', 'Ocean', 'Truck'] | |
| for i in range(n_samples): | |
| mode = np.random.choice(modes) | |
| if mode == 'Air': | |
| data['shipment mode_Air Charter_weight'][i] = data['weight (kilograms)'][i] | |
| data['shipment mode_Air Charter_line_item_value'][i] = data['line item value'][i] | |
| elif mode == 'Ocean': | |
| data['shipment mode_Ocean_weight'][i] = data['weight (kilograms)'][i] | |
| data['shipment mode_Ocean_line_item_value'][i] = data['line item value'][i] | |
| else: | |
| data['shipment mode_Truck_weight'][i] = data['weight (kilograms)'][i] | |
| data['shipment mode_Truck_line_item_value'][i] = data['line item value'][i] | |
| df = pd.DataFrame(data) | |
| base_cost = (df['weight (kilograms)'] * df['cost per kilogram'] * 0.8 + | |
| df['line item value'] * 0.02) | |
| air_multiplier = 1.5 | |
| ocean_multiplier = 0.8 | |
| truck_multiplier = 1.0 | |
| freight_cost = ( | |
| base_cost * (air_multiplier * (df['shipment mode_Air Charter_weight'] > 0) + | |
| ocean_multiplier * (df['shipment mode_Ocean_weight'] > 0) + | |
| truck_multiplier * (df['shipment mode_Truck_weight'] > 0)) | |
| ) | |
| freight_cost = freight_cost + np.random.normal(0, freight_cost * 0.1) | |
| model = XGBRegressor(n_estimators=100, learning_rate=0.1, max_depth=5, random_state=42) | |
| model.fit(df, freight_cost) | |
| return model | |
| def process_uploaded_data(state, sales_file, supplier_file, text_data): | |
| try: | |
| if sales_file is not None: | |
| file_ext = os.path.splitext(sales_file.name)[1].lower() | |
| if file_ext not in ['.xlsx', '.xls', '.csv']: | |
| return 'β Error: Sales data must be in Excel (.xlsx, .xls) or CSV format' | |
| try: | |
| if file_ext == '.csv': | |
| state.sales_df = pd.read_csv(sales_file.name) | |
| else: | |
| state.sales_df = pd.read_excel(sales_file.name) | |
| except Exception as e: | |
| return f'β Error reading sales data: {str(e)}' | |
| if supplier_file is not None: | |
| file_ext = os.path.splitext(supplier_file.name)[1].lower() | |
| if file_ext not in ['.xlsx', '.xls', '.csv']: | |
| return 'β Error: Supplier data must be in Excel (.xlsx, .xls) or CSV format' | |
| try: | |
| if file_ext == '.csv': | |
| state.supplier_df = pd.read_csv(supplier_file.name) | |
| else: | |
| state.supplier_df = pd.read_excel(supplier_file.name) | |
| except Exception as e: | |
| return f'β Error reading supplier data: {str(e)}' | |
| state.text_data = text_data | |
| return "β Data processed successfully" | |
| except Exception as e: | |
| return f'β Error processing data: {str(e)}' | |
| def perform_demand_forecasting(state): | |
| if state.sales_df is None: | |
| return "Error: No sales data provided", None, "Please upload sales data first" | |
| try: | |
| sales_summary = state.sales_df.describe().to_string() | |
| prompt = f"""Analyze the following sales data summary and provide: | |
| 1. A detailed demand forecast for the next quarter | |
| 2. Key trends and seasonality patterns | |
| 3. Actionable recommendations | |
| Data Summary: | |
| {sales_summary} | |
| Please structure your response with clear sections for Forecast, Trends, and Recommendations.""" | |
| response = model.generate_content(prompt) | |
| analysis_text = response.text | |
| fig = px.line(state.sales_df, title='Historical Sales Data and Forecast') | |
| fig.update_layout( | |
| template='plotly_dark', | |
| title_x=0.5, | |
| title_font_size=20, | |
| showlegend=True, | |
| hovermode='x', | |
| paper_bgcolor='#2d2d2d', | |
| plot_bgcolor='#363636', | |
| font=dict(color='white') | |
| ) | |
| return analysis_text, fig, "β Analysis completed successfully" | |
| except Exception as e: | |
| return f"β Error in demand forecasting: {str(e)}", None, "Analysis failed" | |
| def perform_risk_assessment(state): | |
| if state.supplier_df is None: | |
| return "Error: No supplier data provided", None, "Please upload supplier data first" | |
| try: | |
| supplier_summary = state.supplier_df.describe().to_string() | |
| prompt = f"""Perform a comprehensive risk assessment based on: | |
| Supplier Data Summary: | |
| {supplier_summary} | |
| Additional Context: | |
| {state.text_data if state.text_data else 'No additional context provided'} | |
| Please provide: | |
| 1. Risk scoring for each supplier | |
| 2. Identified risk factors | |
| 3. Mitigation recommendations""" | |
| response = model.generate_content(prompt) | |
| analysis_text = response.text | |
| fig = px.scatter(state.supplier_df, title='Supplier Risk Assessment') | |
| fig.update_layout( | |
| template='plotly_dark', | |
| title_x=0.5, | |
| title_font_size=20, | |
| showlegend=True, | |
| hovermode='closest', | |
| paper_bgcolor='#2d2d2d', | |
| plot_bgcolor='#363636', | |
| font=dict(color='white') | |
| ) | |
| return analysis_text, fig, "β Risk assessment completed" | |
| except Exception as e: | |
| return f"β Error in risk assessment: {str(e)}", None, "Assessment failed" | |
| def perform_inventory_optimization(state): | |
| if state.sales_df is None: | |
| return "Error: No sales data provided", None, "Please upload sales data first" | |
| try: | |
| inventory_summary = state.sales_df.describe().to_string() | |
| prompt = f"""Analyze the following inventory data and provide: | |
| 1. Optimal inventory levels | |
| 2. Reorder points | |
| 3. Safety stock recommendations | |
| 4. ABC analysis insights | |
| Data Summary: | |
| {inventory_summary} | |
| Additional Context: | |
| {state.text_data if state.text_data else 'No additional context provided'} | |
| Please structure your response with clear sections for each aspect.""" | |
| response = model.generate_content(prompt) | |
| analysis_text = response.text | |
| fig = go.Figure() | |
| if 'quantity' in state.sales_df.columns: | |
| fig.add_trace(go.Scatter( | |
| y=state.sales_df['quantity'], | |
| name='Inventory Level', | |
| line=dict(color='#3498DB') | |
| )) | |
| fig.update_layout( | |
| title='Inventory Level Analysis', | |
| template='plotly_dark', | |
| title_x=0.5, | |
| title_font_size=20, | |
| showlegend=True, | |
| hovermode='x', | |
| paper_bgcolor='#2d2d2d', | |
| plot_bgcolor='#363636', | |
| font=dict(color='white') | |
| ) | |
| return analysis_text, fig, "β Inventory optimization completed" | |
| except Exception as e: | |
| return f"β Error in inventory optimization: {str(e)}", None, "Analysis failed" | |
| def perform_supplier_performance(state): | |
| if state.supplier_df is None: | |
| return "Error: No supplier data provided", None, "Please upload supplier data first" | |
| try: | |
| supplier_summary = state.supplier_df.describe().to_string() | |
| prompt = f"""Analyze supplier performance based on: | |
| Supplier Data Summary: | |
| {supplier_summary} | |
| Additional Context: | |
| {state.text_data if state.text_data else 'No additional context provided'} | |
| Please provide: | |
| 1. Supplier performance metrics | |
| 2. Performance rankings | |
| 3. Areas for improvement | |
| 4. Supplier development recommendations""" | |
| response = model.generate_content(prompt) | |
| analysis_text = response.text | |
| if 'performance_score' in state.supplier_df.columns: | |
| fig = px.box(state.supplier_df, y='performance_score', | |
| title='Supplier Performance Distribution') | |
| else: | |
| fig = go.Figure(data=[ | |
| go.Bar(name='On-Time Delivery', x=['Supplier A', 'Supplier B', 'Supplier C'], | |
| y=[95, 87, 92]), | |
| go.Bar(name='Quality Score', x=['Supplier A', 'Supplier B', 'Supplier C'], | |
| y=[88, 94, 90]) | |
| ]) | |
| fig.update_layout( | |
| template='plotly_dark', | |
| title_x=0.5, | |
| title_font_size=20, | |
| showlegend=True, | |
| paper_bgcolor='#2d2d2d', | |
| plot_bgcolor='#363636', | |
| font=dict(color='white') | |
| ) | |
| return analysis_text, fig, "β Supplier performance analysis completed" | |
| except Exception as e: | |
| return f"β Error in supplier performance analysis: {str(e)}", None, "Analysis failed" | |
| def perform_sustainability_analysis(state): | |
| if state.supplier_df is None and state.sales_df is None: | |
| return "Error: No data provided", None, "Please upload data first" | |
| try: | |
| data_summary = "" | |
| if state.supplier_df is not None: | |
| data_summary += f"Supplier Data Summary:\n{state.supplier_df.describe().to_string()}\n\n" | |
| if state.sales_df is not None: | |
| data_summary += f"Sales Data Summary:\n{state.sales_df.describe().to_string()}" | |
| prompt = f"""Perform a comprehensive sustainability analysis: | |
| Data Summary: | |
| {data_summary} | |
| Additional Context: | |
| {state.text_data if state.text_data else 'No additional context provided'} | |
| Please provide: | |
| 1. Carbon footprint analysis | |
| 2. Environmental impact metrics | |
| 3. Sustainability recommendations | |
| 4. Green initiative opportunities | |
| 5. ESG performance indicators""" | |
| response = model.generate_content(prompt) | |
| analysis_text = response.text | |
| fig = go.Figure() | |
| categories = ['Carbon Emissions', 'Water Usage', 'Waste Reduction', | |
| 'Energy Efficiency', 'Green Transportation'] | |
| current_scores = [75, 82, 68, 90, 60] | |
| target_scores = [100, 100, 100, 100, 100] | |
| fig.add_trace(go.Scatterpolar( | |
| r=current_scores, | |
| theta=categories, | |
| fill='toself', | |
| name='Current Performance' | |
| )) | |
| fig.add_trace(go.Scatterpolar( | |
| r=target_scores, | |
| theta=categories, | |
| fill='toself', | |
| name='Target' | |
| )) | |
| fig.update_layout( | |
| polar=dict( | |
| radialaxis=dict( | |
| visible=True, | |
| range=[0, 100] | |
| )), | |
| showlegend=True, | |
| title='Sustainability Performance Metrics', | |
| template='plotly_dark', | |
| title_x=0.5, | |
| title_font_size=20, | |
| paper_bgcolor='#2d2d2d', | |
| plot_bgcolor='#363636', | |
| font=dict(color='white') | |
| ) | |
| return analysis_text, fig, "β Sustainability analysis completed" | |
| except Exception as e: | |
| return f"β Error in sustainability analysis: {str(e)}", None, "Analysis failed" | |
| def calculate_shipping_cost(base_cost, params): | |
| """Calculate total shipping cost with all factors""" | |
| total_cost = base_cost | |
| # Fuel surcharge | |
| fuel_charge = base_cost * (params['fuel_surcharge'] / 100) | |
| # Insurance | |
| insurance = params['line_item_value'] * (params['insurance_rate'] / 100) | |
| # Customs duty | |
| duty = params['line_item_value'] * (params['customs_duty'] / 100) | |
| # Special handling charges | |
| handling_charges = 0 | |
| handling_rates = { | |
| "Temperature Controlled": 0.15, | |
| "Hazardous Materials": 0.25, | |
| "Fragile Items": 0.10, | |
| "Express Delivery": 0.20, | |
| "Door-to-Door Service": 0.15 | |
| } | |
| for requirement in params['special_handling']: | |
| if requirement in handling_rates: | |
| handling_charges += base_cost * handling_rates[requirement] | |
| # Distance-based charge | |
| distance_rate = { | |
| "Air": 0.1, | |
| "Ocean": 0.05, | |
| "Truck": 0.15 | |
| } | |
| distance_charge = params['distance'] * distance_rate[params['shipment_mode']] | |
| # Time-based charge | |
| transit_charge = params['transit_time'] * (base_cost * 0.01) | |
| total_cost = base_cost + fuel_charge + insurance + duty + handling_charges + distance_charge + transit_charge | |
| return { | |
| 'base_cost': round(base_cost, 2), | |
| 'fuel_charge': round(fuel_charge, 2), | |
| 'insurance': round(insurance, 2), | |
| 'customs_duty': round(duty, 2), | |
| 'handling_charges': round(handling_charges, 2), | |
| 'distance_charge': round(distance_charge, 2), | |
| 'transit_charge': round(transit_charge, 2), | |
| 'total_cost': round(total_cost, 2) | |
| } | |
| def predict_freight_cost(state, params): | |
| """Predict freight cost with enhanced parameters""" | |
| if state.freight_model is None: | |
| return "Error: Freight prediction model not loaded" | |
| try: | |
| # Clean shipment mode string | |
| mode = params['shipment_mode'].replace("βοΈ ", "").replace("π’ ", "").replace("π ", "") | |
| # Prepare features for the model | |
| features = { | |
| 'weight (kilograms)': params['weight'], | |
| 'line item value': params['line_item_value'], | |
| 'cost per kilogram': params['cost_per_kg'], | |
| 'shipment mode_Air Charter_weight': params['weight'] if mode == "Air" else 0, | |
| 'shipment mode_Ocean_weight': params['weight'] if mode == "Ocean" else 0, | |
| 'shipment mode_Truck_weight': params['weight'] if mode == "Truck" else 0, | |
| 'shipment mode_Air Charter_line_item_value': params['line_item_value'] if mode == "Air" else 0, | |
| 'shipment mode_Ocean_line_item_value': params['line_item_value'] if mode == "Ocean" else 0, | |
| 'shipment mode_Truck_line_item_value': params['line_item_value'] if mode == "Truck" else 0 | |
| } | |
| input_data = pd.DataFrame([features]) | |
| base_prediction = state.freight_model.predict(input_data)[0] | |
| # Calculate total cost with all factors | |
| cost_breakdown = calculate_shipping_cost(base_prediction, params) | |
| return cost_breakdown | |
| except Exception as e: | |
| return f"Error making prediction: {str(e)}" | |
| if state.freight_model is None: | |
| return "Error: Freight prediction model not loaded" | |
| try: | |
| # Set weights based on mode | |
| if "Air" in shipment_mode: | |
| air_charter_weight = weight | |
| air_charter_value = line_item_value | |
| elif "Ocean" in shipment_mode: | |
| ocean_weight = weight | |
| ocean_value = line_item_value | |
| else: | |
| truck_weight = weight | |
| truck_value = line_item_value | |
| features = { | |
| 'weight (kilograms)': weight, | |
| 'line item value': line_item_value, | |
| 'cost per kilogram': cost_per_kg, | |
| 'shipment mode_Air Charter_weight': air_charter_weight, | |
| 'shipment mode_Ocean_weight': ocean_weight, | |
| 'shipment mode_Truck_weight': truck_weight, | |
| 'shipment mode_Air Charter_line_item_value': air_charter_value, | |
| 'shipment mode_Ocean_line_item_value': ocean_value, | |
| 'shipment mode_Truck_line_item_value': truck_value | |
| } | |
| input_data = pd.DataFrame([features]) | |
| prediction = state.freight_model.predict(input_data) | |
| return round(float(prediction[0]), 2) | |
| except Exception as e: | |
| return f"Error making prediction: {str(e)}" | |
| if state.freight_model is None: | |
| return "Error: Freight prediction model not loaded" | |
| try: | |
| features = { | |
| 'weight (kilograms)': weight, | |
| 'line item value': line_item_value, | |
| 'cost per kilogram': cost_per_kg, | |
| 'shipment mode_Air Charter_weight': air_charter_weight if "Air" in shipment_mode else 0, | |
| 'shipment mode_Ocean_weight': ocean_weight if "Ocean" in shipment_mode else 0, | |
| 'shipment mode_Truck_weight': truck_weight if "Truck" in shipment_mode else 0, | |
| 'shipment mode_Air Charter_line_item_value': air_charter_value if "Air" in shipment_mode else 0, | |
| 'shipment mode_Ocean_line_item_value': ocean_value if "Ocean" in shipment_mode else 0, | |
| 'shipment mode_Truck_line_item_value': truck_value if "Truck" in shipment_mode else 0 | |
| } | |
| input_data = pd.DataFrame([features]) | |
| prediction = state.freight_model.predict(input_data) | |
| return round(float(prediction[0]), 2) | |
| except Exception as e: | |
| return f"Error making prediction: {str(e)}" | |
| def chat_with_navigator(state, message): | |
| try: | |
| context = "Available data and analysis:\n" | |
| if state.sales_df is not None: | |
| context += f"- Sales data with {len(state.sales_df)} records\n" | |
| if state.supplier_df is not None: | |
| context += f"- Supplier data with {len(state.supplier_df)} records\n" | |
| if state.text_data: | |
| context += "- Additional context from text data\n" | |
| if state.freight_predictions: | |
| context += f"- Recent freight predictions: {state.freight_predictions[-5:]}\n" | |
| if state.analysis_results: | |
| context += "\nRecent analysis results:\n" | |
| for analysis_type, results in state.analysis_results.items(): | |
| context += f"- {analysis_type} completed\n" | |
| prompt = f"""You are SupplyChainAI Navigator's assistant. Help the user with supply chain analysis, | |
| including demand forecasting, risk assessment, and freight cost predictions. | |
| Available Context: | |
| {context} | |
| Chat History: | |
| {str(state.chat_history[-3:]) if state.chat_history else 'No previous messages'} | |
| User message: {message} | |
| Provide a helpful response based on the available data and analysis results.""" | |
| response = chat_model.generate_content(prompt) | |
| state.chat_history.append({"role": "user", "content": message}) | |
| state.chat_history.append({"role": "assistant", "content": response.text}) | |
| return state.chat_history | |
| except Exception as e: | |
| return [{"role": "assistant", "content": f"Error: {str(e)}"}] | |
| def generate_pdf_report(state, analysis_options): | |
| try: | |
| temp_dir = tempfile.mkdtemp() | |
| pdf_path = os.path.join(temp_dir, "supply_chain_report.pdf") | |
| doc = SimpleDocTemplate(pdf_path, pagesize=letter) | |
| styles = getSampleStyleSheet() | |
| story = [] | |
| # Create custom title style | |
| title_style = ParagraphStyle( | |
| 'CustomTitle', | |
| parent=styles['Heading1'], | |
| fontSize=24, | |
| spaceAfter=30, | |
| textColor=colors.HexColor('#2C3E50') | |
| ) | |
| story.append(Paragraph("SupplyChainAI Navigator Report", title_style)) | |
| story.append(Spacer(1, 12)) | |
| timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| story.append(Paragraph(f"Generated on: {timestamp}", styles['Normal'])) | |
| story.append(Spacer(1, 20)) | |
| if state.analysis_results: | |
| for analysis_type, results in state.analysis_results.items(): | |
| if analysis_type in analysis_options: | |
| story.append(Paragraph(analysis_type, styles['Heading2'])) | |
| story.append(Spacer(1, 12)) | |
| story.append(Paragraph(results['text'], styles['Normal'])) | |
| story.append(Spacer(1, 12)) | |
| if 'figure' in results: | |
| img_path = os.path.join(temp_dir, f"{analysis_type.lower()}_plot.png") | |
| results['figure'].write_image(img_path) | |
| story.append(Image(img_path, width=400, height=300)) | |
| story.append(Spacer(1, 20)) | |
| if state.freight_predictions: | |
| story.append(Paragraph("Recent Freight Cost Predictions", styles['Heading2'])) | |
| story.append(Spacer(1, 12)) | |
| pred_data = [["Prediction #", "Cost (USD)"]] | |
| for i, pred in enumerate(state.freight_predictions[-5:], 1): | |
| pred_data.append([f"Prediction {i}", f"${pred:,.2f}"]) | |
| table = Table(pred_data) | |
| table.setStyle(TableStyle([ | |
| ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#3498DB')), | |
| ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke), | |
| ('ALIGN', (0, 0), (-1, -1), 'CENTER'), | |
| ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), | |
| ('FONTSIZE', (0, 0), (-1, 0), 14), | |
| ('BOTTOMPADDING', (0, 0), (-1, 0), 12), | |
| ('BACKGROUND', (0, 1), (-1, -1), colors.whitesmoke), | |
| ('TEXTCOLOR', (0, 1), (-1, -1), colors.black), | |
| ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'), | |
| ('FONTSIZE', (0, 1), (-1, -1), 12), | |
| ('GRID', (0, 0), (-1, -1), 1, colors.black) | |
| ])) | |
| story.append(table) | |
| story.append(Spacer(1, 20)) | |
| doc.build(story) | |
| return pdf_path | |
| except Exception as e: | |
| print(f"Error generating PDF: {str(e)}") | |
| return None | |
| def run_analyses(state, choices, sales_file, supplier_file, text_data): | |
| results = [] | |
| figures = [] | |
| status_messages = [] | |
| process_status = process_uploaded_data(state, sales_file, supplier_file, text_data) | |
| if "Error" in process_status: | |
| return process_status, None, process_status | |
| for choice in choices: | |
| if "π Demand Forecasting" in choice: | |
| text, fig, status = perform_demand_forecasting(state) | |
| results.append(text) | |
| figures.append(fig) | |
| status_messages.append(status) | |
| if text and fig: | |
| state.analysis_results['Demand Forecasting'] = {'text': text, 'figure': fig} | |
| elif "β οΈ Risk Assessment" in choice: | |
| text, fig, status = perform_risk_assessment(state) | |
| results.append(text) | |
| figures.append(fig) | |
| status_messages.append(status) | |
| if text and fig: | |
| state.analysis_results['Risk Assessment'] = {'text': text, 'figure': fig} | |
| elif "π¦ Inventory Optimization" in choice: | |
| text, fig, status = perform_inventory_optimization(state) | |
| results.append(text) | |
| figures.append(fig) | |
| status_messages.append(status) | |
| if text and fig: | |
| state.analysis_results['Inventory Optimization'] = {'text': text, 'figure': fig} | |
| elif "π€ Supplier Performance" in choice: | |
| text, fig, status = perform_supplier_performance(state) | |
| results.append(text) | |
| figures.append(fig) | |
| status_messages.append(status) | |
| if text and fig: | |
| state.analysis_results['Supplier Performance'] = {'text': text, 'figure': fig} | |
| elif "πΏ Sustainability Analysis" in choice: | |
| text, fig, status = perform_sustainability_analysis(state) | |
| results.append(text) | |
| figures.append(fig) | |
| status_messages.append(status) | |
| if text and fig: | |
| state.analysis_results['Sustainability Analysis'] = {'text': text, 'figure': fig} | |
| combined_results = "\n\n".join(results) | |
| combined_status = "\n".join(status_messages) | |
| final_figure = figures[-1] if figures else None | |
| return combined_results, final_figure, combined_status | |
| def predict_and_store_freight(state, *args): | |
| if len(args) >= 3: | |
| weight, line_item_value, shipment_mode = args[:3] | |
| result = predict_freight_cost(state, weight, line_item_value, 50, shipment_mode) | |
| if isinstance(result, (int, float)): | |
| state.freight_predictions.append(result) | |
| return result | |
| return "Error: Invalid parameters" | |
| def create_interface(): | |
| state = SupplyChainState() | |
| with gr.Blocks(css=CUSTOM_CSS, title="SupplyChainAI Navigator") as demo: | |
| # Header | |
| with gr.Row(elem_classes="main-header"): | |
| with gr.Column(): | |
| gr.Markdown("# π’ SupplyChainAI Navigator", elem_classes="app-title") | |
| gr.Markdown("### Intelligent Supply Chain Analysis & Optimization", elem_classes="app-subtitle") | |
| gr.Markdown("An AI-powered platform for comprehensive supply chain analytics", elem_classes="app-description") | |
| gr.Markdown("### Created by Aditya Ratan", elem_classes="creator-info") | |
| # Main Content Tabs | |
| with gr.Tabs() as tabs: | |
| # Data Upload Tab | |
| with gr.Tab("π Data Upload", elem_classes="tab-content"): | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| sales_data_upload = gr.File( | |
| file_types=[".xlsx", ".xls", ".csv"], | |
| label="π Sales Data (Excel or CSV)", | |
| elem_classes="file-upload" | |
| ) | |
| gr.Markdown("*Upload sales data in Excel (.xlsx, .xls) or CSV format*", elem_classes="file-instructions") | |
| with gr.Column(scale=1): | |
| supplier_data_upload = gr.File( | |
| file_types=[".xlsx", ".xls", ".csv"], | |
| label="π Supplier Data (Excel or CSV)", | |
| elem_classes="file-upload" | |
| ) | |
| gr.Markdown("*Upload supplier data in Excel (.xlsx, .xls) or CSV format*", elem_classes="file-instructions") | |
| with gr.Row(): | |
| text_input_area = gr.Textbox( | |
| label="π Additional Context", | |
| placeholder="Add market updates, news, or other relevant information...", | |
| lines=5 | |
| ) | |
| with gr.Row(): | |
| upload_status = gr.Textbox( | |
| label="Status", | |
| elem_classes="status-box" | |
| ) | |
| upload_button = gr.Button( | |
| "π Process Data", | |
| variant="primary", | |
| elem_classes="action-button" | |
| ) | |
| # Analysis Tab | |
| with gr.Tab("π Analysis", elem_classes="tab-content"): | |
| with gr.Row(): | |
| analysis_options = gr.CheckboxGroup( | |
| choices=[ | |
| "π Demand Forecasting", | |
| "β οΈ Risk Assessment", | |
| "π¦ Inventory Optimization", | |
| "π€ Supplier Performance", | |
| "πΏ Sustainability Analysis" | |
| ], | |
| label="Choose analyses to perform", | |
| value=[] | |
| ) | |
| analyze_button = gr.Button( | |
| "π Run Analysis", | |
| variant="primary", | |
| elem_classes="action-button" | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| analysis_output = gr.Textbox( | |
| label="Analysis Results", | |
| elem_classes="result-box" | |
| ) | |
| with gr.Column(scale=3): | |
| plot_output = gr.Plot( | |
| label="Visualization", | |
| elem_classes="chart-container" | |
| ) | |
| processing_status = gr.Textbox( | |
| label="Processing Status", | |
| elem_classes="status-box" | |
| ) | |
| # Cost Prediction Tab | |
| with gr.Tab("π° Cost Prediction", elem_classes="tab-content"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| shipment_mode = gr.Dropdown( | |
| choices=["βοΈ Air", "π’ Ocean", "π Truck"], | |
| label="Transport Mode", | |
| value="βοΈ Air" | |
| ) | |
| # Basic Parameters | |
| weight = gr.Slider( | |
| label="π¦ Weight (kg)", | |
| minimum=1, | |
| maximum=10000, | |
| step=1, | |
| value=1000 | |
| ) | |
| line_item_value = gr.Slider( | |
| label="π΅ Item Value (USD)", | |
| minimum=1, | |
| maximum=1000000, | |
| step=1, | |
| value=10000 | |
| ) | |
| cost_per_kg = gr.Slider( | |
| label="π² Base Cost per kg (USD)", | |
| minimum=1, | |
| maximum=500, | |
| step=1, | |
| value=50 | |
| ) | |
| # Advanced Parameters | |
| gr.Markdown("### Advanced Parameters") | |
| transit_time = gr.Slider( | |
| label="π Transit Time (Days)", | |
| minimum=1, | |
| maximum=60, | |
| step=1, | |
| value=7 | |
| ) | |
| distance = gr.Slider( | |
| label="π Distance (km)", | |
| minimum=100, | |
| maximum=20000, | |
| step=100, | |
| value=1000 | |
| ) | |
| fuel_surcharge = gr.Slider( | |
| label="β½ Fuel Surcharge (%)", | |
| minimum=0, | |
| maximum=50, | |
| step=0.5, | |
| value=5 | |
| ) | |
| # Risk Factors | |
| gr.Markdown("### Risk Factors") | |
| insurance_rate = gr.Slider( | |
| label="π‘οΈ Insurance Rate (%)", | |
| minimum=0.1, | |
| maximum=10, | |
| step=0.1, | |
| value=1 | |
| ) | |
| customs_duty = gr.Slider( | |
| label="ποΈ Customs Duty (%)", | |
| minimum=0, | |
| maximum=40, | |
| step=0.5, | |
| value=5 | |
| ) | |
| # Special Handling | |
| gr.Markdown("### Special Handling") | |
| special_handling = gr.CheckboxGroup( | |
| choices=[ | |
| "Temperature Controlled", | |
| "Hazardous Materials", | |
| "Fragile Items", | |
| "Express Delivery", | |
| "Door-to-Door Service" | |
| ], | |
| label="Special Requirements" | |
| ) | |
| predict_button = gr.Button( | |
| "π Calculate Total Cost", | |
| variant="primary", | |
| elem_classes="action-button" | |
| ) | |
| with gr.Row(): | |
| freight_result = gr.Number( | |
| label="Base Freight Cost (USD)", | |
| elem_classes="result-box" | |
| ) | |
| total_cost = gr.Number( | |
| label="Total Cost Including All Charges (USD)", | |
| elem_classes="result-box" | |
| ) | |
| cost_breakdown = gr.JSON( | |
| label="Cost Breakdown", | |
| elem_classes="result-box" | |
| ) | |
| # Chat Tab | |
| with gr.Tab("π¬ Chat", elem_classes="tab-content"): | |
| chatbot = gr.Chatbot( | |
| label="Chat History", | |
| elem_classes="chat-container", | |
| height=400 | |
| ) | |
| with gr.Row(): | |
| msg = gr.Textbox( | |
| label="Message", | |
| placeholder="Ask about your supply chain data...", | |
| scale=4 | |
| ) | |
| chat_button = gr.Button( | |
| "π€ Send", | |
| variant="primary", | |
| scale=1, | |
| elem_classes="action-button" | |
| ) | |
| # Report Tab | |
| with gr.Tab("π Report", elem_classes="tab-content"): | |
| report_options = gr.CheckboxGroup( | |
| choices=[ | |
| "π Demand Forecasting", | |
| "β οΈ Risk Assessment", | |
| "π¦ Inventory Optimization", | |
| "π€ Supplier Performance", | |
| "πΏ Sustainability Analysis" | |
| ], | |
| label="Select sections to include", | |
| value=[] | |
| ) | |
| report_button = gr.Button( | |
| "π Generate Report", | |
| variant="primary", | |
| elem_classes="action-button" | |
| ) | |
| report_download = gr.File( | |
| label="Download Report" | |
| ) | |
| # Event Handlers | |
| upload_button.click( | |
| fn=lambda *args: process_uploaded_data(state, *args), | |
| inputs=[sales_data_upload, supplier_data_upload, text_input_area], | |
| outputs=[upload_status]) | |
| analyze_button.click( | |
| fn=lambda choices, sales, supplier, text: run_analyses(state, choices, sales, supplier, text), | |
| inputs=[analysis_options, sales_data_upload, supplier_data_upload, text_input_area], | |
| outputs=[analysis_output, plot_output, processing_status] | |
| ) | |
| predict_button.click( | |
| fn=lambda mode, w, val, cost, time, dist, fuel, ins, duty, special: predict_and_store_freight( | |
| state, | |
| { | |
| 'shipment_mode': mode, | |
| 'weight': w, | |
| 'line_item_value': val, | |
| 'cost_per_kg': cost, | |
| 'transit_time': time, | |
| 'distance': dist, | |
| 'fuel_surcharge': fuel, | |
| 'insurance_rate': ins, | |
| 'customs_duty': duty, | |
| 'special_handling': special | |
| } | |
| ), | |
| inputs=[ | |
| shipment_mode, weight, line_item_value, cost_per_kg, | |
| transit_time, distance, fuel_surcharge, | |
| insurance_rate, customs_duty, special_handling | |
| ], | |
| outputs=[freight_result, total_cost, cost_breakdown] | |
| ) | |
| chat_button.click( | |
| fn=lambda message: chat_with_navigator(state, message), | |
| inputs=[msg], | |
| outputs=[chatbot] | |
| ) | |
| report_button.click( | |
| fn=lambda options: generate_pdf_report(state, options), | |
| inputs=[report_options], | |
| outputs=[report_download] | |
| ) | |
| # Footer | |
| gr.HTML( | |
| '''<div class="footer"> | |
| Made with π§ by Aditya Ratan | |
| </div>''' | |
| ) | |
| return demo | |
| if __name__ == "__main__": | |
| demo = create_interface() | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| debug=True | |
| ) |