galcomis commited on
Commit
1a35bf2
·
verified ·
1 Parent(s): a82a092

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +128 -104
app.py CHANGED
@@ -5,158 +5,182 @@ import os
5
  from sentence_transformers import SentenceTransformer
6
  from sklearn.metrics.pairwise import cosine_similarity
7
 
8
- # --- 1. SETTINGS & MODEL ---
9
- # Using the exact SBERT model from our pipeline
 
 
10
  model = SentenceTransformer('all-MiniLM-L6-v2')
11
 
12
- # File configurations
13
- MAIN_DB = 'bitewise_clean_dataset.csv'
14
- DISH_VECTORS = 'BiteWise_Dish_Embeddings.npy'
15
- USER_DB_PATH = 'BiteWise_User_Database.csv'
16
- REVIEWS_DB_PATH = 'BiteWise_User_Reviews.csv'
17
 
18
- # Pre-loading static assets
19
- df_main = pd.read_csv(MAIN_DB)
20
- dish_embeddings = np.load(DISH_VECTORS)
21
 
22
- # --- 2. THE STYLING (Bespoke Light Brown/Beige Theme) ---
 
 
23
  custom_css = """
24
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&display=swap');
25
- * { font-family: 'Inter', sans-serif !important; }
26
- .gradio-container { background-color: #F8F5F2 !important; }
27
- .main-header { color: #3D2B1F; text-align: center; font-weight: 600; font-size: 2.5em; margin-bottom: 5px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  .action-btn {
29
- background: #5D4037 !important;
30
- color: white !important;
31
- border: none !important;
32
- border-radius: 8px !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  font-weight: 600 !important;
34
- padding: 10px 20px !important;
35
  }
36
- .action-btn:hover { background: #3D2B1F !important; }
37
- input, textarea, .dropdown, .slider { border-radius: 8px !important; border: 1px solid #D7CCC8 !important; }
38
  """
39
 
40
- # --- 3. CORE LOGIC (Part 4 Pipeline) ---
 
 
41
 
42
- def create_user_persona(name, age, cuisine, spice, adventure, diet, fav_dish, drink, frequency):
43
- """Step 1: Building the user database record."""
44
  if not name:
45
- return "⚠️ Error: Full Name is required."
46
 
47
- user_data = {
48
- 'full_name': name, 'age_group': age, 'fav_cuisine': cuisine,
49
- 'spice_level': spice, 'adventure_level': adventure, 'dietary_restrictions': diet,
50
- 'favorite_dish': fav_dish, 'preferred_drink': drink, 'dining_frequency': frequency
51
  }
52
 
53
- # Save to persistent user storage
54
- u_df = pd.DataFrame([user_data])
55
- if os.path.exists(USER_DB_PATH):
56
- pd.concat([pd.read_csv(USER_DB_PATH), u_df], ignore_index=True).to_csv(USER_DB_PATH, index=False)
57
  else:
58
- u_df.to_csv(USER_DB_PATH, index=False)
59
-
60
- # Return exactly as phrased in Colab
61
- return f"""### 👤 Profile Created Successfully!
62
- **Welcome, {name}!**
63
-
64
- **Your Culinary Context:**
65
- * **Preferences:** {cuisine} | **Spice:** {spice}/5 | **Adventure:** {adventure}/5
66
- * **Details:** {diet} | **Favorite:** {fav_dish} | **Vibe:** {drink}
67
-
68
- We have updated our records. You can now use the search engine to find dishes tailored to your taste."""
69
 
70
- def run_search_engine(query):
71
- """Step 2: Semantic search logic."""
72
  try:
73
  query_embedding = model.encode([query])
74
- sims = cosine_similarity(query_embedding, dish_embeddings).flatten()
75
 
76
- # Temporary view for results
77
- search_df = df_main.copy()
78
- search_df['match_score'] = sims
79
- top_3 = search_df.sort_values(by='match_score', ascending=False).head(3)
80
 
81
- output = "### 🍴 Top Matches Found:\n\n"
82
- for _, row in top_3.iterrows():
83
- output += f"**{row['dish_name']}** at **{row['restaurant_name']}**\n"
84
- output += f"Rating: {row['rating']} ⭐ | Review: {row['taste_review']}\n\n"
85
- output += "---\n"
86
- return output
87
  except Exception as e:
88
- return f"Error: {e}"
89
 
90
- def submit_review(dish, rest, rate, taste, visual, user):
91
- """Step 3: Contributing a new review."""
92
- if not dish or not rest:
93
- return "⚠️ Dish and Restaurant names are required.", dish, rest, rate, taste, visual, user
94
-
95
- review_data = {
96
- 'dish_name': dish, 'restaurant_name': rest, 'rating': rate,
97
- 'taste_review': taste, 'visual_description': visual, 'user_name': user
98
  }
99
 
100
- r_df = pd.DataFrame([review_data])
101
- if os.path.exists(REVIEWS_DB_PATH):
102
- pd.concat([pd.read_csv(REVIEWS_DB_PATH), r_df], ignore_index=True).to_csv(REVIEWS_DB_PATH, index=False)
103
  else:
104
- r_df.to_csv(REVIEWS_DB_PATH, index=False)
105
 
106
- return f" Thank you {user}! Your review for '{dish}' has been synced.", "", "", 4.5, "", "", ""
107
 
108
- # --- 4. USER INTERFACE (Gradio) ---
 
 
109
 
110
  with gr.Blocks(css=custom_css) as demo:
111
  gr.HTML("<h1 class='main-header'>BiteWise</h1>")
112
 
113
  with gr.Tabs():
114
- # STEP 1: PERSONA
115
- with gr.Tab("1. User Profile Setup"):
116
- gr.Markdown("### 📋 Create your culinary DNA")
117
  with gr.Row():
118
- u_name = gr.Textbox(label="Full Name")
119
- u_age = gr.Dropdown(choices=["Under 18", "18-25", "26-40", "41-60", "60+"], label="Age Group")
120
  with gr.Row():
121
- u_cuisine = gr.Textbox(label="Favorite Cuisine Type", placeholder="e.g., Asian, Italian")
122
- u_diet = gr.Textbox(label="Dietary Restrictions", placeholder="None, Vegan, etc.")
123
  with gr.Row():
124
- u_dish = gr.Textbox(label="One dish you love", placeholder="e.g., Truffle Pasta")
125
- u_drink = gr.Textbox(label="Preferred Drink", placeholder="e.g., Matcha, Wine")
126
  with gr.Row():
127
- u_freq = gr.Dropdown(choices=["Daily", "Weekly", "Rarely"], label="Dining Frequency")
128
- u_spice = gr.Slider(1, 5, step=1, label="Spice Tolerance")
129
- u_adv = gr.Slider(1, 5, step=1, label="Adventurousness")
130
 
131
- btn_p = gr.Button("Create Profile", elem_classes="action-btn")
132
  out_p = gr.Markdown()
133
- btn_p.click(create_user_persona,
134
- inputs=[u_name, u_age, u_cuisine, u_spice, u_adv, u_diet, u_dish, u_drink, u_freq],
135
  outputs=out_p)
136
 
137
- # STEP 2: SEARCH
138
- with gr.Tab("2. Dish Search"):
139
- gr.Markdown("### 🔍 Find your next flavor")
140
- s_q = gr.Textbox(label="Describe your craving", placeholder="e.g., Something spicy and crunchy")
141
- btn_s = gr.Button("Search Engine", elem_classes="action-btn")
142
  out_s = gr.Markdown()
143
- btn_s.click(run_search_engine, inputs=s_q, outputs=out_s)
144
 
145
- # STEP 3: CONTRIBUTION
146
- with gr.Tab("3. Upload Review"):
147
- gr.Markdown("### ✍️ Share your experience")
148
  with gr.Row():
149
- in_u = gr.Textbox(label="Reviewer Name")
150
- in_d = gr.Textbox(label="Dish Name")
151
- in_r = gr.Textbox(label="Restaurant")
152
  with gr.Row():
153
- in_rate = gr.Slider(1, 5, value=4.5, step=0.1, label="Rating")
154
- in_t = gr.Textbox(label="Taste Notes")
155
- in_v = gr.Textbox(label="Visual Description")
156
 
157
- btn_r = gr.Button("Upload Review", elem_classes="action-btn")
158
  out_r = gr.Markdown()
159
- btn_r.click(submit_review,
160
  inputs=[in_d, in_r, in_rate, in_t, in_v, in_u],
161
  outputs=[out_r, in_d, in_r, in_rate, in_t, in_v, in_u])
162
 
 
5
  from sentence_transformers import SentenceTransformer
6
  from sklearn.metrics.pairwise import cosine_similarity
7
 
8
+ # ==============================================================================
9
+ # 1. CORE ASSETS & MODEL (Directly from Colab Pipeline)
10
+ # ==============================================================================
11
+ # Initializing SBERT for semantic search
12
  model = SentenceTransformer('all-MiniLM-L6-v2')
13
 
14
+ # Loading static data and pre-computed embeddings
15
+ main_df = pd.read_csv('bitewise_clean_dataset.csv')
16
+ dish_embeddings = np.load('BiteWise_Dish_Embeddings.npy')
 
 
17
 
18
+ # Persistent storage for user interaction data
19
+ USER_PROFILES_FILE = "BiteWise_User_Database.csv"
20
+ USER_REVIEWS_FILE = "BiteWise_User_Reviews.csv"
21
 
22
+ # ==============================================================================
23
+ # 2. SOPHISTICATED MINIMALIST DESIGN (Earth Tones & High-End Typography)
24
+ # ==============================================================================
25
  custom_css = """
26
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&display=swap');
27
+
28
+ /* Global Font & Background */
29
+ * { font-family: 'Inter', sans-serif !important; color: #2C2C2C; }
30
+ .gradio-container { background-color: #FAF9F6 !important; border: none !important; }
31
+
32
+ /* Header Styling */
33
+ .main-header {
34
+ text-align: left;
35
+ font-weight: 300;
36
+ letter-spacing: -1px;
37
+ font-size: 3.2em;
38
+ margin-bottom: 0px;
39
+ color: #1A1A1A;
40
+ }
41
+
42
+ /* Button Styling - Deep Earth Tone, No Gradients */
43
  .action-btn {
44
+ background-color: #3E2723 !important;
45
+ color: #FFFFFF !important;
46
+ border-radius: 4px !important;
47
+ border: none !important;
48
+ font-weight: 400 !important;
49
+ text-transform: uppercase;
50
+ letter-spacing: 1px;
51
+ transition: 0.2s ease-in-out;
52
+ }
53
+ .action-btn:hover { background-color: #1B1B1B !important; }
54
+
55
+ /* Input Fields - Subtle & Sharp */
56
+ input, textarea, .dropdown {
57
+ border-radius: 2px !important;
58
+ border: 1px solid #E0E0E0 !important;
59
+ background-color: #FFFFFF !important;
60
+ }
61
+
62
+ /* Tab Navigation Styling */
63
+ .tabs { border-bottom: 1px solid #E0E0E0 !important; }
64
+ .tab-nav button.selected {
65
+ border-bottom: 2px solid #3E2723 !important;
66
+ background: transparent !important;
67
  font-weight: 600 !important;
 
68
  }
 
 
69
  """
70
 
71
+ # ==============================================================================
72
+ # 3. ENGINE LOGIC (1:1 Transfer from Colab Part 4)
73
+ # ==============================================================================
74
 
75
+ def create_user_profile(name, age, cuisine, spice, adventure, diet, fav_dish, drink, frequency):
76
+ """Saves user persona into the persistent database exactly as defined in Part 4."""
77
  if not name:
78
+ return "⚠️ Error: Please provide a name to initialize your profile."
79
 
80
+ user_record = {
81
+ 'Name': name, 'Age': age, 'Favorite Cuisine': cuisine,
82
+ 'Spice Level': spice, 'Adventure Level': adventure, 'Dietary Restrictions': diet,
83
+ 'Favorite Dish': fav_dish, 'Preferred Drink': drink, 'Frequency': frequency
84
  }
85
 
86
+ u_df = pd.DataFrame([user_record])
87
+ if os.path.exists(USER_PROFILES_FILE):
88
+ pd.concat([pd.read_csv(USER_PROFILES_FILE), u_df], ignore_index=True).to_csv(USER_PROFILES_FILE, index=False)
 
89
  else:
90
+ u_df.to_csv(USER_PROFILES_FILE, index=False)
91
+
92
+ return f"**User Profile Created.** Welcome, {name}. Your preferences for '{cuisine}' and {diet} restrictions are now active."
 
 
 
 
 
 
 
 
93
 
94
+ def search_engine(query):
95
+ """Executes semantic search using Cosine Similarity against the pre-computed dish space."""
96
  try:
97
  query_embedding = model.encode([query])
98
+ similarities = cosine_similarity(query_embedding, dish_embeddings).flatten()
99
 
100
+ main_df['match_score'] = similarities
101
+ top_results = main_df.sort_values(by='match_score', ascending=False).head(3)
 
 
102
 
103
+ results_output = "### RECOMMENDED SELECTIONS\n\n"
104
+ for _, row in top_results.iterrows():
105
+ results_output += f"**{row['dish_name']}** | {row['restaurant_name']}\n"
106
+ results_output += f"Score: {row['rating']} ⭐ — *{row['taste_review']}*\n\n"
107
+ results_output += "---\n\n"
108
+ return results_output
109
  except Exception as e:
110
+ return f"Engine Error: {e}"
111
 
112
+ def upload_review(dish_name, restaurant_name, rating, taste_review, visual_description, user_name):
113
+ """Submits new culinary data to the BiteWise ecosystem and resets input fields."""
114
+ if not dish_name or not restaurant_name:
115
+ return "⚠️ Validation Error: Dish and Restaurant names are mandatory.", dish_name, restaurant_name, rating, taste_review, visual_description, user_name
116
+
117
+ new_review = {
118
+ 'dish_name': dish_name, 'restaurant_name': restaurant_name, 'rating': rating,
119
+ 'taste_review': taste_review, 'visual_description': visual_description, 'user_name': user_name
120
  }
121
 
122
+ r_df = pd.DataFrame([new_review])
123
+ if os.path.exists(USER_REVIEWS_FILE):
124
+ pd.concat([pd.read_csv(USER_REVIEWS_FILE), r_df], ignore_index=True).to_csv(USER_REVIEWS_FILE, index=False)
125
  else:
126
+ r_df.to_csv(USER_REVIEWS_FILE, index=False)
127
 
128
+ return f"Successfully synced review for '{dish_name}'.", "", "", 4.5, "", "", ""
129
 
130
+ # ==============================================================================
131
+ # 4. INTERFACE ARCHITECTURE
132
+ # ==============================================================================
133
 
134
  with gr.Blocks(css=custom_css) as demo:
135
  gr.HTML("<h1 class='main-header'>BiteWise</h1>")
136
 
137
  with gr.Tabs():
138
+ # Step 1: User Persona
139
+ with gr.Tab("01. PERSONA"):
140
+ gr.Markdown("Capture your culinary DNA to calibrate the engine.")
141
  with gr.Row():
142
+ u_name = gr.Textbox(label="FULL NAME")
143
+ u_age = gr.Dropdown(choices=["Under 18", "18-25", "26-40", "41-60", "60+"], label="AGE GROUP")
144
  with gr.Row():
145
+ u_cuisine = gr.Textbox(label="FAVORITE CUISINE", placeholder="e.g. Modern Asian")
146
+ u_diet = gr.Textbox(label="DIETARY RESTRICTIONS", placeholder="e.g. None / Vegan")
147
  with gr.Row():
148
+ u_fav_dish = gr.Textbox(label="SIGNATURE DISH", placeholder="What do you always order?")
149
+ u_drink = gr.Textbox(label="DRINK PREFERENCE")
150
  with gr.Row():
151
+ u_freq = gr.Dropdown(choices=["Daily", "Weekly", "Monthly", "Rarely"], label="DINING FREQUENCY")
152
+ u_spice = gr.Slider(1, 5, step=1, label="SPICE TOLERANCE")
153
+ u_adv = gr.Slider(1, 5, step=1, label="ADVENTUROUSNESS")
154
 
155
+ btn_p = gr.Button("INITIALIZE PROFILE", elem_classes="action-btn")
156
  out_p = gr.Markdown()
157
+ btn_p.click(create_user_profile,
158
+ inputs=[u_name, u_age, u_cuisine, u_spice, u_adv, u_diet, u_fav_dish, u_drink, u_freq],
159
  outputs=out_p)
160
 
161
+ # Step 2: Discovery
162
+ with gr.Tab("02. DISCOVERY"):
163
+ gr.Markdown("Search the engine for specific cravings or textures.")
164
+ s_q = gr.Textbox(label="CRAVING QUERY", placeholder="e.g. A rich, smoky pasta with earthy notes")
165
+ btn_s = gr.Button("EXECUTE SEARCH", elem_classes="action-btn")
166
  out_s = gr.Markdown()
167
+ btn_s.click(search_engine, inputs=s_q, outputs=out_s)
168
 
169
+ # Step 3: Contribution
170
+ with gr.Tab("03. CONTRIBUTION"):
171
+ gr.Markdown("Sync new data points to the BiteWise database.")
172
  with gr.Row():
173
+ in_u = gr.Textbox(label="REVIEWER NAME")
174
+ in_d = gr.Textbox(label="DISH NAME")
175
+ in_r = gr.Textbox(label="RESTAURANT NAME")
176
  with gr.Row():
177
+ in_rate = gr.Slider(1, 5, value=4.5, step=0.1, label="RATING")
178
+ in_t = gr.Textbox(label="TASTE NOTES")
179
+ in_v = gr.Textbox(label="VISUAL PRESENTATION")
180
 
181
+ btn_r = gr.Button("SYNC DATA", elem_classes="action-btn")
182
  out_r = gr.Markdown()
183
+ btn_r.click(upload_review,
184
  inputs=[in_d, in_r, in_rate, in_t, in_v, in_u],
185
  outputs=[out_r, in_d, in_r, in_rate, in_t, in_v, in_u])
186