diff --git a/cookbook/integration/integration.py b/cookbook/integration/integration.py new file mode 100644 index 000000000..ac96fdc61 --- /dev/null +++ b/cookbook/integration/integration.py @@ -0,0 +1,8 @@ +class Integration: + @staticmethod + def get_recipe(string): + raise Exception('Method not implemented in storage integration') + + @staticmethod + def get_export(recipe): + raise Exception('Method not implemented in storage integration') diff --git a/cookbook/models.py b/cookbook/models.py index 800e4f1da..81eb6ec07 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -407,13 +407,9 @@ class ShoppingListRecipe(models.Model): class ShoppingListEntry(models.Model): - list_recipe = models.ForeignKey( - ShoppingListRecipe, on_delete=models.CASCADE, null=True, blank=True - ) + list_recipe = models.ForeignKey(ShoppingListRecipe, on_delete=models.CASCADE, null=True, blank=True) food = models.ForeignKey(Food, on_delete=models.CASCADE) - unit = models.ForeignKey( - Unit, on_delete=models.CASCADE, null=True, blank=True - ) + unit = models.ForeignKey(Unit, on_delete=models.CASCADE, null=True, blank=True) amount = models.DecimalField(default=0, decimal_places=16, max_digits=32) order = models.IntegerField(default=0) checked = models.BooleanField(default=False) diff --git a/cookbook/serializer.py b/cookbook/serializer.py index 47da0a95a..5de6f01b3 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -178,7 +178,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer): def create(self, validated_data): # since multi select tags dont have id's # duplicate names might be routed to create - obj, created = Food.objects.get_or_create(**validated_data) + obj, created = Food.objects.get_or_create(name=validated_data['name']) return obj def update(self, instance, validated_data): diff --git a/cookbook/templates/shopping_list.html b/cookbook/templates/shopping_list.html index 8e7be0684..0aece6f01 100644 --- a/cookbook/templates/shopping_list.html +++ b/cookbook/templates/shopping_list.html @@ -135,6 +135,27 @@ +
+
+
+ + +
+ + + +
+ +
+
+ +
+ +
+
+
+
@@ -278,7 +299,8 @@ [[x.amount]] [[x.unit.name]] - [[x.food.name]] ([[x.recipes.join(', ')]]) + [[x.food.name]] ([[x.recipes.join(', ')]]) + @@ -382,6 +404,7 @@ users: [], users_loading: false, onLine: navigator.onLine, + simple_entry: '', }, directives: { tabindex: { @@ -444,7 +467,12 @@ let entry = this.findMergeEntry(categories, item) if (entry !== undefined) { - entry.amount += item.amount * this.servings_cache[item.list_recipe] + let servings = 1 + if (item.list_recipe in this.servings_cache) { + servings = this.servings_cache[item.list_recipe] + } + + entry.amount += item.amount * servings if (item.list_recipe !== null && entry.recipes.indexOf(this.recipe_cache[item.list_recipe]) === -1) { entry.recipes.push(this.recipe_cache[item.list_recipe]) @@ -736,6 +764,28 @@ this.makeToast(gettext('Error'), gettext('Please enter a valid food'), 'danger') } }, + addSimpleEntry: function () { + if (this.simple_entry !== '') { + + this.$http.post('{% url 'api_ingredient_from_string' %}', {text: this.simple_entry}, {emulateJSON: true}).then((response) => { + + console.log(response) + this.shopping_list.entries.push({ + 'list_recipe': null, + 'food': {'name': response.body.food, supermarket_category: null}, + 'unit': {'name': response.body.unit}, + 'amount': response.body.amount, + 'order': 0, + 'checked': false, + }) + + this.simple_entry = '' + }).catch((err) => { + console.log(err) + this.makeToast(gettext('Error'), gettext('Something went wrong while trying to add the simple entry.'), 'danger') + }) + } + }, getRecipes: function () { let url = "{% url 'api:recipe-list' %}?limit=5&internal=true" if (this.recipe_query !== '') { diff --git a/cookbook/urls.py b/cookbook/urls.py index ad6a7d985..5d211e1af 100644 --- a/cookbook/urls.py +++ b/cookbook/urls.py @@ -88,6 +88,7 @@ urlpatterns = [ path('api/plan-ical///', api.get_plan_ical, name='api_get_plan_ical'), path('api/recipe-from-url/', api.recipe_from_url, name='api_recipe_from_url'), path('api/backup/', api.get_backup, name='api_backup'), + path('api/ingredient-from-string/', api.ingredient_from_string, name='api_ingredient_from_string'), path('dal/keyword/', dal.KeywordAutocomplete.as_view(), name='dal_keyword'), path('dal/food/', dal.IngredientsAutocomplete.as_view(), name='dal_food'), diff --git a/cookbook/views/api.py b/cookbook/views/api.py index 21a39f18e..b6036ab30 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -27,6 +27,7 @@ from rest_framework.parsers import MultiPartParser from rest_framework.response import Response from rest_framework.viewsets import ViewSetMixin +from cookbook.helper.ingredient_parser import parse from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest, CustomIsOwner, CustomIsShare, CustomIsShared, CustomIsUser, @@ -536,3 +537,18 @@ def get_backup(request): response["Content-Disposition"] = f'attachment; filename=backup{date_format(timezone.now(), format="SHORT_DATETIME_FORMAT", use_l10n=True)}.json' # noqa: E501 return response + + +@group_required('user') +def ingredient_from_string(request): + text = request.POST['text'] + amount, unit, food, note = parse(text) + + return JsonResponse( + { + 'amount': amount, + 'unit': unit, + 'food': food, + }, + status=200 + )