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
+ )