From 012a1a79159488ed1923b31ebcbe4af129010008 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 23 Apr 2022 13:03:15 +0200 Subject: [PATCH 1/4] ingredient parser produces expected results again --- cookbook/helper/ingredient_parser.py | 95 ++++++++++--------- cookbook/templates/test.html | 18 +--- .../tests/other/test_ingredient_parser.py | 4 +- cookbook/views/views.py | 11 ++- 4 files changed, 62 insertions(+), 66 deletions(-) diff --git a/cookbook/helper/ingredient_parser.py b/cookbook/helper/ingredient_parser.py index ee4b8d916..b310443ef 100644 --- a/cookbook/helper/ingredient_parser.py +++ b/cookbook/helper/ingredient_parser.py @@ -46,7 +46,7 @@ class IngredientParser: def apply_food_automation(self, food): """ - Apply food alias automations to passed foood + Apply food alias automations to passed food :param food: unit as string :return: food as string (possibly changed by automation) """ @@ -155,33 +155,36 @@ class IngredientParser: except ValueError: unit = x[end:] + if unit is not None and unit.strip() == '': + unit = None + if unit is not None and (unit.startswith('(') or unit.startswith('-')): # i dont know any unit that starts with ( or - so its likely an alternative like 1L (500ml) Water or 2-3 - unit = '' + unit = None note = x return amount, unit, note - def parse_ingredient_with_comma(self, tokens): - ingredient = '' + def parse_food_with_comma(self, tokens): + food = '' note = '' start = 0 # search for first occurrence of an argument ending in a comma while start < len(tokens) and not tokens[start].endswith(','): start += 1 if start == len(tokens): - # no token ending in a comma found -> use everything as ingredient - ingredient = ' '.join(tokens) + # no token ending in a comma found -> use everything as food + food = ' '.join(tokens) else: - ingredient = ' '.join(tokens[:start + 1])[:-1] + food = ' '.join(tokens[:start + 1])[:-1] note = ' '.join(tokens[start + 1:]) - return ingredient, note + return food, note - def parse_ingredient(self, tokens): - ingredient = '' + def parse_food(self, tokens): + food = '' note = '' if tokens[-1].endswith(')'): # Check if the matching opening bracket is in the same token if (not tokens[-1].startswith('(')) and ('(' in tokens[-1]): - return self.parse_ingredient_with_comma(tokens) + return self.parse_food_with_comma(tokens) # last argument ends with closing bracket -> look for opening bracket start = len(tokens) - 1 while not tokens[start].startswith('(') and not start == 0: @@ -191,36 +194,41 @@ class IngredientParser: raise ValueError elif start < 0: # no opening bracket anywhere -> just ignore the last bracket - ingredient, note = self.parse_ingredient_with_comma(tokens) + food, note = self.parse_food_with_comma(tokens) else: - # opening bracket found -> split in ingredient and note, remove brackets from note # noqa: E501 + # opening bracket found -> split in food and note, remove brackets from note # noqa: E501 note = ' '.join(tokens[start:])[1:-1] - ingredient = ' '.join(tokens[:start]) + food = ' '.join(tokens[:start]) else: - ingredient, note = self.parse_ingredient_with_comma(tokens) - return ingredient, note + food, note = self.parse_food_with_comma(tokens) + return food, note - def parse(self, x): + def parse(self, ingredient): + """ + Main parsing function, takes an ingredient string (e.g. '1 l Water') and extracts amount, unit, food, ... + :param ingredient: string ingredient + :return: amount, unit (can be None), food, note (can be empty) + """ # initialize default values amount = 0 unit = None - ingredient = '' + food = '' note = '' unit_note = '' - if len(x) == 0: + if len(ingredient) == 0: raise ValueError('string to parse cannot be empty') # if the string contains parenthesis early on remove it and place it at the end # because its likely some kind of note - if re.match('(.){1,6}\s\((.[^\(\)])+\)\s', x): - match = re.search('\((.[^\(])+\)', x) - x = x[:match.start()] + x[match.end():] + ' ' + x[match.start():match.end()] + if re.match('(.){1,6}\s\((.[^\(\)])+\)\s', ingredient): + match = re.search('\((.[^\(])+\)', ingredient) + ingredient = ingredient[:match.start()] + ingredient[match.end():] + ' ' + ingredient[match.start():match.end()] - tokens = x.split() + tokens = ingredient.split() # split at each space into tokens if len(tokens) == 1: - # there only is one argument, that must be the ingredient - ingredient = tokens[0] + # there only is one argument, that must be the food + food = tokens[0] else: try: # try to parse first argument as amount @@ -232,51 +240,50 @@ class IngredientParser: try: if unit is not None: # a unit is already found, no need to try the second argument for a fraction - # probably not the best method to do it, but I didn't want to make an if check and paste the exact same thing in the else as already is in the except # noqa: E501 + # probably not the best method to do it, but I didn't want to make an if check and paste the exact same thing in the else as already is in the except raise ValueError # try to parse second argument as amount and add that, in case of '2 1/2' or '2 ½' amount += self.parse_fraction(tokens[1]) # assume that units can't end with a comma if len(tokens) > 3 and not tokens[2].endswith(','): - # try to use third argument as unit and everything else as ingredient, use everything as ingredient if it fails # noqa: E501 + # try to use third argument as unit and everything else as food, use everything as food if it fails try: - ingredient, note = self.parse_ingredient(tokens[3:]) + food, note = self.parse_food(tokens[3:]) unit = tokens[2] except ValueError: - ingredient, note = self.parse_ingredient(tokens[2:]) + food, note = self.parse_food(tokens[2:]) else: - ingredient, note = self.parse_ingredient(tokens[2:]) + food, note = self.parse_food(tokens[2:]) except ValueError: # assume that units can't end with a comma if not tokens[1].endswith(','): - # try to use second argument as unit and everything else as ingredient, use everything as ingredient if it fails # noqa: E501 + # try to use second argument as unit and everything else as food, use everything as food if it fails try: - ingredient, note = self.parse_ingredient(tokens[2:]) + food, note = self.parse_food(tokens[2:]) if unit is None: unit = tokens[1] else: note = tokens[1] except ValueError: - ingredient, note = self.parse_ingredient(tokens[1:]) + food, note = self.parse_food(tokens[1:]) else: - ingredient, note = self.parse_ingredient(tokens[1:]) + food, note = self.parse_food(tokens[1:]) else: # only two arguments, first one is the amount - # which means this is the ingredient - ingredient = tokens[1] + # which means this is the food + food = tokens[1] except ValueError: try: # can't parse first argument as amount - # -> no unit -> parse everything as ingredient - ingredient, note = self.parse_ingredient(tokens) + # -> no unit -> parse everything as food + food, note = self.parse_food(tokens) except ValueError: - ingredient = ' '.join(tokens[1:]) + food = ' '.join(tokens[1:]) if unit_note not in note: note += ' ' + unit_note - try: - unit = self.apply_unit_automation(unit.strip()) - except Exception: - pass - return amount, unit, self.apply_food_automation(ingredient.strip()), note.strip() + if unit: + unit = self.apply_unit_automation(unit.strip()) + + return amount, unit, self.apply_food_automation(food.strip()), note.strip() diff --git a/cookbook/templates/test.html b/cookbook/templates/test.html index f0c69a800..f1c083de8 100644 --- a/cookbook/templates/test.html +++ b/cookbook/templates/test.html @@ -10,27 +10,11 @@ {% block content_fluid %} -
- - -
- + {{ data }} {% endblock %} {% block script %} - {% if debug %} - - {% else %} - - {% endif %} - - - {% render_bundle 'import_view' %} {% endblock %} \ No newline at end of file diff --git a/cookbook/tests/other/test_ingredient_parser.py b/cookbook/tests/other/test_ingredient_parser.py index d2d6b144a..64ea58e70 100644 --- a/cookbook/tests/other/test_ingredient_parser.py +++ b/cookbook/tests/other/test_ingredient_parser.py @@ -4,7 +4,7 @@ from cookbook.helper.ingredient_parser import IngredientParser def test_ingredient_parser(): expectations = { "2¼ l Wasser": (2.25, "l", "Wasser", ""), - "2¼l Wasser": (2.25, "l", "Wasser", ""), + "3¼l Wasser": (3.25, "l", "Wasser", ""), "¼ l Wasser": (0.25, "l", "Wasser", ""), "3l Wasser": (3, "l", "Wasser", ""), "4 l Wasser": (4, "l", "Wasser", ""), @@ -58,7 +58,7 @@ def test_ingredient_parser(): "2L Wasser": (2, "L", "Wasser", ""), "1 (16 ounce) package dry lentils, rinsed": (1, "package", "dry lentils, rinsed", "16 ounce"), "2-3 c Water": (2, "c", "Water", "2-3"), - "Pane (raffermo o secco) 80 g": (0, "", "Pane 80 g", "raffermo o secco"), # TODO this is actually not a good result but currently expected + "Pane (raffermo o secco) 80 g": (0, None, "Pane 80 g", "raffermo o secco"), # TODO this is actually not a good result but currently expected } # for German you could say that if an ingredient does not have # an amount # and it starts with a lowercase letter, then that diff --git a/cookbook/views/views.py b/cookbook/views/views.py index f47eed694..1234cd045 100644 --- a/cookbook/views/views.py +++ b/cookbook/views/views.py @@ -662,10 +662,15 @@ def test(request): if not settings.DEBUG: return HttpResponseRedirect(reverse('index')) - if (api_token := Token.objects.filter(user=request.user).first()) is None: - api_token = Token.objects.create(user=request.user) + from cookbook.helper.ingredient_parser import IngredientParser + parser = IngredientParser(request, False) - return render(request, 'test.html', {'api_token': api_token}) + data = { + 'original': 'Pane (raffermo o secco) 80 g' + } + data['parsed'] = parser.parse(data['original']) + + return render(request, 'test.html', {'data': data}) def test2(request): From e0b7d1a8f02336b432e2816a69f8c5c4a217770e Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 23 Apr 2022 13:53:04 +0200 Subject: [PATCH 2/4] added support for unit/amount at end of ingredient --- cookbook/helper/ingredient_parser.py | 7 +++++++ cookbook/tests/other/test_ingredient_parser.py | 3 ++- cookbook/views/views.py | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cookbook/helper/ingredient_parser.py b/cookbook/helper/ingredient_parser.py index b310443ef..c26c21abb 100644 --- a/cookbook/helper/ingredient_parser.py +++ b/cookbook/helper/ingredient_parser.py @@ -219,6 +219,13 @@ class IngredientParser: if len(ingredient) == 0: raise ValueError('string to parse cannot be empty') + # some people/languages put amount and unit at the end of the ingredient string + # if something like this is detected move it to the beginning so the parser can handle it + if re.search(r'^([A-z])+(.)*[1-9](\d)*\s([A-z])+', ingredient): + match = re.search(r'[1-9](\d)*\s([A-z])+', ingredient) + print(f'reording from {ingredient} to {ingredient[match.start():match.end()] + " " + ingredient.replace(ingredient[match.start():match.end()], "")}') + ingredient = ingredient[match.start():match.end()] + ' ' + ingredient.replace(ingredient[match.start():match.end()], '') + # if the string contains parenthesis early on remove it and place it at the end # because its likely some kind of note if re.match('(.){1,6}\s\((.[^\(\)])+\)\s', ingredient): diff --git a/cookbook/tests/other/test_ingredient_parser.py b/cookbook/tests/other/test_ingredient_parser.py index 64ea58e70..21b1853e8 100644 --- a/cookbook/tests/other/test_ingredient_parser.py +++ b/cookbook/tests/other/test_ingredient_parser.py @@ -58,7 +58,7 @@ def test_ingredient_parser(): "2L Wasser": (2, "L", "Wasser", ""), "1 (16 ounce) package dry lentils, rinsed": (1, "package", "dry lentils, rinsed", "16 ounce"), "2-3 c Water": (2, "c", "Water", "2-3"), - "Pane (raffermo o secco) 80 g": (0, None, "Pane 80 g", "raffermo o secco"), # TODO this is actually not a good result but currently expected + "Pane (raffermo o secco) 80 g": (80, "g", "Pane", "raffermo o secco"), } # for German you could say that if an ingredient does not have # an amount # and it starts with a lowercase letter, then that @@ -70,4 +70,5 @@ def test_ingredient_parser(): for key, val in expectations.items(): count += 1 parsed = ingredient_parser.parse(key) + print(f'testing if {key} becomes {val}') assert parsed == val diff --git a/cookbook/views/views.py b/cookbook/views/views.py index 1234cd045..d0224afb1 100644 --- a/cookbook/views/views.py +++ b/cookbook/views/views.py @@ -666,7 +666,7 @@ def test(request): parser = IngredientParser(request, False) data = { - 'original': 'Pane (raffermo o secco) 80 g' + 'original': 'Creme Frainche' } data['parsed'] = parser.parse(data['original']) From e2ab3a0efb1c05e5e15d2d74c9088fb4d8c6f095 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 23 Apr 2022 14:15:06 +0200 Subject: [PATCH 3/4] fixed ingredient parser length issues --- cookbook/helper/ingredient_parser.py | 14 ++++++++++++-- cookbook/tests/other/test_ingredient_parser.py | 10 +++++++++- cookbook/views/views.py | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/cookbook/helper/ingredient_parser.py b/cookbook/helper/ingredient_parser.py index c26c21abb..8c07635af 100644 --- a/cookbook/helper/ingredient_parser.py +++ b/cookbook/helper/ingredient_parser.py @@ -4,7 +4,7 @@ import unicodedata from django.core.cache import caches -from cookbook.models import Unit, Food, Automation +from cookbook.models import Unit, Food, Automation, Ingredient class IngredientParser: @@ -293,4 +293,14 @@ class IngredientParser: if unit: unit = self.apply_unit_automation(unit.strip()) - return amount, unit, self.apply_food_automation(food.strip()), note.strip() + food = self.apply_food_automation(food.strip()) + if len(food) > Food._meta.get_field('name').max_length: # test if food name is to long + # try splitting it at a space and taking only the first arg + if len(food.split()) > 1 and len(food.split()[0]) < Food._meta.get_field('name').max_length: + note = ' '.join(food.split()[1:]) + ' ' + note + food = food.split()[0] + else: + note = food + ' ' + note + food = food[:Food._meta.get_field('name').max_length] + + return amount, unit, food, note[:Ingredient._meta.get_field('note').max_length].strip() diff --git a/cookbook/tests/other/test_ingredient_parser.py b/cookbook/tests/other/test_ingredient_parser.py index 21b1853e8..c5ad26ca3 100644 --- a/cookbook/tests/other/test_ingredient_parser.py +++ b/cookbook/tests/other/test_ingredient_parser.py @@ -32,7 +32,7 @@ def test_ingredient_parser(): "1 Ei(er)": (1, None, "Ei(er)", ""), "1 Prise(n) Salz": (1, "Prise(n)", "Salz", ""), "etwas Wasser, lauwarmes": (0, None, "etwas Wasser", "lauwarmes"), - "Strudelblätter, fertige, für zwei Strudel": (0,None, "Strudelblätter", "fertige, für zwei Strudel"), + "Strudelblätter, fertige, für zwei Strudel": (0, None, "Strudelblätter", "fertige, für zwei Strudel"), "barrel-aged Bourbon": (0, None, "barrel-aged Bourbon", ""), "golden syrup": (0, None, "golden syrup", ""), "unsalted butter, for greasing": (0, None, "unsalted butter", "for greasing"), @@ -59,6 +59,14 @@ def test_ingredient_parser(): "1 (16 ounce) package dry lentils, rinsed": (1, "package", "dry lentils, rinsed", "16 ounce"), "2-3 c Water": (2, "c", "Water", "2-3"), "Pane (raffermo o secco) 80 g": (80, "g", "Pane", "raffermo o secco"), + "1 Knoblauchzehe(n), gehackt oder gepresst": (1.0, None, 'Knoblauchzehe(n)', 'gehackt oder gepresst'), + # test for over long food entries to get properly split into the note field + "1 Lorem ipsum dolor sit amet consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut l Lorem ipsum dolor sit amet consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut l": ( + 1.0, 'Lorem', 'ipsum', 'dolor sit amet consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut l Lorem ipsum dolor sit amet consetetur sadipscing elitr sed diam nonumy eirmod tempor invidunt ut l'), + "1 LoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutlLoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutl": ( + 1.0, None, 'LoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutlLoremipsumdolorsitametconsetetursadipscingeli', + 'LoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutlLoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutl') + } # for German you could say that if an ingredient does not have # an amount # and it starts with a lowercase letter, then that diff --git a/cookbook/views/views.py b/cookbook/views/views.py index d0224afb1..ec1553d1a 100644 --- a/cookbook/views/views.py +++ b/cookbook/views/views.py @@ -666,7 +666,7 @@ def test(request): parser = IngredientParser(request, False) data = { - 'original': 'Creme Frainche' + 'original': '1 LoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutlLoremipsumdolorsitametconsetetursadipscingelitrseddiamnonumyeirmodtemporinviduntutl' } data['parsed'] = parser.parse(data['original']) From 66c0cc070a0d9df9689b157f0f5171a69b7a1ec6 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 23 Apr 2022 14:43:03 +0200 Subject: [PATCH 4/4] removed old shopping list --- cookbook/filters.py | 21 +- cookbook/tables.py | 12 +- cookbook/templates/generic/list_template.html | 9 - cookbook/templates/meal_plan_new.html | 36 - cookbook/templates/shopping_list.html | 921 ------------------ cookbook/urls.py | 6 +- cookbook/views/lists.py | 35 +- cookbook/views/views.py | 37 +- vue/src/components/RecipeContextMenu.vue | 5 +- vue/src/locales/en.json | 1 - 10 files changed, 17 insertions(+), 1066 deletions(-) delete mode 100644 cookbook/templates/meal_plan_new.html delete mode 100644 cookbook/templates/shopping_list.html diff --git a/cookbook/filters.py b/cookbook/filters.py index 30d42cf7e..ac377abcb 100644 --- a/cookbook/filters.py +++ b/cookbook/filters.py @@ -6,7 +6,7 @@ from django.utils.translation import gettext as _ from django_scopes import scopes_disabled from cookbook.forms import MultiSelectWidget -from cookbook.models import Food, Keyword, Recipe, ShoppingList +from cookbook.models import Food, Keyword, Recipe with scopes_disabled(): class RecipeFilter(django_filters.FilterSet): @@ -60,22 +60,3 @@ with scopes_disabled(): class Meta: model = Recipe fields = ['name', 'keywords', 'foods', 'internal'] - - # class FoodFilter(django_filters.FilterSet): - # name = django_filters.CharFilter(lookup_expr='icontains') - - # class Meta: - # model = Food - # fields = ['name'] - - class ShoppingListFilter(django_filters.FilterSet): - - def __init__(self, data=None, *args, **kwargs): - if data is not None: - data = data.copy() - data.setdefault("finished", False) - super().__init__(data, *args, **kwargs) - - class Meta: - model = ShoppingList - fields = ['finished'] diff --git a/cookbook/tables.py b/cookbook/tables.py index 1829c6f58..2831c39dc 100644 --- a/cookbook/tables.py +++ b/cookbook/tables.py @@ -3,8 +3,8 @@ from django.utils.html import format_html from django.utils.translation import gettext as _ from django_tables2.utils import A -from .models import (CookLog, InviteLink, Keyword, Recipe, RecipeImport, - ShoppingList, Storage, Sync, SyncLog, ViewLog) +from .models import (CookLog, InviteLink, Recipe, RecipeImport, + Storage, Sync, SyncLog, ViewLog) class ImageUrlColumn(tables.Column): @@ -121,14 +121,6 @@ class RecipeImportTable(tables.Table): fields = ('id', 'name', 'file_path') -class ShoppingListTable(tables.Table): - id = tables.LinkColumn('view_shopping', args=[A('id')]) - - class Meta: - model = ShoppingList - template_name = 'generic/table_template.html' - fields = ('id', 'finished', 'created_by', 'created_at') - class InviteLinkTable(tables.Table): link = tables.TemplateColumn( diff --git a/cookbook/templates/generic/list_template.html b/cookbook/templates/generic/list_template.html index 7d4be443d..84605ce37 100644 --- a/cookbook/templates/generic/list_template.html +++ b/cookbook/templates/generic/list_template.html @@ -26,15 +26,6 @@ {% endif %} - {% if request.resolver_match.url_name in 'list_shopping_list' %} - - - - - - {% endif %} {% if filter %}
diff --git a/cookbook/templates/meal_plan_new.html b/cookbook/templates/meal_plan_new.html deleted file mode 100644 index f835d72a9..000000000 --- a/cookbook/templates/meal_plan_new.html +++ /dev/null @@ -1,36 +0,0 @@ -{% extends "base.html" %} -{% load render_bundle from webpack_loader %} -{% load static %} -{% load i18n %} -{% load l10n %} - -{% block title %}{% trans 'Meal-Plan' %}{% endblock %} - -{% block content_fluid %} - -
- -
- - -{% endblock %} - - -{% block script %} - {% if debug %} - - {% else %} - - {% endif %} - - - - {% render_bundle 'meal_plan_view' %} -{% endblock %} \ No newline at end of file diff --git a/cookbook/templates/shopping_list.html b/cookbook/templates/shopping_list.html deleted file mode 100644 index 2720d5898..000000000 --- a/cookbook/templates/shopping_list.html +++ /dev/null @@ -1,921 +0,0 @@ -{% extends "base.html" %} -{% comment %} TODO: Deprecate {% endcomment %} -{% load django_tables2 %} -{% load crispy_forms_tags %} -{% load static %} -{% load i18n %} -{% block title %}{% trans "Shopping List" %}{% endblock %} -{% block extra_head %} -{% include 'include/vue_base.html' %} - - - - - - - - - - - - -{% endblock %} - -{% block content %} - -
- -

{% trans 'Shopping List' %}

-
- - - - - -
- {% trans 'Edit' %} -
-
- - - -{% endblock %} {% block script %} - - - - -{% endblock %} diff --git a/cookbook/urls.py b/cookbook/urls.py index 90c81d1e9..32cfc7b5f 100644 --- a/cookbook/urls.py +++ b/cookbook/urls.py @@ -64,10 +64,8 @@ urlpatterns = [ path('books/', views.books, name='view_books'), path('plan/', views.meal_plan, name='view_plan'), path('plan/entry/', views.meal_plan_entry, name='view_plan_entry'), - path('shopping/', views.shopping_list, name='view_shopping'), - path('shopping/', views.shopping_list, name='view_shopping'), - path('shopping/latest/', views.latest_shopping_list, name='view_shopping_latest'), - path('shopping/new/', lists.shopping_list_new, name='view_shopping_new'), + path('shopping/latest/', lists.shopping_list, name='view_shopping_latest'), + path('shopping/', lists.shopping_list, name='view_shopping'), path('settings/', views.user_settings, name='view_settings'), path('history/', views.history, name='view_history'), path('supermarket/', views.supermarket, name='view_supermarket'), diff --git a/cookbook/views/lists.py b/cookbook/views/lists.py index 1f0213548..1da69f8fa 100644 --- a/cookbook/views/lists.py +++ b/cookbook/views/lists.py @@ -1,14 +1,13 @@ from datetime import datetime -from django.db.models import Q, Sum +from django.db.models import Sum from django.shortcuts import render from django.utils.translation import gettext as _ from django_tables2 import RequestConfig -from cookbook.filters import ShoppingListFilter from cookbook.helper.permission_helper import group_required -from cookbook.models import InviteLink, RecipeImport, ShoppingList, Storage, SyncLog, UserFile -from cookbook.tables import (ImportLogTable, InviteLinkTable, RecipeImportTable, ShoppingListTable, +from cookbook.models import InviteLink, RecipeImport, Storage, SyncLog, UserFile +from cookbook.tables import (ImportLogTable, InviteLinkTable, RecipeImportTable, StorageTable) @@ -41,20 +40,12 @@ def recipe_import(request): @group_required('user') def shopping_list(request): - f = ShoppingListFilter(request.GET, queryset=ShoppingList.objects.filter(space=request.space).filter( - Q(created_by=request.user) | Q(shared=request.user)).distinct().all().order_by('finished', 'created_at')) - - table = ShoppingListTable(f.qs) - RequestConfig(request, paginate={'per_page': 25}).configure(table) - return render( request, - 'generic/list_template.html', + 'shoppinglist_template.html', { - 'title': _("Shopping Lists"), - 'table': table, - 'filter': f, - 'create_url': 'view_shopping' + "title": _("Shopping List"), + } ) @@ -205,7 +196,7 @@ def custom_filter(request): def user_file(request): try: current_file_size_mb = UserFile.objects.filter(space=request.space).aggregate(Sum('file_size_kb'))[ - 'file_size_kb__sum'] / 1000 + 'file_size_kb__sum'] / 1000 except TypeError: current_file_size_mb = 0 @@ -237,15 +228,3 @@ def step(request): } } ) - - -@group_required('user') -def shopping_list_new(request): - return render( - request, - 'shoppinglist_template.html', - { - "title": _("New Shopping List"), - - } - ) diff --git a/cookbook/views/views.py b/cookbook/views/views.py index ec1553d1a..25b1f1cda 100644 --- a/cookbook/views/views.py +++ b/cookbook/views/views.py @@ -11,9 +11,9 @@ from django.contrib.auth.forms import PasswordChangeForm from django.contrib.auth.models import Group from django.contrib.auth.password_validation import validate_password from django.core.exceptions import ValidationError -from django.db.models import Avg, Q, Sum +from django.db.models import Avg, Q from django.db.models.functions import Lower -from django.http import HttpResponseRedirect, JsonResponse +from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse, reverse_lazy from django.utils import timezone @@ -27,9 +27,9 @@ from cookbook.forms import (CommentForm, Recipe, SearchPreferenceForm, ShoppingP SpaceCreateForm, SpaceJoinForm, SpacePreferenceForm, User, UserCreateForm, UserNameForm, UserPreference, UserPreferenceForm) from cookbook.helper.permission_helper import group_required, has_group_permission, share_link_valid -from cookbook.models import (Comment, CookLog, Food, FoodInheritField, InviteLink, Keyword, +from cookbook.models import (Comment, CookLog, Food, InviteLink, Keyword, MealPlan, RecipeImport, SearchFields, SearchPreference, ShareLink, - ShoppingList, Space, Unit, UserFile, ViewLog) + Space, Unit, ViewLog) from cookbook.tables import (CookLogTable, InviteLinkTable, RecipeTable, RecipeTableSmall, ViewLogTable) from cookbook.views.data import Object @@ -256,35 +256,6 @@ def meal_plan_entry(request, pk): return render(request, 'meal_plan_entry.html', {'plan': plan, 'same_day_plan': same_day_plan}) -@group_required('user') -def latest_shopping_list(request): - sl = ShoppingList.objects.filter(Q(created_by=request.user) | Q(shared=request.user)).filter(finished=False, - space=request.space).order_by( - '-created_at').first() - - if sl: - return HttpResponseRedirect(reverse('view_shopping', kwargs={'pk': sl.pk}) + '?edit=true') - else: - return HttpResponseRedirect(reverse('view_shopping') + '?edit=true') - - -@group_required('user') -def shopping_list(request, pk=None): # TODO deprecate - html_list = request.GET.getlist('r') - - recipes = [] - for r in html_list: - r = r.replace('[', '').replace(']', '') - if len(r) < 10000 and re.match(r'^([0-9])+,([0-9])+[.]*([0-9])*$', r): - rid, multiplier = r.split(',') - if recipe := Recipe.objects.filter(pk=int(rid), space=request.space).first(): - recipes.append({'recipe': recipe.id, 'multiplier': multiplier}) - - edit = True if 'edit' in request.GET and request.GET['edit'] == 'true' else False - - return render(request, 'shopping_list.html', {'shopping_list_id': pk, 'recipes': recipes, 'edit': edit}) - - @group_required('guest') def user_settings(request): if request.space.demo: diff --git a/vue/src/components/RecipeContextMenu.vue b/vue/src/components/RecipeContextMenu.vue index 682cd6c26..67a631454 100644 --- a/vue/src/components/RecipeContextMenu.vue +++ b/vue/src/components/RecipeContextMenu.vue @@ -14,10 +14,7 @@ - - {{ $t("Add_to_Shopping") }} - - {{ $t("create_shopping_new") }} + {{ $t("Add_to_Shopping") }} {{ $t("Add_to_Plan") }} diff --git a/vue/src/locales/en.json b/vue/src/locales/en.json index 6a3f1810e..f1d23db93 100644 --- a/vue/src/locales/en.json +++ b/vue/src/locales/en.json @@ -270,7 +270,6 @@ "CategoryInstruction": "Drag categories to change the order categories appear in shopping list.", "shopping_recent_days_desc": "Days of recent shopping list entries to display.", "shopping_recent_days": "Recent Days", - "create_shopping_new": "Add to NEW Shopping List", "download_pdf": "Download PDF", "download_csv": "Download CSV", "csv_delim_help": "Delimiter to use for CSV exports.",