matteobardelle commited on
Commit
c3e0b01
·
verified ·
1 Parent(s): ce0676f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +8 -682
app.py CHANGED
@@ -1,687 +1,13 @@
1
- import os
2
- import json
3
- import time
4
- import traceback
5
- from pathlib import Path
6
- from typing import Dict, Any, List, Tuple
7
-
8
- import pandas as pd
9
  import gradio as gr
10
- import papermill as pm
11
- import plotly.graph_objects as go
12
-
13
- # Optional LLM (kept for compatibility with template)
14
- try:
15
- from huggingface_hub import InferenceClient
16
- except Exception:
17
- InferenceClient = None
18
-
19
-
20
- # =========================================================
21
- # CONFIG
22
- # =========================================================
23
-
24
- BASE_DIR = Path(__file__).resolve().parent
25
-
26
- NB1 = os.environ.get("NB1", "datacreation.ipynb").strip()
27
- NB2 = os.environ.get("NB2", "pythonanalysis.ipynb").strip()
28
-
29
- RUNS_DIR = BASE_DIR / "runs"
30
- ART_DIR = BASE_DIR / "artifacts"
31
- PY_FIG_DIR = ART_DIR / "py" / "figures"
32
- PY_TAB_DIR = ART_DIR / "py" / "tables"
33
-
34
- PAPERMILL_TIMEOUT = int(os.environ.get("PAPERMILL_TIMEOUT", "1800"))
35
- MAX_PREVIEW_ROWS = int(os.environ.get("MAX_FILE_PREVIEW_ROWS", "50"))
36
- MAX_LOG_CHARS = int(os.environ.get("MAX_LOG_CHARS", "8000"))
37
-
38
- HF_API_KEY = os.environ.get("HF_API_KEY", "").strip()
39
- MODEL_NAME = os.environ.get("MODEL_NAME", "deepseek-ai/DeepSeek-R1").strip()
40
- HF_PROVIDER = os.environ.get("HF_PROVIDER", "novita").strip()
41
- N8N_WEBHOOK_URL = os.environ.get("N8N_WEBHOOK_URL", "").strip()
42
-
43
- LLM_ENABLED = False
44
- llm_client = None
45
-
46
-
47
- # =========================================================
48
- # HELPERS
49
- # =========================================================
50
-
51
- def ensure_dirs():
52
- for p in [RUNS_DIR, ART_DIR, PY_FIG_DIR, PY_TAB_DIR]:
53
- p.mkdir(parents=True, exist_ok=True)
54
-
55
-
56
- def stamp():
57
- return time.strftime("%Y%m%d-%H%M%S")
58
-
59
-
60
- def tail(text: str, n: int = MAX_LOG_CHARS) -> str:
61
- return (text or "")[-n:]
62
-
63
-
64
- def _ls(dir_path: Path, exts: Tuple[str, ...]) -> List[str]:
65
- if not dir_path.is_dir():
66
- return []
67
- return sorted(
68
- p.name for p in dir_path.iterdir()
69
- if p.is_file() and p.suffix.lower() in exts
70
- )
71
-
72
-
73
- def _read_csv(path: Path) -> pd.DataFrame:
74
- return pd.read_csv(path, nrows=MAX_PREVIEW_ROWS)
75
-
76
-
77
- def _read_json(path: Path):
78
- with path.open(encoding="utf-8") as f:
79
- return json.load(f)
80
-
81
-
82
- def artifacts_index() -> Dict[str, Any]:
83
- return {
84
- "python": {
85
- "figures": _ls(PY_FIG_DIR, (".png", ".jpg", ".jpeg")),
86
- "tables": _ls(PY_TAB_DIR, (".csv", ".json")),
87
- },
88
- }
89
-
90
-
91
- def load_css() -> str:
92
- css_path = BASE_DIR / "style.css"
93
- return css_path.read_text(encoding="utf-8") if css_path.exists() else ""
94
-
95
-
96
- # =========================================================
97
- # DATA LOADING
98
- # =========================================================
99
-
100
- def make_demo_dashboard_df() -> pd.DataFrame:
101
- data = [
102
- ["Paris", "E-Scooter", 4.6, 4.1, 0.12, 0.06],
103
- ["Paris", "E-Bike", 4.3, 4.2, 0.14, 0.05],
104
- ["Berlin", "E-Scooter", 4.9, 3.8, 0.05, 0.08],
105
- ["Berlin", "E-Bike", 4.5, 4.0, 0.09, 0.06],
106
- ["Madrid", "E-Scooter", 4.2, 4.3, 0.17, 0.05],
107
- ["Madrid", "Bus-Connect", 3.9, 4.1, 0.16, 0.04],
108
- ["Warsaw", "E-Scooter", 4.4, 3.9, 0.08, 0.07],
109
- ["Warsaw", "Shared-EV", 5.0, 4.0, 0.07, 0.05],
110
- ["Turin", "E-Bike", 4.1, 4.2, 0.10, 0.04],
111
- ["Turin", "Shared-EV", 4.8, 4.1, 0.09, 0.05],
112
- ]
113
- return pd.DataFrame(
114
- data,
115
- columns=[
116
- "city",
117
- "vehicle_type",
118
- "avg_final_price_eur",
119
- "avg_rating",
120
- "avg_sentiment",
121
- "cancellation_rate",
122
- ],
123
- )
124
-
125
-
126
- def load_dashboard_df() -> pd.DataFrame:
127
- candidates = [
128
- BASE_DIR / "merged_summary.csv",
129
- BASE_DIR / "dashboard_data.csv",
130
- PY_TAB_DIR / "merged_summary.csv",
131
- PY_TAB_DIR / "dashboard_data.csv",
132
- ]
133
-
134
- for path in candidates:
135
- if path.exists():
136
- try:
137
- df = pd.read_csv(path)
138
- df.columns = [str(c).strip() for c in df.columns]
139
- return df
140
- except Exception:
141
- pass
142
-
143
- return make_demo_dashboard_df()
144
-
145
-
146
- def normalize_dashboard_df(df: pd.DataFrame) -> pd.DataFrame:
147
- df = df.copy()
148
- cols = {c.lower().strip(): c for c in df.columns}
149
-
150
- rename_map = {}
151
- if "city" not in cols and "City" in df.columns:
152
- rename_map["City"] = "city"
153
- if "vehicle_type" not in cols:
154
- for candidate in ["ride_type", "vehicle", "VehicleType", "vehicle"]:
155
- if candidate in df.columns:
156
- rename_map[candidate] = "vehicle_type"
157
- break
158
- if "avg_final_price_eur" not in cols:
159
- for candidate in ["final_price_eur", "avg_price", "avg_final_price", "price"]:
160
- if candidate in df.columns:
161
- rename_map[candidate] = "avg_final_price_eur"
162
- break
163
- if "avg_rating" not in cols:
164
- for candidate in ["rating", "avg_star_rating", "star_rating"]:
165
- if candidate in df.columns:
166
- rename_map[candidate] = "avg_rating"
167
- break
168
- if "avg_sentiment" not in cols:
169
- for candidate in ["sentiment", "compound", "vader_compound", "avg_compound_score"]:
170
- if candidate in df.columns:
171
- rename_map[candidate] = "avg_sentiment"
172
- break
173
- if "cancellation_rate" not in cols:
174
- for candidate in ["cancel_rate", "avg_cancellation_rate"]:
175
- if candidate in df.columns:
176
- rename_map[candidate] = "cancellation_rate"
177
- break
178
-
179
- if rename_map:
180
- df = df.rename(columns=rename_map)
181
-
182
- for needed in [
183
- "city",
184
- "vehicle_type",
185
- "avg_final_price_eur",
186
- "avg_rating",
187
- "avg_sentiment",
188
- "cancellation_rate",
189
- ]:
190
- if needed not in df.columns:
191
- if needed in ["city", "vehicle_type"]:
192
- df[needed] = "Unknown"
193
- else:
194
- df[needed] = 0.0
195
-
196
- return df
197
-
198
-
199
- def filter_dashboard_df(df: pd.DataFrame, city: str, vehicle: str) -> pd.DataFrame:
200
- out = df.copy()
201
- if city != "All":
202
- out = out[out["city"] == city]
203
- if vehicle != "All":
204
- out = out[out["vehicle_type"] == vehicle]
205
- return out
206
-
207
-
208
- # =========================================================
209
- # PIPELINE RUNNERS
210
- # =========================================================
211
-
212
- def run_notebook(nb_name: str) -> str:
213
- ensure_dirs()
214
- nb_in = BASE_DIR / nb_name
215
- if not nb_in.exists():
216
- return f"ERROR: {nb_name} not found."
217
- nb_out = RUNS_DIR / f"run_{stamp()}_{nb_name}"
218
- pm.execute_notebook(
219
- input_path=str(nb_in),
220
- output_path=str(nb_out),
221
- cwd=str(BASE_DIR),
222
- log_output=True,
223
- progress_bar=False,
224
- request_save_on_cell_execute=True,
225
- execution_timeout=PAPERMILL_TIMEOUT,
226
- )
227
- return f"Executed {nb_name}"
228
-
229
-
230
- def run_datacreation() -> str:
231
- try:
232
- log = run_notebook(NB1)
233
- csvs = [f.name for f in BASE_DIR.glob("*.csv")]
234
- return f"OK {log}\n\nCSVs now in /app:\n" + "\n".join(
235
- f" - {c}" for c in sorted(csvs)
236
- )
237
- except Exception as e:
238
- return f"FAILED {e}\n\n{traceback.format_exc()[-2000:]}"
239
-
240
-
241
- def run_pythonanalysis() -> str:
242
- try:
243
- log = run_notebook(NB2)
244
- idx = artifacts_index()
245
- figs = idx["python"]["figures"]
246
- tabs = idx["python"]["tables"]
247
- return (
248
- f"OK {log}\n\n"
249
- f"Figures: {', '.join(figs) or '(none)'}\n"
250
- f"Tables: {', '.join(tabs) or '(none)'}"
251
- )
252
- except Exception as e:
253
- return f"FAILED {e}\n\n{traceback.format_exc()[-2000:]}"
254
-
255
-
256
- def run_full_pipeline() -> str:
257
- logs = []
258
- logs.append("=" * 50)
259
- logs.append("STEP 1/2: Data Creation")
260
- logs.append("=" * 50)
261
- logs.append(run_datacreation())
262
- logs.append("")
263
- logs.append("=" * 50)
264
- logs.append("STEP 2/2: Python Analysis")
265
- logs.append("=" * 50)
266
- logs.append(run_pythonanalysis())
267
- return "\n".join(logs)
268
-
269
-
270
- # =========================================================
271
- # GALLERY LOADERS
272
- # =========================================================
273
-
274
- def _load_all_figures() -> List[Tuple[str, str]]:
275
- items = []
276
- for p in sorted(PY_FIG_DIR.glob("*.png")):
277
- items.append((str(p), p.stem.replace("_", " ").title()))
278
- return items
279
-
280
-
281
- def _load_table_safe(path: Path) -> pd.DataFrame:
282
- try:
283
- if path.suffix == ".json":
284
- obj = _read_json(path)
285
- if isinstance(obj, dict):
286
- return pd.DataFrame([obj])
287
- return pd.DataFrame(obj)
288
- return _read_csv(path)
289
- except Exception as e:
290
- return pd.DataFrame([{"error": str(e)}])
291
-
292
-
293
- def refresh_gallery():
294
- figures = _load_all_figures()
295
- idx = artifacts_index()
296
- table_choices = list(idx["python"]["tables"])
297
-
298
- default_df = pd.DataFrame()
299
- if table_choices:
300
- default_df = _load_table_safe(PY_TAB_DIR / table_choices[0])
301
-
302
- return (
303
- figures if figures else [],
304
- gr.update(
305
- choices=table_choices,
306
- value=table_choices[0] if table_choices else None
307
- ),
308
- default_df,
309
- )
310
-
311
-
312
- def on_table_select(choice: str):
313
- if not choice:
314
- return pd.DataFrame([{"hint": "Select a table above."}])
315
- path = PY_TAB_DIR / choice
316
- if not path.exists():
317
- return pd.DataFrame([{"error": f"File not found: {choice}"}])
318
- return _load_table_safe(path)
319
-
320
-
321
- # =========================================================
322
- # DASHBOARD + PREDICTION
323
- # =========================================================
324
-
325
- def render_kpi_cards(city: str = "All", vehicle: str = "All") -> str:
326
- df = normalize_dashboard_df(load_dashboard_df())
327
- df = filter_dashboard_df(df, city, vehicle)
328
-
329
- if df.empty:
330
- return """
331
- <div style="padding:16px;background:#fef2f2;border-radius:12px;">
332
- <h4>No data available for the selected filters.</h4>
333
- </div>
334
- """
335
-
336
- avg_price = df["avg_final_price_eur"].mean()
337
- avg_rating = df["avg_rating"].mean()
338
- avg_sentiment = df["avg_sentiment"].mean()
339
- avg_cancel = df["cancellation_rate"].mean()
340
-
341
- positive_reviews_pct = 53.8
342
- if "avg_sentiment" in df.columns:
343
- positive_reviews_pct = round((df["avg_sentiment"] > 0.05).mean() * 100, 1)
344
-
345
- return f"""
346
- <div style="display:grid;grid-template-columns:repeat(4,1fr);gap:12px;">
347
- <div style="padding:16px;background:#f5f5f5;border-radius:12px;">
348
- <h4>Avg Final Price</h4><p>€{avg_price:.2f}</p>
349
- </div>
350
- <div style="padding:16px;background:#f5f5f5;border-radius:12px;">
351
- <h4>Avg Rating</h4><p>{avg_rating:.2f} / 5</p>
352
- </div>
353
- <div style="padding:16px;background:#f5f5f5;border-radius:12px;">
354
- <h4>Cancellation Rate</h4><p>{avg_cancel * 100:.1f}%</p>
355
- </div>
356
- <div style="padding:16px;background:#f5f5f5;border-radius:12px;">
357
- <h4>Positive Segments</h4><p>{positive_reviews_pct:.1f}%</p>
358
- </div>
359
- </div>
360
- """
361
-
362
-
363
- def refresh_dashboard(city: str = "All", vehicle: str = "All"):
364
- df = normalize_dashboard_df(load_dashboard_df())
365
- df = filter_dashboard_df(df, city, vehicle)
366
-
367
- if df.empty:
368
- empty_fig = go.Figure()
369
- empty_fig.update_layout(title="No data for selected filters")
370
- return render_kpi_cards(city, vehicle), empty_fig, empty_fig, empty_fig
371
-
372
- by_segment = df.groupby(["city", "vehicle_type"], as_index=False).agg(
373
- avg_final_price_eur=("avg_final_price_eur", "mean"),
374
- avg_sentiment=("avg_sentiment", "mean"),
375
- avg_rating=("avg_rating", "mean"),
376
- cancellation_rate=("cancellation_rate", "mean"),
377
- )
378
-
379
- fig1 = go.Figure()
380
- fig1.add_bar(
381
- x=[f"{r['city']} - {r['vehicle_type']}" for _, r in by_segment.iterrows()],
382
- y=by_segment["avg_final_price_eur"],
383
- )
384
- fig1.update_layout(
385
- title="Average Final Price by City / Vehicle",
386
- xaxis_title="Segment",
387
- yaxis_title="EUR",
388
- )
389
-
390
- fig2 = go.Figure()
391
- fig2.add_bar(
392
- x=[f"{r['city']} - {r['vehicle_type']}" for _, r in by_segment.iterrows()],
393
- y=by_segment["avg_sentiment"],
394
- )
395
- fig2.update_layout(
396
- title="Average Sentiment by City / Vehicle",
397
- xaxis_title="Segment",
398
- yaxis_title="Sentiment",
399
- )
400
-
401
- city_group = df.groupby("city", as_index=False).agg(
402
- avg_rating=("avg_rating", "mean"),
403
- cancellation_rate=("cancellation_rate", "mean"),
404
- )
405
-
406
- fig3 = go.Figure()
407
- fig3.add_bar(name="Avg Rating", x=city_group["city"], y=city_group["avg_rating"])
408
- fig3.add_bar(
409
- name="Cancellation Rate",
410
- x=city_group["city"],
411
- y=city_group["cancellation_rate"] * 100,
412
- )
413
- fig3.update_layout(
414
- title="Average Rating / Cancellation View",
415
- xaxis_title="City",
416
- yaxis_title="Value",
417
- barmode="group",
418
- )
419
-
420
- return render_kpi_cards(city, vehicle), fig1, fig2, fig3
421
-
422
-
423
- def predict_satisfaction(
424
- city,
425
- vehicle,
426
- distance_km,
427
- duration_min,
428
- final_price_eur,
429
- discount_pct,
430
- time_slot,
431
- cancellation_flag,
432
- ):
433
- score = 0.50
434
-
435
- if final_price_eur <= 4.5:
436
- score += 0.15
437
- else:
438
- score -= 0.10
439
-
440
- if discount_pct >= 10:
441
- score += 0.10
442
-
443
- if cancellation_flag == 1:
444
- score -= 0.25
445
-
446
- if time_slot == "Night":
447
- score -= 0.10
448
-
449
- if vehicle == "E-Bike":
450
- score += 0.05
451
-
452
- if distance_km <= 4:
453
- score += 0.03
454
-
455
- if duration_min > 25:
456
- score -= 0.05
457
-
458
- score = max(0.0, min(1.0, score))
459
-
460
- return {
461
- "city": city,
462
- "vehicle_type": vehicle,
463
- "high_satisfaction_probability": round(score, 3),
464
- "low_satisfaction_probability": round(1 - score, 3),
465
- "predicted_label": (
466
- "High Satisfaction" if score >= 0.5 else "Low Satisfaction"
467
- ),
468
- }
469
-
470
-
471
- def get_pricing_recommendation(city, vehicle):
472
- if city in ["Berlin", "Warsaw"] and vehicle == "E-Scooter":
473
- return {
474
- "decision": "Price Review",
475
- "reason": "Lower sentiment and higher price sensitivity in this segment.",
476
- "city": city,
477
- "vehicle_type": vehicle,
478
- }
479
-
480
- if city == "Madrid":
481
- return {
482
- "decision": "Maintain Pricing",
483
- "reason": "Strong satisfaction profile and positive sentiment.",
484
- "city": city,
485
- "vehicle_type": vehicle,
486
- }
487
-
488
- return {
489
- "decision": "Maintain Pricing",
490
- "reason": "Segment looks stable based on current sentiment and pricing.",
491
- "city": city,
492
- "vehicle_type": vehicle,
493
- }
494
-
495
-
496
- # =========================================================
497
- # PRE-LOAD CHARTS AT STARTUP (fix for gradio 4.31 compatibility)
498
- # =========================================================
499
- _kpi_init = render_kpi_cards()
500
- _, _fig1_init, _fig2_init, _fig3_init = refresh_dashboard()
501
-
502
-
503
- # =========================================================
504
- # APP UI
505
- # =========================================================
506
-
507
- with gr.Blocks(title="Urban Mobility AI App", css=load_css()) as demo:
508
- gr.Markdown(
509
- "# Urban Mobility Pricing & Satisfaction App\n"
510
- "*AI-enhanced dashboard for Group 08*",
511
- elem_id="escp_title",
512
- )
513
-
514
- # ===========================================================
515
- # TAB 1 -- Pipeline Runner
516
- # ===========================================================
517
- with gr.Tab("Pipeline Runner"):
518
- gr.Markdown("Run the data creation and analysis notebooks.")
519
-
520
- with gr.Row():
521
- with gr.Column(scale=1):
522
- btn_nb1 = gr.Button("Step 1: Data Creation", variant="secondary")
523
- with gr.Column(scale=1):
524
- btn_nb2 = gr.Button("Step 2: Python Analysis", variant="secondary")
525
-
526
- with gr.Row():
527
- btn_all = gr.Button("Run Full Pipeline (Both Steps)", variant="primary")
528
-
529
- run_log = gr.Textbox(
530
- label="Execution Log",
531
- lines=18,
532
- max_lines=30,
533
- interactive=False,
534
- )
535
-
536
- btn_nb1.click(run_datacreation, outputs=[run_log])
537
- btn_nb2.click(run_pythonanalysis, outputs=[run_log])
538
- btn_all.click(run_full_pipeline, outputs=[run_log])
539
-
540
- # ===========================================================
541
- # TAB 2 -- Urban Mobility Dashboard
542
- # ===========================================================
543
- with gr.Tab("Urban Mobility Dashboard"):
544
- gr.Markdown("### Urban Mobility KPIs & Visual Insights")
545
-
546
- kpi_html = gr.HTML(value=_kpi_init)
547
-
548
- with gr.Row():
549
- city_filter = gr.Dropdown(
550
- label="Select City",
551
- choices=["All", "Paris", "Berlin", "Madrid", "Warsaw", "Turin"],
552
- value="All",
553
- interactive=True,
554
- )
555
- vehicle_filter = gr.Dropdown(
556
- label="Select Vehicle Type",
557
- choices=["All", "E-Scooter", "E-Bike", "Shared-EV", "Bus-Connect"],
558
- value="All",
559
- interactive=True,
560
- )
561
-
562
- refresh_btn = gr.Button("Refresh Dashboard", variant="primary")
563
-
564
- gr.Markdown("#### Interactive Charts")
565
- chart_price = gr.Plot(value=_fig1_init, label="Average Final Price by City / Vehicle")
566
- chart_sentiment = gr.Plot(value=_fig2_init, label="Sentiment by City / Vehicle")
567
- chart_rating = gr.Plot(value=_fig3_init, label="Average Rating / Cancellation View")
568
-
569
- gr.Markdown("#### Static Figures (from notebooks)")
570
- gallery = gr.Gallery(
571
- label="Generated Figures",
572
- columns=2,
573
- height=480,
574
- object_fit="contain",
575
- )
576
-
577
- gr.Markdown("#### Data Tables")
578
- table_dropdown = gr.Dropdown(
579
- label="Select a table to view",
580
- choices=[],
581
- interactive=True,
582
- )
583
- table_display = gr.Dataframe(
584
- label="Table Preview",
585
- interactive=False,
586
- )
587
-
588
- def _on_refresh(city, vehicle):
589
- kpi, c1, c2, c3 = refresh_dashboard(city, vehicle)
590
- figs, dd, df = refresh_gallery()
591
- return kpi, c1, c2, c3, figs, dd, df
592
-
593
- refresh_btn.click(
594
- _on_refresh,
595
- inputs=[city_filter, vehicle_filter],
596
- outputs=[
597
- kpi_html,
598
- chart_price,
599
- chart_sentiment,
600
- chart_rating,
601
- gallery,
602
- table_dropdown,
603
- table_display,
604
- ],
605
- )
606
-
607
- table_dropdown.change(
608
- on_table_select,
609
- inputs=[table_dropdown],
610
- outputs=[table_display],
611
- )
612
-
613
- # ===========================================================
614
- # TAB 3 -- Prediction + Recommendation
615
- # ===========================================================
616
- with gr.Tab("Prediction + Recommendation"):
617
- _ai_status = (
618
- "Connected to your **n8n workflow**." if N8N_WEBHOOK_URL
619
- else "**LLM active.**" if LLM_ENABLED
620
- else "Using local logic. Add `N8N_WEBHOOK_URL` later for workflow integration."
621
- )
622
-
623
- gr.Markdown(
624
- "### Predict user satisfaction and generate pricing recommendations\n\n"
625
- f"{_ai_status}"
626
- )
627
-
628
- with gr.Row():
629
- with gr.Column():
630
- pred_city = gr.Dropdown(
631
- label="City",
632
- choices=["Paris", "Berlin", "Madrid", "Warsaw", "Turin"],
633
- value="Berlin",
634
- )
635
- pred_vehicle = gr.Dropdown(
636
- label="Vehicle Type",
637
- choices=["E-Scooter", "E-Bike", "Shared-EV", "Bus-Connect"],
638
- value="E-Scooter",
639
- )
640
- pred_distance = gr.Number(label="Distance (km)", value=3.5)
641
- pred_duration = gr.Number(label="Duration (min)", value=12)
642
- pred_final_price = gr.Number(label="Final Price (EUR)", value=4.2)
643
- pred_discount = gr.Number(label="Discount (%)", value=10)
644
- pred_time_slot = gr.Dropdown(
645
- label="Time Slot",
646
- choices=["Morning", "Afternoon", "Evening", "Night"],
647
- value="Evening",
648
- )
649
- pred_cancel = gr.Dropdown(
650
- label="Cancellation Flag",
651
- choices=[0, 1],
652
- value=0,
653
- )
654
-
655
- predict_btn = gr.Button("Predict Satisfaction", variant="primary")
656
- recommend_btn = gr.Button("Get Pricing Recommendation")
657
-
658
- with gr.Column():
659
- prediction_output = gr.JSON(label="Prediction Output")
660
- recommendation_output = gr.JSON(label="Recommendation Output")
661
-
662
- predict_btn.click(
663
- predict_satisfaction,
664
- inputs=[
665
- pred_city,
666
- pred_vehicle,
667
- pred_distance,
668
- pred_duration,
669
- pred_final_price,
670
- pred_discount,
671
- pred_time_slot,
672
- pred_cancel,
673
- ],
674
- outputs=[prediction_output],
675
- )
676
-
677
- recommend_btn.click(
678
- get_pricing_recommendation,
679
- inputs=[pred_city, pred_vehicle],
680
- outputs=[recommendation_output],
681
- )
682
 
 
 
683
 
684
- ensure_dirs()
 
 
 
 
685
 
686
  if __name__ == "__main__":
687
- demo.queue().launch(allowed_paths=[str(BASE_DIR)])
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
+ def ping():
4
+ return "OK"
5
 
6
+ with gr.Blocks() as demo:
7
+ gr.Markdown("# Test Gradio")
8
+ btn = gr.Button("Ping")
9
+ out = gr.Textbox()
10
+ btn.click(ping, outputs=out)
11
 
12
  if __name__ == "__main__":
13
+ demo.launch()