From 5608f802466f3c9b0634ae52c826b130ed33fa50 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Mon, 1 Dec 2025 18:39:40 +0100 Subject: [PATCH] lots of WIP stuff --- cookbook/models.py | 11 +- cookbook/serializer.py | 32 +- cookbook/views/api.py | 19 +- .../components/display/ShoppingLineItem.vue | 38 +- .../components/display/ShoppingListView.vue | 46 +- vue3/src/locales/ar.json | 1 + vue3/src/locales/bg.json | 1 + vue3/src/locales/ca.json | 1 + vue3/src/locales/cs.json | 1 + vue3/src/locales/da.json | 1 + vue3/src/locales/de.json | 1 + vue3/src/locales/el.json | 1 + vue3/src/locales/en.json | 1 + vue3/src/locales/es.json | 1 + vue3/src/locales/fi.json | 1 + vue3/src/locales/fr.json | 1 + vue3/src/locales/he.json | 1 + vue3/src/locales/hr.json | 1 + vue3/src/locales/hu.json | 1 + vue3/src/locales/hy.json | 1 + vue3/src/locales/id.json | 1 + vue3/src/locales/is.json | 1 + vue3/src/locales/it.json | 1751 +++++++++-------- vue3/src/locales/ko.json | 1 + vue3/src/locales/lt.json | 1 + vue3/src/locales/lv.json | 1 + vue3/src/locales/nb_NO.json | 1 + vue3/src/locales/nl.json | 1 + vue3/src/locales/pl.json | 1 + vue3/src/locales/pt.json | 1 + vue3/src/locales/pt_BR.json | 1 + vue3/src/locales/ro.json | 1 + vue3/src/locales/ru.json | 1 + vue3/src/locales/sl.json | 1 + vue3/src/locales/sv.json | 1401 ++++++------- vue3/src/locales/tr.json | 1 + vue3/src/locales/uk.json | 1 + vue3/src/locales/zh_Hans.json | 1 + vue3/src/locales/zh_Hant.json | 1 + vue3/src/stores/ShoppingStore.ts | 12 +- vue3/src/stores/UserPreferenceStore.ts | 1 + vue3/src/types/settings.ts | 3 +- vue3/src/utils/logic_utils.ts | 21 +- 43 files changed, 1751 insertions(+), 1616 deletions(-) diff --git a/cookbook/models.py b/cookbook/models.py index 91f9a0dfc..f53e814e3 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -945,8 +945,8 @@ class Ingredient(ExportModelOperationsMixin('ingredient'), models.Model, Permiss space = models.ForeignKey(Space, on_delete=models.CASCADE) objects = ScopedManager(space='space') - def __str__(self): - return f'{self.pk}: {self.amount} ' + (self.food.name if self.food else ' ') + (self.unit.name if self.unit else '') + # def __str__(self): + # return f'{self.pk}: {self.amount} ' + (self.food.name if self.food else ' ') + (self.unit.name if self.unit else '') class Meta: ordering = ['order', 'pk'] @@ -1299,8 +1299,8 @@ class ShoppingListRecipe(ExportModelOperationsMixin('shopping_list_recipe'), mod objects = ScopedManager(space='space') - def __str__(self): - return f'Shopping list recipe {self.id} - {self.recipe}' + # def __str__(self): + # return f'Shopping list recipe {self.id} - {self.recipe}' class Meta: ordering = ('pk',) @@ -1317,6 +1317,9 @@ class ShoppingList(ExportModelOperationsMixin('shopping_list'), models.Model, Pe space = models.ForeignKey(Space, on_delete=models.CASCADE) objects = ScopedManager(space='space') + class Meta: + ordering = ('pk',) + class ShoppingListEntry(ExportModelOperationsMixin('shopping_list_entry'), models.Model, PermissionModelMixin): shopping_lists = models.ManyToManyField(ShoppingList, blank=True) diff --git a/cookbook/serializer.py b/cookbook/serializer.py index d97389c11..127d8d829 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -186,11 +186,29 @@ class SpaceFilterSerializer(serializers.ListSerializer): if isinstance(self.context['request'].user, AnonymousUser): data = [] else: - data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all() + iterable = data.all() if hasattr(data, 'all') else data + if isinstance(iterable, list) or (isinstance(iterable, QuerySet) and getattr(iterable, '_result_cache', None) is not None): + data = [d for d in iterable if d.userspace.space.id == self.context['request'].space.id] + else: + if hasattr(self.context['request'], 'space'): + data = data.filter(userspace__space=self.context['request'].space).all() + else: + # not sure why but this branch can be hit (just normal page load, need to see why) + data = data.filter(userspace__space=self.context['request'].user.get_active_space()).all() + elif isinstance(data, list): data = [d for d in data if getattr(d, self.child.Meta.model.get_space_key()[0]) == self.context['request'].space] else: - data = data.filter(**{'__'.join(self.child.Meta.model.get_space_key()): self.context['request'].space}) + iterable = data.all() if hasattr(data, 'all') else data + if isinstance(iterable, list) or (isinstance(iterable, QuerySet) and getattr(iterable, '_result_cache', None) is not None): + keys = self.child.Meta.model.get_space_key() + if keys == ('space',): + data = [d for d in iterable if getattr(d, 'space_id') == self.context['request'].space.id] + else: + # use cached results here too, just dont have time to test this now, probably obj.get_space() + data = data.filter(**{'__'.join(self.child.Meta.model.get_space_key()): self.context['request'].space}) + else: + data = data.filter(**{'__'.join(self.child.Meta.model.get_space_key()): self.context['request'].space}) return super().to_representation(data) @@ -648,7 +666,7 @@ class KeywordLabelSerializer(serializers.ModelSerializer): @extend_schema_field(str) def get_label(self, obj): - return str(obj) + return obj.name class Meta: list_serializer_class = SpaceFilterSerializer @@ -665,7 +683,7 @@ class KeywordSerializer(UniqueFieldsMixin, ExtendedRecipeMixin): @extend_schema_field(str) def get_label(self, obj): - return str(obj) + return obj.name def create(self, validated_data): # since multi select tags dont have id's @@ -1327,7 +1345,7 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer): @extend_schema_field(bool) def in_shopping(self, obj): - return ShoppingListRecipe.objects.filter(mealplan=obj.id).exists() + return obj.shoppinglistrecipe_set.count() > 0 def create(self, validated_data): validated_data['created_by'] = self.context['request'].user @@ -1393,7 +1411,7 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer): class Meta: model = ShoppingListRecipe - fields = ('id', 'name', 'recipe', 'recipe_data', 'mealplan', 'meal_plan_data', 'servings', 'created_by',) + fields = ('id', 'name', 'recipe', 'recipe_data', 'meal_plan_data', 'mealplan', 'servings', 'created_by',) read_only_fields = ('id', 'created_by',) @@ -1412,7 +1430,7 @@ class ShoppingListSerializer(SpacedModelSerializer, WritableNestedModelSerialize class ShoppingListEntrySerializer(WritableNestedModelSerializer): - food = FoodSerializer(allow_null=True) + food = FoodSimpleSerializer(allow_null=True) unit = UnitSerializer(allow_null=True, required=False) shopping_lists = ShoppingListSerializer(many=True, required=False) list_recipe_data = ShoppingListRecipeSerializer(source='list_recipe', read_only=True) diff --git a/cookbook/views/api.py b/cookbook/views/api.py index 417cf53a3..4dd0b2d9c 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -2043,17 +2043,18 @@ class ShoppingListEntryViewSet(LoggingMixin, viewsets.ModelViewSet): self.queryset = self.queryset.filter( Q(created_by=self.request.user) - | Q(created_by__in=list(self.request.user.get_shopping_share()))).prefetch_related('created_by', 'food', - 'food__properties', - 'food__properties__property_type', - 'food__inherit_fields', - 'food__supermarket_category', - 'food__onhand_users', - 'food__substitute', - 'food__child_inherit_fields', - 'unit', 'list_recipe', + | Q(created_by__in=list(self.request.user.get_shopping_share()))).prefetch_related('created_by', + 'food', + 'shopping_lists', + 'unit', + 'list_recipe', + 'list_recipe__recipe__keywords', + 'list_recipe__recipe__created_by', 'list_recipe__mealplan', + 'list_recipe__mealplan__shared', + 'list_recipe__mealplan__shoppinglistrecipe_set', 'list_recipe__mealplan__recipe', + 'list_recipe__mealplan__recipe__keywords', ).distinct().all() updated_after = self.request.query_params.get('updated_after', None) diff --git a/vue3/src/components/display/ShoppingLineItem.vue b/vue3/src/components/display/ShoppingLineItem.vue index 30c5428b1..7387e2ffc 100644 --- a/vue3/src/components/display/ShoppingLineItem.vue +++ b/vue3/src/components/display/ShoppingLineItem.vue @@ -1,14 +1,17 @@