galcomis commited on
Commit
b8bb1b3
·
verified ·
1 Parent(s): 3f81ba5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +83 -99
app.py CHANGED
@@ -1,137 +1,121 @@
1
  import gradio as gr
2
  import pandas as pd
3
  import numpy as np
 
4
  import os
5
  import zipfile
6
- import glob
7
  from sentence_transformers import SentenceTransformer
8
  from sklearn.metrics.pairwise import cosine_similarity
9
 
10
- # --- 1. חילוץ תמונות חכם (לתוך תיקייה ייעודית) ---
11
- def setup_images():
12
- if os.path.exists('images.zip'):
13
- try:
14
- with zipfile.ZipFile('images.zip', 'r') as zip_ref:
15
- zip_ref.extractall('extracted_images')
16
- print("✅ Images extracted successfully.")
17
- except Exception as e:
18
- print(f"❌ Zip Error: {e}")
19
 
20
- setup_images()
21
-
22
- # --- 2. מנוע המלצות (סנכרון מלא 100%) ---
23
  model = SentenceTransformer('all-MiniLM-L6-v2')
24
 
25
- def initialize_engine():
26
- df = pd.read_csv('bitewise_clean_dataset.csv').fillna("N/A")
27
- # יצירת הוקטורים מחדש מבטיחה שאינדקס X בטבלה הוא תמונה X
28
- dish_texts = (df['dish_name'] + " " + df['food_vibe'] + " " + df['restaurant_name']).tolist()
29
- dish_embeddings = model.encode(dish_texts, show_progress_bar=False)
30
- return df, dish_embeddings
31
-
32
- main_df, dish_embeddings = initialize_engine()
33
-
34
- # פונקציית עזר למציאת נתיב התמונה המדויק
35
- def get_image_path(idx):
36
- # מחפש את הקובץ בכל עומק התיקיות שחולצו
37
- pattern = f"extracted_images/**/dish_{idx}_*.jpg"
38
- files = glob.glob(pattern, recursive=True)
39
- if files:
40
- return f"file/{files[0]}"
41
- return "https://via.placeholder.com/300?text=BiteWise+Dish"
42
-
43
- # --- 3. העיצוב היוקרתי (Premium Brown/Wood) ---
44
- custom_css = """
45
- @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700&family=Inter:wght@400;600&display=swap');
46
- .gradio-container { background-color: #FAF9F6 !important; font-family: 'Inter', sans-serif !important; }
47
- .main-title { font-family: 'Playfair Display', serif; color: #3E2723; font-size: 3.5em; text-align: center; margin-bottom: 0px; }
48
- .sub-title { text-align: center; color: #7F4F24; letter-spacing: 2px; text-transform: uppercase; font-size: 0.8em; margin-bottom: 30px; }
49
 
50
- .dish-card {
51
- background: white; border-radius: 15px; padding: 25px; margin-bottom: 25px;
52
- box-shadow: 0 10px 30px rgba(62, 39, 35, 0.08); border: 1px solid #EFEBE9;
53
- display: flex; gap: 25px; border-left: 10px solid #3E2723;
54
- }
55
- .match-score { background: #3E2723; color: white; padding: 6px 16px; border-radius: 20px; font-weight: bold; font-size: 0.85em; }
56
- .info-tag { background: #F5F1EE; color: #5D4037; padding: 5px 12px; border-radius: 6px; font-weight: 700; font-size: 0.75em; text-transform: uppercase; border: 1px solid #D7CCC8; }
57
- .review-text { font-family: 'Playfair Display', serif; font-style: italic; color: #4E342E; font-size: 1.15em; margin: 15px 0; border-left: 2px solid #D7CCC8; padding-left: 15px; line-height: 1.5; }
58
- .twin-bio { background: #FFF9F0; padding: 15px; border-radius: 10px; font-size: 0.95em; color: #5D4037; border: 1px solid #FFE0B2; }
59
- """
60
 
61
- # --- 4. לוגיקה עסקית ---
62
  def get_recommendations(query, origin, hobbies, style):
 
 
 
 
 
 
63
  q_vec = model.encode([str(query)])
64
- d_sim = cosine_similarity(q_vec, dish_embeddings).flatten()
 
 
 
 
 
65
 
66
- res = main_df.copy()
67
- res['score'] = d_sim
68
- # מניעת כפילויות: מסנן אם אותה מנה מופיעה פעמיים מאותה מסעדה
69
- top_results = res.sort_values('score', ascending=False).drop_duplicates(subset=['dish_name', 'restaurant_name']).head(3)
 
 
 
 
70
 
71
  html = ""
72
- for idx, row in top_results.iterrows():
73
- pct = f"{min(98.8, 88 + (row['score'] * 20)):.1f}%"
74
- img_url = get_image_path(idx)
 
 
 
 
 
75
 
76
  html += f"""
77
- <div class="dish-card">
78
- <img src="{img_url}" style="width: 280px; height: 280px; object-fit: cover; border-radius: 12px;">
79
  <div style="flex: 1;">
80
- <div style="display: flex; justify-content: space-between; align-items: center;">
81
- <h2 style="margin: 0; font-family: 'Playfair Display', serif; color: #3E2723; font-size: 2em;">{row['dish_name']}</h2>
82
- <span class="match-score">{pct} MATCH</span>
83
- </div>
84
- <p style="color: #7F4F24; font-weight: 600; margin: 5px 0;">📍 {row['restaurant_name']}</p>
85
- <div style="margin: 15px 0; display: flex; gap: 10px;">
86
- <span class="info-tag">✧ {row['food_vibe']}</span>
87
- <span class="info-tag">✧ {row['ideal_context']}</span>
88
- <span class="info-tag">✧ RATING: {row['rating']}</span>
89
- </div>
90
- <div class="review-text">"{row['taste_review']}"</div>
91
- <div class="twin-bio">
92
- <strong>Twin Bio:</strong> Your culinary twin from <b>{origin}</b> who shares your interest in <b>{hobbies}</b> recommends this <b>{style}</b> experience.
93
  </div>
 
 
 
94
  </div>
95
  </div>
96
  """
97
  return html
98
 
99
- def handle_share(dish, rest, review):
100
- if not dish or not rest or not review:
101
- return "### ⚠️ Please fill in all fields before uploading."
102
- return f"### ✅ Success!\n**{dish}** from **{rest}** has been shared with the community. Your review helps others find their Culinary Twin!"
 
103
 
104
- # --- 5. ממשק המשתמש ---
105
  with gr.Blocks(css=custom_css) as demo:
106
- gr.HTML("<div class='main-title'>BiteWise</div>")
107
- gr.HTML("<div class='sub-title'>Curated Culinary Twin Experiences</div>")
108
 
109
  with gr.Tabs() as tabs:
110
  with gr.Tab("01. YOUR DNA", id=0):
111
  with gr.Row():
112
- u_n = gr.Textbox(label="Full Name", placeholder="What's your name?")
113
- u_o = gr.Dropdown(list(main_df['user_origin'].unique()), label="Origin", value="Tel Aviv")
114
- with gr.Row():
115
- u_h = gr.Dropdown(list(main_df['user_hobbies'].unique()), label="Hobbies", value="Photography")
116
- u_s = gr.Dropdown(list(main_df['user_fashion_style'].unique()), label="Style", value="Modern/Minimalist")
117
- btn1 = gr.Button("SYNC MY DNA", variant="primary")
118
  btn1.click(lambda: gr.update(selected=1), None, tabs)
119
 
120
  with gr.Tab("02. DISCOVERY", id=1):
121
- with gr.Row():
122
- q_in = gr.Textbox(label="What are you craving?", placeholder="e.g. Sushi, Pasta...", scale=4)
123
- btn2 = gr.Button("FIND", variant="primary", scale=1)
124
- out_html = gr.HTML()
125
- btn2.click(get_recommendations, [q_in, u_o, u_h, u_s], out_html)
126
-
127
- with gr.Tab("03. SHARE", id=2):
128
- gr.Markdown("### 📸 Share your discovery with the community")
129
- with gr.Column():
130
- s_dish = gr.Textbox(label="Dish Name")
131
- s_rest = gr.Textbox(label="Restaurant")
132
- s_rev = gr.Textbox(label="Your Review", lines=3)
133
- btn_up = gr.Button("UPLOAD TO COMMUNITY")
134
- s_msg = gr.Markdown()
135
- btn_up.click(handle_share, [s_dish, s_rest, s_rev], s_msg)
136
 
137
  demo.launch(allowed_paths=["."])
 
1
  import gradio as gr
2
  import pandas as pd
3
  import numpy as np
4
+ import pickle
5
  import os
6
  import zipfile
 
7
  from sentence_transformers import SentenceTransformer
8
  from sklearn.metrics.pairwise import cosine_similarity
9
 
10
+ # --- 1. הכנה: פריקת תמונות ---
11
+ if os.path.exists('images.zip'):
12
+ with zipfile.ZipFile('images.zip', 'r') as zip_ref:
13
+ zip_ref.extractall('extracted_images')
 
 
 
 
 
14
 
15
+ # --- 2. טעינת נכסים (Assets) מקוריים מהקולאב ---
16
+ # אנחנו משתמשים במודל רק כדי לקודד את החיפוש הנוכחי של המשתמש
 
17
  model = SentenceTransformer('all-MiniLM-L6-v2')
18
 
19
+ def load_data():
20
+ # קריאת הנתונים המדויקים שלכם
21
+ df = pd.read_csv('bitewise_clean_dataset.csv')
22
+ dish_embeddings = np.load('BiteWise_Dish_Embeddings.npy')
23
+
24
+ with open('BiteWise_User_Embeddings.pkl', 'rb') as f:
25
+ user_embeddings = pickle.load(f)
26
+ if isinstance(user_embeddings, list):
27
+ user_embeddings = np.array(user_embeddings)
28
+
29
+ # סנכרון אורכים למניעת תזוזות במידע
30
+ min_len = min(len(df), len(dish_embeddings), len(user_embeddings))
31
+ df = df.iloc[:min_len].copy()
32
+ dish_embeddings = dish_embeddings[:min_len]
33
+ user_embeddings = user_embeddings[:min_l]
34
+
35
+ return df, dish_embeddings, user_embeddings
 
 
 
 
 
 
 
36
 
37
+ # טעינה חד פעמית עם עליית האפליקציה
38
+ try:
39
+ main_df, dish_embeddings, user_embeddings = load_data()
40
+ except Exception as e:
41
+ print(f"Error loading files: {e}")
42
+ main_df, dish_embeddings, user_embeddings = None, None, None
 
 
 
 
43
 
44
+ # --- 3. לוגיקת המלצה עתק מהקולאב) ---
45
  def get_recommendations(query, origin, hobbies, style):
46
+ if main_df is None:
47
+ return "שגיאה: הקבצים המקוריים (CSV/NPY/PKL) חסרים בשרת."
48
+
49
+ alpha = 0.7 # המשקולת המקורית שלכם
50
+
51
+ # וקטוריזציה של הקלט הנוכחי
52
  q_vec = model.encode([str(query)])
53
+ u_input_str = f"Origin: {origin}, Hobbies: {hobbies}, Style: {style}"
54
+ u_input_vec = model.encode([u_input_str])
55
+
56
+ # חישוב דמיון מול הוקטורים המוכנים שלכם
57
+ dish_sim = cosine_similarity(q_vec, dish_embeddings).flatten()
58
+ user_sim = cosine_similarity(u_input_vec, user_embeddings).flatten()
59
 
60
+ # ציון משולב
61
+ scores = (dish_sim * alpha) + (user_sim * (1 - alpha))
62
+
63
+ temp_df = main_df.copy()
64
+ temp_df['final_score'] = scores
65
+
66
+ # מניעת כפילויות: לוקחים את המנה הכי טובה מכל מסעדה
67
+ top_3 = temp_df.sort_values('final_score', ascending=False).drop_duplicates('restaurant_name').head(3)
68
 
69
  html = ""
70
+ for idx, row in top_3.iterrows():
71
+ # אחוז התאמה לפי הלוגיקה שלכם
72
+ pct = f"{min(98.8, 88 + (row['final_score'] * 20)):.1f}%"
73
+
74
+ # חיפוש תמונה בתוך התיקייה שפרקנו
75
+ img_filename = f"dish_{idx}_0.jpg"
76
+ img_path = os.path.join('extracted_images', img_filename)
77
+ img_url = f"file/{img_path}" if os.path.exists(img_path) else "https://via.placeholder.com/300?text=BiteWise"
78
 
79
  html += f"""
80
+ <div style="border: 2px solid #3E2723; border-radius: 15px; padding: 20px; margin-bottom: 20px; display: flex; gap: 20px; background: white;">
81
+ <img src="{img_url}" style="width: 250px; height: 250px; object-fit: cover; border-radius: 10px;">
82
  <div style="flex: 1;">
83
+ <h2 style="margin: 0; color: #3E2723;">{row['dish_name']} <small>@{row['restaurant_name']}</small></h2>
84
+ <p style="font-weight: bold; color: #7F4F24; font-size: 1.1em;">{pct} Match</p>
85
+ <div style="margin: 10px 0;">
86
+ <span style="background: #F5F1EE; padding: 5px 10px; border-radius: 5px; font-weight: bold;">🍽️ {row['food_vibe']}</span>
87
+ <span style="background: #F5F1EE; padding: 5px 10px; border-radius: 5px; font-weight: bold; margin-left: 5px;">👥 {row['ideal_context']}</span>
 
 
 
 
 
 
 
 
88
  </div>
89
+ <p style="font-style: italic; color: #444;">"{row['taste_review']}"</p>
90
+ <hr style="border: 0; border-top: 1px solid #eee; margin: 10px 0;">
91
+ <p style="margin: 0; font-size: 0.9em;"><b>Rating:</b> {row['rating']}/5 | <b>Twin:</b> Based on {style} DNA</p>
92
  </div>
93
  </div>
94
  """
95
  return html
96
 
97
+ # --- 4. הממשק היוקרתי (CSS) ---
98
+ custom_css = """
99
+ .gradio-container { background-color: #FAF9F6 !important; }
100
+ .main-title { color: #3E2723; font-family: 'Serif'; text-align: center; font-size: 3em; }
101
+ """
102
 
 
103
  with gr.Blocks(css=custom_css) as demo:
104
+ gr.HTML("<h1 class='main-title'>BiteWise</h1>")
 
105
 
106
  with gr.Tabs() as tabs:
107
  with gr.Tab("01. YOUR DNA", id=0):
108
  with gr.Row():
109
+ u_o = gr.Dropdown(["Tel Aviv", "London", "New York", "Paris", "Berlin", "Tokyo"], label="Origin", value="Tel Aviv")
110
+ u_h = gr.Dropdown(["Photography", "Cooking", "Fashion", "Traveling", "Gaming", "Yoga"], label="Hobbies", value="Photography")
111
+ u_s = gr.Dropdown(["Modern/Minimalist", "Classic/Elegant", "Street/Urban", "Nature/Boho"], label="Style", value="Modern/Minimalist")
112
+ btn1 = gr.Button("SAVE DNA", variant="primary")
 
 
113
  btn1.click(lambda: gr.update(selected=1), None, tabs)
114
 
115
  with gr.Tab("02. DISCOVERY", id=1):
116
+ q_in = gr.Textbox(label="What are you craving?", placeholder="e.g. Pasta")
117
+ btn2 = gr.Button("FIND", variant="primary")
118
+ out = gr.HTML()
119
+ btn2.click(get_recommendations, [q_in, u_o, u_h, u_s], out)
 
 
 
 
 
 
 
 
 
 
 
120
 
121
  demo.launch(allowed_paths=["."])