Commit 7e26d653 authored by Barthelet Thibault's avatar Barthelet Thibault
Browse files

fix forecast random, better cmd visual

parent 3388768c
Loading
Loading
Loading
Loading
+381 −0
Original line number Diff line number Diff line
@@ -607,6 +607,387 @@ class VisualSimulation(SupplyChainSimulation):
        
        console.print(product_table)

        # Supply Chain Summary



        console.print("\n[bold cyan]📦 SUPPLY CHAIN OPERATIONS[/bold cyan]\n")


        


        operations_table = Table(show_header=False, box=None)


        operations_table.add_column("Metric", style="cyan")


        operations_table.add_column("Value", style="green", justify="right")


        operations_table.add_column("Details", style="dim")


        


        # Component analysis


        component_cost = sum(o.cost for o in self.all_orders)


        avg_lead_time = sum(o.expected_delivery for o in self.all_orders) / len(self.all_orders) if self.all_orders else 0


        


        operations_table.add_row("Component Orders", str(total_orders), f"Total value: ${component_cost:,.0f}")


        operations_table.add_row("Average Lead Time", f"{avg_lead_time:.1f} days", "From order to delivery")


        operations_table.add_row("Watches Assembled", str(self.total_assembled), f"Capacity: {self.assembly.assembly_capacity}/day")


        operations_table.add_row("Distribution Points", str(len(self.distribution.distribution_history)), "Shipments to retailers")


        


        # Inventory status


        warehouse_components = sum(inv.quantity for inv in self.warehouse_inventories if inv.item_type == "component")


        warehouse_watches = sum(inv.quantity for inv in self.warehouse_inventories if inv.item_type == "watch")


        retailer_watches = sum(inv.quantity for inv in self.retailer_inventories)


        


        operations_table.add_row("Warehouse Components", str(warehouse_components), "Current stock")


        operations_table.add_row("Warehouse Watches", str(warehouse_watches), "Ready to ship")


        operations_table.add_row("Retailer Stock", str(retailer_watches), "Available for sale")


        


        console.print(Panel(operations_table, title="Operations Summary", border_style="cyan"))


        


        # Customer Analytics


        console.print("\n[bold cyan]👥 CUSTOMER ANALYTICS[/bold cyan]\n")


        


        # Sales by wealth class


        sales_by_wealth = defaultdict(lambda: {'count': 0, 'revenue': 0})


        unique_buyers = set()


        for sale in self.all_sales:


            customer = next((c for c in self.setup.customers if c.id == sale.customer_id), None)


            if customer:


                sales_by_wealth[customer.wealth_class]['count'] += 1


                sales_by_wealth[customer.wealth_class]['revenue'] += sale.total_price


                unique_buyers.add(customer.id)


        


        customer_table = Table(show_header=True, header_style="bold magenta")


        customer_table.add_column("Wealth Class", style="cyan")


        customer_table.add_column("Customers", justify="right")


        customer_table.add_column("Buyers", justify="right")


        customer_table.add_column("Sales", justify="right")


        customer_table.add_column("Revenue", justify="right", style="green")


        customer_table.add_column("Avg Ticket", justify="right")


        


        # Count customers by wealth class


        customers_by_wealth = defaultdict(int)


        buyers_by_wealth = defaultdict(set)


        for customer in self.setup.customers:


            customers_by_wealth[customer.wealth_class] += 1


        for sale in self.all_sales:


            customer = next((c for c in self.setup.customers if c.id == sale.customer_id), None)


            if customer:


                buyers_by_wealth[customer.wealth_class].add(customer.id)


        


        for wc in range(1, 11):


            if wc in sales_by_wealth or wc in customers_by_wealth:


                data = sales_by_wealth[wc]


                n_customers = customers_by_wealth[wc]


                n_buyers = len(buyers_by_wealth[wc])


                avg_ticket = data['revenue'] / data['count'] if data['count'] > 0 else 0


                


                customer_table.add_row(


                    f"Class {wc}",


                    str(n_customers),


                    str(n_buyers),


                    str(data['count']),


                    f"${data['revenue']:,.0f}",


                    f"${avg_ticket:.0f}"


                )


        


        console.print(customer_table)


        


        console.print(f"\n[cyan]Total Unique Buyers: {len(unique_buyers)} ({len(unique_buyers)/len(self.setup.customers)*100:.1f}% penetration)[/cyan]")


        console.print(f"[cyan]Return Rate: {len(self.customer_behavior.returns)/len(self.all_sales)*100:.1f}% ({len(self.customer_behavior.returns)} returns)[/cyan]")


        


        # Model Performance


        console.print("\n[bold cyan]🤖 AI MODEL PERFORMANCE[/bold cyan]\n")


        


        model_summary = self.model_update.get_model_performance_summary()


        accuracy_metrics = model_summary['forecast_accuracy']


        


        model_table = Table(show_header=False, box=None)


        model_table.add_column("Metric", style="cyan")


        model_table.add_column("Value", style="green", justify="right")


        


        model_table.add_row("Training Data Points", str(model_summary['total_sales_records']))


        model_table.add_row("Customer Segments", str(model_summary['customer_segments']))


        model_table.add_row("Forecasts Evaluated", str(model_summary['forecasts_evaluated']))


        model_table.add_row("MAPE", f"{accuracy_metrics['mape']:.1f}%")


        model_table.add_row("RMSE", f"{accuracy_metrics['rmse']:.2f}")


        model_table.add_row("ML Accuracy", f"{accuracy_metrics['accuracy']*100:.1f}%")


        


        console.print(Panel(model_table, title="Machine Learning Metrics", border_style="yellow"))


        


        # Final KPIs


        console.print("\n[bold cyan]💰 FINANCIAL PERFORMANCE[/bold cyan]\n")


        


        if self.research_logging.kpi_history:


            final_kpis = self.research_logging.kpi_history[-1]


            


            financial_table = Table(show_header=False, box=None)


            financial_table.add_column("Metric", style="cyan", width=25)


            financial_table.add_column("Value", style="bold green", justify="right")


            financial_table.add_column("Status", justify="center")


            


            # GMROI assessment


            gmroi_status = "🟢" if final_kpis['gmroi'] > 2 else "🟡" if final_kpis['gmroi'] > 1 else "🔴"


            financial_table.add_row("GMROI", f"{final_kpis['gmroi']:.3f}", gmroi_status)


            


            financial_table.add_row("Total Revenue", f"${final_kpis['total_revenue']:,.0f}", "🟢")


            financial_table.add_row("Gross Margin", f"${final_kpis['gross_margin']:,.0f}", "🟢")


            financial_table.add_row("COGS", f"${final_kpis['cogs']:,.0f}", "")


            financial_table.add_row("Logistics Costs", f"${final_kpis['logistics_costs']:,.0f}", "")


            


            margin_pct = (final_kpis['gross_margin'] / final_kpis['total_revenue'] * 100) if final_kpis['total_revenue'] > 0 else 0


            financial_table.add_row("Margin %", f"{margin_pct:.1f}%", "🟢" if margin_pct > 60 else "🟡")


            


            financial_table.add_row("", "", "")


            financial_table.add_row("Warehouse Utilization", f"{final_kpis['warehouse_capacity_utilization']:.1%}", "🟢")


            financial_table.add_row("Retailer Utilization", f"{final_kpis['retailer_capacity_utilization']:.1%}", "🟢")


            


            console.print(Panel(financial_table, title="[bold]Financial KPIs[/bold]", border_style="green"))
        
        # Final completion
        console.print("\n[bold green]✅ Simulation completed successfully![/bold green]\n")
        log_to_file("Simulation completed successfully!")
+5 −6
Original line number Diff line number Diff line
@@ -54,14 +54,13 @@ class Forecasting:
        return forecasts
    
    def _calculate_realistic_demand(self, watch, region: str) -> float:
        """Calculate realistic demand based on watch characteristics."""
        # Base demand starts lower and more realistic
        # Current base demands are too high - reduce by factor of 10
        if watch.category == 'luxury':
            base_demand = np.random.uniform(5, 15)  # Luxury has lower volume
            base_demand = np.random.uniform(0.5, 1.5)  # was 5-15
        elif watch.category == 'sport':  
            base_demand = np.random.uniform(10, 25)  # Sport has medium volume
            base_demand = np.random.uniform(1, 2.5)    # was 10-25
        else:  # casual
            base_demand = np.random.uniform(15, 30)  # Casual has higher volume
            base_demand = np.random.uniform(1.5, 3)    # was 15-30
        
        # Price sensitivity - higher prices reduce demand
        price_factor = max(0.3, 1.0 - (watch.base_cost - 200) / 2000)
+1 −1
Original line number Diff line number Diff line
@@ -132,7 +132,7 @@ class SupplyOrderLogic:
        remaining_orders = []
        
        for order in self.pending_orders:
            order.expected_delivery -= time_elapsed
            max(0, order.expected_delivery - time_elapsed)
            
            if order.expected_delivery <= 0:
                delivered_orders.append(order)