tdurzynski commited on
Commit
ca3db9f
Β·
verified Β·
1 Parent(s): 7c6ffbd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +134 -62
app.py CHANGED
@@ -1,14 +1,17 @@
1
  import streamlit as st
2
  import json
3
  import os
 
 
 
 
 
 
4
  import boto3
5
  from PIL import Image
6
  import firebase_admin
7
  from firebase_admin import credentials, auth
8
  import pandas as pd
9
- import uuid
10
- from datetime import datetime
11
- from io import BytesIO
12
  import streamlit_tags as st_tags
13
  from dotenv import load_dotenv
14
 
@@ -164,13 +167,17 @@ def save_metadata(user_id, s3_path, food_name, portion_size, portion_unit, cooki
164
  image_id = str(uuid.uuid4())
165
  timestamp = datetime.now().isoformat()
166
 
 
 
 
 
167
  # Create item for DynamoDB
168
  item = {
169
  'image_id': image_id,
170
  'user_id': user_id,
171
  'upload_timestamp': timestamp,
172
  'food_name': food_name,
173
- 'portion_size': portion_size,
174
  'portion_unit': portion_unit,
175
  'cooking_method': cooking_method,
176
  'ingredients': ingredients,
@@ -207,6 +214,10 @@ if "tokens" not in st.session_state:
207
  if "uploads_count" not in st.session_state:
208
  st.session_state["uploads_count"] = 0
209
 
 
 
 
 
210
  # Streamlit Layout - Authentication Section
211
  st.sidebar.title("πŸ”‘ User Authentication")
212
  auth_option = st.sidebar.radio("Select an option", ["Login", "Sign Up", "Logout"])
@@ -252,22 +263,31 @@ if "user_id" not in st.session_state and not st.session_state.get("demo_mode", F
252
  # Add links to guidelines and terms
253
  st.markdown("### πŸ“š While You're Here")
254
  st.markdown("Take a moment to read our guidelines and token system:")
255
- col1, col2, col3 = st.columns(3)
256
- with col1:
257
- if st.button("Participation Guidelines"):
 
258
  with open("PARTICIPATION_GUIDELINES.md", "r") as f:
259
  guidelines = f.read()
260
- st.markdown(guidelines)
261
- with col2:
262
- if st.button("Token Rewards"):
 
 
 
263
  with open("TOKEN_REWARDS.md", "r") as f:
264
  rewards = f.read()
265
- st.markdown(rewards)
266
- with col3:
267
- if st.button("Terms of Service"):
 
 
 
268
  with open("TERMS_OF_SERVICE.md", "r") as f:
269
  terms = f.read()
270
- st.markdown(terms)
 
 
271
 
272
  st.stop()
273
 
@@ -296,7 +316,7 @@ if uploaded_file:
296
  if "original_image" in st.session_state:
297
  original_img = st.session_state["original_image"]
298
 
299
- # Process the image - resize and compress
300
  processed_img = resize_image(original_img, max_size=512, quality=85)
301
  st.session_state["processed_image"] = processed_img
302
 
@@ -305,21 +325,36 @@ if "original_image" in st.session_state:
305
  processed_size = get_image_size_kb(processed_img)
306
  size_reduction = ((original_size - processed_size) / original_size) * 100 if original_size > 0 else 0
307
 
308
- # Display images side by side
309
  col1, col2 = st.columns(2)
310
  with col1:
311
  st.subheader("πŸ“· Original Image")
 
312
  st.image(original_img, caption=f"Original ({original_img.width}x{original_img.height} px, {original_size:.1f} KB)", use_container_width=True)
 
313
  with col2:
314
  st.subheader("πŸ–ΌοΈ Processed Image")
 
315
  st.image(processed_img, caption=f"Processed ({processed_img.width}x{processed_img.height} px, {processed_size:.1f} KB)", use_container_width=True)
 
316
 
317
  # Show size reduction
318
  if size_reduction > 5: # Only show if there's a meaningful reduction
319
  st.success(f"βœ… Image size reduced by {size_reduction:.1f}% for faster uploads and processing")
320
 
 
 
 
 
 
 
 
 
 
 
 
321
  # Food metadata form
322
- st.subheader("🍲 Food Details")
323
 
324
  food_name = st.selectbox("Food Name", options=[""] + FOOD_SUGGESTIONS, index=0)
325
  if food_name == "":
@@ -327,7 +362,7 @@ if "original_image" in st.session_state:
327
 
328
  col1, col2 = st.columns(2)
329
  with col1:
330
- portion_size = st.number_input("Portion Size", min_value=0.1, step=0.1)
331
  with col2:
332
  portion_unit = st.selectbox("Unit", options=UNIT_OPTIONS)
333
 
@@ -341,50 +376,87 @@ if "original_image" in st.session_state:
341
  maxtags=5
342
  )
343
 
344
- # Submit button
345
- if st.button("πŸ“€ Submit Food Image"):
346
- with st.spinner("Processing your submission..."):
347
- # Determine image quality (simplified version)
348
- image_quality = "high" if original_img.width >= 1000 and original_img.height >= 1000 else "standard"
349
-
350
- # Check if metadata is complete
351
- has_metadata = bool(food_name and portion_size and portion_unit and cooking_method)
352
-
353
- # Check if the food is in a unique category (simplified)
354
- is_unique_category = food_name not in ["Pizza", "Burger", "Pasta", "Salad"]
355
-
356
- # Calculate tokens
357
- tokens_awarded = calculate_tokens(image_quality, has_metadata, is_unique_category)
358
-
359
- # Upload image to S3
360
- s3_path = upload_to_s3(processed_img, st.session_state["user_id"])
361
-
362
- if s3_path:
363
- # Save metadata to DynamoDB
364
- success = save_metadata(
365
- st.session_state["user_id"],
366
- s3_path,
367
- food_name,
368
- float(portion_size),
369
- portion_unit,
370
- cooking_method,
371
- ingredients,
372
- tokens_awarded
373
- )
374
-
375
- if success:
376
- st.session_state["tokens"] += tokens_awarded
377
- st.session_state["uploads_count"] += 1
378
- st.success(f"βœ… Food image uploaded successfully! You earned {tokens_awarded} tokens.")
379
-
380
- # Clear the form and image for a new submission
381
- st.session_state.pop("original_image", None)
382
- st.session_state.pop("processed_image", None)
383
- st.experimental_rerun()
384
- else:
385
- st.error("Failed to save metadata. Please try again.")
386
  else:
387
- st.error("Failed to upload image. Please try again.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
 
389
  # Display earned tokens
390
  st.sidebar.markdown("---")
@@ -408,4 +480,4 @@ if st.sidebar.button("Token Rewards System"):
408
  if st.sidebar.button("Terms of Service"):
409
  with open("TERMS_OF_SERVICE.md", "r") as f:
410
  terms = f.read()
411
- st.sidebar.markdown(terms)
 
1
  import streamlit as st
2
  import json
3
  import os
4
+ import uuid
5
+ from datetime import datetime
6
+ from io import BytesIO
7
+ from decimal import Decimal # Add this import for DynamoDB float handling
8
+
9
+ # Third-party library imports
10
  import boto3
11
  from PIL import Image
12
  import firebase_admin
13
  from firebase_admin import credentials, auth
14
  import pandas as pd
 
 
 
15
  import streamlit_tags as st_tags
16
  from dotenv import load_dotenv
17
 
 
167
  image_id = str(uuid.uuid4())
168
  timestamp = datetime.now().isoformat()
169
 
170
+ # Ensure portion_size is a Decimal (DynamoDB doesn't support float)
171
+ if not isinstance(portion_size, Decimal):
172
+ portion_size = Decimal(str(portion_size))
173
+
174
  # Create item for DynamoDB
175
  item = {
176
  'image_id': image_id,
177
  'user_id': user_id,
178
  'upload_timestamp': timestamp,
179
  'food_name': food_name,
180
+ 'portion_size': portion_size, # Decimal type
181
  'portion_unit': portion_unit,
182
  'cooking_method': cooking_method,
183
  'ingredients': ingredients,
 
214
  if "uploads_count" not in st.session_state:
215
  st.session_state["uploads_count"] = 0
216
 
217
+ # Initialize food items list for storing multiple annotations
218
+ if "food_items" not in st.session_state:
219
+ st.session_state["food_items"] = []
220
+
221
  # Streamlit Layout - Authentication Section
222
  st.sidebar.title("πŸ”‘ User Authentication")
223
  auth_option = st.sidebar.radio("Select an option", ["Login", "Sign Up", "Logout"])
 
263
  # Add links to guidelines and terms
264
  st.markdown("### πŸ“š While You're Here")
265
  st.markdown("Take a moment to read our guidelines and token system:")
266
+
267
+ # Use expanders instead of columns for better document display
268
+ with st.expander("πŸ“‹ Participation Guidelines"):
269
+ try:
270
  with open("PARTICIPATION_GUIDELINES.md", "r") as f:
271
  guidelines = f.read()
272
+ st.markdown(guidelines, unsafe_allow_html=True)
273
+ except Exception as e:
274
+ st.error(f"Could not load guidelines: {e}")
275
+
276
+ with st.expander("πŸͺ™ Token Rewards System"):
277
+ try:
278
  with open("TOKEN_REWARDS.md", "r") as f:
279
  rewards = f.read()
280
+ st.markdown(rewards, unsafe_allow_html=True)
281
+ except Exception as e:
282
+ st.error(f"Could not load rewards information: {e}")
283
+
284
+ with st.expander("πŸ“œ Terms of Service"):
285
+ try:
286
  with open("TERMS_OF_SERVICE.md", "r") as f:
287
  terms = f.read()
288
+ st.markdown(terms, unsafe_allow_html=True)
289
+ except Exception as e:
290
+ st.error(f"Could not load terms: {e}")
291
 
292
  st.stop()
293
 
 
316
  if "original_image" in st.session_state:
317
  original_img = st.session_state["original_image"]
318
 
319
+ # Process the image - resize and compress with more visible difference
320
  processed_img = resize_image(original_img, max_size=512, quality=85)
321
  st.session_state["processed_image"] = processed_img
322
 
 
325
  processed_size = get_image_size_kb(processed_img)
326
  size_reduction = ((original_size - processed_size) / original_size) * 100 if original_size > 0 else 0
327
 
328
+ # Display images side by side with border to highlight differences
329
  col1, col2 = st.columns(2)
330
  with col1:
331
  st.subheader("πŸ“· Original Image")
332
+ st.markdown(f"<div style='border:2px solid red;padding:5px;'>", unsafe_allow_html=True)
333
  st.image(original_img, caption=f"Original ({original_img.width}x{original_img.height} px, {original_size:.1f} KB)", use_container_width=True)
334
+ st.markdown("</div>", unsafe_allow_html=True)
335
  with col2:
336
  st.subheader("πŸ–ΌοΈ Processed Image")
337
+ st.markdown(f"<div style='border:2px solid green;padding:5px;'>", unsafe_allow_html=True)
338
  st.image(processed_img, caption=f"Processed ({processed_img.width}x{processed_img.height} px, {processed_size:.1f} KB)", use_container_width=True)
339
+ st.markdown("</div>", unsafe_allow_html=True)
340
 
341
  # Show size reduction
342
  if size_reduction > 5: # Only show if there's a meaningful reduction
343
  st.success(f"βœ… Image size reduced by {size_reduction:.1f}% for faster uploads and processing")
344
 
345
+ # Display existing food annotations if any
346
+ if st.session_state["food_items"]:
347
+ st.subheader("πŸ“‹ Added Food Items")
348
+ for i, item in enumerate(st.session_state["food_items"]):
349
+ with st.expander(f"🍽️ {item['food_name']} ({item['portion_size']} {item['portion_unit']})"):
350
+ st.write(f"**Cooking Method:** {item['cooking_method']}")
351
+ st.write(f"**Ingredients:** {', '.join(item['ingredients'])}")
352
+ if st.button(f"Remove Item #{i+1}", key=f"remove_{i}"):
353
+ st.session_state["food_items"].pop(i)
354
+ st.experimental_rerun()
355
+
356
  # Food metadata form
357
+ st.subheader("🍲 Add Food Details")
358
 
359
  food_name = st.selectbox("Food Name", options=[""] + FOOD_SUGGESTIONS, index=0)
360
  if food_name == "":
 
362
 
363
  col1, col2 = st.columns(2)
364
  with col1:
365
+ portion_size = st.number_input("Portion Size", min_value=0.1, step=0.1, format="%.2f")
366
  with col2:
367
  portion_unit = st.selectbox("Unit", options=UNIT_OPTIONS)
368
 
 
376
  maxtags=5
377
  )
378
 
379
+ # Add this food to the list
380
+ col1, col2 = st.columns([1, 1])
381
+
382
+ with col1:
383
+ if st.button("βž• Add This Food Item"):
384
+ if food_name and portion_size and portion_unit and cooking_method:
385
+ # Add the food item to the session state
386
+ st.session_state["food_items"].append({
387
+ "food_name": food_name,
388
+ "portion_size": portion_size,
389
+ "portion_unit": portion_unit,
390
+ "cooking_method": cooking_method,
391
+ "ingredients": ingredients
392
+ })
393
+ st.success(f"βœ… Added {food_name} to your submission")
394
+ st.experimental_rerun()
395
+ else:
396
+ st.error("❌ Please fill in all required fields")
397
+
398
+ # Submit all foods button
399
+ with col2:
400
+ if st.button("πŸ“€ Submit All Food Items"):
401
+ if not st.session_state["food_items"]:
402
+ st.error("❌ Please add at least one food item before submitting")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
403
  else:
404
+ with st.spinner("Processing your submission..."):
405
+ all_saved = True
406
+ total_tokens = 0
407
+
408
+ # Determine image quality (simplified version)
409
+ image_quality = "high" if original_img.width >= 1000 and original_img.height >= 1000 else "standard"
410
+
411
+ # Upload image to S3 once
412
+ s3_path = upload_to_s3(processed_img, st.session_state["user_id"])
413
+
414
+ if s3_path:
415
+ # Save each food item with the same image
416
+ for food_item in st.session_state["food_items"]:
417
+ # Check if metadata is complete
418
+ has_metadata = True # Already validated
419
+
420
+ # Check if the food is in a unique category (simplified)
421
+ is_unique_category = food_item["food_name"] not in ["Pizza", "Burger", "Pasta", "Salad"]
422
+
423
+ # Calculate tokens for this item
424
+ tokens_awarded = calculate_tokens(image_quality, has_metadata, is_unique_category)
425
+ total_tokens += tokens_awarded
426
+
427
+ # Convert float to Decimal for DynamoDB
428
+ portion_size_decimal = Decimal(str(food_item["portion_size"]))
429
+
430
+ # Save metadata to DynamoDB
431
+ success = save_metadata(
432
+ st.session_state["user_id"],
433
+ s3_path,
434
+ food_item["food_name"],
435
+ portion_size_decimal, # Use Decimal type
436
+ food_item["portion_unit"],
437
+ food_item["cooking_method"],
438
+ food_item["ingredients"],
439
+ tokens_awarded
440
+ )
441
+
442
+ if not success:
443
+ all_saved = False
444
+ break
445
+
446
+ if all_saved:
447
+ st.session_state["tokens"] += total_tokens
448
+ st.session_state["uploads_count"] += 1
449
+ st.success(f"βœ… All food items uploaded successfully! You earned {total_tokens} tokens.")
450
+
451
+ # Clear the form and image for a new submission
452
+ st.session_state.pop("original_image", None)
453
+ st.session_state.pop("processed_image", None)
454
+ st.session_state["food_items"] = []
455
+ st.experimental_rerun()
456
+ else:
457
+ st.error("Failed to save some items. Please try again.")
458
+ else:
459
+ st.error("Failed to upload image. Please try again.")
460
 
461
  # Display earned tokens
462
  st.sidebar.markdown("---")
 
480
  if st.sidebar.button("Terms of Service"):
481
  with open("TERMS_OF_SERVICE.md", "r") as f:
482
  terms = f.read()
483
+ st.sidebar.markdown(terms)