rebase and fixes
This commit is contained in:
parent
ee4ab41c1c
commit
453b1eb5b9
@ -17,7 +17,6 @@ class SelectWidget(widgets.Select):
|
|||||||
|
|
||||||
|
|
||||||
class MultiSelectWidget(widgets.SelectMultiple):
|
class MultiSelectWidget(widgets.SelectMultiple):
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
js = ('custom/js/form_multiselect.js',)
|
js = ('custom/js/form_multiselect.js',)
|
||||||
|
|
||||||
@ -75,13 +74,10 @@ class UserPreferenceForm(forms.ModelForm):
|
|||||||
'Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'),
|
'Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'),
|
||||||
# noqa: E501
|
# noqa: E501
|
||||||
'use_kj': _('Display nutritional energy amounts in joules instead of calories'), # noqa: E501
|
'use_kj': _('Display nutritional energy amounts in joules instead of calories'), # noqa: E501
|
||||||
'plan_share': _(
|
'plan_share': _('Users with whom newly created meal plans should be shared by default.'),
|
||||||
'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
|
# noqa: E501
|
||||||
'show_recent': _('Show recently viewed recipes on search page.'), # noqa: E501
|
'show_recent': _('Show recently viewed recipes on search page.'), # noqa: E501
|
||||||
|
|
||||||
'ingredient_decimals': _('Number of decimals to round ingredients.'), # noqa: E501
|
'ingredient_decimals': _('Number of decimals to round ingredients.'), # noqa: E501
|
||||||
'comments': _('If you want to be able to create and see comments underneath recipes.'), # noqa: E501
|
'comments': _('If you want to be able to create and see comments underneath recipes.'), # noqa: E501
|
||||||
'shopping_auto_sync': _(
|
'shopping_auto_sync': _(
|
||||||
@ -96,7 +92,6 @@ class UserPreferenceForm(forms.ModelForm):
|
|||||||
widgets = {
|
widgets = {
|
||||||
'plan_share': MultiSelectWidget,
|
'plan_share': MultiSelectWidget,
|
||||||
'shopping_share': MultiSelectWidget,
|
'shopping_share': MultiSelectWidget,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -205,6 +205,9 @@ class CustomIsShared(permissions.BasePermission):
|
|||||||
return request.user.is_authenticated
|
return request.user.is_authenticated
|
||||||
|
|
||||||
def has_object_permission(self, request, view, obj):
|
def has_object_permission(self, request, view, obj):
|
||||||
|
# temporary hack to make old shopping list work with new shopping list
|
||||||
|
if obj.__class__.__name__ == 'ShoppingList':
|
||||||
|
return is_object_shared(request.user, obj) or obj.created_by in list(request.user.get_shopping_share())
|
||||||
return is_object_shared(request.user, obj)
|
return is_object_shared(request.user, obj)
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,7 +142,6 @@ class TreeModel(MP_Node):
|
|||||||
return self.get_children().count()
|
return self.get_children().count()
|
||||||
|
|
||||||
# use self.objects.get_or_create() instead
|
# use self.objects.get_or_create() instead
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_root(self, **kwargs):
|
def add_root(self, **kwargs):
|
||||||
with scopes_disabled():
|
with scopes_disabled():
|
||||||
|
@ -4,7 +4,6 @@ from decimal import Decimal
|
|||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db import transaction
|
|
||||||
from django.db.models import Avg, QuerySet, Sum
|
from django.db.models import Avg, QuerySet, Sum
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@ -96,7 +95,9 @@ class CustomDecimalField(serializers.Field):
|
|||||||
class SpaceFilterSerializer(serializers.ListSerializer):
|
class SpaceFilterSerializer(serializers.ListSerializer):
|
||||||
|
|
||||||
def to_representation(self, data):
|
def to_representation(self, data):
|
||||||
if (type(data) == QuerySet and data.query.is_sliced) or not self.context.get('request', None):
|
if self.context.get('request', None) is None:
|
||||||
|
return
|
||||||
|
if (type(data) == QuerySet and data.query.is_sliced):
|
||||||
# if query is sliced it came from api request not nested serializer
|
# if query is sliced it came from api request not nested serializer
|
||||||
return super().to_representation(data)
|
return super().to_representation(data)
|
||||||
if self.child.Meta.model == User:
|
if self.child.Meta.model == User:
|
||||||
@ -158,7 +159,7 @@ class UserPreferenceSerializer(serializers.ModelSerializer):
|
|||||||
plan_share = UserNameSerializer(many=True, allow_null=True, required=False, read_only=True)
|
plan_share = UserNameSerializer(many=True, allow_null=True, required=False, read_only=True)
|
||||||
|
|
||||||
# TODO decide: default inherit field values for foods are being handled via VUE client through user preference
|
# TODO decide: default inherit field values for foods are being handled via VUE client through user preference
|
||||||
## should inherit field instead be set during the django model create?
|
# should inherit field instead be set during the django model create?
|
||||||
def get_ignore_default(self, obj):
|
def get_ignore_default(self, obj):
|
||||||
return FoodInheritFieldSerializer(Food.inherit_fields.difference(obj.space.food_inherit.all()), many=True).data
|
return FoodInheritFieldSerializer(Food.inherit_fields.difference(obj.space.food_inherit.all()), many=True).data
|
||||||
|
|
||||||
@ -307,7 +308,6 @@ class KeywordSerializer(UniqueFieldsMixin, ExtendedRecipeMixin):
|
|||||||
|
|
||||||
|
|
||||||
class UnitSerializer(UniqueFieldsMixin, ExtendedRecipeMixin):
|
class UnitSerializer(UniqueFieldsMixin, ExtendedRecipeMixin):
|
||||||
|
|
||||||
recipe_filter = 'steps__ingredients__unit'
|
recipe_filter = 'steps__ingredients__unit'
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
|
@ -77,16 +77,20 @@ def test_shopping_food_delete(request, arg, food):
|
|||||||
|
|
||||||
def test_shopping_food_share(u1_s1, u2_s1, food, space_1):
|
def test_shopping_food_share(u1_s1, u2_s1, food, space_1):
|
||||||
with scope(space=space_1):
|
with scope(space=space_1):
|
||||||
|
user1 = auth.get_user(u1_s1)
|
||||||
|
user2 = auth.get_user(u2_s1)
|
||||||
food2 = FoodFactory(space=space_1)
|
food2 = FoodFactory(space=space_1)
|
||||||
r = u1_s1.put(reverse(SHOPPING_FOOD_URL, args={food.id}))
|
r = u1_s1.put(reverse(SHOPPING_FOOD_URL, args={food.id}))
|
||||||
r = u2_s1.put(reverse(SHOPPING_FOOD_URL, args={food.id}))
|
r = u2_s1.put(reverse(SHOPPING_FOOD_URL, args={food2.id}))
|
||||||
assert len(json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 1
|
sl_1 = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
|
||||||
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 1
|
sl_2 = json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)
|
||||||
|
assert len(sl_1) == 1
|
||||||
|
assert len(sl_2) == 1
|
||||||
|
sl_1[0]['created_by']['id'] == user1.id
|
||||||
|
sl_2[0]['created_by']['id'] == user2.id
|
||||||
|
|
||||||
with scopes_disabled():
|
with scopes_disabled():
|
||||||
user = auth.get_user(u1_s1)
|
user1.userpreference.shopping_share.add(user2)
|
||||||
user2 = auth.get_user(u2_s1)
|
user1.userpreference.save()
|
||||||
user.userpreference.shopping_share.add(user2)
|
|
||||||
user.userpreference.save()
|
|
||||||
assert len(json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 1
|
assert len(json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 1
|
||||||
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 2
|
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 2
|
||||||
|
@ -5,7 +5,7 @@ from django.contrib import auth
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django_scopes import scopes_disabled
|
from django_scopes import scopes_disabled
|
||||||
|
|
||||||
from cookbook.models import RecipeBook, Storage, Sync, SyncLog, ShoppingList
|
from cookbook.models import RecipeBook, ShoppingList, Storage, Sync, SyncLog
|
||||||
|
|
||||||
LIST_URL = 'api:shoppinglist-list'
|
LIST_URL = 'api:shoppinglist-list'
|
||||||
DETAIL_URL = 'api:shoppinglist-detail'
|
DETAIL_URL = 'api:shoppinglist-detail'
|
||||||
@ -56,6 +56,21 @@ def test_share(obj_1, u1_s1, u2_s1, u1_s2):
|
|||||||
assert u1_s2.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 404
|
assert u1_s2.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 404
|
||||||
|
|
||||||
|
|
||||||
|
def test_new_share(request, obj_1, u1_s1, u2_s1, u1_s2):
|
||||||
|
assert u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 200
|
||||||
|
assert u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 404
|
||||||
|
assert u1_s2.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 404
|
||||||
|
|
||||||
|
with scopes_disabled():
|
||||||
|
user = auth.get_user(u1_s1)
|
||||||
|
user.userpreference.shopping_share.add(auth.get_user(u2_s1))
|
||||||
|
user.userpreference.save()
|
||||||
|
|
||||||
|
assert u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 200
|
||||||
|
assert u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 200
|
||||||
|
assert u1_s2.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 404
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", [
|
@pytest.mark.parametrize("arg", [
|
||||||
['a_u', 403],
|
['a_u', 403],
|
||||||
['g1_s1', 404],
|
['g1_s1', 404],
|
||||||
|
@ -117,18 +117,3 @@ def test_delete(u1_s1, u1_s2, obj_1):
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert r.status_code == 204
|
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 delay
|
|
||||||
# test completed_at when checked
|
|
||||||
# test completed_at cleared when unchecked
|
|
||||||
|
@ -217,8 +217,3 @@ def test_recent(sle, u1_s1):
|
|||||||
r = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?recent=1').content)
|
r = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?recent=1').content)
|
||||||
assert len(r) == 10
|
assert len(r) == 10
|
||||||
assert [x['checked'] for x in r].count(False) == 9
|
assert [x['checked'] for x in r].count(False) == 9
|
||||||
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
@ -15,7 +15,7 @@ from cookbook.tests.factories import FoodFactory, SpaceFactory, UserFactory
|
|||||||
register(SpaceFactory, 'space_1')
|
register(SpaceFactory, 'space_1')
|
||||||
register(SpaceFactory, 'space_2')
|
register(SpaceFactory, 'space_2')
|
||||||
# register(FoodFactory, space=LazyFixture('space_2'))
|
# register(FoodFactory, space=LazyFixture('space_2'))
|
||||||
# TODO refactor user fixtures https://stackoverflow.com/questions/40966571/how-to-create-a-field-with-a-list-of-instances-in-factory-boy
|
# TODO refactor clients to be factories
|
||||||
|
|
||||||
# hack from https://github.com/raphaelm/django-scopes to disable scopes for all fixtures
|
# hack from https://github.com/raphaelm/django-scopes to disable scopes for all fixtures
|
||||||
# does not work on yield fixtures as only one yield can be used per fixture (i think)
|
# does not work on yield fixtures as only one yield can be used per fixture (i think)
|
||||||
|
@ -72,9 +72,8 @@ class UserFactory(factory.django.DjangoModelFactory):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if extracted:
|
if extracted:
|
||||||
# TODO this doesn't work and needs saved
|
|
||||||
for prefs in extracted:
|
for prefs in extracted:
|
||||||
self.userpreference[prefs] = prefs
|
self.userpreference[prefs] = extracted[prefs]/0 # intentionally break so it can be debugged later
|
||||||
self.userpreference.space = self.space
|
self.userpreference.space = self.space
|
||||||
self.userpreference.save()
|
self.userpreference.save()
|
||||||
|
|
||||||
@ -154,7 +153,6 @@ class KeywordFactory(factory.django.DjangoModelFactory):
|
|||||||
@register
|
@register
|
||||||
class IngredientFactory(factory.django.DjangoModelFactory):
|
class IngredientFactory(factory.django.DjangoModelFactory):
|
||||||
"""Ingredient factory."""
|
"""Ingredient factory."""
|
||||||
# TODO add optional recipe food
|
|
||||||
food = factory.SubFactory(FoodFactory, space=factory.SelfAttribute('..space'))
|
food = factory.SubFactory(FoodFactory, space=factory.SelfAttribute('..space'))
|
||||||
unit = factory.SubFactory(UnitFactory, space=factory.SelfAttribute('..space'))
|
unit = factory.SubFactory(UnitFactory, space=factory.SelfAttribute('..space'))
|
||||||
amount = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=10))
|
amount = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=10))
|
||||||
@ -332,7 +330,7 @@ class RecipeFactory(factory.django.DjangoModelFactory):
|
|||||||
if not create:
|
if not create:
|
||||||
return
|
return
|
||||||
|
|
||||||
food_recipe_count = kwargs.get('food_recipe_count', {}) # TODO - pass this
|
food_recipe_count = kwargs.get('food_recipe_count', {})
|
||||||
num_steps = kwargs.get('count', 0)
|
num_steps = kwargs.get('count', 0)
|
||||||
num_recipe_steps = kwargs.get('recipe_count', 0)
|
num_recipe_steps = kwargs.get('recipe_count', 0)
|
||||||
if num_steps > 0:
|
if num_steps > 0:
|
||||||
|
@ -747,12 +747,13 @@ class ShoppingListViewSet(viewsets.ModelViewSet):
|
|||||||
serializer_class = ShoppingListSerializer
|
serializer_class = ShoppingListSerializer
|
||||||
permission_classes = [CustomIsOwner | CustomIsShared]
|
permission_classes = [CustomIsOwner | CustomIsShared]
|
||||||
|
|
||||||
# TODO update to include settings shared user - make both work for a period of time
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(
|
return self.queryset.filter(
|
||||||
space=self.request.space).distinct()
|
Q(created_by=self.request.user)
|
||||||
|
| Q(shared=self.request.user)
|
||||||
|
| Q(created_by__in=list(self.request.user.get_shopping_share()))
|
||||||
|
).filter(space=self.request.space).distinct()
|
||||||
|
|
||||||
# TODO deprecate
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
try:
|
try:
|
||||||
autosync = self.request.query_params.get('autosync', False)
|
autosync = self.request.query_params.get('autosync', False)
|
||||||
|
@ -42,4 +42,4 @@ django-prometheus==2.1.0
|
|||||||
django-hCaptcha==0.1.0
|
django-hCaptcha==0.1.0
|
||||||
python-ldap==3.4.0
|
python-ldap==3.4.0
|
||||||
django-auth-ldap==3.0.0
|
django-auth-ldap==3.0.0
|
||||||
factory-boy==3.2.1
|
pytest-factoryboy==2.1.0
|
||||||
|
14761
vue/package-lock.json
generated
14761
vue/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -89,7 +89,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
config.optimization.minimize(false)
|
config.optimization.minimize(true)
|
||||||
)
|
)
|
||||||
|
|
||||||
config.plugin("BundleTracker").use(BundleTracker, [{ relativePath: true, path: "../vue/" }])
|
config.plugin("BundleTracker").use(BundleTracker, [{ relativePath: true, path: "../vue/" }])
|
||||||
|
Loading…
Reference in New Issue
Block a user