Files
routesapi/run_simulation.py

174 lines
6.2 KiB
Python

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())