import json import logging import asyncio from app.services.core.assignment_service import AssignmentService from app.services.routing.route_optimizer import RouteOptimizer from app.core.arrow_utils import save_optimized_route_parquet # Setup logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Load Environment Variables try: from dotenv import load_dotenv load_dotenv() print("✅ Loaded .env file") except ImportError: print("⚠️ python-dotenv not installed, skipping .env load") async def run_simulation(): print("🚀 Starting Logic Simulation (High Efficiency Mode + K-wMeans)...") # 1. Load Orders (using route.json as source) try: with open('route.json', 'r') as f: route_data = json.load(f) except FileNotFoundError: print("❌ route.json not found.") return raw_orders = route_data.get('details', []) # Strip assignment data to simulate fresh orders clean_orders = [] for o in raw_orders: o_copy = o.copy() for key in ['userid', 'step', 'cumulativekms', 'eta']: o_copy.pop(key, None) clean_orders.append(o_copy) print(f"📦 Loaded {len(clean_orders)} orders.") # 2. Mock Riders # Using the 5 rider fleet as agreed rider_ids = [753, 883, 1114, 1271, 1116, 1096, 897, 950, 1272, 1133] # Full Active Riders List # Rider Starting Locations (Based on "Mostly Available Location") # Coordinates approximated for Coimbatore areas rider_locations = { 1116: (11.0067, 76.9558), # VIVEK ANANDAN: RS PURAM 1096: (11.0450, 76.9000), # NARAYANASAMY: VADAVALI 897: (11.0430, 76.9380), # VARUN EDWARD: KAVUNDAMPALAYAM 950: (11.0330, 76.9800), # JAYASABESH: GANAPATHY 1114: (11.0450, 77.0000), # TAMILAZHAGAN: GANDHIMA NAGAR 883: (11.0200, 77.0000), # RAJAN: PEELAMEDU 1272: (10.9950, 77.0000), # MUTHURAJA: RAMANATHAPURAM 753: (11.0000, 77.0300), # MANIKANDAN: SINGANALLUR 1133: (11.0067, 76.9558), # THATCHINAMOORTHI: RS PURAM (Covering Kavundampalayam to Kovaipudur) 1271: (11.0067, 76.9558) # Legacy ID for Thatchinamoorthi } riders = [] for i, rid in enumerate(rider_ids): lat, lon = rider_locations.get(rid, (11.0168, 76.9558)) # Default to Central if unknown riders.append({ "userid": rid, "status": "idle", "onduty": 1, "latitude": str(lat), "longitude": str(lon) }) # 3. Run Assignment assignment_service = AssignmentService() try: assignments, unassigned_orders = assignment_service.assign_orders(clean_orders, riders) except Exception as e: print(f"❌ Error during assignment: {e}") import traceback traceback.print_exc() return # 4. Generate Output (Mirroring API Logic) optimizer = RouteOptimizer() output_details = [] distribution = {} assigned_count = 0 # Prepare async tasks tasks = [] task_rids = [] for rid, orders in assignments.items(): if not orders: continue distribution[rid] = len(orders) assigned_count += len(orders) # Optimize Route & Add Metrics (Cumulative KMS, Step, etc.) mock_rider = next((r for r in riders if r["userid"] == rid), None) start_coords = None if mock_rider: start_coords = (float(mock_rider['latitude']), float(mock_rider['longitude'])) tasks.append(optimizer.optimize_provider_payload(orders, start_coords=start_coords)) task_rids.append(rid) # Run tasks if tasks: results = await asyncio.gather(*tasks) for rid, optimized_route in zip(task_rids, results): mock_rider = next((r for r in riders if r["userid"] == rid), {}) r_name = mock_rider.get("username", "") r_contact = mock_rider.get("contactno", "") total_kms = 0 if optimized_route: try: total_kms = max([float(o.get("cumulativekms", 0)) for o in optimized_route]) except: total_kms = sum([float(o.get("actualkms", o.get("kms", 0))) for o in optimized_route]) for o in optimized_route: o['userid'] = rid o['username'] = r_name o['rider'] = r_name o['ridercontactno'] = r_contact o['riderkms'] = str(round(total_kms, 2)) output_details.append(o) # 5. Zone Processing fuel_charge = 2.5 base_pay = 30.0 from app.services.routing.zone_service import ZoneService zone_service = ZoneService() zone_data = zone_service.group_by_zones(output_details, unassigned_orders, fuel_charge=fuel_charge, base_pay=base_pay) # 6. Save output.json output_data = { "message": "Success", "status": True, "details": output_details, "zone_summary": zone_data["zone_analysis"], "zones": zone_data["detailed_zones"], "meta": { "total_orders": len(clean_orders), "total_riders": len(rider_ids), "assigned_orders": assigned_count, "unassigned_orders": len(unassigned_orders), "total_profit": round(sum(z["total_profit"] for z in zone_data["zone_analysis"]), 2), "unassigned_details": [ {"id": o.get("orderid") or o.get("_id"), "reason": o.get("unassigned_reason")} for o in unassigned_orders ], "distribution_summary": distribution } } with open('output.json', 'w') as f: json.dump(output_data, f, indent=4) # Apache Arrow / Parquet Export try: save_optimized_route_parquet(output_details, 'output.parquet') print("📊 Also saved results to output.parquet (Apache Arrow format)") except Exception as e: print(f"⚠️ Could not save Parquet: {e}") print("✅ Simulation Complete. Saved to output.json") print("📊 Distribution Summary:") print(json.dumps(distribution, indent=4)) if __name__ == "__main__": asyncio.run(run_simulation())