From da426d625ee3e5e2b4f6bd4b9c8c53dceec918f5 Mon Sep 17 00:00:00 2001 From: sstent Date: Fri, 19 Sep 2025 18:58:05 -0700 Subject: [PATCH] openfood test --- __pycache__/main.cpython-313.pyc | Bin 46875 -> 51498 bytes main.py | 152 +++++++++++++++++++++++++++---- migrate_db_schema.py | 63 +++++++++++++ requirements.txt | 3 +- templates/detailed.html | 3 + templates/imports.html | 128 ++++++++++++++++++++++++-- templates/plan.html | 28 +++++- 7 files changed, 352 insertions(+), 25 deletions(-) create mode 100644 migrate_db_schema.py diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc index 4cf116e080d52633dd1577b41b2286d82a000463..6dd5220c3b29c12ce96b84340ece3707a4fbf32d 100644 GIT binary patch delta 15084 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 LqD-*?7SaC)MCjxI delta 11253 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.0.24 #psycopg2-binary==2.9.9 python-multipart==0.0.6 -jinja2==3.1.2 \ No newline at end of file +jinja2==3.1.2 +openfoodfacts>=3.0.0 \ No newline at end of file diff --git a/templates/detailed.html b/templates/detailed.html index 873c6c1..e5d72d3 100644 --- a/templates/detailed.html +++ b/templates/detailed.html @@ -145,6 +145,9 @@
{{ meal_detail.plan.meal.name }} - {{ meal_detail.plan.meal.meal_type.title() }} + {% if meal_detail.plan.meal_time %} + ({{ meal_detail.plan.meal_time }}) + {% endif %} {{ "%.0f"|format(meal_detail.nutrition.calories) }} cal
diff --git a/templates/imports.html b/templates/imports.html index 9e4f6d8..bb5b3c6 100644 --- a/templates/imports.html +++ b/templates/imports.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% block content %}
-
+

Food Import

@@ -11,8 +11,22 @@
- -
+ +
+

OpenFoodFacts Search

+
+ + +
+ + + +
+ +

Meal Import

@@ -40,10 +54,10 @@ document.querySelectorAll('form').forEach(form => { e.preventDefault(); const submitBtn = form.querySelector('button[type="submit"]'); const resultsDiv = document.getElementById('upload-results'); - + submitBtn.disabled = true; submitBtn.innerHTML = ' Uploading...'; - + try { const formData = new FormData(form); const response = await fetch(form.action, { @@ -52,7 +66,7 @@ document.querySelectorAll('form').forEach(form => { }); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); - + const results = await response.json(); resultsDiv.style.display = 'block'; document.getElementById('created-count').textContent = results.created || 0; @@ -79,5 +93,107 @@ document.querySelectorAll('form').forEach(form => { } }); }); + +// OpenFoodFacts search functionality +async function searchOpenFoodFacts() { + const query = document.getElementById('offSearch').value.trim(); + if (!query) { + alert('Please enter a search term'); + return; + } + + const resultsDiv = document.getElementById('offResults'); + const resultsList = document.getElementById('offResultsList'); + + // Show loading + resultsDiv.style.display = 'block'; + resultsList.innerHTML = '
Searching...
'; + + try { + const response = await fetch(`/foods/search_openfoodfacts?query=${encodeURIComponent(query)}`); + const data = await response.json(); + + if (data.status === 'success') { + displayOpenFoodFactsResults(data.results); + } else { + resultsList.innerHTML = `
Error: ${data.message}
`; + } + } catch (error) { + resultsList.innerHTML = `
Error: ${error.message}
`; + } +} + +function displayOpenFoodFactsResults(results) { + const resultsList = document.getElementById('offResultsList'); + + if (results.length === 0) { + resultsList.innerHTML = '
No results found
'; + return; + } + + let html = ''; + results.forEach((food, index) => { + html += ` +
+
+
+
${food.name}
+

+ ${food.serving_size}${food.serving_unit} | + ${food.calories} cal | + P: ${food.protein}g, C: ${food.carbs}g, F: ${food.fat}g +

+ ${food.brand ? `Brand: ${food.brand}` : ''} +
+ +
+
+ `; + }); + + resultsList.innerHTML = html; + + // Store results for later use + window.offSearchResults = results; +} + +async function addOpenFoodFactsFood(index) { + const food = window.offSearchResults[index]; + if (!food) return; + + try { + const formData = new FormData(); + Object.keys(food).forEach(key => { + if (key !== 'image_url' && key !== 'openfoodfacts_id' && key !== 'brand') { + formData.append(key, food[key]); + } + }); + + const response = await fetch('/foods/add_openfoodfacts', { + method: 'POST', + body: formData + }); + + const result = await response.json(); + + if (result.status === 'success') { + alert('Food added successfully!'); + // Optionally reload the page or update UI + } else { + alert('Error adding food: ' + result.message); + } + } catch (error) { + alert('Error adding food: ' + error.message); + } +} + +// Allow Enter key to trigger search +document.getElementById('offSearch').addEventListener('keypress', function(e) { + if (e.key === 'Enter') { + searchOpenFoodFacts(); + } +}); {% endblock %} \ No newline at end of file diff --git a/templates/plan.html b/templates/plan.html index 4de5f36..c4b4bbb 100644 --- a/templates/plan.html +++ b/templates/plan.html @@ -43,7 +43,9 @@ {% for plan in plans[day.date.isoformat()] %} - {{ plan.meal.name }} + + {{ plan.meal_time }}: {{ plan.meal.name }} + {% endfor %} {% if not plans[day.date.isoformat()] %} No meals @@ -89,6 +91,18 @@ {% endfor %}
+
+ + +
+
+ + +