GROUP13_AS2 / app.py
AdrienVDS05's picture
Update app.py
7e3a92a verified
raw
history blame
7.27 kB
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=(
"<b>%{text}</b><br>"
"Stars: %{x:.1f}<br>"
"True Score: %{y:.1f}<br>"
"Verdict: " + label +
"<extra></extra>"
),
)
)
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()