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()