Spaces:
Runtime error
Runtime error
| # app_improved.py — Redesigned UI | |
| import io | |
| import os | |
| import json | |
| import tempfile | |
| import pandas as pd | |
| # Compatibility shim for huggingface_hub | |
| try: | |
| import huggingface_hub as _hf | |
| except Exception: | |
| _hf = None | |
| if _hf is not None: | |
| if not hasattr(_hf, "HfFolder"): | |
| class _HfFolder: | |
| def path(*args, **kwargs): | |
| return None | |
| _hf.HfFolder = _HfFolder | |
| if not hasattr(_hf, "whoami"): | |
| def _whoami(token=None): | |
| return {} | |
| _hf.whoami = _whoami | |
| import gradio as gr | |
| from solver import ( | |
| generate_random_instance, | |
| solve_vrp_tw, | |
| plot_solution, | |
| parse_uploaded_csv, | |
| make_template_dataframe, | |
| ) | |
| # ---------- Design notes | |
| # Goals: | |
| # - Sidebar controls (compact) and large visualization area | |
| # - Clear download buttons and readable metrics | |
| # - Presets + small help text | |
| TITLE = "Ride-Sharing Optimizer" | |
| FOOTER = "Gradio UI — improved layout" | |
| CUSTOM_CSS = r""" | |
| :root{--bg:#0b1220;--panel:#0f1724;--muted:#94a3b8;--accent:#7c3aed} | |
| body{background:linear-gradient(180deg,#061022 0%, var(--bg) 100%);color:#e6eef8} | |
| .gradio-container{padding:18px} | |
| .sidebar{background:var(--panel);padding:14px;border-radius:10px} | |
| .brand{font-weight:700;font-size:18px;color:var(--accent)} | |
| .small{color:var(--muted);font-size:13px} | |
| .controls .gr-button{width:100%} | |
| .metrics{background:rgba(255,255,255,0.02);padding:10px;border-radius:8px} | |
| .table-wrap{max-height:280px;overflow:auto} | |
| """ | |
| # ---------- helpers | |
| def format_metrics(metrics): | |
| if not metrics: | |
| return "No metrics available" | |
| lines = [f"**Vehicles used:** {metrics.get('vehicles_used',0)} ", | |
| f"**Capacity:** {metrics.get('capacity',0)} ", | |
| f"**Total distance:** {metrics.get('total_distance',0):.2f} | |
| "] | |
| per = metrics.get('per_route_distance', []) | |
| if per: | |
| lines.append("**Per-route distances:** | |
| ") | |
| for i, d in enumerate(per, 1): | |
| load = metrics.get('per_route_load', [])[i-1] if metrics.get('per_route_load') else "-" | |
| lines.append(f"- Route {i}: {d:.2f} (load {load}) ") | |
| tw = metrics.get('time_window_report', {}) | |
| if tw: | |
| lines.append(' | |
| **Time windows** ') | |
| lines.append(f"- lateness count: {tw.get('total_lateness_count',0)} ") | |
| lines.append(f"- total lateness: {tw.get('total_lateness',0):.2f} ") | |
| lines.append(f"- status: {tw.get('status','-')} ") | |
| return " | |
| ".join(lines) | |
| # Safe file writer | |
| def _write_df_to_temp(df): | |
| tf = tempfile.NamedTemporaryFile(delete=False, suffix='.csv') | |
| df.to_csv(tf.name, index=False) | |
| tf.flush(); tf.close() | |
| return tf.name | |
| # ---------- core app functions (return types compatible with Gradio) | |
| def run_generate(n_clients, n_vehicles, capacity, spread, demand_min, demand_max, seed, speed, preset): | |
| # presets tweak spread / capacities | |
| if preset == 'City (dense)': | |
| spread = max(20, spread/2) | |
| elif preset == 'Suburban': | |
| spread = max(80, spread*1.5) | |
| df = generate_random_instance( | |
| n_clients=int(n_clients), | |
| n_vehicles=int(n_vehicles), | |
| capacity=int(capacity), | |
| spread=float(spread), | |
| demand_min=int(demand_min), | |
| demand_max=int(demand_max), | |
| seed=int(seed), | |
| ) | |
| sol = solve_vrp_tw(df, depot=(0,0), n_vehicles=int(n_vehicles), capacity=int(capacity), speed=float(speed)) | |
| img = plot_solution(df, sol, depot=(0,0)) | |
| csv_path = _write_df_to_temp(sol['assignments_table']) | |
| metrics_md = format_metrics(sol.get('metrics', {})) | |
| return img, sol['assignments_table'], metrics_md, csv_path | |
| def run_upload(file, n_vehicles, capacity, speed): | |
| if file is None: | |
| raise gr.Error('Please upload a CSV') | |
| # Gradio's File returns a tempfile-like object with .name | |
| df = parse_uploaded_csv(file.name if hasattr(file, 'name') else file) | |
| sol = solve_vrp_tw(df, depot=(0,0), n_vehicles=int(n_vehicles), capacity=int(capacity), speed=float(speed)) | |
| img = plot_solution(df, sol, depot=(0,0)) | |
| csv_path = _write_df_to_temp(sol['assignments_table']) | |
| metrics_md = format_metrics(sol.get('metrics', {})) | |
| return img, sol['assignments_table'], metrics_md, csv_path | |
| def download_template(): | |
| df = make_template_dataframe() | |
| return _write_df_to_temp(df) | |
| # ---------- UI | |
| with gr.Blocks(title=TITLE, css=CUSTOM_CSS) as demo: | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| with gr.Box(elem_classes='sidebar'): | |
| gr.Markdown(f"<div class='brand'>{TITLE}</div>") | |
| gr.Markdown("<div class='small'>Quickly generate scenarios or upload your data. Use presets for common patterns.</div>") | |
| with gr.TabbedInterface(): | |
| with gr.Tab('Generate'): | |
| preset = gr.Dropdown(['Default','City (dense)','Suburban'], value='Default', label='Preset') | |
| n_clients = gr.Slider(5, 200, value=30, step=1, label='Clients') | |
| n_vehicles = gr.Slider(1, 50, value=5, step=1, label='Vehicles') | |
| capacity = gr.Slider(1, 100, value=10, step=1, label='Capacity') | |
| spread = gr.Slider(10, 500, value=60, step=5, label='Spread') | |
| demand_min = gr.Slider(1, 5, value=1, step=1, label='Demand min') | |
| demand_max = gr.Slider(1, 10, value=3, step=1, label='Demand max') | |
| seed = gr.Number(value=42, label='Seed') | |
| speed = gr.Number(value=10.0, label='Vehicle speed') | |
| gen_btn = gr.Button('Generate & Solve') | |
| gen_dl = gr.File(label='Download last assignments') | |
| with gr.Tab('Upload'): | |
| upload_file = gr.File(label='Upload CSV (id,x,y,demand,tw_start,tw_end,service)') | |
| up_vehicles = gr.Slider(1, 100, value=5, step=1, label='Vehicles') | |
| up_capacity = gr.Slider(1, 200, value=15, step=1, label='Capacity') | |
| up_speed = gr.Number(value=10.0, label='Vehicle speed') | |
| up_btn = gr.Button('Optimize uploaded CSV') | |
| tmpl_btn = gr.Button('Download template CSV') | |
| up_dl = gr.File(label='Download assignments') | |
| gr.Markdown('---') | |
| gr.Markdown('*Tip: click a run button, then download CSV from the right panel.*', elem_classes='small') | |
| with gr.Column(scale=2): | |
| with gr.Box(): | |
| out_img = gr.Image(type='pil', label='Route map') | |
| with gr.Row(): | |
| with gr.Column(): | |
| out_table = gr.Dataframe(headers=None, label='Assignments (table)', interactive=False) | |
| with gr.Column(min_width=300): | |
| out_metrics = gr.Markdown('No results yet', label='Metrics', elem_classes='metrics') | |
| out_csv = gr.File(label='Download CSV') | |
| gr.Markdown(f"--- | |
| {FOOTER}") | |
| gen_btn.click( | |
| fn=run_generate, | |
| inputs=[n_clients, n_vehicles, capacity, spread, demand_min, demand_max, seed, speed, preset], | |
| outputs=[out_img, out_table, out_metrics, gen_dl], | |
| ) | |
| up_btn.click( | |
| fn=run_upload, | |
| inputs=[upload_file, up_vehicles, up_capacity, up_speed], | |
| outputs=[out_img, out_table, out_metrics, up_dl], | |
| ) | |
| tmpl_btn.click(fn=download_template, inputs=[], outputs=[up_dl]) | |
| if __name__ == '__main__': | |
| demo.launch() | |