diff --git a/cookbook/forms.py b/cookbook/forms.py index 1801efd95..9dae6d9b4 100644 --- a/cookbook/forms.py +++ b/cookbook/forms.py @@ -45,7 +45,8 @@ class UserPreferenceForm(forms.ModelForm): fields = ( 'default_unit', 'use_fractions', 'use_kj', 'theme', 'nav_color', 'sticky_navbar', 'default_page', 'show_recent', 'search_style', - 'plan_share', 'ingredient_decimals', 'comments', + 'plan_share', 'shopping_share', 'ingredient_decimals', 'shopping_auto_sync', + 'comments' ) labels = { @@ -74,7 +75,8 @@ class UserPreferenceForm(forms.ModelForm): 'use_kj': _('Display nutritional energy amounts in joules instead of calories'), # noqa: E501 'plan_share': _( 'Users with whom newly created meal plans should be shared by default.'), - 'shopping_share': _('Users with whom to share shopping lists.'), + 'shopping_share': _( + 'Users with whom to share shopping lists.'), # noqa: E501 'show_recent': _('Show recently viewed recipes on search page.'), # noqa: E501 @@ -91,8 +93,7 @@ class UserPreferenceForm(forms.ModelForm): widgets = { 'plan_share': MultiSelectWidget, - 'shopping_share': MultiSelectWidget, - + 'shopping_share': MultiSelectWidget } diff --git a/cookbook/helper/shopping_helper.py b/cookbook/helper/shopping_helper.py index 26d590f85..731b8f35c 100644 --- a/cookbook/helper/shopping_helper.py +++ b/cookbook/helper/shopping_helper.py @@ -1,40 +1,11 @@ -from datetime import timedelta - -from django.contrib.postgres.aggregates import ArrayAgg -from django.db.models import F, OuterRef, Q, Subquery, Value -from django.db.models.functions import Coalesce +from django.db.models import Q from django.utils import timezone -from django.utils.translation import gettext as _ -from cookbook.helper.HelperFunctions import Round, str2bool -from cookbook.models import SupermarketCategoryRelation -from recipes import settings +from cookbook.models import UserPreference def shopping_helper(qs, request): - supermarket = request.query_params.get('supermarket', None) - checked = request.query_params.get('checked', 'recent') - - supermarket_order = ['food__supermarket_category__name', 'food__name'] - - # TODO created either scheduled task or startup task to delete very old shopping list entries - # TODO create user preference to define 'very old' - - # qs = qs.annotate(supermarket_category=Coalesce(F('food__supermarket_category__name'), Value(_('Undefined')))) - # TODO add supermarket to API - order by category order - if supermarket: - supermarket_categories = SupermarketCategoryRelation.objects.filter(supermarket=supermarket, category=OuterRef('food__supermarket_category')) - qs = qs.annotate(supermarket_order=Coalesce(Subquery(supermarket_categories.values('order')), Value(9999))) - supermarket_order = ['supermarket_order'] + supermarket_order - if checked in ['false', 0, '0']: - qs = qs.filter(checked=False) - elif checked in ['true', 1, '1']: - qs = qs.filter(checked=True) - elif checked in ['recent']: - today_start = timezone.now().replace(hour=0, minute=0, second=0) - # TODO make recent a user setting - week_ago = today_start - timedelta(days=7) - qs = qs.filter(Q(checked=False) | Q(completed_at__gte=week_ago)) - supermarket_order = ['checked'] + supermarket_order - - return qs.order_by(*supermarket_order).select_related('unit', 'food', 'ingredient', 'created_by', 'list_recipe', 'list_recipe__mealplan', 'list_recipe__recipe') + today_start = timezone.now().replace(hour=0, minute=0, second=0) + qs = qs.filter(Q(shoppinglist__created_by=request.user) | Q(shoppinglist__shared=request.user)).filter(shoppinglist__space=request.space) + qs = qs.filter(Q(checked=False) | Q(completed_at__gte=today_start)) + return qs diff --git a/cookbook/migrations/0158_auto_20211001_1552.py b/cookbook/migrations/0158_auto_20211001_1552.py new file mode 100644 index 000000000..7916731ef --- /dev/null +++ b/cookbook/migrations/0158_auto_20211001_1552.py @@ -0,0 +1,58 @@ +# Generated by Django 3.2.7 on 2021-10-01 20:52 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +def copy_values_to_sle(apps, schema_editor): + ShoppingListEntry = apps.get_model('app', 'ShoppingListEntry') + db_alias = schema_editor.connection.alias + from django.db.models import F + MetaDataValue.objects.using(db_alias).all().update( + short_value=F('shoppinglist__created_by') + ) + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('cookbook', '0157_alter_searchpreference_trigram'), + ] + + operations = [ + migrations.AddField( + model_name='food', + name='on_hand', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='shoppinglistentry', + name='completed_at', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name='shoppinglistentry', + name='created_at', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='shoppinglistentry', + name='created_by', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='auth.user'), + preserve_default=False, + ), + migrations.AddField( + model_name='shoppinglistentry', + name='recipe', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cookbook.recipe'), + ), + migrations.AddField( + model_name='userpreference', + name='shopping_share', + field=models.ManyToManyField(blank=True, related_name='shopping_share', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/cookbook/models.py b/cookbook/models.py index e33854350..6d0342bf0 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -487,8 +487,6 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin): ignore_shopping = models.BooleanField(default=False) description = models.TextField(default='', blank=True) on_hand = models.BooleanField(default=False) - inherit = models.BooleanField(default=False) - ignore_inherit = models.ManyToManyField(FoodInheritField, blank=True) # is this better as inherit instead of ignore inherit? which is more intuitive? space = models.ForeignKey(Space, on_delete=models.CASCADE) objects = ScopedManager(space='space', _manager_class=TreeManager) @@ -840,10 +838,10 @@ class ShoppingListEntry(ExportModelOperationsMixin('shopping_list_entry'), model amount = models.DecimalField(default=0, decimal_places=16, max_digits=32) order = models.IntegerField(default=0) checked = models.BooleanField(default=False) + recipe = models.ForeignKey(Recipe, on_delete=models.SET_NULL, null=True, blank=True) created_by = models.ForeignKey(User, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) completed_at = models.DateTimeField(null=True, blank=True) - delay_until = models.DateTimeField(null=True, blank=True) space = models.ForeignKey(Space, on_delete=models.CASCADE) objects = ScopedManager(space='space') diff --git a/cookbook/serializer.py b/cookbook/serializer.py index ce21111b9..3d714062a 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -390,10 +390,7 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR class Meta: model = Food - fields = ( - 'id', 'name', 'description', 'shopping', 'recipe', 'ignore_shopping', 'supermarket_category', - 'image', 'parent', 'numchild', 'numrecipe', 'on_hand', 'inherit', 'ignore_inherit', - ) + fields = ('id', 'name', 'description', 'recipe', 'ignore_shopping', 'supermarket_category', 'image', 'parent', 'numchild', 'numrecipe', 'on_hand') read_only_fields = ('id', 'numchild', 'parent', 'image', 'numrecipe') diff --git a/cookbook/tests/api/test_api_shopping_list_entry.py b/cookbook/tests/api/test_api_shopping_list_entry.py index 846f67d86..0cf36597c 100644 --- a/cookbook/tests/api/test_api_shopping_list_entry.py +++ b/cookbook/tests/api/test_api_shopping_list_entry.py @@ -119,13 +119,5 @@ def test_delete(u1_s1, u1_s2, obj_1): assert r.status_code == 204 -# TODO test sharing -# TODO test completed entries still visible if today, but not yesterday -# TODO test create shopping list from recipe -# TODO test delete shopping list from recipe - include created by, shared with and not shared with -# TODO test create shopping list from food -# TODO test delete shopping list from food - include created by, shared with and not shared with -# TODO test create shopping list from mealplan -# TODO test create shopping list from recipe, excluding ingredients -# TODO test auto creating shopping list from meal plan -# TODO test excluding on-hand when auto creating shopping list +# test sharing +# test completed entries still visible if today, but not yesterday \ No newline at end of file