Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -48,13 +48,13 @@ def init_db():
|
|
| 48 |
init_db()
|
| 49 |
|
| 50 |
# ============================================================================
|
| 51 |
-
# PARSER
|
| 52 |
# ============================================================================
|
| 53 |
def parse_nutrition_text(text):
|
| 54 |
result = {}
|
| 55 |
key_variants = {
|
| 56 |
-
'calories': ['calories', 'kcal'],
|
| 57 |
-
'protein': ['protein'],
|
| 58 |
'fat': ['fat'],
|
| 59 |
'saturated_fat': ['saturated fat', 'sat fat', 'saturated'],
|
| 60 |
'carbs': ['carbs', 'carbohydrates'],
|
|
@@ -101,7 +101,7 @@ def parse_nutrition_text(text):
|
|
| 101 |
val_str = numbers[-1]
|
| 102 |
pos = line.rfind(val_str)
|
| 103 |
label = line[:pos].strip().lower()
|
| 104 |
-
label = re.sub(r'\s*\([^)]*\)', '', label).strip()
|
| 105 |
|
| 106 |
all_keys = []
|
| 107 |
for k, vs in key_variants.items():
|
|
@@ -118,7 +118,7 @@ def parse_nutrition_text(text):
|
|
| 118 |
return result
|
| 119 |
|
| 120 |
# ============================================================================
|
| 121 |
-
#
|
| 122 |
# ============================================================================
|
| 123 |
def get_db():
|
| 124 |
conn = sqlite3.connect("health_tracker.db")
|
|
@@ -154,13 +154,13 @@ def delete_food(fid):
|
|
| 154 |
conn.close()
|
| 155 |
return "Deleted"
|
| 156 |
|
| 157 |
-
def log_meal(
|
| 158 |
conn = get_db()
|
| 159 |
c = conn.cursor()
|
| 160 |
cols = ', '.join(nutrition.keys())
|
| 161 |
vals = ', '.join(['?'] * len(nutrition))
|
| 162 |
c.execute(f"INSERT INTO meals (date, time, food_name, portion, {cols}) VALUES (?, ?, ?, ?, {vals})",
|
| 163 |
-
(
|
| 164 |
conn.commit()
|
| 165 |
conn.close()
|
| 166 |
return "Meal logged"
|
|
@@ -173,19 +173,19 @@ def get_meals(start_date, end_date):
|
|
| 173 |
return df
|
| 174 |
|
| 175 |
# ============================================================================
|
| 176 |
-
# GRADIO
|
| 177 |
# ============================================================================
|
| 178 |
def parse_and_show(text):
|
| 179 |
parsed = parse_nutrition_text(text)
|
| 180 |
-
return parsed, "
|
| 181 |
|
| 182 |
def save_food(name, parsed):
|
| 183 |
if not name.strip():
|
| 184 |
return "Enter food name", None
|
| 185 |
if not parsed:
|
| 186 |
-
return "No data
|
| 187 |
msg = add_food_to_library(name.strip(), parsed)
|
| 188 |
-
return msg, None
|
| 189 |
|
| 190 |
def refresh_foods():
|
| 191 |
df = get_all_foods()
|
|
@@ -194,88 +194,98 @@ def refresh_foods():
|
|
| 194 |
choices = [f"{row['id']} - {row['food_name']}" for _, row in df.iterrows()]
|
| 195 |
return df.to_string(index=False), gr.update(choices=choices)
|
| 196 |
|
| 197 |
-
def
|
| 198 |
if not selected:
|
| 199 |
-
return "Select food
|
| 200 |
fid = int(selected.split(" - ")[0])
|
| 201 |
msg = delete_food(fid)
|
| 202 |
-
_,
|
| 203 |
-
return msg,
|
| 204 |
|
| 205 |
-
def
|
| 206 |
if not food_choice:
|
| 207 |
-
return "Select food
|
| 208 |
fid = int(food_choice.split(" - ")[0])
|
| 209 |
foods = get_all_foods()
|
|
|
|
|
|
|
| 210 |
food_row = foods[foods['id'] == fid].iloc[0].to_dict()
|
| 211 |
|
| 212 |
nutrition = {}
|
| 213 |
-
for k in
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
if k == 'calories':
|
| 215 |
-
nutrition[k] = str(float(
|
| 216 |
else:
|
| 217 |
-
nutrition[k] =
|
| 218 |
|
| 219 |
-
|
|
|
|
|
|
|
|
|
|
| 220 |
return msg
|
| 221 |
|
| 222 |
-
def
|
| 223 |
today = datetime.now().strftime("%Y-%m-%d")
|
| 224 |
df = get_meals(today, today)
|
| 225 |
if df.empty:
|
| 226 |
return "No meals today"
|
| 227 |
-
|
|
|
|
| 228 |
pro = df['protein'].sum()
|
| 229 |
fat = df['fat'].sum()
|
| 230 |
carb = df['carbs'].sum()
|
| 231 |
-
summary = f"Today: {cals:.0f} kcal |
|
| 232 |
-
return summary +
|
| 233 |
|
| 234 |
-
with gr.Blocks(title="
|
| 235 |
-
gr.Markdown("# 🍛
|
| 236 |
|
| 237 |
with gr.Tabs():
|
| 238 |
-
with gr.Tab("Add Food
|
| 239 |
with gr.Row():
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
parsed_output = gr.JSON(label="Parsed Values")
|
| 248 |
-
status = gr.Textbox(label="Status")
|
| 249 |
-
|
| 250 |
-
parse_btn.click(parse_and_show, inputs=paste_box, outputs=[parsed_output, status])
|
| 251 |
-
save_btn.click(save_food, inputs=[food_name, parsed_output], outputs=[status, parsed_output])
|
| 252 |
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
food_choices = [f"{row['id']} - {row['food_name']}" for _, row in foods_df.iterrows()] if not foods_df.empty else []
|
| 256 |
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
|
|
|
| 263 |
|
| 264 |
-
log_btn.click(
|
| 265 |
|
| 266 |
with gr.Tab("Today's Log"):
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
|
| 271 |
with gr.Tab("Food Library"):
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
delete_btn = gr.Button("
|
| 275 |
lib_status = gr.Textbox(label="Status")
|
| 276 |
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
delete_btn.click(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
|
| 281 |
-
app.launch(server_name="0.0.0.0", server_port=7860,
|
|
|
|
| 48 |
init_db()
|
| 49 |
|
| 50 |
# ============================================================================
|
| 51 |
+
# PARSER
|
| 52 |
# ============================================================================
|
| 53 |
def parse_nutrition_text(text):
|
| 54 |
result = {}
|
| 55 |
key_variants = {
|
| 56 |
+
'calories': ['calories', 'kcal', 'energy'],
|
| 57 |
+
'protein': ['protein', 'prot'],
|
| 58 |
'fat': ['fat'],
|
| 59 |
'saturated_fat': ['saturated fat', 'sat fat', 'saturated'],
|
| 60 |
'carbs': ['carbs', 'carbohydrates'],
|
|
|
|
| 101 |
val_str = numbers[-1]
|
| 102 |
pos = line.rfind(val_str)
|
| 103 |
label = line[:pos].strip().lower()
|
| 104 |
+
label = re.sub(r'\s*\([^)]*\)', '', label).strip()
|
| 105 |
|
| 106 |
all_keys = []
|
| 107 |
for k, vs in key_variants.items():
|
|
|
|
| 118 |
return result
|
| 119 |
|
| 120 |
# ============================================================================
|
| 121 |
+
# DB HELPERS
|
| 122 |
# ============================================================================
|
| 123 |
def get_db():
|
| 124 |
conn = sqlite3.connect("health_tracker.db")
|
|
|
|
| 154 |
conn.close()
|
| 155 |
return "Deleted"
|
| 156 |
|
| 157 |
+
def log_meal(date_str, time_str, food_name, portion, nutrition):
|
| 158 |
conn = get_db()
|
| 159 |
c = conn.cursor()
|
| 160 |
cols = ', '.join(nutrition.keys())
|
| 161 |
vals = ', '.join(['?'] * len(nutrition))
|
| 162 |
c.execute(f"INSERT INTO meals (date, time, food_name, portion, {cols}) VALUES (?, ?, ?, ?, {vals})",
|
| 163 |
+
(date_str, time_str, food_name, portion, *nutrition.values()))
|
| 164 |
conn.commit()
|
| 165 |
conn.close()
|
| 166 |
return "Meal logged"
|
|
|
|
| 173 |
return df
|
| 174 |
|
| 175 |
# ============================================================================
|
| 176 |
+
# GRADIO APP – fixed for 4.44.0
|
| 177 |
# ============================================================================
|
| 178 |
def parse_and_show(text):
|
| 179 |
parsed = parse_nutrition_text(text)
|
| 180 |
+
return parsed, "Parsed! Now enter name and save."
|
| 181 |
|
| 182 |
def save_food(name, parsed):
|
| 183 |
if not name.strip():
|
| 184 |
return "Enter food name", None
|
| 185 |
if not parsed:
|
| 186 |
+
return "No data parsed", None
|
| 187 |
msg = add_food_to_library(name.strip(), parsed)
|
| 188 |
+
return msg, None
|
| 189 |
|
| 190 |
def refresh_foods():
|
| 191 |
df = get_all_foods()
|
|
|
|
| 194 |
choices = [f"{row['id']} - {row['food_name']}" for _, row in df.iterrows()]
|
| 195 |
return df.to_string(index=False), gr.update(choices=choices)
|
| 196 |
|
| 197 |
+
def delete_selected(selected):
|
| 198 |
if not selected:
|
| 199 |
+
return "Select food"
|
| 200 |
fid = int(selected.split(" - ")[0])
|
| 201 |
msg = delete_food(fid)
|
| 202 |
+
_, choices = refresh_foods()
|
| 203 |
+
return msg, choices
|
| 204 |
|
| 205 |
+
def log_meal_fn(food_choice, qty, date_obj, time_obj):
|
| 206 |
if not food_choice:
|
| 207 |
+
return "Select food"
|
| 208 |
fid = int(food_choice.split(" - ")[0])
|
| 209 |
foods = get_all_foods()
|
| 210 |
+
if foods.empty or fid not in foods['id'].values:
|
| 211 |
+
return "Food not found"
|
| 212 |
food_row = foods[foods['id'] == fid].iloc[0].to_dict()
|
| 213 |
|
| 214 |
nutrition = {}
|
| 215 |
+
for k in ['calories', 'protein', 'fat', 'carbs', 'fiber', 'sodium', 'cholesterol', 'sugar', 'saturated_fat',
|
| 216 |
+
'vitamin_a', 'vitamin_b1', 'vitamin_b2', 'vitamin_b3', 'vitamin_b5', 'vitamin_b6', 'vitamin_b12',
|
| 217 |
+
'vitamin_c', 'vitamin_d', 'vitamin_e', 'vitamin_k', 'folate', 'calcium', 'iron', 'magnesium',
|
| 218 |
+
'phosphorus', 'potassium', 'zinc', 'copper', 'manganese', 'selenium', 'iodine', 'chromium',
|
| 219 |
+
'molybdenum', 'omega_3', 'omega_6', 'water', 'ash']:
|
| 220 |
+
val = food_row.get(k, 0)
|
| 221 |
if k == 'calories':
|
| 222 |
+
nutrition[k] = str(float(val) * qty if val else 0)
|
| 223 |
else:
|
| 224 |
+
nutrition[k] = float(val) * qty if val else 0
|
| 225 |
|
| 226 |
+
date_str = date_obj.strftime("%Y-%m-%d") if date_obj else datetime.now().strftime("%Y-%m-%d")
|
| 227 |
+
time_str = time_obj.strftime("%H:%M") if time_obj else datetime.now().strftime("%H:%M")
|
| 228 |
+
|
| 229 |
+
msg = log_meal(date_str, time_str, food_row['food_name'], f"{qty}x", nutrition)
|
| 230 |
return msg
|
| 231 |
|
| 232 |
+
def show_today():
|
| 233 |
today = datetime.now().strftime("%Y-%m-%d")
|
| 234 |
df = get_meals(today, today)
|
| 235 |
if df.empty:
|
| 236 |
return "No meals today"
|
| 237 |
+
df['calories'] = pd.to_numeric(df['calories'], errors='coerce').fillna(0)
|
| 238 |
+
cals = df['calories'].sum()
|
| 239 |
pro = df['protein'].sum()
|
| 240 |
fat = df['fat'].sum()
|
| 241 |
carb = df['carbs'].sum()
|
| 242 |
+
summary = f"Today: {cals:.0f} kcal | Protein {pro:.1f}g | Fat {fat:.1f}g | Carbs {carb:.1f}g\n\n"
|
| 243 |
+
return summary + df[['time', 'food_name', 'portion', 'calories']].to_string(index=False)
|
| 244 |
|
| 245 |
+
with gr.Blocks(title="Nutrition Tracker") as app:
|
| 246 |
+
gr.Markdown("# 🍛 Nutrition Tracker - Paste & Track")
|
| 247 |
|
| 248 |
with gr.Tabs():
|
| 249 |
+
with gr.Tab("Add Food"):
|
| 250 |
with gr.Row():
|
| 251 |
+
paste = gr.Textbox(label="Paste Nutrition Text", lines=15, placeholder="Calories (kcal) 340\nProtein (g) 13.00\n...")
|
| 252 |
+
parse_btn = gr.Button("Parse")
|
| 253 |
+
parsed_json = gr.JSON(label="Parsed Result")
|
| 254 |
+
status_parse = gr.Textbox(label="Parse Status")
|
| 255 |
+
food_name_in = gr.Textbox(label="Food Name")
|
| 256 |
+
save_btn = gr.Button("Save Food", variant="primary")
|
| 257 |
+
save_status = gr.Textbox(label="Save Status")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 258 |
|
| 259 |
+
parse_btn.click(parse_and_show, inputs=paste, outputs=[parsed_json, status_parse])
|
| 260 |
+
save_btn.click(save_food, inputs=[food_name_in, parsed_json], outputs=[save_status, parsed_json])
|
|
|
|
| 261 |
|
| 262 |
+
with gr.Tab("Log Meal"):
|
| 263 |
+
food_dropdown = gr.Dropdown(label="Select Food", choices=[])
|
| 264 |
+
qty_input = gr.Number(label="Quantity", value=1.0, minimum=0.1)
|
| 265 |
+
date_input = gr.DatePicker(label="Date")
|
| 266 |
+
time_input = gr.TimePicker(label="Time")
|
| 267 |
+
log_btn = gr.Button("Log Meal")
|
| 268 |
+
log_status_out = gr.Textbox(label="Log Status")
|
| 269 |
|
| 270 |
+
log_btn.click(log_meal_fn, inputs=[food_dropdown, qty_input, date_input, time_input], outputs=log_status_out)
|
| 271 |
|
| 272 |
with gr.Tab("Today's Log"):
|
| 273 |
+
today_text = gr.Textbox(label="Today's Summary", lines=12, interactive=False)
|
| 274 |
+
refresh_today_btn = gr.Button("Refresh")
|
| 275 |
+
refresh_today_btn.click(show_today, outputs=today_text)
|
| 276 |
|
| 277 |
with gr.Tab("Food Library"):
|
| 278 |
+
foods_text = gr.Textbox(label="Foods List", lines=15, interactive=False)
|
| 279 |
+
delete_dropdown = gr.Dropdown(label="Delete Food", choices=[])
|
| 280 |
+
delete_btn = gr.Button("Delete", variant="stop")
|
| 281 |
lib_status = gr.Textbox(label="Status")
|
| 282 |
|
| 283 |
+
refresh_lib_btn = gr.Button("Refresh Library")
|
| 284 |
+
refresh_lib_btn.click(refresh_foods, outputs=[foods_text, delete_dropdown])
|
| 285 |
+
delete_btn.click(delete_selected, inputs=delete_dropdown, outputs=[lib_status, delete_dropdown])
|
| 286 |
+
|
| 287 |
+
# Initial load
|
| 288 |
+
app.load(refresh_foods, outputs=[foods_text, delete_dropdown])
|
| 289 |
+
app.load(refresh_foods, outputs=[food_dropdown]) # for log tab
|
| 290 |
|
| 291 |
+
app.launch(server_name="0.0.0.0", server_port=7860, theme=gr.themes.Soft())
|