174 lines
6.2 KiB
Python
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())
|