Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import pandas as pd | |
| import numpy as np | |
| import pickle | |
| import os | |
| import zipfile | |
| import glob | |
| import random | |
| from sentence_transformers import SentenceTransformer | |
| from sklearn.metrics.pairwise import cosine_similarity | |
| # --- 1. הכנת תמונות --- | |
| IMAGE_DIR = "extracted_images" | |
| if os.path.exists('images.zip'): | |
| with zipfile.ZipFile('images.zip', 'r') as zip_ref: | |
| zip_ref.extractall(IMAGE_DIR) | |
| def find_dish_image(idx): | |
| pattern = os.path.join(IMAGE_DIR, "Dishes_Images", f"dish_{idx}_*.jpg") | |
| files = glob.glob(pattern) | |
| if not files: | |
| files = glob.glob(os.path.join(IMAGE_DIR, "**", f"dish_{idx}_*.jpg"), recursive=True) | |
| return f"file/{files[0]}" if files else "https://via.placeholder.com/400x400?text=BiteWise+Dish" | |
| # --- 2. טעינה וסנכרון ברזל --- | |
| model = SentenceTransformer('all-MiniLM-L6-v2') | |
| def load_data(): | |
| df = pd.read_csv('bitewise_clean_dataset.csv').fillna("N/A") | |
| dish_emb = np.load('BiteWise_Dish_Embeddings.npy') | |
| with open('BiteWise_User_Embeddings.pkl', 'rb') as f: | |
| u_emb = pickle.load(f) | |
| if isinstance(u_emb, list): u_emb = np.array(u_emb) | |
| min_l = min(len(df), len(dish_emb), len(u_emb)) | |
| return df.iloc[:min_l].reset_index(drop=True), dish_emb[:min_l], u_emb[:min_l] | |
| main_df, dish_embeddings, user_embeddings = load_data() | |
| NAMES = ["James Miller", "Sarah Johnson", "Michael Brown", "Emily Davis", "Robert Wilson"] | |
| # --- 3. מנוע החיפוש --- | |
| def run_discovery(query, origin, hobbies, style): | |
| q_vec = model.encode([str(query)]) | |
| u_dna = f"Origin: {origin}, Hobbies: {hobbies}, Style: {style}" | |
| u_vec = model.encode([u_dna]) | |
| scores = (cosine_similarity(q_vec, dish_embeddings).flatten() * 0.7) + (cosine_similarity(u_vec, user_embeddings).flatten() * 0.3) | |
| res = main_df.copy() | |
| res['similarity_score'] = scores | |
| top = res.sort_values('similarity_score', ascending=False).head(10) | |
| html = "" | |
| seen = set() | |
| for idx, row in top.iterrows(): | |
| u_name = row['user_name'] if row['user_name'] != "N/A" else random.choice(NAMES) | |
| if u_name not in seen: | |
| pct = f"{min(99.0, 85 + (row['similarity_score'] * 15)):.1f}%" | |
| img = find_dish_image(idx) | |
| html += f""" | |
| <div style="border: 1px solid #C4A484; padding: 25px; margin-bottom: 25px; background: #FFF9F5; border-left: 10px solid #3E2723; display: flex; gap: 20px; color: #3E2723;"> | |
| <img src="{img}" style="width: 200px; height: 200px; object-fit: cover; border: 1px solid #D2B48C; background: white;"> | |
| <div style="flex: 1;"> | |
| <div style="display: flex; justify-content: space-between;"> | |
| <h2 style="margin: 0; font-family: Serif;">{row['dish_name']}</h2> | |
| <span style="background: #3E2723; color: white; padding: 2px 10px; border-radius: 20px; font-size: 0.8em;">{pct} MATCH</span> | |
| </div> | |
| <p style="margin: 5px 0; font-weight: bold;">📍 {row['restaurant_name']} | {row.get('cuisine_type', 'Gourmet')}</p> | |
| <p style="font-style: italic;">"{row['taste_review']}"</p> | |
| <div style="margin: 10px 0;"> | |
| <span style="background: #EEDDCC; padding: 4px 8px; border: 1px solid #D2B48C; font-size: 0.8em;">💰 {row.get('price_range', '$$')}</span> | |
| <span style="background: #EEDDCC; padding: 4px 8px; border: 1px solid #D2B48C; font-size: 0.8em;">👥 BEST FOR: {row.get('best_for', 'Friends')}</span> | |
| </div> | |
| <p style="font-size: 0.85em;"><b>Twin:</b> {u_name} from {row['user_origin']}</p> | |
| </div> | |
| </div> | |
| """ | |
| seen.add(u_name) | |
| if len(seen) == 3: break | |
| return html | |
| # --- 4. הממשק (זרימת מסכים אמיתית) --- | |
| custom_css = """ | |
| @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,700;1,400&display=swap'); | |
| .gradio-container { background-color: #FDFCF8 !important; } | |
| button.primary { background: #3E2723 !important; color: white !important; border-radius: 0px !important; text-transform: uppercase; letter-spacing: 2px; } | |
| """ | |
| with gr.Blocks(css=custom_css) as demo: | |
| 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>") | |
| # מסך 1: DNA | |
| with gr.Column(visible=True) as screen_dna: | |
| with gr.Row(): | |
| u_n = gr.Textbox(label="IDENTIFICATION") | |
| u_o = gr.Dropdown(list(main_df['user_origin'].unique()), label="ORIGIN", value="Tel Aviv") | |
| with gr.Row(): | |
| u_h = gr.Dropdown(list(main_df['user_hobbies'].unique()), label="INTERESTS") | |
| u_s = gr.Dropdown(list(main_df['user_fashion_style'].unique()) + ["Vintage/Retro"], label="STYLE", value="Vintage/Retro") | |
| btn_sync = gr.Button("SYNC PERSONALITY", variant="primary") | |
| # מסך 2: Discovery | |
| with gr.Column(visible=False) as screen_discovery: | |
| q_in = gr.Textbox(label="CRAVING", placeholder="What are you in the mood for?") | |
| btn_search = gr.Button("COMMENCE SEARCH", variant="primary") | |
| out_html = gr.HTML() | |
| with gr.Row(): | |
| btn_back_to_dna = gr.Button("⬅ BACK TO DNA") | |
| btn_to_archive = gr.Button("✚ ADD DISCOVERY") | |
| # מסך 3: Archive | |
| with gr.Column(visible=False) as screen_archive: | |
| gr.Markdown("### ARCHIVE A NEW CULINARY DISCOVERY") | |
| with gr.Row(): | |
| gr.Textbox(label="DISH") | |
| gr.Textbox(label="ESTABLISHMENT") | |
| gr.Textbox(label="REVIEW", lines=3) | |
| btn_submit = gr.Button("SUBMIT & SHARE", variant="primary") | |
| status_msg = gr.Markdown() | |
| btn_back_to_disc = gr.Button("⬅ BACK TO DISCOVERY") | |
| # פונקציות מעבר בין מסכים | |
| def show_discovery(): return {screen_dna: gr.update(visible=False), screen_discovery: gr.update(visible=True), screen_archive: gr.update(visible=False)} | |
| def show_dna(): return {screen_dna: gr.update(visible=True), screen_discovery: gr.update(visible=False), screen_archive: gr.update(visible=False)} | |
| def show_archive(): return {screen_dna: gr.update(visible=False), screen_discovery: gr.update(visible=False), screen_archive: gr.update(visible=True)} | |
| btn_sync.click(show_discovery, None, [screen_dna, screen_discovery, screen_archive]) | |
| btn_back_to_dna.click(show_dna, None, [screen_dna, screen_discovery, screen_archive]) | |
| btn_to_archive.click(show_archive, None, [screen_dna, screen_discovery, screen_archive]) | |
| btn_back_to_disc.click(show_discovery, None, [screen_dna, screen_discovery, screen_archive]) | |
| btn_search.click(run_discovery, [q_in, u_o, u_h, u_s], out_html) | |
| btn_submit.click(lambda: "### Thank you! \nYour discovery has been successfully shared.", None, status_msg) | |
| demo.launch(allowed_paths=["."]) |