Commit 2fbcc1ab authored by Barthelet Thibault's avatar Barthelet Thibault
Browse files

simple prints

parent 11f92151
Loading
Loading
Loading
Loading
+185 −13
Original line number Diff line number Diff line
@@ -128,8 +128,8 @@ class ResearchLogging:
        
        return avg_kpis
    
    def print_final_report(self):
        """Print final simulation report."""
    def print_final_report(self, simulation_data=None):
        """Print comprehensive final simulation report."""
        print("\n" + "="*60)
        print("SIMULATION FINAL REPORT")
        print("="*60)
@@ -137,33 +137,205 @@ class ResearchLogging:
        # Scenario parameters
        print("\nScenario Parameters:")
        for key, value in self.scenario_params.items():
            if key != 'timestamp':
                print(f"  {key}: {value}")
        
        # Show detailed product performance if simulation data provided
        if simulation_data:
            self._print_product_performance(simulation_data)
            self._print_supply_chain_summary(simulation_data)
            self._print_customer_summary(simulation_data)
            self._print_inventory_summary(simulation_data)
        
        # Latest KPIs
        if self.kpi_history:
            latest = self.kpi_history[-1]
            print("\nFinal KPIs:")
            print("\n📊 FINAL KPIs:")
            print(f"  GMROI: {latest['gmroi']:.3f}")
            print(f"  Fill Rate: {latest['fill_rate']:.2%}")
            print(f"  Forecast Accuracy: {latest['forecast_accuracy']:.2%}")
            print(f"  Total Revenue: ${latest['total_revenue']:,.0f}")
            print(f"  Total Cost: ${latest['total_cost']:,.0f}")
            print(f"  Total Sales: {latest['total_sales']}")
            print(f"  Forecast MAPE: {latest['mape']:.1f}%")
        
        # Summary statistics
        summary = self.get_summary_statistics()
        if summary:
            print("\nAverage Performance:")
            print("\n📈 AVERAGE PERFORMANCE:")
            print(f"  Avg GMROI: {summary.get('avg_gmroi', 0):.3f}")
            print(f"  Avg Revenue: ${summary.get('avg_total_revenue', 0):,.0f}")
            print(f"  Avg Fill Rate: {summary.get('avg_fill_rate', 0):.2%}")
        
        # Baseline comparison
        comparison = self.compare_against_baselines()
        if comparison:
            print("\nVs Baseline:")
            for metric, data in comparison.items():
                symbol = "" if data['improved'] else ""
                print(f"  {metric}: {data['change_pct']:+.1f}% {symbol}")
        
        print("="*60)
    
    def _print_product_performance(self, sim_data):
        """Print detailed product performance metrics."""
        print("\n🏷️ PRODUCT PERFORMANCE (Predicted vs Actual Sales):")
        print("-" * 50)
        
        # Aggregate forecasts and sales by watch
        forecast_by_watch = {}
        sales_by_watch = {}
        revenue_by_watch = {}
        
        # Aggregate forecasts
        for f in sim_data.get('all_forecasts', []):
            if f.watch_id not in forecast_by_watch:
                forecast_by_watch[f.watch_id] = 0
            forecast_by_watch[f.watch_id] += f.forecasted_demand
        
        # Aggregate sales
        for sale in sim_data.get('all_sales', []):
            if sale.watch_id not in sales_by_watch:
                sales_by_watch[sale.watch_id] = 0
                revenue_by_watch[sale.watch_id] = 0
            sales_by_watch[sale.watch_id] += sale.quantity
            revenue_by_watch[sale.watch_id] += sale.total_price
        
        # Get watch details
        watches = sim_data.get('watches', [])
        
        # Print header
        print(f"{'Watch Name':<20} {'Category':<10} {'Price':<10} {'Predicted':<12} {'Sold':<8} {'Accuracy':<10} {'Revenue':<12}")
        print("-" * 100)
        
        total_predicted = 0
        total_sold = 0
        total_revenue = 0
        
        for watch in watches:
            predicted = forecast_by_watch.get(watch.id, 0)
            sold = sales_by_watch.get(watch.id, 0)
            revenue = revenue_by_watch.get(watch.id, 0)
            
            accuracy = (sold / predicted * 100) if predicted > 0 else 0
            
            print(f"{watch.name:<20} {watch.category:<10} ${watch.base_price:<9.0f} "
                  f"{predicted:<12.0f} {sold:<8} {accuracy:<9.1f}% ${revenue:<11,.0f}")
            
            total_predicted += predicted
            total_sold += sold
            total_revenue += revenue
        
        print("-" * 100)
        print(f"{'TOTAL':<20} {'':<10} {'':<10} {total_predicted:<12.0f} {total_sold:<8} "
              f"{(total_sold/total_predicted*100) if total_predicted > 0 else 0:<9.1f}% ${total_revenue:<11,.0f}")
    
    def _print_supply_chain_summary(self, sim_data):
        """Print supply chain metrics."""
        print("\n📦 SUPPLY CHAIN SUMMARY:")
        print("-" * 50)
        
        # Component orders
        orders = sim_data.get('all_orders', [])
        if orders:
            print(f"Total component orders placed: {len(orders)}")
            total_order_cost = sum(o.cost for o in orders)
            print(f"Total ordering cost: ${total_order_cost:,.0f}")
            
            # Group by component
            by_component = {}
            for order in orders:
                if order.component_id not in by_component:
                    by_component[order.component_id] = {'quantity': 0, 'cost': 0}
                by_component[order.component_id]['quantity'] += order.quantity
                by_component[order.component_id]['cost'] += order.cost
            
            print(f"\nTop 5 ordered components:")
            sorted_components = sorted(by_component.items(), key=lambda x: x[1]['quantity'], reverse=True)[:5]
            for comp_id, data in sorted_components:
                print(f"  Component {comp_id}: {data['quantity']} units (${data['cost']:,.0f})")
        
        # Assembly
        assembled = sim_data.get('total_assembled', 0)
        print(f"\nTotal watches assembled: {assembled}")
        
        # Distribution
        distributions = sim_data.get('distribution_history', [])
        if distributions:
            total_distributed = sum(d['quantity'] for d in distributions)
            print(f"Total watches distributed: {total_distributed}")
    
    def _print_customer_summary(self, sim_data):
        """Print customer behavior summary."""
        print("\n👥 CUSTOMER BEHAVIOR:")
        print("-" * 50)
        
        customers = sim_data.get('customers', [])
        sales = sim_data.get('all_sales', [])
        
        print(f"Total customers: {len(customers)}")
        
        # Calculate unique buyers
        unique_buyers = len(set(s.customer_id for s in sales))
        print(f"Unique buyers: {unique_buyers} ({unique_buyers/len(customers)*100:.1f}%)")
        
        # Sales by wealth class
        sales_by_wealth = {}
        for sale in sales:
            customer = next((c for c in customers if c.id == sale.customer_id), None)
            if customer:
                wc = customer.wealth_class
                if wc not in sales_by_wealth:
                    sales_by_wealth[wc] = {'count': 0, 'revenue': 0}
                sales_by_wealth[wc]['count'] += 1
                sales_by_wealth[wc]['revenue'] += sale.total_price
        
        if sales_by_wealth:
            print("\nSales by wealth class:")
            for wc in sorted(sales_by_wealth.keys()):
                data = sales_by_wealth[wc]
                print(f"  Class {wc}: {data['count']} sales (${data['revenue']:,.0f})")
        
        # Returns
        returns = sim_data.get('returns', [])
        if sales:
            return_rate = len(returns) / len(sales) * 100
            print(f"\nReturn rate: {return_rate:.1f}% ({len(returns)} returns)")
    
    def _print_inventory_summary(self, sim_data):
        """Print inventory status."""
        print("\n📊 INVENTORY STATUS:")
        print("-" * 50)
        
        warehouse_inv = sim_data.get('warehouse_inventories', [])
        retailer_inv = sim_data.get('retailer_inventories', [])
        
        # Warehouse inventory
        warehouse_components = sum(inv.quantity for inv in warehouse_inv if inv.item_type == "component")
        warehouse_watches = sum(inv.quantity for inv in warehouse_inv if inv.item_type == "watch")
        print(f"Warehouse inventory:")
        print(f"  Components: {warehouse_components} units")
        print(f"  Watches: {warehouse_watches} units")
        
        # Retailer inventory
        retailer_watches = sum(inv.quantity for inv in retailer_inv)
        print(f"\nRetailer inventory:")
        print(f"  Watches: {retailer_watches} units")
        
        # Inventory value
        components = sim_data.get('components', [])
        watches = sim_data.get('watches', [])
        
        warehouse_value = 0
        for inv in warehouse_inv:
            if inv.item_type == "component":
                comp = next((c for c in components if c.id == inv.item_id), None)
                if comp:
                    warehouse_value += inv.quantity * comp.cost
            elif inv.item_type == "watch":
                watch = next((w for w in watches if w.id == inv.item_id), None)
                if watch:
                    warehouse_value += inv.quantity * watch.base_price
        
        retailer_value = 0
        for inv in retailer_inv:
            watch = next((w for w in watches if w.id == inv.watch_id), None)
            if watch:
                retailer_value += inv.quantity * watch.base_price
        
        print(f"\nInventory value:")
        print(f"  Warehouse: ${warehouse_value:,.0f}")
        print(f"  Retailers: ${retailer_value:,.0f}")
        print(f"  Total: ${warehouse_value + retailer_value:,.0f}")
 No newline at end of file
+5 −4
Original line number Diff line number Diff line
@@ -17,9 +17,9 @@ def main():
    
    # Configure logging
    if args.debug:
        logging.basicConfig(level=logging.DEBUG)
        logging.basicConfig(level=logging.DEBUG, format='%(message)s')
    else:
        logging.basicConfig(level=logging.INFO)
        logging.basicConfig(level=logging.INFO, format='%(message)s')
    
    # Simulation configuration
    config = {
@@ -35,19 +35,20 @@ def main():
        'timestep_days': 1
    }
    
    print("\SUPPLY CHAIN SIMULATION - WATCH INDUSTRY")
    print("\nSUPPLY CHAIN SIMULATION - WATCH INDUSTRY")
    print("=" * 60)
    print(f"Configuration:")
    print(f"  • Simulation days: {config['simulation_days']}")
    print(f"  • Customers: {config['n_customers']}")
    print(f"  • Retailers: {config['n_retailers']}")
    print(f"  • Watches: {config['n_watches']}")
    print(f"  • Random seed: {config['seed']}")
    print("=" * 60)
    
    # Run simulation
    simulation = run_oneshot_simulation(config)
    
    print("\Simulation complete!")
    print("\nSimulation complete!")

if __name__ == "__main__":
    main()
 No newline at end of file
+52 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3
"""
Simple runner for the supply chain simulation.
Shows detailed product performance and comprehensive metrics.
"""

import sys
import os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from supply_chain_sim import run_oneshot_simulation

def main():
    # Simple configuration - adjust as needed
    config = {
        'seed': 42,              # Random seed for reproducibility
        'n_brands': 2,           # Number of watch brands
        'n_suppliers': 4,        # Number of component suppliers
        'n_components': 10,      # Number of different components
        'n_watches': 6,          # Number of watch models
        'n_warehouses': 2,       # Number of warehouses
        'n_retailers': 4,        # Number of retailers
        'n_customers': 200,      # Number of customers
        'simulation_days': 10,   # Days to simulate
        'timestep_days': 1       # Days per timestep
    }
    
    print("\n" + "🏭 WATCH INDUSTRY SUPPLY CHAIN SIMULATION 🏭".center(60))
    print("=" * 60)
    print("\n📋 Configuration:")
    print(f"{config['n_watches']} watch models")
    print(f"{config['n_customers']} customers")
    print(f"{config['n_retailers']} retailers")
    print(f"{config['simulation_days']} simulation days")
    print("=" * 60)
    
    # Run the simulation
    print("\n🚀 Starting simulation...\n")
    simulation = run_oneshot_simulation(config)
    
    print("\n" + "✨ SIMULATION COMPLETE! ✨".center(60))
    print("=" * 60)
    print("\nThe comprehensive report above shows:")
    print("  ✓ Product performance (predicted vs actual sales)")
    print("  ✓ Supply chain metrics (orders, assembly, distribution)")
    print("  ✓ Customer behavior (purchases by segment, returns)")
    print("  ✓ Inventory status (warehouse and retailer levels)")
    print("  ✓ Financial KPIs (GMROI, revenue, costs)")
    print("\n" + "=" * 60)

if __name__ == "__main__":
    main()
 No newline at end of file
+6 −0
Original line number Diff line number Diff line
@@ -44,6 +44,12 @@ class SupplyChainSimulation:
        self.warehouse_inventories = []
        self.retailer_inventories = []
        
        # Data collection for final report
        self.all_forecasts = []
        self.all_sales = []
        self.all_orders = []
        self.total_assembled = 0
        
    def _default_config(self):
        return {
            'seed': 42,