From 678cfaca125e6ceb91f9579e27e3e6a41be0592e Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Tue, 22 Sep 2020 12:01:11 +0200 Subject: [PATCH] fixed several shopping list issues --- cookbook/helper/permission_helper.py | 6 +- .../migrations/0082_auto_20200922_1143.py | 24 ++++++ cookbook/models.py | 14 +++- cookbook/serializer.py | 1 + cookbook/templates/shopping_list.html | 74 ++++++++++--------- cookbook/views/api.py | 2 +- 6 files changed, 82 insertions(+), 39 deletions(-) create mode 100644 cookbook/migrations/0082_auto_20200922_1143.py diff --git a/cookbook/helper/permission_helper.py b/cookbook/helper/permission_helper.py index 1c9f49f97..b9a9c98f7 100644 --- a/cookbook/helper/permission_helper.py +++ b/cookbook/helper/permission_helper.py @@ -61,12 +61,14 @@ def is_object_owner(user, obj): # TODO this could be improved/cleaned up by adding get_owner methods to all models that allow owner checks if not user.is_authenticated: return False - if user.is_superuser: - return True if owner := getattr(obj, 'created_by', None): return owner == user if owner := getattr(obj, 'user', None): return owner == user + if getattr(obj, 'get_owner', None): + return obj.get_owner() == user + if user.is_superuser: + return True return False diff --git a/cookbook/migrations/0082_auto_20200922_1143.py b/cookbook/migrations/0082_auto_20200922_1143.py new file mode 100644 index 000000000..bc58831b3 --- /dev/null +++ b/cookbook/migrations/0082_auto_20200922_1143.py @@ -0,0 +1,24 @@ +# Generated by Django 3.0.7 on 2020-09-22 09:43 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0081_auto_20200921_2349'), + ] + + operations = [ + migrations.AlterField( + model_name='invitelink', + name='valid_until', + field=models.DateField(default=datetime.date(2020, 10, 6)), + ), + migrations.AlterField( + model_name='shoppinglistentry', + name='amount', + field=models.DecimalField(decimal_places=16, default=0, max_digits=32), + ), + ] diff --git a/cookbook/models.py b/cookbook/models.py index 1f5d9ae58..2b7ce98ec 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -275,18 +275,30 @@ class ShoppingListRecipe(models.Model): def __str__(self): return f'Shopping list recipe {self.id} - {self.recipe}' + def get_owner(self): + try: + return self.shoppinglist_set.first().created_by + except AttributeError: + return None + class ShoppingListEntry(models.Model): 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) - amount = models.IntegerField(default=1) + amount = models.DecimalField(default=0, decimal_places=16, max_digits=32) order = models.IntegerField(default=0) checked = models.BooleanField(default=False) def __str__(self): return f'Shopping list entry {self.id}' + def get_owner(self): + try: + return self.shoppinglist_set.first().created_by + except AttributeError: + return None + class ShoppingList(models.Model): uuid = models.UUIDField(default=uuid.uuid4) diff --git a/cookbook/serializer.py b/cookbook/serializer.py index 3fe76d8a6..408946bf2 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -208,6 +208,7 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer): class ShoppingListEntrySerializer(WritableNestedModelSerializer): food = FoodSerializer(allow_null=True) unit = UnitSerializer(allow_null=True) + amount = CustomDecimalField() class Meta: model = ShoppingListEntry diff --git a/cookbook/templates/shopping_list.html b/cookbook/templates/shopping_list.html index 6ac9eca30..95f5ca7ec 100644 --- a/cookbook/templates/shopping_list.html +++ b/cookbook/templates/shopping_list.html @@ -313,23 +313,13 @@ }, */ mounted: function () { - if (this.shopping_list_id) { - this.loadShoppingList() - } else { - //TODO default share users - this.shopping_list = { - "recipes": [], - "entries": [], - "entries_display": [], - "shared": [], - "created_by": 1 - } - this.loading = false - } + this.loadShoppingList() {% if request.user.userpreference.shopping_auto_sync > 0 %} setInterval(() => { - this.loadShoppingList(true) + if ((this.shopping_list_id !== null) && !this.edit_mode) { + this.loadShoppingList(true) + } }, {{ request.user.userpreference.shopping_auto_sync }} * 1000 ) {% endif %} }, @@ -352,25 +342,39 @@ }) }, loadShoppingList: function (autosync = false) { - this.$http.get("{% url 'api:shoppinglist-detail' shopping_list_id %}" + ((autosync) ? '?autosync=true' : '')).then((response) => { - if (!autosync) { - this.shopping_list = response.body - this.loading = false - } else { - let check_map = {} - for (let e of response.body.entries) { - check_map[e.id] = {checked: e.checked} + + if (this.shopping_list_id) { + this.$http.get("{% url 'api:shoppinglist-detail' 123456 %}".replace('123456', this.shopping_list_id) + ((autosync) ? '?autosync=true' : '')).then((response) => { + if (!autosync) { + this.shopping_list = response.body + this.loading = false + } else { + let check_map = {} + for (let e of response.body.entries) { + check_map[e.id] = {checked: e.checked} + } + + for (let se of this.shopping_list.entries) { + if (check_map[se.id] !== undefined) { + se.checked = check_map[se.id].checked + } + } } - for (let se of this.shopping_list.entries) { - se.checked = check_map[se.id].checked - } + }).catch((err) => { + console.log(err) + this.makeToast('{% trans 'Error' %}', '{% trans 'There was an error loading a resource!' %}' + err.bodyText, 'danger') + }) + } else { + this.shopping_list = { + "recipes": [], + "entries": [], + "entries_display": [], + "shared": [], + "created_by": 1 } - - }).catch((err) => { - console.log(err) - this.makeToast('{% trans 'Error' %}', '{% trans 'There was an error loading a resource!' %}' + err.bodyText, 'danger') - }) + this.loading = false + } }, updateShoppingList: function () { this.loading = true @@ -382,7 +386,7 @@ recipe_promises.push(this.$http.post("{% url 'api:shoppinglistrecipe-list' %}", this.shopping_list.recipes[i], {}).then((response) => { let old_id = this.shopping_list.recipes[i].id console.log("list recipe create respose ", response.body) - this.shopping_list.recipes[i] = response.body + this.$set(this.shopping_list.recipes, i, response.body) for (let e of this.shopping_list.entries.filter(item => item.list_recipe === old_id)) { console.log("found recipe updating ID") e.list_recipe = this.shopping_list.recipes[i].id @@ -403,6 +407,7 @@ this.makeToast('{% trans 'Updated' %}', '{% trans 'Object created successfully!' %}', 'success') this.loading = false this.shopping_list = response.body + this.shopping_list_id = this.shopping_list.id }).catch((err) => { console.log(err) this.makeToast('{% trans 'Error' %}', '{% trans 'There was an error creating a resource!' %}' + err.bodyText, 'danger') @@ -485,7 +490,7 @@ return '{% url 'view_recipe' 123456 %}'.replace('123456', id) }, addRecipeToList: function (recipe) { - console.log(this.shopping_list) + let slr = { "created": true, @@ -494,12 +499,12 @@ "recipe_name": recipe.name, "multiplier": 1 } + this.shopping_list.recipes.push(slr) for (let s of recipe.steps) { for (let i of s.ingredients) { - - if (!i.is_header) { + if (!i.is_header && i.food !== null) { this.shopping_list.entries.push({ 'list_recipe': slr.id, 'food': i.food, @@ -510,7 +515,6 @@ } } } - }, searchKeywords: function (query) { this.keywords_loading = true diff --git a/cookbook/views/api.py b/cookbook/views/api.py index 0f09d6722..e3a2a154b 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -252,7 +252,7 @@ class ShoppingListRecipeViewSet(viewsets.ModelViewSet): class ShoppingListEntryViewSet(viewsets.ModelViewSet): queryset = ShoppingListEntry.objects.all() serializer_class = ShoppingListEntrySerializer - permission_classes = [CustomIsUser] # TODO add custom validation + permission_classes = [CustomIsOwner] # TODO add custom validation # TODO custom get qs