Osole7's picture
demo.load( fn=analyze_restaurant, inputs=[restaurant_input], outputs=[overview_output, insight_output, revenue_plot], ) demo.launch()
ee0a25f verified
import pandas as pd
import gradio as gr
import matplotlib.pyplot as plt
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
DATA_FILE = BASE_DIR / "restaurants_synthetic_dataset.csv"
SUMMARY_FILE = BASE_DIR / "restaurants_synthetic_reviews_summary.csv"
business_df = pd.read_csv(DATA_FILE, low_memory=False)
summary_df = pd.read_csv(SUMMARY_FILE, low_memory=False)
business_df["date"] = pd.to_datetime(business_df["date"], errors="coerce")
business_df["chiffre_affaire_eur"] = pd.to_numeric(business_df["chiffre_affaire_eur"], errors="coerce")
business_df["google_rating"] = pd.to_numeric(business_df["google_rating"], errors="coerce")
numeric_cols = [
"nb_reviews",
"note_review_moyenne",
"note_review_min",
"note_review_max",
"part_reviews_positives",
"part_reviews_mitigees",
"part_reviews_negatives",
]
for col in numeric_cols:
if col in summary_df.columns:
summary_df[col] = pd.to_numeric(summary_df[col], errors="coerce")
restaurant_choices = sorted(summary_df["restaurant_nom"].dropna().astype(str).unique().tolist())
def safe_str(value, fallback="N/A"):
if pd.isna(value):
return fallback
text = str(value).strip()
return text if text else fallback
def recommendation_text(avg_review, neg_share, sanitary_level, google_rating):
issues = []
strengths = []
if pd.notna(avg_review):
if avg_review >= 4.3:
strengths.append("very strong customer satisfaction")
elif avg_review >= 3.8:
strengths.append("solid customer satisfaction")
else:
issues.append("customer satisfaction is below the target level")
if pd.notna(neg_share):
if neg_share >= 0.30:
issues.append("negative reviews are relatively high")
elif neg_share <= 0.10:
strengths.append("negative reviews remain low")
sanitary_text = safe_str(sanitary_level, "").lower()
if "à améliorer" in sanitary_text or "ameliorer" in sanitary_text:
issues.append("sanitary reference level suggests improvement is needed")
elif sanitary_text:
strengths.append(f"sanitary status is {safe_str(sanitary_level)}")
if pd.notna(google_rating):
if google_rating >= 4.3:
strengths.append("google rating is strong")
elif google_rating < 4.0:
issues.append("google rating could be improved")
if issues and strengths:
return (
"Monitor this restaurant closely. It shows positive signals, but priority should be given "
f"to improving weak points. Strengths: {', '.join(strengths[:2])}. "
f"Main issues: {', '.join(issues[:2])}."
)
if issues:
return (
"Improvement plan needed. Focus first on the most visible weaknesses in customer experience "
f"and operations. Main issues: {', '.join(issues[:3])}."
)
return (
"Maintain current performance and continue monitoring quality. "
f"Main strengths: {', '.join(strengths[:3]) if strengths else 'overall stable performance'}."
)
def build_chart(restaurant_name):
subset = business_df[business_df["restaurant_nom"] == restaurant_name].copy()
subset = subset.sort_values("date")
fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(subset["date"], subset["chiffre_affaire_eur"], marker="o")
ax.set_title(f"Revenue trend - {restaurant_name}")
ax.set_xlabel("Date")
ax.set_ylabel("Revenue (EUR)")
plt.xticks(rotation=45)
plt.tight_layout()
return fig
def analyze_restaurant(restaurant_name):
if not restaurant_name:
return "Please choose a restaurant.", "No data yet.", None
review_rows = summary_df[summary_df["restaurant_nom"] == restaurant_name].copy()
business_rows = business_df[business_df["restaurant_nom"] == restaurant_name].copy()
if review_rows.empty and business_rows.empty:
return f"No data found for {restaurant_name}.", "No data available.", None
review_row = review_rows.iloc[0] if not review_rows.empty else pd.Series(dtype=object)
city = safe_str(review_row.get("ville"))
price_range = safe_str(review_row.get("gamme_prix"))
sanitary = safe_str(review_row.get("niveau_sanitaire_reference"))
nb_reviews = review_row.get("nb_reviews")
avg_review = review_row.get("note_review_moyenne")
pos_share = review_row.get("part_reviews_positives")
mixed_share = review_row.get("part_reviews_mitigees")
neg_share = review_row.get("part_reviews_negatives")
type_restauration = "N/A"
if not business_rows.empty and "type_restauration" in business_rows.columns:
mode_vals = business_rows["type_restauration"].mode()
if not mode_vals.empty:
type_restauration = safe_str(mode_vals.iloc[0])
avg_google_rating = business_rows["google_rating"].mean() if not business_rows.empty else None
avg_revenue = business_rows["chiffre_affaire_eur"].mean() if not business_rows.empty else None
latest_revenue = None
if not business_rows.empty and business_rows["chiffre_affaire_eur"].notna().any():
latest_revenue = (
business_rows.sort_values("date")["chiffre_affaire_eur"].dropna().iloc[-1]
)
avg_review_text = f"{avg_review:.2f}/5" if pd.notna(avg_review) else "N/A"
avg_google_text = f"{avg_google_rating:.2f}/5" if pd.notna(avg_google_rating) else "N/A"
avg_revenue_text = f"{avg_revenue:,.0f} EUR" if pd.notna(avg_revenue) else "N/A"
latest_revenue_text = f"{latest_revenue:,.0f} EUR" if pd.notna(latest_revenue) else "N/A"
nb_reviews_text = str(int(nb_reviews)) if pd.notna(nb_reviews) else "N/A"
pos_text = f"{pos_share * 100:.1f}%" if pd.notna(pos_share) else "N/A"
mixed_text = f"{mixed_share * 100:.1f}%" if pd.notna(mixed_share) else "N/A"
neg_text = f"{neg_share * 100:.1f}%" if pd.notna(neg_share) else "N/A"
overview = f"""
## Restaurant Overview
- **Name:** {restaurant_name}
- **City:** {city}
- **Type:** {type_restauration}
- **Price range:** {price_range}
- **Sanitary reference:** {sanitary}
- **Number of reviews:** {nb_reviews_text}
- **Average review score:** {avg_review_text}
- **Average Google rating:** {avg_google_text}
- **Average monthly revenue:** {avg_revenue_text}
- **Latest revenue observed:** {latest_revenue_text}
"""
insight = f"""
## Review Insight
- **Positive reviews:** {pos_text}
- **Mixed reviews:** {mixed_text}
- **Negative reviews:** {neg_text}
## Recommendation
{recommendation_text(avg_review, neg_share, sanitary, avg_google_rating)}
"""
fig = build_chart(restaurant_name) if not business_rows.empty else None
return overview, insight, fig
with gr.Blocks() as demo:
gr.Markdown("# Restaurant Insight Dashboard")
gr.Markdown(
"Choose a restaurant to view its customer review profile, business indicators, and a simple recommendation."
)
with gr.Row():
with gr.Column(scale=1):
restaurant_input = gr.Dropdown(
choices=restaurant_choices,
label="Restaurant name",
value=restaurant_choices[0] if restaurant_choices else None,
)
analyze_btn = gr.Button("Analyze restaurant")
with gr.Column(scale=2):
overview_output = gr.Markdown()
insight_output = gr.Markdown()
revenue_plot = gr.Plot()
analyze_btn.click(
fn=analyze_restaurant,
inputs=[restaurant_input],
outputs=[overview_output, insight_output, revenue_plot],
)
demo.load(
fn=analyze_restaurant,
inputs=[restaurant_input],
outputs=[overview_output, insight_output, revenue_plot],
)
demo.load(
fn=analyze_restaurant,
inputs=[restaurant_input],
outputs=[overview_output, insight_output, revenue_plot],
)
demo.launch()