MBG0903's picture
Update app.py
6130836 verified
import gradio as gr
import pandas as pd
import numpy as np
import requests
import folium
from folium.plugins import AntPath
from geopy.geocoders import Nominatim
from geopy.extra.rate_limiter import RateLimiter
from ortools.constraint_solver import pywrapcp, routing_enums_pb2
import os
from openai import OpenAI
from datetime import datetime
import base64
# ---------------------------------------
# CONSTANTS & BRANDING
# ---------------------------------------
PRIMARY_COLOR = "#0F2C59" # Procelevate Blue
VEHICLE_MILEAGE = {
"Truck (32 ft)": 2.5,
"Mini-Truck / Tata Ace": 12,
"Bike Delivery": 45,
"Car / SUV": 12
}
# GPT Client
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
client = OpenAI(api_key=OPENAI_API_KEY)
# ---------------------------------------
# GEOCODING
# ---------------------------------------
geolocator = Nominatim(user_agent="procelevate_route_app")
geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1)
def get_coordinates(address):
try:
loc = geocode(address)
if loc:
return (loc.latitude, loc.longitude)
except:
return None
return None
# ---------------------------------------
# OSRM Distance + Time Fetcher
# ---------------------------------------
def osrm_query(coord1, coord2):
url = f"http://router.project-osrm.org/route/v1/driving/{coord1[1]},{coord1[0]};{coord2[1]},{coord2[0]}?overview=false"
r = requests.get(url).json()
if "routes" in r:
return r["routes"][0]["distance"], r["routes"][0]["duration"]
return None, None
def build_matrices(coords):
n = len(coords)
dist = np.zeros((n,n))
time = np.zeros((n,n))
for i in range(n):
for j in range(n):
if i == j:
continue
d, t = osrm_query(coords[i], coords[j])
if not d:
return None, None
dist[i][j] = d
time[i][j] = t
return dist, time
# ---------------------------------------
# OR-TOOLS Optimization
# ---------------------------------------
def optimize_route(distance_matrix):
n = len(distance_matrix)
manager = pywrapcp.RoutingIndexManager(n, 1, 0)
routing = pywrapcp.RoutingModel(manager)
def distance_callback(from_i, to_i):
return int(distance_matrix[manager.IndexToNode(from_i)][manager.IndexToNode(to_i)])
transit_idx = routing.RegisterTransitCallback(distance_callback)
routing.SetArcCostEvaluatorOfAllVehicles(transit_idx)
params = pywrapcp.DefaultRoutingSearchParameters()
params.first_solution_strategy = routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
sol = routing.SolveWithParameters(params)
if not sol:
return None
index = routing.Start(0)
order = []
while not routing.IsEnd(index):
order.append(manager.IndexToNode(index))
index = sol.Value(routing.NextVar(index))
return order
# ---------------------------------------
# Fuel, Cost, Toll Calculations
# ---------------------------------------
def calculate_kpis(distance_km, mileage, fuel_price, toll_estimate=0):
fuel_needed = distance_km / mileage
fuel_cost = fuel_needed * fuel_price
total_cost = fuel_cost + toll_estimate
return fuel_needed, fuel_cost, total_cost
# ---------------------------------------
# Folium Map
# ---------------------------------------
def make_map(coords, addresses, order):
m = folium.Map(location=coords[order[0]], zoom_start=12)
path = []
for i, idx in enumerate(order):
folium.Marker(
coords[idx],
tooltip=f"{i+1}. {addresses[idx]}",
icon=folium.Icon(color="blue")
).add_to(m)
path.append(coords[idx])
AntPath(path, color="blue", weight=4).add_to(m)
return m._repr_html_()
# ---------------------------------------
# AI Explanation via GPT
# ---------------------------------------
def generate_ai_explanation(opt_km, naive_km, fuel_saved, cost_saved, time_saved):
prompt = f"""
You are an AI logistics expert. Explain clearly why the optimized route is better.
Optimized distance: {opt_km:.2f} km
Naive distance: {naive_km:.2f} km
Fuel saved: {fuel_saved:.2f} litres
Cost saved: ₹{cost_saved:.2f}
Time saved: {time_saved:.2f} minutes
Write a short, professional explanation suitable for a supply-chain manager at CEVA Logistics.
"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
# ---------------------------------------
# HTML REPORT GENERATOR
# ---------------------------------------
def generate_report_html(summary_text, ai_text, map_html):
html = f"""
<html>
<head>
<style>
body {{ font-family: Arial; }}
h2 {{ color: {PRIMARY_COLOR}; }}
</style>
</head>
<body>
<h2>Procelevate AI Route Optimization Report</h2>
<p><b>Generated:</b> {datetime.now().strftime('%Y-%m-%d %H:%M')}</p>
<h3>Summary</h3>
<p>{summary_text}</p>
<h3>AI Explanation</h3>
<p>{ai_text}</p>
<h3>Route Map</h3>
{map_html}
</body>
</html>
"""
return html
# ---------------------------------------
# PART 3 — FULL ENTERPRISE DASHBOARD UI
# ---------------------------------------
def run_route_engine(start, end, stops_text, vehicle_type, fuel_price):
stops = [s.strip() for s in stops_text.split("\n") if s.strip()]
# Build address list
addresses = [start] + stops + [end]
addresses = [a for a in addresses if a.strip()]
if len(addresses) < 2:
return ["❌ Please enter valid addresses."] + [None]*5
# Geocode all addresses
coords = []
for a in addresses:
c = get_coordinates(a)
if not c:
return [f"❌ Failed to locate address: {a}"] + [None]*5
coords.append(c)
# Build OSRM matrices
dist_m, time_m = build_matrices(coords)
if dist_m is None:
return ["❌ OSRM routing failed. Try different locations."] + [None]*5
# Convert meters → km, seconds → minutes
dist_km = dist_m / 1000
time_min = time_m / 60
# Optimized route
opt_order = optimize_route(dist_m)
if opt_order is None:
return ["❌ Optimization failed."] + [None]*5
# Naive + reverse orders
naive_order = list(range(len(addresses)))
rev_order = list(reversed(naive_order))
def compute_totals(order):
total_km = 0
total_min = 0
for i in range(len(order)-1):
total_km += dist_km[order[i]][order[i+1]]
total_min += time_min[order[i]][order[i+1]]
return total_km, total_min
opt_km, opt_min = compute_totals(opt_order)
naive_km, naive_min = compute_totals(naive_order)
rev_km, rev_min = compute_totals(rev_order)
mileage = VEHICLE_MILEAGE[vehicle_type]
# KPI calculations
opt_fuel, opt_fuel_cost, opt_total_cost = calculate_kpis(opt_km, mileage, fuel_price)
naive_fuel, naive_fuel_cost, naive_total_cost = calculate_kpis(naive_km, mileage, fuel_price)
rev_fuel, rev_fuel_cost, rev_total_cost = calculate_kpis(rev_km, mileage, fuel_price)
# Savings
fuel_saved = naive_fuel - opt_fuel
cost_saved = naive_total_cost - opt_total_cost
time_saved = naive_min - opt_min
# AI Explanation
ai_text = generate_ai_explanation(opt_km, naive_km, fuel_saved, cost_saved, time_saved)
# Optimized route map
map_html = make_map(coords, addresses, opt_order)
# Summary panel text
summary_text = f"""
<b>Optimized Distance:</b> {opt_km:.2f} km<br>
<b>Optimized Time:</b> {opt_min:.2f} minutes<br>
<b>Fuel Needed:</b> {opt_fuel:.2f} L<br>
<b>Total Cost:</b> ₹{opt_total_cost:.2f}<br>
<b>Efficiency Gain vs Naive:</b> {((naive_km-opt_km)/naive_km)*100:.2f}%<br>
"""
# Comparison table
comp_df = pd.DataFrame({
"Route Type": ["Optimized", "Naive", "Reverse"],
"Distance (km)": [opt_km, naive_km, rev_km],
"Time (min)": [opt_min, naive_min, rev_min],
"Fuel (L)": [opt_fuel, naive_fuel, rev_fuel],
"Cost (₹)": [opt_total_cost, naive_total_cost, rev_total_cost]
})
# Data tables
dist_df = pd.DataFrame(dist_km, columns=addresses, index=addresses)
time_df = pd.DataFrame(time_min, columns=addresses, index=addresses)
# Downloadable report
report_html = generate_report_html(summary_text, ai_text, map_html)
b64 = base64.b64encode(report_html.encode()).decode()
download_link = f'<a href="data:text/html;base64,{b64}" download="route_report.html">Download Report</a>'
return (
summary_text, # TAB 1
map_html, # TAB 2
comp_df, # TAB 3
ai_text, # TAB 4
dist_df, # TAB 5A
time_df, # TAB 5B
download_link # TAB 6
)
# ---------------------------------------
# GRADIO UI LAYOUT — TABS + BRANDING
# ---------------------------------------
with gr.Blocks() as demo:
gr.HTML(f"""
<style>
:root {{
--primary-color: {PRIMARY_COLOR};
}}
h1 {{
color: {PRIMARY_COLOR} !important;
}}
.gradio-container {{
--primary-hue: 220;
}}
</style>
""")
gr.Markdown(f"<h1 style='color:{PRIMARY_COLOR}'>Procelevate AI Route Optimization Suite</h1>")
# -------------------------
# Address Inputs
# -------------------------
with gr.Row():
start = gr.Textbox(label="From", placeholder="Starting point (e.g., Bangalore Airport)")
end = gr.Textbox(label="To", placeholder="Final destination (e.g., Whitefield)")
stops_text = gr.Textbox(
label="Stops (one per line, optional)",
lines=4,
placeholder="Example:\nMG Road\nBTM Layout\nElectronic City"
)
# -------------------------
# Vehicle + Fuel Inputs
# -------------------------
vehicle_type = gr.Dropdown(
list(VEHICLE_MILEAGE.keys()),
label="Vehicle Type",
value="Truck (32 ft)"
)
fuel_price = gr.Number(
label="Fuel Price (₹ per litre)",
value=91
)
# -------------------------
# Submit Button
# -------------------------
submit_btn = gr.Button("Optimize Route", variant="primary")
# TAB STRUCTURE
with gr.Tabs():
with gr.Tab("Overview"):
overview_out = gr.HTML()
with gr.Tab("Optimized Route"):
map_out = gr.HTML()
with gr.Tab("Route Comparison"):
comp_out = gr.Dataframe()
with gr.Tab("AI Explanation"):
ai_out = gr.Markdown()
with gr.Tab("Data Tables"):
dist_out = gr.Dataframe(label="Distance (km)")
time_out = gr.Dataframe(label="Time (min)")
with gr.Tab("Download Report"):
report_out = gr.HTML()
# CONNECT BUTTON
submit_btn.click(
fn=run_route_engine,
inputs=[start, end, stops_text, vehicle_type, fuel_price],
outputs=[overview_out, map_out, comp_out, ai_out, dist_out, time_out, report_out]
)
demo.launch(
server_name="0.0.0.0",
server_port=7860,
ssr_mode=False
)