import gradio as gr import pandas as pd import plotly.graph_objects as go # ── load data ────────────────────────────────────────────── df = pd.read_csv("restaurant_master_scored.csv") CUISINES = ["All"] + sorted(df["cuisine_type"].unique().tolist()) PRICES = ["All"] + ["€", "€€", "€€€", "€€€€"] ARRONDS = ["All"] + sorted(df["arrondissement"].unique().tolist()) LABEL_COLOR = {"Worth It": "#1A6B3C", "Maybe": "#7D5A00", "Skip It": "#8B1A1A"} LABEL_BG = {"Worth It": "#EAF3DE", "Maybe": "#FAEEDA", "Skip It": "#FCEBEB"} # ── charts ────────────────────────────────────────────── def make_chart(row, filtered_df): # First chart: score component chart for top result components = { "Sentiment adj": round(row["sentiment_adj_norm"] * 0.35 * 100, 1), "Price fairness": round(row["price_fairness"] * 0.25 * 100, 1), "Consistency": round(row["consistency_index"] * 0.20 * 100, 1), "Trust": round(row["trust_score"] * 0.10 * 100, 1), "Divergence −": round(-row["star_sentiment_divergence"] * 0.10 * 100, 1), } colors = ["#3D2B8E", "#7B6FCA", "#1A6B3C", "#7D5A00", "#8B1A1A"] fig_bar = go.Figure( go.Bar( x=list(components.values()), y=list(components.keys()), orientation="h", marker_color=colors, text=[f"{v:+.1f}" for v in components.values()], textposition="outside", ) ) fig_bar.update_layout( margin=dict(l=10, r=60, t=10, b=10), height=220, xaxis=dict( showgrid=False, zeroline=True, zerolinecolor="#cccccc", title="Points", ), yaxis=dict(showgrid=False), paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)", font=dict(size=12), ) # Second chart: scatter plot of all filtered restaurants fig_scatter = go.Figure() for label in ["Worth It", "Maybe", "Skip It"]: subset = filtered_df[filtered_df["worth_it_label"] == label] if not subset.empty: fig_scatter.add_trace( go.Scatter( x=subset["star_rating"], y=subset["true_score"], mode="markers", name=label, marker=dict( size=10, color=LABEL_COLOR.get(label, "#666666"), line=dict(width=1, color="white"), ), text=subset["restaurant_name"], hovertemplate=( "%{text}
" "Stars: %{x:.1f}
" "True Score: %{y:.1f}
" "Verdict: " + label + "" ), ) ) fig_scatter.update_layout( margin=dict(l=10, r=10, t=40, b=10), height=320, xaxis=dict(title="Star rating", showgrid=True, gridcolor="#eeeeee"), yaxis=dict(title="True score", showgrid=True, gridcolor="#eeeeee"), paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)", font=dict(size=12), legend=dict(orientation="h", y=1.12, x=0), ) return fig_bar, fig_scatter # ── AI explanation (rule-based, no API key needed) ───────── import requests def explain(row): payload = { "restaurant_name": row["restaurant_name"], "true_score": float(row["true_score"]), "worth_it_label": row["worth_it_label"], "sentiment_score": float(row["sentiment_score"]), "star_rating": float(row["star_rating"]), "price_category": row["price_category"], } try: r = requests.post( "https://scohen3012.app.n8n.cloud/webhook/explain", json=payload, timeout=8 ) return r.json().get("explanation", "No explanation returned.") except Exception as e: return f"Explanation service unavailable: {e}" # ── main function ────────────────────────────────────────── def search(cuisine, price, arrond, top_n): filtered = df.copy() if cuisine != "All": filtered = filtered[filtered["cuisine_type"] == cuisine] if price != "All": filtered = filtered[filtered["price_category"] == price] if arrond != "All": filtered = filtered[filtered["arrondissement"] == arrond] if filtered.empty: empty_fig = go.Figure() return "No restaurants match your filters.", empty_fig, empty_fig, "" top = filtered.nlargest(int(top_n), "true_score").reset_index(drop=True) best = top.iloc[0] # Results table display = top[ [ "restaurant_name", "cuisine_type", "arrondissement", "price_category", "star_rating", "true_score", "worth_it_label", ] ].rename( columns={ "restaurant_name": "Name", "cuisine_type": "Cuisine", "arrondissement": "Area", "price_category": "Price", "star_rating": "Stars", "true_score": "True Score", "worth_it_label": "Verdict", } ) score_chart, scatter_chart = make_chart(best, top) explanation = explain(best) return display, score_chart, scatter_chart, explanation # ── interface ────────────────────────────────────────────── with gr.Blocks(title="Restaurant Worth-It Score — Paris") as demo: gr.Markdown( "## Restaurant Worth-It Score — Paris\n" "AI-powered ratings that go beyond the stars. " "Select your filters to find restaurants that truly deliver." ) with gr.Row(): cuisine_dd = gr.Dropdown(CUISINES, value="All", label="Cuisine type") price_dd = gr.Dropdown(PRICES, value="All", label="Price tier") arrond_dd = gr.Dropdown(ARRONDS, value="All", label="Arrondissement") top_n_sl = gr.Slider(3, 20, value=10, step=1, label="Results to show") search_btn = gr.Button("Find restaurants", variant="primary") gr.Markdown("### Top results") results_table = gr.Dataframe(label="Ranked restaurants", interactive=False) with gr.Row(): with gr.Column(scale=1): gr.Markdown("#### Score breakdown — top result") score_chart = gr.Plot(label="") with gr.Column(scale=1): gr.Markdown("#### All filtered restaurants") scatter_chart = gr.Plot(label="") gr.Markdown("#### AI explanation — top result") explanation_box = gr.Markdown() search_btn.click( fn=search, inputs=[cuisine_dd, price_dd, arrond_dd, top_n_sl], outputs=[results_table, score_chart, scatter_chart, explanation_box], ) demo.launch()