Liori25 commited on
Commit
a14d91c
·
verified ·
1 Parent(s): 8253086

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +154 -134
app.py CHANGED
@@ -7,14 +7,13 @@ import random
7
  import base64
8
  from huggingface_hub import InferenceClient
9
  from sklearn.metrics.pairwise import cosine_similarity
10
- from IO_pipeline import RecipeDigitalizerPipeline
11
 
12
  # ==========================================
13
- # 1. SETUP & DATA LOADING
14
  # ==========================================
15
  hf_token = os.getenv("HF_TOKEN")
16
  API_MODEL = "BAAI/bge-small-en-v1.5"
17
-
18
  client = InferenceClient(token=hf_token) if hf_token else None
19
 
20
  print("⏳ Loading Data...")
@@ -40,16 +39,17 @@ except Exception as e:
40
  # ==========================================
41
  def image_to_base64(image_path):
42
  if not os.path.exists(image_path):
43
- return ""
 
44
  with open(image_path, "rb") as img_file:
45
  return base64.b64encode(img_file.read()).decode('utf-8')
46
 
47
- # Load Images (Ensure filenames match exactly what is in your Files tab)
48
  logo_b64 = image_to_base64("logo.jpg")
49
  profile_b64 = image_to_base64("232px-Tv_the_muppet_show_bein_green.jpg")
50
 
51
  # ==========================================
52
- # 3. BACKEND LOGIC
53
  # ==========================================
54
  def get_embedding_via_api(text):
55
  if not client: raise ValueError("HF_TOKEN missing")
@@ -92,197 +92,217 @@ def magic_pipeline(image_path):
92
  return f"Error: {e}", f"Error: {e}"
93
 
94
  # ==========================================
95
- # 4. CSS STYLING
96
  # ==========================================
97
- fb_css = """
98
- body {background-color: #f0f2f5 !important; font-family: Helvetica, Arial, sans-serif;}
99
 
100
- /* --- HEADER --- */
101
- .fb-header {
102
- background-color: #ffffff;
103
- color: #1877f2;
104
- padding: 15px 20px;
105
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
106
- border-bottom: 1px solid #ddd;
107
- margin-bottom: 20px;
108
- display: flex;
109
- align-items: center;
110
- }
111
 
112
- .logo-img {
113
- height: 120px;
114
- width: 120px;
115
- object-fit: cover;
116
- margin-right: 20px;
117
- border-radius: 8px;
118
- border: 1px solid #eee;
119
  }
120
 
121
- .header-text-col {
 
 
 
 
 
122
  display: flex;
123
- flex-direction: column;
124
- justify-content: center;
 
 
 
 
125
  }
126
 
127
- .app-title { font-size: 32px; font-weight: 900; line-height: 1.1; }
128
- .app-slogan { font-size: 18px; font-weight: normal; color: #606770; }
 
129
 
130
- /* --- SIDEBAR NAV --- */
131
- .sidebar-btn {
132
- background-color: transparent !important;
133
- color: #050505 !important;
134
  text-align: left !important;
135
  justify-content: flex-start !important;
136
- display: flex !important;
137
- align-items: center !important;
138
  border: none !important;
 
 
139
  font-weight: 600 !important;
140
- padding: 15px 10px !important;
141
  font-size: 16px !important;
142
- box-shadow: none !important;
143
- width: 100% !important;
144
- border-radius: 8px !important;
145
- border-bottom: 1px solid #e4e6eb !important;
146
- margin-bottom: 8px !important;
147
  }
148
- .sidebar-btn:hover { background-color: #e4e6eb !important; }
 
149
 
150
- /* --- IMAGES & AVATARS --- */
151
- .profile-avatar {
152
- height: 100px;
153
- width: 100px;
154
- object-fit: cover;
155
- border-radius: 50%;
156
- margin-right: 15px;
157
- border: 2px solid #fff;
158
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
159
- }
160
-
161
- .feed-avatar {
162
- width: 40px; height: 40px;
163
- background-color: #e4e6eb;
164
- border-radius: 50%;
165
- text-align: center;
166
- line-height: 40px;
167
- margin-right: 10px;
168
- }
169
-
170
- /* --- CARDS --- */
171
- .fb-card {
172
  background: white;
173
- border-radius: 8px;
174
- box-shadow: 0 1px 2px rgba(0,0,0,0.2);
 
175
  padding: 20px;
176
  margin-bottom: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  }
178
  """
179
 
180
  # ==========================================
181
- # 5. LAYOUT
182
  # ==========================================
183
- with gr.Blocks(css=fb_css, title="Legacy Kitchen") as demo:
184
 
185
  # --- HEADER ---
186
  gr.HTML(f"""
187
- <div class="fb-header">
188
- <img src="data:image/jpeg;base64,{logo_b64}" class="logo-img" alt="Logo">
189
- <div class="header-text-col">
190
- <span class="app-title">Legacy Kitchen</span>
191
- <span class="app-slogan">Your Family Cookbook</span>
192
  </div>
 
193
  </div>
194
  """)
195
 
196
  with gr.Row():
197
 
198
- # --- LEFT SIDEBAR ---
199
- with gr.Column(scale=1, min_width=250):
200
- # MY PROFILE SECTION
201
  gr.HTML(f"""
202
- <div style="display:flex; align-items:center; margin-bottom:30px; padding-left:10px;">
203
- <img src="data:image/jpeg;base64,{profile_b64}" class="profile-avatar">
204
- <b style="font-size: 18px;">My Profile</b>
205
  </div>
206
  """)
207
 
208
- nav_feed = gr.Button("📰 Feed", elem_classes=["sidebar-btn"])
209
- nav_digital = gr.Button("📸 Digitizer", elem_classes=["sidebar-btn"])
210
- nav_about = gr.Button("ℹ️ About Us", elem_classes=["sidebar-btn"])
211
-
212
- gr.Markdown("---")
213
- gr.Markdown("Privacy · Terms · Cookies")
214
 
215
- # --- RIGHT CONTENT ---
216
  with gr.Column(scale=3):
217
 
218
- # VIEW 1: FEED
219
  with gr.Group(visible=True) as feed_view:
220
- with gr.Group(elem_classes=["fb-card"]):
 
221
  with gr.Row():
222
- # Small Profile image for the post box
223
- gr.HTML(f'<img src="data:image/jpeg;base64,{profile_b64}" style="width:40px; height:40px; border-radius:50%; object-fit:cover; margin-right:10px;">')
224
- gr.Textbox(placeholder="What's cooking?", show_label=False, container=False, scale=5)
225
-
226
  if not df_recipes.empty:
227
  feed_samples = df_recipes.sample(10)
228
  for index, row in feed_samples.iterrows():
229
- with gr.Group(elem_classes=["fb-card"]):
230
- user = random.choice(["Grandma Rose", "Chef Mike", "Sarah J."])
231
- emoji = random.choice(["🥘", "🥗", "🍰"])
232
-
 
233
  gr.HTML(f"""
234
- <div style="display:flex; margin-bottom:10px;">
235
  <div class="feed-avatar">{emoji}</div>
236
- <div><b>{user}</b><br><span style="color:gray; font-size:12px;">2h · 🌍</span></div>
 
 
 
237
  </div>
238
  """)
239
 
240
- gr.Markdown(f"**{row['Title']}**\n\n{str(row['Raw_Output'])[:200]}... [Read More]")
 
 
 
241
 
 
242
  with gr.Row():
243
- like_btn = gr.Button("👍 Like", size="sm")
244
- gr.Button("💬 Comment", size="sm")
245
- gr.Button("↗️ Share", size="sm")
246
-
247
- def toggle_like(x): return "👍 Liked!"
248
- like_btn.click(toggle_like, like_btn, like_btn)
249
  else:
250
- gr.Markdown("Database empty.")
251
 
252
- # VIEW 2: DIGITALIZER
253
  with gr.Group(visible=False) as digitalizer_view:
254
- gr.Markdown("### 📸 Digitize Recipe", elem_classes=["fb-card"])
255
 
256
- with gr.Group(elem_classes=["fb-card"]):
257
- with gr.Row():
258
- with gr.Column():
259
- input_img = gr.Image(type="filepath", label="Upload Photo")
260
- magic_btn = gr.Button("✨ Digitize", variant="primary")
261
- with gr.Column():
262
- out_text = gr.Textbox(label="Text", lines=10)
263
- out_sim = gr.Textbox(label="Similar", lines=10)
264
 
265
- magic_btn.click(magic_pipeline, input_img, [out_text, out_sim])
266
-
267
- # VIEW 3: ABOUT US (Fixed string formatting)
 
 
 
 
268
  with gr.Group(visible=False) as about_view:
269
- gr.Markdown("### ℹ️ About Us", elem_classes=["fb-card"])
270
- with gr.Group(elem_classes=["fb-card"]):
271
  gr.Markdown("""
272
- **Legacy Kitchen** is a project dedicated to preserving culinary history.
273
 
274
- We believe that every handwritten recipe tells a story. Our AI-powered platform helps you:
275
- 1. **Digitize** old handwritten notes into clear text.
276
- 2. **Discover** similar dishes from a database of 10,000+ recipes.
277
- 3. **Connect** with a community of food lovers.
278
 
279
- *Preserving the Past, Cooking for the Future.*
 
 
 
 
 
280
  """)
281
 
282
- # Nav Logic
283
- nav_feed.click(lambda: (gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)), None, [feed_view, digitalizer_view, about_view])
284
- nav_digital.click(lambda: (gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)), None, [feed_view, digitalizer_view, about_view])
285
- nav_about.click(lambda: (gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)), None, [feed_view, digitalizer_view, about_view])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
 
287
  if __name__ == "__main__":
288
  demo.launch()
 
7
  import base64
8
  from huggingface_hub import InferenceClient
9
  from sklearn.metrics.pairwise import cosine_similarity
10
+ from IO_pipeline import RecipeDigitalizerPipeline
11
 
12
  # ==========================================
13
+ # 1. SETUP & DATA LOADING (Unchanged)
14
  # ==========================================
15
  hf_token = os.getenv("HF_TOKEN")
16
  API_MODEL = "BAAI/bge-small-en-v1.5"
 
17
  client = InferenceClient(token=hf_token) if hf_token else None
18
 
19
  print("⏳ Loading Data...")
 
39
  # ==========================================
40
  def image_to_base64(image_path):
41
  if not os.path.exists(image_path):
42
+ # Return a transparent pixel if missing to prevent broken image icon
43
+ return "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
44
  with open(image_path, "rb") as img_file:
45
  return base64.b64encode(img_file.read()).decode('utf-8')
46
 
47
+ # Load Images
48
  logo_b64 = image_to_base64("logo.jpg")
49
  profile_b64 = image_to_base64("232px-Tv_the_muppet_show_bein_green.jpg")
50
 
51
  # ==========================================
52
+ # 3. BACKEND LOGIC (Unchanged)
53
  # ==========================================
54
  def get_embedding_via_api(text):
55
  if not client: raise ValueError("HF_TOKEN missing")
 
92
  return f"Error: {e}", f"Error: {e}"
93
 
94
  # ==========================================
95
+ # 4. MODERN UI THEME & CSS
96
  # ==========================================
 
 
97
 
98
+ # Create a modern Soft theme (Cleaner fonts, rounder corners)
99
+ theme = gr.themes.Soft(
100
+ primary_hue="indigo",
101
+ secondary_hue="blue",
102
+ neutral_hue="slate",
103
+ font=[gr.themes.GoogleFont('Inter'), 'ui-sans-serif', 'system-ui']
104
+ )
 
 
 
 
105
 
106
+ modern_css = """
107
+ /* Background and Base */
108
+ body, .gradio-container {
109
+ background-color: #f0f2f5;
 
 
 
110
  }
111
 
112
+ /* Custom Header */
113
+ .custom-header {
114
+ background: rgba(255, 255, 255, 0.9);
115
+ backdrop-filter: blur(10px);
116
+ border-bottom: 1px solid #e4e6eb;
117
+ padding: 10px 20px;
118
  display: flex;
119
+ align-items: center;
120
+ justify-content: space-between;
121
+ position: sticky;
122
+ top: 0;
123
+ z-index: 1000;
124
+ box-shadow: 0 2px 10px rgba(0,0,0,0.05);
125
  }
126
 
127
+ .logo-area { display: flex; align-items: center; gap: 15px; }
128
+ .logo-img { height: 40px; width: 40px; border-radius: 50%; }
129
+ .app-name { font-weight: 800; font-size: 24px; background: -webkit-linear-gradient(45deg, #1877f2, #6b21a8); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
130
 
131
+ /* Sidebar Navigation Buttons */
132
+ .nav-btn {
 
 
133
  text-align: left !important;
134
  justify-content: flex-start !important;
135
+ background: transparent !important;
 
136
  border: none !important;
137
+ box-shadow: none !important;
138
+ color: #65676b !important;
139
  font-weight: 600 !important;
 
140
  font-size: 16px !important;
141
+ padding: 12px 16px !important;
142
+ border-radius: 10px !important;
143
+ transition: all 0.2s ease;
 
 
144
  }
145
+ .nav-btn:hover { background-color: #e4e6eb !important; color: #050505 !important; }
146
+ .nav-btn.selected { background-color: #e7f3ff !important; color: #1877f2 !important; }
147
 
148
+ /* Cards (Feed & Tools) */
149
+ .content-card {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  background: white;
151
+ border-radius: 12px;
152
+ box-shadow: 0 1px 2px rgba(0,0,0,0.1);
153
+ border: 1px solid #ddd;
154
  padding: 20px;
155
  margin-bottom: 20px;
156
+ transition: transform 0.2s;
157
+ }
158
+ .content-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
159
+
160
+ /* Feed Specifics */
161
+ .feed-user-row { display: flex; gap: 10px; align-items: center; margin-bottom: 12px; }
162
+ .feed-avatar { width: 40px; height: 40px; background: #e4e6eb; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 20px; }
163
+ .feed-meta { display: flex; flex-direction: column; }
164
+ .feed-name { font-weight: 700; color: #050505; font-size: 15px; }
165
+ .feed-time { font-size: 12px; color: #65676b; }
166
+
167
+ /* Innovation/Digitalizer Styling */
168
+ .digitalizer-container {
169
+ background: linear-gradient(135deg, #ffffff 0%, #f0f7ff 100%);
170
+ border: 1px solid #1877f2;
171
  }
172
  """
173
 
174
  # ==========================================
175
+ # 5. LAYOUT CONSTRUCTION
176
  # ==========================================
177
+ with gr.Blocks(theme=theme, css=modern_css, title="Legacy Kitchen") as demo:
178
 
179
  # --- HEADER ---
180
  gr.HTML(f"""
181
+ <div class="custom-header">
182
+ <div class="logo-area">
183
+ <img src="data:image/jpeg;base64,{logo_b64}" class="logo-img">
184
+ <span class="app-name">Legacy Kitchen</span>
 
185
  </div>
186
+ <div style="color: #65676b; font-weight: 600;">v2.0 Beta</div>
187
  </div>
188
  """)
189
 
190
  with gr.Row():
191
 
192
+ # --- LEFT SIDEBAR (Navigation) ---
193
+ with gr.Column(scale=1, min_width=200):
194
+ # User Profile Snippet
195
  gr.HTML(f"""
196
+ <div style="display:flex; align-items:center; padding: 10px; margin-bottom: 10px;">
197
+ <img src="data:image/jpeg;base64,{profile_b64}" style="width:36px; height:36px; border-radius:50%; margin-right:10px;">
198
+ <b style="font-size: 15px;">My Profile</b>
199
  </div>
200
  """)
201
 
202
+ nav_feed = gr.Button("📰 News Feed", elem_classes=["nav-btn", "selected"])
203
+ nav_digital = gr.Button(" AI Digitizer", elem_classes=["nav-btn"])
204
+ nav_about = gr.Button("ℹ️ About", elem_classes=["nav-btn"])
 
 
 
205
 
206
+ # --- CENTER CONTENT ---
207
  with gr.Column(scale=3):
208
 
209
+ # === VIEW 1: FEED ===
210
  with gr.Group(visible=True) as feed_view:
211
+ # "What's on your mind" Box
212
+ with gr.Group(elem_classes=["content-card"]):
213
  with gr.Row():
214
+ gr.HTML(f'<img src="data:image/jpeg;base64,{profile_b64}" style="width:40px; height:40px; border-radius:50%;">')
215
+ gr.Textbox(placeholder=f"What recipe are you cooking today?", show_label=False, container=False, scale=10)
216
+
217
+ # Dynamic Feed Generation
218
  if not df_recipes.empty:
219
  feed_samples = df_recipes.sample(10)
220
  for index, row in feed_samples.iterrows():
221
+ user_name = random.choice(["Grandma Rose", "Chef Mike", "Sarah J."])
222
+ emoji = random.choice(["🥘", "🥗", "🍰", "🌮"])
223
+
224
+ with gr.Group(elem_classes=["content-card"]):
225
+ # Header of the Post
226
  gr.HTML(f"""
227
+ <div class="feed-user-row">
228
  <div class="feed-avatar">{emoji}</div>
229
+ <div class="feed-meta">
230
+ <span class="feed-name">{user_name}</span>
231
+ <span class="feed-time">2h · 🌍 Public</span>
232
+ </div>
233
  </div>
234
  """)
235
 
236
+ # Content
237
+ gr.Markdown(f"### {row['Title']}")
238
+ gr.Markdown(f"{str(row['Raw_Output'])[:250]}...")
239
+ gr.Markdown(f"*[Click to see full recipe...]*")
240
 
241
+ # Action Buttons (Facebook Style)
242
  with gr.Row():
243
+ gr.Button("👍 Like", size="sm", variant="secondary")
244
+ gr.Button("💬 Comment", size="sm", variant="secondary")
245
+ gr.Button("↗️ Share", size="sm", variant="secondary")
 
 
 
246
  else:
247
+ gr.Markdown("⚠️ Database is empty. Please check your CSV file.")
248
 
249
+ # === VIEW 2: AI DIGITALIZER ===
250
  with gr.Group(visible=False) as digitalizer_view:
251
+ gr.Markdown("## 📸 Recipe Digitalizer", elem_classes=["content-card"])
252
 
253
+ with gr.Row():
254
+ with gr.Column(elem_classes=["content-card", "digitalizer-container"]):
255
+ input_img = gr.Image(type="filepath", label="Upload Handwritten Note", height=300)
256
+ magic_btn = gr.Button("✨ Convert to Text", variant="primary", size="lg")
 
 
 
 
257
 
258
+ with gr.Column(elem_classes=["content-card"]):
259
+ out_text = gr.Textbox(label="Digitized Text", lines=6, show_copy_button=True)
260
+ out_sim = gr.Textbox(label="Similar Recipes in Database", lines=6)
261
+
262
+ magic_btn.click(magic_pipeline, input_img, [out_text, out_sim])
263
+
264
+ # === VIEW 3: ABOUT ===
265
  with gr.Group(visible=False) as about_view:
266
+ with gr.Group(elem_classes=["content-card"]):
 
267
  gr.Markdown("""
268
+ # ℹ️ About Legacy Kitchen
269
 
270
+ **Preserving Heritage through AI.**
 
 
 
271
 
272
+ Legacy Kitchen allows you to upload photos of your grandmother's handwritten recipe cards and instantly converts them into digital text using OCR and AI models.
273
+
274
+ ### Tech Stack
275
+ * **Hugging Face Spaces** for hosting
276
+ * **Gradio** for the UI
277
+ * **Transformers** for embeddings
278
  """)
279
 
280
+ # --- RIGHT COLUMN (Sponsored/Trending - Optional Facebook Feature) ---
281
+ with gr.Column(scale=1, min_width=200):
282
+ gr.Markdown("### Trending Recipes")
283
+ gr.HTML("""
284
+ <div style="background:white; padding:10px; border-radius:8px; margin-bottom:10px;">
285
+ <b>🍜 Ramen Hack</b><br>
286
+ <span style="color:gray; font-size:12px;">12k likes</span>
287
+ </div>
288
+ <div style="background:white; padding:10px; border-radius:8px;">
289
+ <b>🍪 Best Cookies</b><br>
290
+ <span style="color:gray; font-size:12px;">8k likes</span>
291
+ </div>
292
+ """)
293
+
294
+ # ==========================================
295
+ # 6. JAVASCRIPT LOGIC (Client Side)
296
+ # ==========================================
297
+ # We use simple visibility toggles for the Single Page App feel
298
+
299
+ def go_feed(): return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
300
+ def go_digi(): return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)
301
+ def go_about(): return gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)
302
+
303
+ nav_feed.click(go_feed, None, [feed_view, digitalizer_view, about_view])
304
+ nav_digital.click(go_digi, None, [feed_view, digitalizer_view, about_view])
305
+ nav_about.click(go_about, None, [feed_view, digitalizer_view, about_view])
306
 
307
  if __name__ == "__main__":
308
  demo.launch()