diff --git a/app.py b/app.py index 2e2e1c3..0a35683 100644 --- a/app.py +++ b/app.py @@ -2456,9 +2456,8 @@ def admin_receipts(id): if id == "all": all_filenames = {r.filename for r in Receipt.query.all()} - pagination = ( - Receipt.query.order_by(Receipt.uploaded_at.desc()) - .paginate(page=page, per_page=per_page, error_out=False) + pagination = Receipt.query.order_by(Receipt.uploaded_at.desc()).paginate( + page=page, per_page=per_page, error_out=False ) receipts_paginated = pagination.items @@ -2839,11 +2838,13 @@ def list_products(): per_page = request.args.get("per_page", 125, type=int) per_page = max(1, min(per_page, 300)) - # Pobierz wszystkie itemy – tylko name, id i relacje potrzebne do odtworzenia unikalnych - all_items = Item.query.options( - joinedload(Item.shopping_list), - joinedload(Item.added_by_user) - ).order_by(Item.id.desc()).all() + all_items = ( + Item.query.options( + joinedload(Item.added_by_user), + ) + .order_by(Item.id.desc()) + .all() + ) seen_names = set() unique_items = [] @@ -2853,38 +2854,64 @@ def list_products(): unique_items.append(item) seen_names.add(key) - # Paginacja ręczna na unikalnych total_items = len(unique_items) total_pages = (total_items + per_page - 1) // per_page start = (page - 1) * per_page end = start + per_page items = unique_items[start:end] - # Słownik użytkowników (ograniczony do używanych) user_ids = {item.added_by for item in items if item.added_by} - users = User.query.filter(User.id.in_(user_ids)).all() + users = User.query.filter(User.id.in_(user_ids)).all() if user_ids else [] users_dict = {u.id: u.username for u in users} - # Wszystkie sugestie – do dopasowań suggestions = SuggestedProduct.query.all() all_suggestions_dict = { - (s.name or "").strip().lower(): s for s in suggestions if s.name and s.name.strip() + (s.name or "").strip().lower(): s + for s in suggestions + if s.name and s.name.strip() } + used_suggestion_names = {(i.name or "").strip().lower() for i in unique_items} - used_suggestion_names = {(item.name or "").strip().lower() for item in unique_items} - - # Powiązane i osierocone suggestions_dict = { name: all_suggestions_dict[name] for name in used_suggestion_names if name in all_suggestions_dict } - orphan_suggestions = [ - s for name, s in all_suggestions_dict.items() + s + for name, s in all_suggestions_dict.items() if name not in used_suggestion_names ] + orphan_names_lower = {(s.name or "").strip().lower() for s in orphan_suggestions} + if orphan_names_lower: + items_for_orphans = ( + Item.query.filter(func.lower(Item.name).in_(orphan_names_lower)) + .filter(Item.added_by.isnot(None)) + .options(joinedload(Item.added_by_user)) + .order_by(Item.id.desc()) + .all() + ) + name_to_user = {} + for it in items_for_orphans: + key = (it.name or "").strip().lower() + if key not in name_to_user and it.added_by: + name_to_user[key] = it.added_by + suggestion_added_by_map = { + s.id: name_to_user.get((s.name or "").strip().lower()) + for s in orphan_suggestions + } + missing_user_ids = { + uid + for uid in suggestion_added_by_map.values() + if uid and uid not in users_dict + } + if missing_user_ids: + extra_users = User.query.filter(User.id.in_(missing_user_ids)).all() + users_dict.update({u.id: u.username for u in extra_users}) + else: + suggestion_added_by_map = {} + query_string = urlencode({k: v for k, v in request.args.items() if k != "page"}) return render_template( @@ -2893,10 +2920,12 @@ def list_products(): users_dict=users_dict, suggestions_dict=suggestions_dict, orphan_suggestions=orphan_suggestions, + suggestion_added_by_map=suggestion_added_by_map, page=page, per_page=per_page, total_pages=total_pages, query_string=query_string, + total_items=total_items, ) @@ -3000,7 +3029,6 @@ def recalculate_filesizes_all(): return redirect(url_for("admin_receipts", id="all")) - @app.route("/admin/mass_edit_categories", methods=["GET", "POST"]) @login_required @admin_required @@ -3034,7 +3062,9 @@ def admin_mass_edit_categories(): l.categories.extend(cats) db.session.commit() flash("Zaktualizowano kategorie dla wybranych list", "success") - return redirect(url_for("admin_mass_edit_categories", page=page, per_page=per_page)) + return redirect( + url_for("admin_mass_edit_categories", page=page, per_page=per_page) + ) query_string = urlencode({k: v for k, v in request.args.items() if k != "page"}) @@ -3071,14 +3101,15 @@ def admin_list_items_json(list_id): purchased_count = sum(1 for item in l.items if item.purchased) total_expense = sum(exp.amount for exp in l.expenses) - return jsonify({ - "title": l.title, - "items": items, - "total_count": len(l.items), - "purchased_count": purchased_count, - "total_expense": round(total_expense, 2) - }) - + return jsonify( + { + "title": l.title, + "items": items, + "total_count": len(l.items), + "purchased_count": purchased_count, + "total_expense": round(total_expense, 2), + } + ) @app.route("/healthcheck") diff --git a/templates/admin/list_products.html b/templates/admin/list_products.html index a48c88c..785fc4e 100644 --- a/templates/admin/list_products.html +++ b/templates/admin/list_products.html @@ -9,11 +9,9 @@
- -
-

📦 Produkty (z synchronizacją sugestii)

- {{ items|length }} produktów +

📦 Produkty (z synchronizacją sugestii o unikalnych nazwach)

+ {{ total_items }} produktów
@@ -22,8 +20,7 @@ - - + @@ -49,26 +46,11 @@ Synchronizuj {% endif %} - - {% endfor %} {% if items|length == 0 %} - + {% endif %} @@ -90,6 +72,7 @@ + @@ -99,6 +82,14 @@ + - + {% endif %} @@ -145,24 +136,8 @@ - - - {% block scripts %} - {% endblock %} {% endblock %} \ No newline at end of file
ID Nazwa Dodany przezSugestiaListaAkcja
-
- - - 📄 Otwórz - - -
-
Brak produktów do wyświetlenia.Pusta lista produktów.
ID NazwaDodany przez Akcje
{{ suggestion.id }} {{ suggestion.name }} + {% set uid = suggestion_added_by_map.get(suggestion.id) %} + {% if uid and users_dict.get(uid) %} + 👤 {{ users_dict[uid] }} ({{ uid }}) + {% else %} + - + {% endif %} + @@ -108,7 +99,7 @@ {% endfor %} {% if suggestions_dict|length == 0 %}
Brak sugestii do wyświetlenia.Brak sugestii do wyświetlenia.