added bulk api endpoint for SLE checking

This commit is contained in:
vabene1111 2024-01-16 08:06:26 +08:00
parent 2b1eda12d1
commit 2697e42af7
4 changed files with 260 additions and 3816 deletions

View File

@ -57,7 +57,8 @@ class ExtendedRecipeMixin(serializers.ModelSerializer):
api_serializer = None
# extended values are computationally expensive and not needed in normal circumstances
try:
if str2bool(self.context['request'].query_params.get('extended', False)) and self.__class__ == api_serializer:
if str2bool(
self.context['request'].query_params.get('extended', False)) and self.__class__ == api_serializer:
return fields
except (AttributeError, KeyError):
pass
@ -81,12 +82,14 @@ class ExtendedRecipeMixin(serializers.ModelSerializer):
class OpenDataModelMixin(serializers.ModelSerializer):
def create(self, validated_data):
if 'open_data_slug' in validated_data and validated_data['open_data_slug'] is not None and validated_data['open_data_slug'].strip() == '':
if 'open_data_slug' in validated_data and validated_data['open_data_slug'] is not None and validated_data[
'open_data_slug'].strip() == '':
validated_data['open_data_slug'] = None
return super().create(validated_data)
def update(self, instance, validated_data):
if 'open_data_slug' in validated_data and validated_data['open_data_slug'] is not None and validated_data['open_data_slug'].strip() == '':
if 'open_data_slug' in validated_data and validated_data['open_data_slug'] is not None and validated_data[
'open_data_slug'].strip() == '':
validated_data['open_data_slug'] = None
return super().update(instance, validated_data)
@ -122,7 +125,8 @@ class CustomOnHandField(serializers.Field):
if not self.context["request"].user.is_authenticated:
return []
shared_users = []
if c := caches['default'].get(f'shopping_shared_users_{self.context["request"].space.id}_{self.context["request"].user.id}', None):
if c := caches['default'].get(
f'shopping_shared_users_{self.context["request"].space.id}_{self.context["request"].user.id}', None):
shared_users = c
else:
try:
@ -312,7 +316,8 @@ class SpaceSerializer(WritableNestedModelSerializer):
'id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users',
'allow_sharing', 'demo', 'food_inherit', 'user_count', 'recipe_count', 'file_size_mb',
'image', 'nav_logo', 'space_theme', 'custom_space_theme', 'nav_bg_color', 'nav_text_color', 'use_plural',
'logo_color_32', 'logo_color_128', 'logo_color_144', 'logo_color_180', 'logo_color_192', 'logo_color_512', 'logo_color_svg',)
'logo_color_32', 'logo_color_128', 'logo_color_144', 'logo_color_180', 'logo_color_192', 'logo_color_512',
'logo_color_svg',)
read_only_fields = (
'id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing',
'demo',)
@ -332,7 +337,8 @@ class UserSpaceSerializer(WritableNestedModelSerializer):
class Meta:
model = UserSpace
fields = ('id', 'user', 'space', 'groups', 'active', 'internal_note', 'invite_link', 'created_at', 'updated_at',)
fields = (
'id', 'user', 'space', 'groups', 'active', 'internal_note', 'invite_link', 'created_at', 'updated_at',)
read_only_fields = ('id', 'invite_link', 'created_at', 'updated_at', 'space')
@ -348,7 +354,8 @@ class MealTypeSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
validated_data['name'] = validated_data['name'].strip()
space = validated_data.pop('space', self.context['request'].space)
validated_data['created_by'] = self.context['request'].user
obj, created = MealType.objects.get_or_create(name__iexact=validated_data['name'], space=space, defaults=validated_data)
obj, created = MealType.objects.get_or_create(name__iexact=validated_data['name'], space=space,
defaults=validated_data)
return obj
class Meta:
@ -382,13 +389,15 @@ class UserPreferenceSerializer(WritableNestedModelSerializer):
class Meta:
model = UserPreference
fields = (
'user', 'image', 'theme', 'nav_bg_color', 'nav_text_color', 'nav_show_logo', 'default_unit', 'default_page', 'use_fractions', 'use_kj',
'user', 'image', 'theme', 'nav_bg_color', 'nav_text_color', 'nav_show_logo', 'default_unit', 'default_page',
'use_fractions', 'use_kj',
'plan_share', 'nav_sticky',
'ingredient_decimals', 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping',
'food_inherit_default', 'default_delay',
'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days',
'csv_delim', 'csv_prefix',
'filter_to_supermarket', 'shopping_add_onhand', 'left_handed', 'show_step_ingredients', 'food_children_exist'
'filter_to_supermarket', 'shopping_add_onhand', 'left_handed', 'show_step_ingredients',
'food_children_exist'
)
@ -477,10 +486,13 @@ class UnitSerializer(UniqueFieldsMixin, ExtendedRecipeMixin, OpenDataModelMixin)
if x := validated_data.get('name', None):
validated_data['plural_name'] = x.strip()
if unit := Unit.objects.filter(Q(name__iexact=validated_data['name']) | Q(plural_name__iexact=validated_data['name']), space=space).first():
if unit := Unit.objects.filter(
Q(name__iexact=validated_data['name']) | Q(plural_name__iexact=validated_data['name']),
space=space).first():
return unit
obj, created = Unit.objects.get_or_create(name__iexact=validated_data['name'], space=space, defaults=validated_data)
obj, created = Unit.objects.get_or_create(name__iexact=validated_data['name'], space=space,
defaults=validated_data)
return obj
def update(self, instance, validated_data):
@ -500,7 +512,8 @@ class SupermarketCategorySerializer(UniqueFieldsMixin, WritableNestedModelSerial
def create(self, validated_data):
validated_data['name'] = validated_data['name'].strip()
space = validated_data.pop('space', self.context['request'].space)
obj, created = SupermarketCategory.objects.get_or_create(name__iexact=validated_data['name'], space=space, defaults=validated_data)
obj, created = SupermarketCategory.objects.get_or_create(name__iexact=validated_data['name'], space=space,
defaults=validated_data)
return obj
def update(self, instance, validated_data):
@ -525,7 +538,8 @@ class SupermarketSerializer(UniqueFieldsMixin, SpacedModelSerializer, OpenDataMo
def create(self, validated_data):
validated_data['name'] = validated_data['name'].strip()
space = validated_data.pop('space', self.context['request'].space)
obj, created = Supermarket.objects.get_or_create(name__iexact=validated_data['name'], space=space, defaults=validated_data)
obj, created = Supermarket.objects.get_or_create(name__iexact=validated_data['name'], space=space,
defaults=validated_data)
return obj
class Meta:
@ -540,7 +554,8 @@ class PropertyTypeSerializer(OpenDataModelMixin, WritableNestedModelSerializer,
def create(self, validated_data):
validated_data['name'] = validated_data['name'].strip()
space = validated_data.pop('space', self.context['request'].space)
obj, created = PropertyType.objects.get_or_create(name__iexact=validated_data['name'], space=space, defaults=validated_data)
obj, created = PropertyType.objects.get_or_create(name__iexact=validated_data['name'], space=space,
defaults=validated_data)
return obj
class Meta:
@ -665,12 +680,14 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
properties = validated_data.pop('properties', None)
obj, created = Food.objects.get_or_create(name=name, plural_name=plural_name, space=space, properties_food_unit=properties_food_unit,
obj, created = Food.objects.get_or_create(name=name, plural_name=plural_name, space=space,
properties_food_unit=properties_food_unit,
defaults=validated_data)
if properties and len(properties) > 0:
for p in properties:
obj.properties.add(Property.objects.create(property_type_id=p['property_type']['id'], property_amount=p['property_amount'], space=space))
obj.properties.add(Property.objects.create(property_type_id=p['property_type']['id'],
property_amount=p['property_amount'], space=space))
return obj
@ -702,7 +719,8 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
'properties', 'properties_food_amount', 'properties_food_unit', 'fdc_id',
'food_onhand', 'supermarket_category',
'image', 'parent', 'numchild', 'numrecipe', 'inherit_fields', 'full_name', 'ignore_shopping',
'substitute', 'substitute_siblings', 'substitute_children', 'substitute_onhand', 'child_inherit_fields', 'open_data_slug',
'substitute', 'substitute_siblings', 'substitute_children', 'substitute_onhand', 'child_inherit_fields',
'open_data_slug',
)
read_only_fields = ('id', 'numchild', 'parent', 'image', 'numrecipe')
@ -726,7 +744,8 @@ class IngredientSimpleSerializer(WritableNestedModelSerializer):
uch = UnitConversionHelper(self.context['request'].space)
conversions = []
for c in uch.get_conversions(obj):
conversions.append({'food': c.food.name, 'unit': c.unit.name, 'amount': c.amount}) # TODO do formatting in helper
conversions.append(
{'food': c.food.name, 'unit': c.unit.name, 'amount': c.amount}) # TODO do formatting in helper
return conversions
else:
return []
@ -828,7 +847,8 @@ class UnitConversionSerializer(WritableNestedModelSerializer, OpenDataModelMixin
class Meta:
model = UnitConversion
fields = ('id', 'name', 'base_amount', 'base_unit', 'converted_amount', 'converted_unit', 'food', 'open_data_slug')
fields = (
'id', 'name', 'base_amount', 'base_unit', 'converted_amount', 'converted_unit', 'food', 'open_data_slug')
class NutritionInformationSerializer(serializers.ModelSerializer):
@ -898,7 +918,8 @@ class RecipeSerializer(RecipeBaseSerializer):
fields = (
'id', 'name', 'description', 'image', 'keywords', 'steps', 'working_time',
'waiting_time', 'created_by', 'created_at', 'updated_at', 'source_url',
'internal', 'show_ingredient_overview', 'nutrition', 'properties', 'food_properties', 'servings', 'file_path', 'servings_text', 'rating',
'internal', 'show_ingredient_overview', 'nutrition', 'properties', 'food_properties', 'servings',
'file_path', 'servings_text', 'rating',
'last_cooked',
'private', 'shared',
)
@ -977,7 +998,8 @@ class RecipeBookEntrySerializer(serializers.ModelSerializer):
def create(self, validated_data):
book = validated_data['book']
recipe = validated_data['recipe']
if not book.get_owner() == self.context['request'].user and not self.context['request'].user in book.get_shared():
if not book.get_owner() == self.context['request'].user and not self.context[
'request'].user in book.get_shared():
raise NotFound(detail=None, code=None)
obj, created = RecipeBookEntry.objects.get_or_create(book=book, recipe=recipe)
return obj
@ -1066,7 +1088,8 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer):
class Meta:
model = ShoppingListRecipe
fields = ('id', 'recipe_name', 'name', 'recipe', 'mealplan', 'servings', 'mealplan_note', 'mealplan_from_date', 'mealplan_type')
fields = ('id', 'recipe_name', 'name', 'recipe', 'mealplan', 'servings', 'mealplan_note', 'mealplan_from_date',
'mealplan_type')
read_only_fields = ('id',)
@ -1132,6 +1155,11 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
read_only_fields = ('id', 'created_by', 'created_at',)
class ShoppingListEntryBulkSerializer(serializers.Serializer):
ids = serializers.ListField()
checked = serializers.BooleanField()
# TODO deprecate
class ShoppingListEntryCheckedSerializer(serializers.ModelSerializer):
class Meta:
@ -1284,7 +1312,8 @@ class InviteLinkSerializer(WritableNestedModelSerializer):
class Meta:
model = InviteLink
fields = (
'id', 'uuid', 'email', 'group', 'valid_until', 'used_by', 'reusable', 'internal_note', 'created_by', 'created_at',)
'id', 'uuid', 'email', 'group', 'valid_until', 'used_by', 'reusable', 'internal_note', 'created_by',
'created_at',)
read_only_fields = ('id', 'uuid', 'created_by', 'created_at',)

View File

@ -103,7 +103,8 @@ from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer,
SupermarketCategorySerializer, SupermarketSerializer,
SyncLogSerializer, SyncSerializer, UnitConversionSerializer,
UnitSerializer, UserFileSerializer, UserPreferenceSerializer,
UserSerializer, UserSpaceSerializer, ViewLogSerializer)
UserSerializer, UserSpaceSerializer, ViewLogSerializer,
ShoppingListEntryBulkSerializer)
from cookbook.views.import_export import get_integration
from recipes import settings
from recipes.settings import FDC_API_KEY, DRF_THROTTLE_RECIPE_URL_IMPORT
@ -1175,6 +1176,22 @@ class ShoppingListEntryViewSet(viewsets.ModelViewSet):
# TODO once old shopping list is removed this needs updated to sharing users in preferences
return self.queryset
@decorators.action(
detail=False,
methods=['POST'],
serializer_class=ShoppingListEntryBulkSerializer,
)
def bulk(self, request):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
ShoppingListEntry.objects.filter(
Q(created_by=self.request.user)
| Q(shoppinglist__shared=self.request.user)
| Q(created_by__in=list(self.request.user.get_shopping_share()))
).filter(space=request.space, id__in=serializer.validated_data['ids']).update(checked=serializer.validated_data['checked'])
return Response(serializer.data)
else:
return Response(serializer.errors, 400)
# TODO deprecate
class ShoppingListViewSet(viewsets.ModelViewSet):

View File

@ -299,10 +299,19 @@ export const useShoppingListStore = defineStore(_STORE_ID, {
this.registerChange((checked ? 'CHECKED' : 'UNCHECKED'), entries)
}
let entry_id_list = []
for (let i in entries) {
this.entries[i].checked = checked
this.updateObject(this.entries[i])
Vue.set(this.entries[i], 'checked', checked)
Vue.set(this.entries[i], 'update_at', moment().format())
entry_id_list.push(i)
}
let apiClient = new ApiApiFactory()
apiClient.bulkShoppingListEntry({'ids': entry_id_list, 'checked': checked}).then((r) => {
}).catch((err) => {
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err)
})
},
/**
* function to handle user "delaying" and "undelaying" shopping entries

File diff suppressed because it is too large Load Diff