From 29bcc304b7c55cbdf5d8166d213bdc205e0f12f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Thu, 10 Jul 2025 17:23:18 +0200 Subject: [PATCH] zmiany w produktach i sugestoach / panel --- add_products.py | 92 ++++++++++++++++++++++++------ app.py | 5 +- static/js/product_suggestion.js | 41 ++----------- templates/admin/list_products.html | 44 ++++++++++++-- 4 files changed, 123 insertions(+), 59 deletions(-) diff --git a/add_products.py b/add_products.py index 5b23ad3..379025d 100644 --- a/add_products.py +++ b/add_products.py @@ -1,31 +1,87 @@ -# add_products_from_openfoodfacts.py -# Uruchom w katalogu z app.py - import urllib.request import json from app import db, SuggestedProduct, app -# URL do pobrania polskich produktów (tylko nazwy) -url = 'https://pl.openfoodfacts.org/cgi/search.pl?search_simple=1&action=process&json=1&fields=product_name&page_size=1000' - -with urllib.request.urlopen(url) as response: - data = json.loads(response.read()) +CATEGORIES = { + "Przyprawa": [ + "przyprawa", "pieprz", "sól", "bazylia", "oregano", "papryka", "majeranek", "czosnek", + "tymianek", "rozmaryn", "kolendra", "curry", "imbir", "goździki", "chili", "koper", + "kminek", "liść laurowy", "ziele angielskie", "kurkuma", "musztarda", "chrzan" + ], + "Mięso": [ + "kurczak", "piersi z kurczaka", "udka z kurczaka", "wołowina", "mielona wołowina", + "wieprzowina", "schab", "łopatka", "szynka", "boczek", "indyk", "filet z indyka", + "gulasz", "pasztet", "karkówka", "żeberka", "kiełbasa", "parówki", "salami", "kabanos" + ], + "Ryba i owoce morza": [ + "łosoś", "dorsz", "mintaj", "makrela", "pstrąg", "karp", "śledź", "tuńczyk", + "morszczuk", "sardynka", "szproty", "anchois", "tilapia", "sandacz", "halibut", + "sum", "flądra", "ostrobok", "paluszki rybne", "konserwa rybna" + ], + "Nabiał": [ + "mleko", "jogurt", "ser żółty", "ser biały", "twaróg", "śmietana", "masło", + "kefir", "maślanka", "serek wiejski", "serek topiony", "mozzarella", "feta", + "parmezan", "gouda", "emmental", "ser pleśniowy", "ser homogenizowany", + "serek mascarpone", "ser ricotta" + ], + "Warzywo": [ + "pomidor", "ogórek", "marchew", "cebula", "sałata", "papryka", "ziemniak", + "kapusta", "brokuł", "kalafior", "cukinia", "bakłażan", "szpinak", "rukola", + "seler", "por", "burak", "dynia", "rzodkiewka", "fasola" + ], + "Owoc": [ + "jabłko", "banan", "gruszka", "truskawka", "winogrono", "malina", "borówka", + "czereśnia", "wiśnia", "brzoskwinia", "nektaryna", "śliwka", "ananas", + "mango", "kiwi", "cytryna", "limonka", "pomarańcza", "mandarynka", "grejpfrut" + ], + "Pieczywo i zboża": [ + "chleb", "bułka", "bagietka", "kajzerka", "pumpernikiel", "chleb razowy", + "chleb żytni", "tost", "grahamka", "croissant", "tortilla", "pizza", + "pierogi", "ryż", "makaron", "kasza jaglana", "kasza gryczana", "owsianka", + "płatki kukurydziane", "musli" + ], + "Słodycze i przekąski": [ + "czekolada", "baton", "ciastko", "wafel", "lody", "cukierek", "żelki", + "herbatnik", "paluszki", "chipsy", "orzeszki", "popcorn", "krakersy", + "ciasto", "muffin", "pączek", "drożdżówka", "babeczka", "piernik", "beza" + ], + "Napoje": [ + "woda", "sok jabłkowy", "sok pomarańczowy", "sok multiwitamina", "cola", + "pepsi", "napój gazowany", "kawa", "herbata", "piwo", "wino czerwone", + "wino białe", "tonik", "lemoniada", "napój izotoniczny", "kompot", + "napój mleczny", "maślanka pitna", "koktajl owocowy", "nektar" + ], + "Tłuszcze i oleje": [ + "oliwa", "olej rzepakowy", "olej słonecznikowy", "masło klarowane", + "margaryna", "smalec", "masło orzechowe", "tłuszcz kokosowy", + "olej lniany", "olej z pestek winogron", "olej sezamowy", + "olej ryżowy", "olej z awokado", "olej kukurydziany", "olej arachidowy", + "olej palmowy", "olej konopny", "olej sojowy", "olej dyniowy", "olej z orzechów włoskich" + ], + "Dania gotowe": [ + "pizza", "hamburger", "hot dog", "zupa", "gulasz", "pierogi ruskie", + "pierogi z mięsem", "lasagne", "sałatka warzywna", "kanapka", + "wrap", "tortilla", "zapiekanka", "sushi", "falafel", "kebab", + "pyzy", "kluski śląskie", "kotlet schabowy", "gołąbki" + ] +} produkty = [] -for prod in data.get('products', []): - name = prod.get('product_name', '').strip() - if name: - produkty.append(name) -print(f"Znaleziono {len(produkty)} produktów do dodania.") +for category, names in CATEGORIES.items(): + for name in names: + produkty.append((category, name.lower().strip())) + +print(f"Przygotowano {len(produkty)} produktów do dodania.") -dodane = 0 with app.app_context(): - for name in produkty: - if not SuggestedProduct.query.filter_by(name=name).first(): - prod = SuggestedProduct(name=name) + dodane = 0 + for category, name in produkty: + full_name = f"{category}: {name}" + if not SuggestedProduct.query.filter_by(name=full_name).first(): + prod = SuggestedProduct(name=full_name) db.session.add(prod) dodane += 1 db.session.commit() -print(f'Dodano {dodane} produktów do bazy.') +print(f"Dodano {dodane} produktów do bazy.") diff --git a/app.py b/app.py index 99842d0..e6f2a7a 100644 --- a/app.py +++ b/app.py @@ -945,8 +945,8 @@ def list_products(): users = User.query.all() users_dict = {user.id: user.username for user in users} - # Wszystkie sugestie do słownika - suggestions = SuggestedProduct.query.all() + # Stabilne sortowanie sugestii + suggestions = SuggestedProduct.query.order_by(SuggestedProduct.name.asc()).all() suggestions_dict = {s.name.lower(): s for s in suggestions} return render_template( @@ -956,6 +956,7 @@ def list_products(): suggestions_dict=suggestions_dict ) + @app.route('/admin/sync_suggestion/', methods=['POST']) @login_required def sync_suggestion_ajax(item_id): diff --git a/static/js/product_suggestion.js b/static/js/product_suggestion.js index 9c37360..b6bec5f 100644 --- a/static/js/product_suggestion.js +++ b/static/js/product_suggestion.js @@ -1,5 +1,5 @@ document.addEventListener("DOMContentLoaded", function() { - // Usuń stare eventy + // Odśwież eventy document.querySelectorAll('.sync-btn').forEach(btn => { btn.replaceWith(btn.cloneNode(true)); }); @@ -8,14 +8,12 @@ document.addEventListener("DOMContentLoaded", function() { }); // Synchronizacja sugestii - const buttons = document.querySelectorAll('.sync-btn'); - buttons.forEach(btn => { + document.querySelectorAll('.sync-btn').forEach(btn => { btn.addEventListener('click', function(e) { e.preventDefault(); const itemId = this.getAttribute('data-item-id'); const button = this; - button.disabled = true; fetch(`/admin/sync_suggestion/${itemId}`, { @@ -36,7 +34,7 @@ document.addEventListener("DOMContentLoaded", function() { button.disabled = false; } }) - .catch(error => { + .catch(() => { showToast('Błąd synchronizacji', 'danger'); button.disabled = false; }); @@ -44,14 +42,12 @@ document.addEventListener("DOMContentLoaded", function() { }); // Usuwanie sugestii - const deleteButtons = document.querySelectorAll('.delete-suggestion-btn'); - deleteButtons.forEach(btn => { + document.querySelectorAll('.delete-suggestion-btn').forEach(btn => { btn.addEventListener('click', function(e) { e.preventDefault(); const suggestionId = this.getAttribute('data-suggestion-id'); const button = this; - button.disabled = true; fetch(`/admin/delete_suggestion/${suggestionId}`, { @@ -65,41 +61,16 @@ document.addEventListener("DOMContentLoaded", function() { showToast(data.message, data.success ? 'success' : 'danger'); if (data.success) { - // Możesz usunąć cały wiersz lub np. odświeżyć kolumnę const row = button.closest('tr'); - if (row) { - row.remove(); - } + if (row) row.remove(); } else { button.disabled = false; } }) - .catch(error => { + .catch(() => { showToast('Błąd usuwania sugestii', 'danger'); button.disabled = false; }); }); }); }); - - -// Funkcja do wyświetlania toast -function showToast(message, type = 'success') { - const toastContainer = document.getElementById('toast-container'); - const toast = document.createElement('div'); - toast.className = `toast align-items-center text-white bg-${type} border-0 show`; - toast.role = 'alert'; - toast.style.minWidth = '250px'; - toast.innerHTML = ` -
-
${message}
- -
- `; - toastContainer.appendChild(toast); - - // Automatyczne usunięcie po 1.75 sekundy - setTimeout(() => { - toast.remove(); - }, 1750); -} diff --git a/templates/admin/list_products.html b/templates/admin/list_products.html index 2fce4cb..9cf8be5 100644 --- a/templates/admin/list_products.html +++ b/templates/admin/list_products.html @@ -7,7 +7,7 @@ ← Powrót do panelu -
+

📦 Produkty (z synchronizacją sugestii)

{{ items|length }} produktów @@ -39,8 +39,7 @@ {% set suggestion = suggestions_dict.get(item.name.lower()) %} {% if suggestion %} ✅ Istnieje (ID: {{ suggestion.id }}) - - + {% else %} {% endif %} @@ -60,8 +59,45 @@
-{% block scripts %} + +
+
+

💡 Wszystkie sugestie (poza powiązanymi)

+ {{ suggestions_dict|length }} sugestii +
+
+ {% set item_names = items | map(attribute='name') | map('lower') | list %} + + + + + + + + + + {% for suggestion in suggestions_dict.values() %} + {% if suggestion.name.lower() not in item_names %} + + + + + + {% endif %} + {% endfor %} + {% if suggestions_dict|length == 0 %} + + + + {% endif %} + +
IDNazwaAkcje
{{ suggestion.id }}{{ suggestion.name }} + +
Brak sugestii do wyświetlenia.
+
+
+{% block scripts %} {% endblock %}