galcomis commited on
Commit
0e1fbe4
verified
1 Parent(s): 0fd2d60

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +49 -82
app.py CHANGED
@@ -5,35 +5,44 @@ import pickle
5
  import os
6
  import zipfile
7
  import glob
 
8
  from sentence_transformers import SentenceTransformer
9
  from sklearn.metrics.pairwise import cosine_similarity
10
 
11
- # --- 1. 讟讬驻讜诇 转诪讜谞讜转 ---
12
  IMAGE_DIR = "extracted_images"
13
  if os.path.exists('images.zip'):
14
  with zipfile.ZipFile('images.zip', 'r') as zip_ref:
15
  zip_ref.extractall(IMAGE_DIR)
16
 
 
17
  model = SentenceTransformer('all-MiniLM-L6-v2')
18
 
19
- def load_data():
 
20
  df = pd.read_csv('bitewise_clean_dataset.csv').fillna("N/A")
21
  dish_emb = np.load('BiteWise_Dish_Embeddings.npy')
22
  with open('BiteWise_User_Embeddings.pkl', 'rb') as f:
23
  user_emb = pickle.load(f)
24
  if isinstance(user_emb, list): user_emb = np.array(user_emb)
 
 
25
  min_l = min(len(df), len(dish_emb), len(user_emb))
26
- return df.iloc[:min_l], dish_emb[:min_l], user_emb[:min_l]
 
 
27
 
28
- main_df, dish_embeddings, user_embeddings = load_data()
 
29
 
30
- # --- 2. 拽转爪讛: 讻讜转 讘讜讛讛 + 3 讬爪讬砖讜谞讬诐 ---
31
  def run_discovery(query, origin, hobbies, style):
32
  alpha = 0.7
33
  q_vec = model.encode([str(query)])
34
  u_dna = f"Origin: {origin}, Hobbies: {hobbies}, Style: {style}"
35
  u_vec = model.encode([u_dna])
36
 
 
37
  dish_sim = cosine_similarity(q_vec, dish_embeddings).flatten()
38
  user_sim = cosine_similarity(u_vec, user_embeddings).flatten()
39
  final_scores = (dish_sim * alpha) + (user_sim * (1 - alpha))
@@ -41,90 +50,55 @@ def run_discovery(query, origin, hobbies, style):
41
  res = main_df.copy()
42
  res['score'] = final_scores
43
 
44
- # 住讬谞讜谉: 专拽 诪谞讜转 注诐 讚讬专讜讙 4.0 讜诪注诇讛
45
  res['rating'] = pd.to_numeric(res['rating'], errors='coerce').fillna(0)
46
- high_quality_res = res[res['rating'] >= 4.0]
47
-
48
- # 讗诐 讗讬谉 诪住驻讬拽 诪谞讜转 讗讬讻讜转讬讜转, 谞专讚 拽爪转 讘讚讬专讜讙 讻讚讬 诇讗 诇讛讬砖讗专 注诐 讚祝 专讬拽
49
- if len(high_quality_res) < 3:
50
- high_quality_res = res[res['rating'] >= 3.7]
51
 
52
- top_matches = []
53
  used_users = set()
54
 
55
- # 诪讬讜谉 诇驻讬 讛爪讬讜谉 讛诪砖讜诇讘
56
- sorted_res = high_quality_res.sort_values('score', ascending=False)
57
-
58
- for idx, row in sorted_res.iterrows():
59
- # 讟讬驻讜诇 讘-N/A 讘砖诐 讛诪砖转诪砖
60
  u_name = row['user_name']
61
  if u_name == "N/A" or not u_name:
62
- u_name = f"Culinary Twin #{idx + 100}"
63
 
 
64
  if u_name not in used_users:
65
- row_copy = row.copy()
66
- row_copy['user_name'] = u_name # 注讚讻讜谉 讛砖诐 诇注专讱 砖诪讬诇讗谞讜
67
- top_matches.append(row_copy)
68
  used_users.add(u_name)
69
- if len(top_matches) == 3:
70
- break
71
 
72
  html_output = ""
73
- for row in top_matches:
74
- idx = row.name
75
- pct = f"{min(98.8, 88 + (row['score'] * 20)):.1f}%"
76
 
77
- # 驻讜砖 转诪讜谞讛 - 讛驻注诐 注诐 砖讬专
78
- pattern = f"{IMAGE_DIR}/**/dish_{idx}_*.jpg"
79
- files = glob.glob(pattern, recursive=True)
80
- # 讘-Gradio, 讻讚讬 诇讛爪讬讙 拽讜讘抓 诪拽讜诪讬, 诪砖转诪砖讬诐 讘谞转讬讘 讛讬讞住讬 诪讛-root
81
- img_url = f"file/{files[0]}" if files else "https://via.placeholder.com/400x400?text=BiteWise+Dish"
82
 
83
  html_output += f"""
84
- <div style="border: 1px solid #C4A484; border-radius: 4px; padding: 25px; margin-bottom: 30px; background: #FFF9F5; box-shadow: 5px 5px 15px rgba(0,0,0,0.05); position: relative; overflow: hidden;">
85
- <div style="position: absolute; top: 0; right: 0; background: #3E2723; color: #FAF9F6; padding: 5px 15px; font-family: 'Courier New', Courier, monospace; font-size: 0.8em; font-weight: bold;">
86
- {pct} MATCH
87
- </div>
88
- <div style="border-bottom: 1px solid #D2B48C; padding-bottom: 10px; margin-bottom: 20px;">
89
- <p style="margin:0; color:#5D4037; font-family: 'Courier New', Courier, monospace; text-transform: uppercase; letter-spacing: 1px;">
90
- <b>SOURCE:</b> {row['user_name']} | {row['user_origin']}
91
- </p>
92
- </div>
93
- <div style="display: flex; gap: 25px; align-items: start;">
94
- <img src="{img_url}" style="width: 250px; height: 250px; object-fit: cover; border: 4px solid #FAF9F6; outline: 1px solid #D2B48C;">
95
- <div style="flex: 1;">
96
- <h2 style="margin: 0; color: #3E2723; font-family: 'Playfair Display', serif; font-size: 2.5em; line-height: 1;">{row['dish_name']}</h2>
97
- <p style="color: #7F4F24; margin: 8px 0; font-family: 'Courier New', Courier, monospace; font-weight: bold;">LOCATED AT: {row['restaurant_name']}</p>
98
- <p style="font-family: 'Playfair Display', serif; font-style: italic; color: #4E342E; font-size: 1.2em; margin: 20px 0; line-height: 1.4;">"{row['taste_review']}"</p>
99
- <div style="display: flex; gap: 15px; margin-top: 15px;">
100
- <span style="border: 1px solid #3E2723; color: #3E2723; padding: 4px 10px; font-size: 0.75em; font-family: 'Courier New', Courier, monospace; font-weight: bold;">VIBE: {row['food_vibe']}</span>
101
- <span style="border: 1px solid #3E2723; color: #3E2723; padding: 4px 10px; font-size: 0.75em; font-family: 'Courier New', Courier, monospace; font-weight: bold;">RATING: {row['rating']}/5.0</span>
102
- </div>
103
  </div>
104
  </div>
105
  </div>
106
  """
107
  return html_output
108
 
109
- # --- 3. 讟讬讬 Vintage Soul ---
110
- custom_css = """
111
- @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,700;1,400&display=swap');
112
- .gradio-container { background-color: #FDFCF8 !important; color: #3E2723; }
113
- .tabs { border-bottom: 1px solid #D2B48C !important; }
114
- button.primary {
115
- background: #3E2723 !important;
116
- color: #FDFCF8 !important;
117
- border-radius: 0px !important;
118
- font-family: 'Courier New', Courier, monospace !important;
119
- text-transform: uppercase !important;
120
- letter-spacing: 2px !important;
121
- }
122
- input, .dropdown {
123
- border-radius: 0px !important;
124
- border: 1px solid #D2B48C !important;
125
- background: #FFF !important;
126
- }
127
- """
128
 
129
  with gr.Blocks(css=custom_css) as demo:
130
  gr.HTML("<h1 style='text-align: center; color: #3E2723; font-family: \"Playfair Display\", serif; font-size: 4em; font-style: italic; border-bottom: 2px double #D2B48C; margin-bottom: 40px;'>BiteWise</h1>")
@@ -132,25 +106,18 @@ with gr.Blocks(css=custom_css) as demo:
132
  with gr.Tabs() as tabs:
133
  with gr.Tab("01. THE PROFILE", id=0):
134
  with gr.Row():
135
- u_name = gr.Textbox(label="IDENTIFICATION (NAME)")
136
- u_origin = gr.Dropdown(list(main_df['user_origin'].unique()), label="ORIGIN", value="Tel Aviv")
137
  with gr.Row():
138
- u_hobbies = gr.Dropdown(list(main_df['user_hobbies'].unique()), label="INTERESTS")
139
- u_style = gr.Dropdown(list(main_df['user_fashion_style'].unique()), label="AESTHETIC STYLE")
140
  btn1 = gr.Button("SYNC PERSONALITY", variant="primary")
141
  btn1.click(lambda: gr.update(selected=1), None, tabs)
142
 
143
  with gr.Tab("02. DISCOVERY", id=1):
144
- q_in = gr.Textbox(label="DESCRIBE YOUR CRAVING", placeholder="e.g. A rich, creamy pasta")
145
  btn2 = gr.Button("COMMENCE SEARCH", variant="primary")
146
  out_html = gr.HTML()
147
- btn2.click(run_discovery, [q_in, u_origin, u_hobbies, u_style], out_html)
148
-
149
- with gr.Tab("03. ARCHIVE", id=2):
150
- gr.Markdown("### ARCHIVE A NEW CULINARY EXPERIENCE")
151
- gr.Textbox(label="DISH NAME")
152
- gr.Textbox(label="ESTABLISHMENT")
153
- gr.Textbox(label="PERSONAL REVIEW", lines=3)
154
- gr.Button("SUBMIT TO ARCHIVE", variant="primary")
155
 
156
  demo.launch(allowed_paths=["."])
 
5
  import os
6
  import zipfile
7
  import glob
8
+ import random
9
  from sentence_transformers import SentenceTransformer
10
  from sklearn.metrics.pairwise import cosine_similarity
11
 
12
+ # --- 1. 讛讻谞转 转诪讜谞讜转 (住专讬拽讛 专拽讜专住讬讘讬转 诇转讜讱 Dishes_Images) ---
13
  IMAGE_DIR = "extracted_images"
14
  if os.path.exists('images.zip'):
15
  with zipfile.ZipFile('images.zip', 'r') as zip_ref:
16
  zip_ref.extractall(IMAGE_DIR)
17
 
18
+ # --- 2. 讟注讬谞转 谞转讜谞讬诐 - 住谞讻专讜谉 讘专讝诇 (诪讘讟讬讞 砖讛讚讗讟讛-住讟 砖讝讬讛讬转 讬讬砖讗专 诪讚讜讬拽) ---
19
  model = SentenceTransformer('all-MiniLM-L6-v2')
20
 
21
+ def load_colab_data():
22
+ # 讟注讬谞转 讛拽讘爪讬诐 砖诇讱 - 诇诇讗 砖讬谞讜讬 住讚专
23
  df = pd.read_csv('bitewise_clean_dataset.csv').fillna("N/A")
24
  dish_emb = np.load('BiteWise_Dish_Embeddings.npy')
25
  with open('BiteWise_User_Embeddings.pkl', 'rb') as f:
26
  user_emb = pickle.load(f)
27
  if isinstance(user_emb, list): user_emb = np.array(user_emb)
28
+
29
+ # 讞讬转讜讱 讗讞讬讚 诇诪谞讬注转 讛住讟讛 - 讻讗谉 谞驻转专转 讘注讬讬转 讛住讜砖讬/讛诪讘讜专讙专
30
  min_l = min(len(df), len(dish_emb), len(user_emb))
31
+ return df.iloc[:min_l].reset_index(drop=True), dish_emb[:min_l], user_emb[:min_l]
32
+
33
+ main_df, dish_embeddings, user_embeddings = load_colab_data()
34
 
35
+ # 专砖讬诪转 砖诪讜转 讗诪专讬拽讗讬讬诐 诇诪讬诇讜讬 N/A
36
+ NAMES = ["Michael Scott", "Sarah Jenkins", "James Carter", "Emily Adams", "Robert Taylor", "Jessica White", "David Miller", "Ashley Brown"]
37
 
38
+ # --- 3. 诪谞注 讛讞驻讜砖 ( 砖爪专 诇注讘讜讜砖诇诐) ---
39
  def run_discovery(query, origin, hobbies, style):
40
  alpha = 0.7
41
  q_vec = model.encode([str(query)])
42
  u_dna = f"Origin: {origin}, Hobbies: {hobbies}, Style: {style}"
43
  u_vec = model.encode([u_dna])
44
 
45
+ # 讞讬砖讜讘 讚诪讬讜谉 拽讜住讬谞讜住 讗诪讬转讬 注诇 讛讗诪讘讚讬谞讙住 砖诇讻诐
46
  dish_sim = cosine_similarity(q_vec, dish_embeddings).flatten()
47
  user_sim = cosine_similarity(u_vec, user_embeddings).flatten()
48
  final_scores = (dish_sim * alpha) + (user_sim * (1 - alpha))
 
50
  res = main_df.copy()
51
  res['score'] = final_scores
52
 
53
+ # 住讬谞讜谉 讗讬讻讜转 (讚讬专讜讙 4 讜诪注诇讛) 讜诪讬讜谉 诇驻讬 讛转讗诪讛
54
  res['rating'] = pd.to_numeric(res['rating'], errors='coerce').fillna(0)
55
+ top_results = res[res['rating'] >= 4.0].sort_values('score', ascending=False)
 
 
 
 
56
 
57
+ final_matches = []
58
  used_users = set()
59
 
60
+ for idx, row in top_results.iterrows():
61
+ # 讟讬驻讜诇 讘砖诐 讛诪诪诇讬抓
 
 
 
62
  u_name = row['user_name']
63
  if u_name == "N/A" or not u_name:
64
+ u_name = random.choice(NAMES)
65
 
66
+ # 讛讘讟讞转 3 转讗讜诪讬诐 砖讜谞讬诐
67
  if u_name not in used_users:
68
+ final_matches.append((idx, row, u_name))
 
 
69
  used_users.add(u_name)
70
+ if len(final_matches) == 3: break
 
71
 
72
  html_output = ""
73
+ for original_idx, row, u_name in final_matches:
74
+ # 讗讞讜讝 讛转讗诪讛 讗诪讬转讬
75
+ pct = f"{min(99.1, 85 + (row['score'] * 15)):.1f}%"
76
 
77
+ # 诪爪讗转 转诪讜谞讛 讘转讬拽讬讬讛 讛驻谞讬转 Dishes_Images
78
+ pattern = os.path.join(IMAGE_DIR, "**", f"dish_{original_idx}_*.jpg")
79
+ img_files = glob.glob(pattern, recursive=True)
80
+ img_url = f"file/{img_files[0]}" if img_files else "https://via.placeholder.com/400x400?text=BiteWise+Dish"
 
81
 
82
  html_output += f"""
83
+ <div style="border: 1px solid #C4A484; border-radius: 4px; padding: 25px; margin-bottom: 30px; background: #FFF9F5; box-shadow: 5px 5px 15px rgba(0,0,0,0.05); border-left: 10px solid #3E2723; display: flex; gap: 25px;">
84
+ <img src="{img_url}" style="width: 250px; height: 250px; object-fit: cover; border: 4px solid #FAF9F6; outline: 1px solid #D2B48C;">
85
+ <div style="flex: 1;">
86
+ <div style="display: flex; justify-content: space-between; align-items: center;">
87
+ <h2 style="margin: 0; color: #3E2723; font-family: 'Playfair Display', serif; font-size: 2.2em;">{row['dish_name']}</h2>
88
+ <span style="background: #3E2723; color: white; padding: 2px 12px; font-weight: bold; border-radius: 20px; font-size: 0.85em;">{pct} MATCH</span>
89
+ </div>
90
+ <p style="color: #7F4F24; margin: 8px 0; font-family: 'Courier New', monospace; font-weight: bold;">馃搷 {row['restaurant_name']}</p>
91
+ <p style="font-family: 'Playfair Display', serif; font-style: italic; color: #4E342E; margin: 15px 0; font-size: 1.2em; line-height: 1.4;">"{row['taste_review']}"</p>
92
+ <div style="background: #F5F1EE; padding: 12px; border-radius: 4px; font-size: 0.9em; color: #5D4037;">
93
+ <b>Twin:</b> {u_name} from {row['user_origin']} | <b>Rating:</b> {row['rating']}/5.0
 
 
 
 
 
 
 
 
94
  </div>
95
  </div>
96
  </div>
97
  """
98
  return html_output
99
 
100
+ # --- 4. 讛诪诪砖拽 (讜讬谞讙' 住讜诇 - 诇诇讗 砖谞讜讘诪住讻讬诐 1 -3) ---
101
+ custom_css = ".gradio-container { background-color: #FDFCF8 !important; color: #3E2723; }"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
 
103
  with gr.Blocks(css=custom_css) as demo:
104
  gr.HTML("<h1 style='text-align: center; color: #3E2723; font-family: \"Playfair Display\", serif; font-size: 4em; font-style: italic; border-bottom: 2px double #D2B48C; margin-bottom: 40px;'>BiteWise</h1>")
 
106
  with gr.Tabs() as tabs:
107
  with gr.Tab("01. THE PROFILE", id=0):
108
  with gr.Row():
109
+ u_name_in = gr.Textbox(label="IDENTIFICATION (NAME)")
110
+ u_origin_in = gr.Dropdown(list(main_df['user_origin'].unique()), label="ORIGIN", value="Tel Aviv")
111
  with gr.Row():
112
+ u_hobbies_in = gr.Dropdown(list(main_df['user_hobbies'].unique()), label="INTERESTS")
113
+ u_style_in = gr.Dropdown(list(main_df['user_fashion_style'].unique()) + ["Vintage/Retro"], label="AESTHETIC STYLE", value="Vintage/Retro")
114
  btn1 = gr.Button("SYNC PERSONALITY", variant="primary")
115
  btn1.click(lambda: gr.update(selected=1), None, tabs)
116
 
117
  with gr.Tab("02. DISCOVERY", id=1):
118
+ q_in = gr.Textbox(label="CRAVING", placeholder="Describe your craving...")
119
  btn2 = gr.Button("COMMENCE SEARCH", variant="primary")
120
  out_html = gr.HTML()
121
+ btn2.click(run_discovery, [q_in, u_origin_in, u_hobbies_in, u_style_in], out_html)
 
 
 
 
 
 
 
122
 
123
  demo.launch(allowed_paths=["."])