wip shopping dialog

This commit is contained in:
vabene1111
2024-12-22 18:36:56 +01:00
parent d611391bea
commit e0223e0c5c
13 changed files with 520 additions and 42 deletions

View File

@@ -1139,7 +1139,7 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer):
fields = ( fields = (
'id', 'title', 'recipe', 'servings', 'note', 'note_markdown', 'id', 'title', 'recipe', 'servings', 'note', 'note_markdown',
'from_date', 'to_date', 'meal_type', 'created_by', 'shared', 'recipe_name', 'from_date', 'to_date', 'meal_type', 'created_by', 'shared', 'recipe_name',
'meal_type_name', 'shopping','addshopping' 'meal_type_name', 'shopping', 'addshopping'
) )
read_only_fields = ('created_by',) read_only_fields = ('created_by',)
@@ -1155,26 +1155,12 @@ class AutoMealPlanSerializer(serializers.Serializer):
class ShoppingListRecipeSerializer(serializers.ModelSerializer): class ShoppingListRecipeSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField('get_name') # should this be done at the front end?
recipe_name = serializers.ReadOnlyField(source='recipe.name') recipe_name = serializers.ReadOnlyField(source='recipe.name')
mealplan_note = serializers.ReadOnlyField(source='mealplan.note') mealplan_note = serializers.ReadOnlyField(source='mealplan.note')
mealplan_from_date = serializers.ReadOnlyField(source='mealplan.from_date') mealplan_from_date = serializers.ReadOnlyField(source='mealplan.from_date')
mealplan_type = serializers.ReadOnlyField(source='mealplan.meal_type.name') mealplan_type = serializers.ReadOnlyField(source='mealplan.meal_type.name')
servings = CustomDecimalField() servings = CustomDecimalField()
@extend_schema_field(str)
def get_name(self, obj):
if not isinstance(value := obj.servings, Decimal):
value = Decimal(value)
value = value.quantize(
Decimal(1)) if value == value.to_integral() else value.normalize() # strips trailing zero
return (
obj.name
or getattr(obj.mealplan, 'title', None)
or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)])
or obj.recipe.name
) + f' ({value:.2g})'
def update(self, instance, validated_data): def update(self, instance, validated_data):
# TODO remove once old shopping list # TODO remove once old shopping list
if 'servings' in validated_data and self.context.get('view', None).__class__.__name__ != 'ShoppingListViewSet': if 'servings' in validated_data and self.context.get('view', None).__class__.__name__ != 'ShoppingListViewSet':
@@ -1252,6 +1238,16 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
read_only_fields = ('id', 'created_by', 'created_at') read_only_fields = ('id', 'created_by', 'created_at')
class ShoppingListEntrySimpleCreateSerializer(serializers.Serializer):
amount = CustomDecimalField()
unit_id = serializers.IntegerField(allow_null=True)
food_id = serializers.IntegerField(allow_null=True)
class ShoppingListEntryBulkCreateSerializer(serializers.Serializer):
entries = serializers.ListField(child=ShoppingListEntrySimpleCreateSerializer())
class ShoppingListEntryBulkSerializer(serializers.Serializer): class ShoppingListEntryBulkSerializer(serializers.Serializer):
ids = serializers.ListField() ids = serializers.ListField()
checked = serializers.BooleanField() checked = serializers.BooleanField()
@@ -1537,8 +1533,8 @@ class RecipeExportSerializer(WritableNestedModelSerializer):
class RecipeShoppingUpdateSerializer(serializers.ModelSerializer): class RecipeShoppingUpdateSerializer(serializers.ModelSerializer):
list_recipe = serializers.IntegerField(write_only=True, allow_null=True, required=False, list_recipe = serializers.IntegerField(write_only=True, allow_null=True, required=False,
help_text=_("Existing shopping list to update")) help_text=_("Existing shopping list to update"))
ingredients = serializers.IntegerField(write_only=True, allow_null=True, required=False, help_text=_( ingredients = serializers.ListField(child=serializers.IntegerField(write_only=True, allow_null=True, required=False, help_text=_(
"List of ingredient IDs from the recipe to add, if not provided all ingredients will be added.")) "List of ingredient IDs from the recipe to add, if not provided all ingredients will be added.")))
servings = serializers.IntegerField(default=1, write_only=True, allow_null=True, required=False, help_text=_( servings = serializers.IntegerField(default=1, write_only=True, allow_null=True, required=False, help_text=_(
"Providing a list_recipe ID and servings of 0 will delete that shopping list.")) "Providing a list_recipe ID and servings of 0 will delete that shopping list."))
@@ -1627,9 +1623,10 @@ class SourceImportDuplicateSerializer(serializers.Serializer):
id = serializers.IntegerField() id = serializers.IntegerField()
name = serializers.CharField() name = serializers.CharField()
class RecipeFromSourceResponseSerializer(serializers.Serializer): class RecipeFromSourceResponseSerializer(serializers.Serializer):
recipe = SourceImportRecipeSerializer(default=None) recipe = SourceImportRecipeSerializer(default=None)
images = serializers.ListField(child=serializers.CharField(),default=[], allow_null=False) images = serializers.ListField(child=serializers.CharField(), default=[], allow_null=False)
error = serializers.BooleanField(default=False) error = serializers.BooleanField(default=False)
msg = serializers.CharField(max_length=1024, default='') msg = serializers.CharField(max_length=1024, default='')
duplicates = serializers.ListField(child=SourceImportDuplicateSerializer(), default=[], allow_null=False) duplicates = serializers.ListField(child=SourceImportDuplicateSerializer(), default=[], allow_null=False)

View File

@@ -105,7 +105,7 @@ from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer, Au
SupermarketSerializer, SyncLogSerializer, SyncSerializer, SupermarketSerializer, SyncLogSerializer, SyncSerializer,
UnitConversionSerializer, UnitSerializer, UserFileSerializer, UserPreferenceSerializer, UnitConversionSerializer, UnitSerializer, UserFileSerializer, UserPreferenceSerializer,
UserSerializer, UserSpaceSerializer, ViewLogSerializer, ImportImageSerializer, UserSerializer, UserSpaceSerializer, ViewLogSerializer, ImportImageSerializer,
LocalizationSerializer, ServerSettingsSerializer, RecipeFromSourceResponseSerializer LocalizationSerializer, ServerSettingsSerializer, RecipeFromSourceResponseSerializer, ShoppingListEntryBulkCreateSerializer
) )
from cookbook.version_info import TANDOOR_VERSION from cookbook.version_info import TANDOOR_VERSION
from cookbook.views.import_export import get_integration from cookbook.views.import_export import get_integration
@@ -1289,7 +1289,8 @@ class RecipeViewSet(LoggingMixin, viewsets.ModelViewSet):
return Response(content, status=http_status) return Response(content, status=http_status)
@decorators.action(detail=True, methods=['GET'], serializer_class=RecipeSimpleSerializer) @extend_schema(responses=RecipeSimpleSerializer(many=True))
@decorators.action(detail=True, pagination_class=None, methods=['GET'], serializer_class=RecipeSimpleSerializer)
def related(self, request, pk): def related(self, request, pk):
obj = self.get_object() obj = self.get_object()
if obj.get_space() != request.space: if obj.get_space() != request.space:
@@ -1371,8 +1372,34 @@ class ShoppingListRecipeViewSet(LoggingMixin, viewsets.ModelViewSet):
self.queryset = self.queryset.filter(Q(entries__space=self.request.space) | Q(recipe__space=self.request.space)) self.queryset = self.queryset.filter(Q(entries__space=self.request.space) | Q(recipe__space=self.request.space))
return self.queryset.filter(Q(entries__isnull=True) return self.queryset.filter(Q(entries__isnull=True)
| Q(entries__created_by=self.request.user) | Q(entries__created_by=self.request.user)
| Q( | Q(entries__created_by__in=list(self.request.user.get_shopping_share()))).distinct().all()
entries__created_by__in=list(self.request.user.get_shopping_share()))).distinct().all()
@decorators.action(detail=True, methods=['POST'], serializer_class=ShoppingListEntryBulkCreateSerializer, permission_classes=[CustomIsUser])
def bulk_create_entries(self, request, pk):
obj = self.get_object()
if obj.get_space() != request.space:
raise PermissionDenied(detail='You do not have the required permission to perform this action', code=403)
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
entries = []
for e in serializer.validated_data['entries']:
entries.append(
ShoppingListEntry(
list_recipe_id=obj.pk,
amount=e['amount'],
unit_id=e['unit_id'],
food_id=e['food_id'],
created_by_id=request.user.id,
space_id=request.space.id,
)
)
ShoppingListEntry.objects.bulk_create(entries)
return Response(serializer.validated_data)
else:
return Response(serializer.errors, 400)
@extend_schema_view(list=extend_schema(parameters=[ @extend_schema_view(list=extend_schema(parameters=[

View File

@@ -1,9 +1,133 @@
<template> <template>
<v-dialog activator="parent" max-width="600px" v-model="dialog">
<v-card :loading="loading">
<v-closable-card-title :title="$t('Add_Servings_to_Shopping', servings)" v-model="dialog"></v-closable-card-title>
<v-card-text>
<v-expansion-panels variant="accordion">
<v-expansion-panel>
<v-expansion-panel-title>{{ recipe.name }}</v-expansion-panel-title>
<v-expansion-panel-text>
<v-list>
<template v-for="s in recipe.steps">
<v-list-item v-for="i in s.ingredients">
{{ i }}
</v-list-item>
</template>
</v-list>
</v-expansion-panel-text>
</v-expansion-panel>
<v-expansion-panel v-for="r in relatedRecipes">
<v-expansion-panel-title>{{ r.name }}</v-expansion-panel-title>
<v-expansion-panel-text>
<v-list>
<template v-for="s in r.steps">
<v-list-item v-for="i in s.ingredients">{{ i }}</v-list-item>
</template>
</v-list>
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
<v-number-input v-model="servings" class="mt-3" control-variant="split" :label="$t('Servings')"></v-number-input>
</v-card-text>
<v-card-actions>
<v-btn class="float-right" prepend-icon="$create" color="create" @click="createShoppingListRecipe()">{{$t('Add_to_Shopping')}}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {computed, onMounted, PropType, ref} from "vue";
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
import {ApiApi, Recipe, RecipeFlat, RecipeOverview, type RecipeShoppingUpdate, type ShoppingListEntryBulkCreate, ShoppingListRecipe} from "@/openapi";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
import {VNumberInput} from 'vuetify/labs/VNumberInput'
const props = defineProps({
recipe: {type: Object as PropType<Recipe | RecipeFlat | RecipeOverview>, required: true},
})
const dialog = ref(false)
const loading = ref(false)
const servings = ref(1)
const recipe = ref({} as Recipe)
const relatedRecipes = ref([] as Recipe[])
onMounted(() => {
loadRecipeData()
})
/**
* load data for the given recipe and all of its related recipes
*/
function loadRecipeData() {
let api = new ApiApi()
let promises: Promise<any>[] = []
let recipeRequest = api.apiRecipeRetrieve({id: props.recipe.id!}).then(r => {
recipe.value = r
servings.value = r.servings ? r.servings : 1
console.log('main loaded')
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
})
promises.push(recipeRequest)
api.apiRecipeRelatedList({id: props.recipe.id!}).then(r => {
r.forEach(rs => {
let p = api.apiRecipeRetrieve({id: rs.id!}).then(recipe => {
relatedRecipes.value.push(recipe)
console.log('related loaded', recipe.name)
})
promises.push(p)
})
Promise.allSettled(promises).then(() => {
console.log('ALL LOADED')
loading.value = false
})
})
}
/**
* creates a shopping list recipe from all selected ingredients
*/
function createShoppingListRecipe() {
let api = new ApiApi()
let shoppingListRecipe = {
recipe: props.recipe.id,
servings: servings.value,
} as ShoppingListRecipe
let shoppingListEntries = {
entries: []
} as ShoppingListEntryBulkCreate
recipe.value.steps.forEach(step => {
step.ingredients.forEach(ingredient => {
shoppingListEntries.entries.push({
amount: ingredient.amount * (servings.value / (recipe.value.servings ? recipe.value.servings : 1)),
foodId: ingredient.food ? ingredient.food.id! : null,
unitId: ingredient.unit ? ingredient.unit.id! : null
})
})
})
api.apiShoppingListRecipeCreate({shoppingListRecipe: shoppingListRecipe}).then(slr => {
api.apiShoppingListRecipeBulkCreateEntriesCreate({id: slr.id!, shoppingListEntryBulkCreate: shoppingListEntries}).then(r => {
}).catch(err => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
})
}).catch(err => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
})
}
</script> </script>
<style scoped> <style scoped>

View File

@@ -10,6 +10,10 @@
{{ $t('Add_to_Plan') }} {{ $t('Add_to_Plan') }}
<model-edit-dialog model="MealPlan" :itemDefaults="{recipe: recipe}"></model-edit-dialog> <model-edit-dialog model="MealPlan" :itemDefaults="{recipe: recipe}"></model-edit-dialog>
</v-list-item> </v-list-item>
<v-list-item prepend-icon="$shopping" link>
{{ $t('Add_to_Shopping') }}
<add-to-shopping-dialog :recipe="props.recipe"></add-to-shopping-dialog>
</v-list-item>
<v-list-item prepend-icon="fa-solid fa-share-nodes" link> <v-list-item prepend-icon="fa-solid fa-share-nodes" link>
{{ $t('Share') }} {{ $t('Share') }}
<recipe-share-dialog :recipe="props.recipe"></recipe-share-dialog> <recipe-share-dialog :recipe="props.recipe"></recipe-share-dialog>
@@ -26,6 +30,7 @@ import {PropType} from 'vue'
import {Recipe, RecipeFlat, RecipeOverview} from "@/openapi"; import {Recipe, RecipeFlat, RecipeOverview} from "@/openapi";
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue"; import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
import RecipeShareDialog from "@/components/dialogs/RecipeShareDialog.vue"; import RecipeShareDialog from "@/components/dialogs/RecipeShareDialog.vue";
import AddToShoppingDialog from "@/components/dialogs/AddToShoppingDialog.vue";
const props = defineProps({ const props = defineProps({

View File

@@ -133,6 +133,8 @@ models/ServerSettings.ts
models/ShareLink.ts models/ShareLink.ts
models/ShoppingListEntry.ts models/ShoppingListEntry.ts
models/ShoppingListEntryBulk.ts models/ShoppingListEntryBulk.ts
models/ShoppingListEntryBulkCreate.ts
models/ShoppingListEntrySimpleCreate.ts
models/ShoppingListRecipe.ts models/ShoppingListRecipe.ts
models/SourceImportDuplicate.ts models/SourceImportDuplicate.ts
models/SourceImportFood.ts models/SourceImportFood.ts

View File

@@ -131,6 +131,7 @@ import type {
ShareLink, ShareLink,
ShoppingListEntry, ShoppingListEntry,
ShoppingListEntryBulk, ShoppingListEntryBulk,
ShoppingListEntryBulkCreate,
ShoppingListRecipe, ShoppingListRecipe,
Space, Space,
Step, Step,
@@ -381,6 +382,8 @@ import {
ShoppingListEntryToJSON, ShoppingListEntryToJSON,
ShoppingListEntryBulkFromJSON, ShoppingListEntryBulkFromJSON,
ShoppingListEntryBulkToJSON, ShoppingListEntryBulkToJSON,
ShoppingListEntryBulkCreateFromJSON,
ShoppingListEntryBulkCreateToJSON,
ShoppingListRecipeFromJSON, ShoppingListRecipeFromJSON,
ShoppingListRecipeToJSON, ShoppingListRecipeToJSON,
SpaceFromJSON, SpaceFromJSON,
@@ -1214,7 +1217,7 @@ export interface ApiRecipePartialUpdateRequest {
patchedRecipe?: Omit<PatchedRecipe, 'image'|'created_by'|'created_at'|'updated_at'|'food_properties'|'rating'|'last_cooked'>; patchedRecipe?: Omit<PatchedRecipe, 'image'|'created_by'|'created_at'|'updated_at'|'food_properties'|'rating'|'last_cooked'>;
} }
export interface ApiRecipeRelatedRetrieveRequest { export interface ApiRecipeRelatedListRequest {
id: number; id: number;
} }
@@ -1224,7 +1227,7 @@ export interface ApiRecipeRetrieveRequest {
export interface ApiRecipeShoppingUpdateRequest { export interface ApiRecipeShoppingUpdateRequest {
id: number; id: number;
recipeShoppingUpdate?: RecipeShoppingUpdate; recipeShoppingUpdate: RecipeShoppingUpdate;
} }
export interface ApiRecipeUpdateRequest { export interface ApiRecipeUpdateRequest {
@@ -1268,8 +1271,13 @@ export interface ApiShoppingListEntryUpdateRequest {
shoppingListEntry: Omit<ShoppingListEntry, 'recipe_mealplan'|'created_by'|'created_at'|'updated_at'>; shoppingListEntry: Omit<ShoppingListEntry, 'recipe_mealplan'|'created_by'|'created_at'|'updated_at'>;
} }
export interface ApiShoppingListRecipeBulkCreateEntriesCreateRequest {
id: number;
shoppingListEntryBulkCreate: ShoppingListEntryBulkCreate;
}
export interface ApiShoppingListRecipeCreateRequest { export interface ApiShoppingListRecipeCreateRequest {
shoppingListRecipe: Omit<ShoppingListRecipe, 'recipe_name'|'name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'>; shoppingListRecipe: Omit<ShoppingListRecipe, 'recipe_name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'>;
} }
export interface ApiShoppingListRecipeDestroyRequest { export interface ApiShoppingListRecipeDestroyRequest {
@@ -1283,7 +1291,7 @@ export interface ApiShoppingListRecipeListRequest {
export interface ApiShoppingListRecipePartialUpdateRequest { export interface ApiShoppingListRecipePartialUpdateRequest {
id: number; id: number;
patchedShoppingListRecipe?: Omit<PatchedShoppingListRecipe, 'recipe_name'|'name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'>; patchedShoppingListRecipe?: Omit<PatchedShoppingListRecipe, 'recipe_name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'>;
} }
export interface ApiShoppingListRecipeRetrieveRequest { export interface ApiShoppingListRecipeRetrieveRequest {
@@ -1292,7 +1300,7 @@ export interface ApiShoppingListRecipeRetrieveRequest {
export interface ApiShoppingListRecipeUpdateRequest { export interface ApiShoppingListRecipeUpdateRequest {
id: number; id: number;
shoppingListRecipe: Omit<ShoppingListRecipe, 'recipe_name'|'name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'>; shoppingListRecipe: Omit<ShoppingListRecipe, 'recipe_name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'>;
} }
export interface ApiSpaceListRequest { export interface ApiSpaceListRequest {
@@ -8856,11 +8864,11 @@ export class ApiApi extends runtime.BaseAPI {
/** /**
* logs request counts to redis cache total/per user/ * logs request counts to redis cache total/per user/
*/ */
async apiRecipeRelatedRetrieveRaw(requestParameters: ApiRecipeRelatedRetrieveRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<RecipeSimple>> { async apiRecipeRelatedListRaw(requestParameters: ApiRecipeRelatedListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<Array<RecipeSimple>>> {
if (requestParameters['id'] == null) { if (requestParameters['id'] == null) {
throw new runtime.RequiredError( throw new runtime.RequiredError(
'id', 'id',
'Required parameter "id" was null or undefined when calling apiRecipeRelatedRetrieve().' 'Required parameter "id" was null or undefined when calling apiRecipeRelatedList().'
); );
} }
@@ -8879,14 +8887,14 @@ export class ApiApi extends runtime.BaseAPI {
query: queryParameters, query: queryParameters,
}, initOverrides); }, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => RecipeSimpleFromJSON(jsonValue)); return new runtime.JSONApiResponse(response, (jsonValue) => jsonValue.map(RecipeSimpleFromJSON));
} }
/** /**
* logs request counts to redis cache total/per user/ * logs request counts to redis cache total/per user/
*/ */
async apiRecipeRelatedRetrieve(requestParameters: ApiRecipeRelatedRetrieveRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<RecipeSimple> { async apiRecipeRelatedList(requestParameters: ApiRecipeRelatedListRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<Array<RecipeSimple>> {
const response = await this.apiRecipeRelatedRetrieveRaw(requestParameters, initOverrides); const response = await this.apiRecipeRelatedListRaw(requestParameters, initOverrides);
return await response.value(); return await response.value();
} }
@@ -8938,6 +8946,13 @@ export class ApiApi extends runtime.BaseAPI {
); );
} }
if (requestParameters['recipeShoppingUpdate'] == null) {
throw new runtime.RequiredError(
'recipeShoppingUpdate',
'Required parameter "recipeShoppingUpdate" was null or undefined when calling apiRecipeShoppingUpdate().'
);
}
const queryParameters: any = {}; const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {}; const headerParameters: runtime.HTTPHeaders = {};
@@ -9388,6 +9403,53 @@ export class ApiApi extends runtime.BaseAPI {
return await response.value(); return await response.value();
} }
/**
* logs request counts to redis cache total/per user/
*/
async apiShoppingListRecipeBulkCreateEntriesCreateRaw(requestParameters: ApiShoppingListRecipeBulkCreateEntriesCreateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<ShoppingListEntryBulkCreate>> {
if (requestParameters['id'] == null) {
throw new runtime.RequiredError(
'id',
'Required parameter "id" was null or undefined when calling apiShoppingListRecipeBulkCreateEntriesCreate().'
);
}
if (requestParameters['shoppingListEntryBulkCreate'] == null) {
throw new runtime.RequiredError(
'shoppingListEntryBulkCreate',
'Required parameter "shoppingListEntryBulkCreate" was null or undefined when calling apiShoppingListRecipeBulkCreateEntriesCreate().'
);
}
const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {};
headerParameters['Content-Type'] = 'application/json';
if (this.configuration && this.configuration.apiKey) {
headerParameters["Authorization"] = await this.configuration.apiKey("Authorization"); // ApiKeyAuth authentication
}
const response = await this.request({
path: `/api/shopping-list-recipe/{id}/bulk_create_entries/`.replace(`{${"id"}}`, encodeURIComponent(String(requestParameters['id']))),
method: 'POST',
headers: headerParameters,
query: queryParameters,
body: ShoppingListEntryBulkCreateToJSON(requestParameters['shoppingListEntryBulkCreate']),
}, initOverrides);
return new runtime.JSONApiResponse(response, (jsonValue) => ShoppingListEntryBulkCreateFromJSON(jsonValue));
}
/**
* logs request counts to redis cache total/per user/
*/
async apiShoppingListRecipeBulkCreateEntriesCreate(requestParameters: ApiShoppingListRecipeBulkCreateEntriesCreateRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<ShoppingListEntryBulkCreate> {
const response = await this.apiShoppingListRecipeBulkCreateEntriesCreateRaw(requestParameters, initOverrides);
return await response.value();
}
/** /**
* logs request counts to redis cache total/per user/ * logs request counts to redis cache total/per user/
*/ */

View File

@@ -0,0 +1,99 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { RecipeSimple } from './RecipeSimple';
import {
RecipeSimpleFromJSON,
RecipeSimpleFromJSONTyped,
RecipeSimpleToJSON,
RecipeSimpleToJSONTyped,
} from './RecipeSimple';
/**
*
* @export
* @interface PaginatedRecipeSimpleList
*/
export interface PaginatedRecipeSimpleList {
/**
*
* @type {number}
* @memberof PaginatedRecipeSimpleList
*/
count: number;
/**
*
* @type {string}
* @memberof PaginatedRecipeSimpleList
*/
next?: string | null;
/**
*
* @type {string}
* @memberof PaginatedRecipeSimpleList
*/
previous?: string | null;
/**
*
* @type {Array<RecipeSimple>}
* @memberof PaginatedRecipeSimpleList
*/
results: Array<RecipeSimple>;
}
/**
* Check if a given object implements the PaginatedRecipeSimpleList interface.
*/
export function instanceOfPaginatedRecipeSimpleList(value: object): value is PaginatedRecipeSimpleList {
if (!('count' in value) || value['count'] === undefined) return false;
if (!('results' in value) || value['results'] === undefined) return false;
return true;
}
export function PaginatedRecipeSimpleListFromJSON(json: any): PaginatedRecipeSimpleList {
return PaginatedRecipeSimpleListFromJSONTyped(json, false);
}
export function PaginatedRecipeSimpleListFromJSONTyped(json: any, ignoreDiscriminator: boolean): PaginatedRecipeSimpleList {
if (json == null) {
return json;
}
return {
'count': json['count'],
'next': json['next'] == null ? undefined : json['next'],
'previous': json['previous'] == null ? undefined : json['previous'],
'results': ((json['results'] as Array<any>).map(RecipeSimpleFromJSON)),
};
}
export function PaginatedRecipeSimpleListToJSON(json: any): PaginatedRecipeSimpleList {
return PaginatedRecipeSimpleListToJSONTyped(json, false);
}
export function PaginatedRecipeSimpleListToJSONTyped(value?: PaginatedRecipeSimpleList | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'count': value['count'],
'next': value['next'],
'previous': value['previous'],
'results': ((value['results'] as Array<any>).map(RecipeSimpleToJSON)),
};
}

View File

@@ -36,7 +36,7 @@ export interface PatchedShoppingListRecipe {
* @type {string} * @type {string}
* @memberof PatchedShoppingListRecipe * @memberof PatchedShoppingListRecipe
*/ */
readonly name?: string; name?: string;
/** /**
* *
* @type {number} * @type {number}
@@ -108,7 +108,7 @@ export function PatchedShoppingListRecipeToJSON(json: any): PatchedShoppingListR
return PatchedShoppingListRecipeToJSONTyped(json, false); return PatchedShoppingListRecipeToJSONTyped(json, false);
} }
export function PatchedShoppingListRecipeToJSONTyped(value?: Omit<PatchedShoppingListRecipe, 'recipe_name'|'name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'> | null, ignoreDiscriminator: boolean = false): any { export function PatchedShoppingListRecipeToJSONTyped(value?: Omit<PatchedShoppingListRecipe, 'recipe_name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'> | null, ignoreDiscriminator: boolean = false): any {
if (value == null) { if (value == null) {
return value; return value;
} }
@@ -116,6 +116,7 @@ export function PatchedShoppingListRecipeToJSONTyped(value?: Omit<PatchedShoppin
return { return {
'id': value['id'], 'id': value['id'],
'name': value['name'],
'recipe': value['recipe'], 'recipe': value['recipe'],
'mealplan': value['mealplan'], 'mealplan': value['mealplan'],
'servings': value['servings'], 'servings': value['servings'],

View File

@@ -32,11 +32,11 @@ export interface RecipeShoppingUpdate {
*/ */
listRecipe?: number | null; listRecipe?: number | null;
/** /**
* List of ingredient IDs from the recipe to add, if not provided all ingredients will be added. *
* @type {number} * @type {Array<number | null>}
* @memberof RecipeShoppingUpdate * @memberof RecipeShoppingUpdate
*/ */
ingredients?: number | null; ingredients: Array<number | null>;
/** /**
* Providing a list_recipe ID and servings of 0 will delete that shopping list. * Providing a list_recipe ID and servings of 0 will delete that shopping list.
* @type {number} * @type {number}
@@ -49,6 +49,7 @@ export interface RecipeShoppingUpdate {
* Check if a given object implements the RecipeShoppingUpdate interface. * Check if a given object implements the RecipeShoppingUpdate interface.
*/ */
export function instanceOfRecipeShoppingUpdate(value: object): value is RecipeShoppingUpdate { export function instanceOfRecipeShoppingUpdate(value: object): value is RecipeShoppingUpdate {
if (!('ingredients' in value) || value['ingredients'] === undefined) return false;
return true; return true;
} }
@@ -64,7 +65,7 @@ export function RecipeShoppingUpdateFromJSONTyped(json: any, ignoreDiscriminator
'id': json['id'] == null ? undefined : json['id'], 'id': json['id'] == null ? undefined : json['id'],
'listRecipe': json['list_recipe'] == null ? undefined : json['list_recipe'], 'listRecipe': json['list_recipe'] == null ? undefined : json['list_recipe'],
'ingredients': json['ingredients'] == null ? undefined : json['ingredients'], 'ingredients': json['ingredients'],
'servings': json['servings'] == null ? undefined : json['servings'], 'servings': json['servings'] == null ? undefined : json['servings'],
}; };
} }

View File

@@ -0,0 +1,74 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
import type { ShoppingListEntrySimpleCreate } from './ShoppingListEntrySimpleCreate';
import {
ShoppingListEntrySimpleCreateFromJSON,
ShoppingListEntrySimpleCreateFromJSONTyped,
ShoppingListEntrySimpleCreateToJSON,
ShoppingListEntrySimpleCreateToJSONTyped,
} from './ShoppingListEntrySimpleCreate';
/**
*
* @export
* @interface ShoppingListEntryBulkCreate
*/
export interface ShoppingListEntryBulkCreate {
/**
*
* @type {Array<ShoppingListEntrySimpleCreate>}
* @memberof ShoppingListEntryBulkCreate
*/
entries: Array<ShoppingListEntrySimpleCreate>;
}
/**
* Check if a given object implements the ShoppingListEntryBulkCreate interface.
*/
export function instanceOfShoppingListEntryBulkCreate(value: object): value is ShoppingListEntryBulkCreate {
if (!('entries' in value) || value['entries'] === undefined) return false;
return true;
}
export function ShoppingListEntryBulkCreateFromJSON(json: any): ShoppingListEntryBulkCreate {
return ShoppingListEntryBulkCreateFromJSONTyped(json, false);
}
export function ShoppingListEntryBulkCreateFromJSONTyped(json: any, ignoreDiscriminator: boolean): ShoppingListEntryBulkCreate {
if (json == null) {
return json;
}
return {
'entries': ((json['entries'] as Array<any>).map(ShoppingListEntrySimpleCreateFromJSON)),
};
}
export function ShoppingListEntryBulkCreateToJSON(json: any): ShoppingListEntryBulkCreate {
return ShoppingListEntryBulkCreateToJSONTyped(json, false);
}
export function ShoppingListEntryBulkCreateToJSONTyped(value?: ShoppingListEntryBulkCreate | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'entries': ((value['entries'] as Array<any>).map(ShoppingListEntrySimpleCreateToJSON)),
};
}

View File

@@ -0,0 +1,84 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { mapValues } from '../runtime';
/**
*
* @export
* @interface ShoppingListEntrySimpleCreate
*/
export interface ShoppingListEntrySimpleCreate {
/**
*
* @type {number}
* @memberof ShoppingListEntrySimpleCreate
*/
amount: number;
/**
*
* @type {number}
* @memberof ShoppingListEntrySimpleCreate
*/
unitId: number | null;
/**
*
* @type {number}
* @memberof ShoppingListEntrySimpleCreate
*/
foodId: number | null;
}
/**
* Check if a given object implements the ShoppingListEntrySimpleCreate interface.
*/
export function instanceOfShoppingListEntrySimpleCreate(value: object): value is ShoppingListEntrySimpleCreate {
if (!('amount' in value) || value['amount'] === undefined) return false;
if (!('unitId' in value) || value['unitId'] === undefined) return false;
if (!('foodId' in value) || value['foodId'] === undefined) return false;
return true;
}
export function ShoppingListEntrySimpleCreateFromJSON(json: any): ShoppingListEntrySimpleCreate {
return ShoppingListEntrySimpleCreateFromJSONTyped(json, false);
}
export function ShoppingListEntrySimpleCreateFromJSONTyped(json: any, ignoreDiscriminator: boolean): ShoppingListEntrySimpleCreate {
if (json == null) {
return json;
}
return {
'amount': json['amount'],
'unitId': json['unit_id'],
'foodId': json['food_id'],
};
}
export function ShoppingListEntrySimpleCreateToJSON(json: any): ShoppingListEntrySimpleCreate {
return ShoppingListEntrySimpleCreateToJSONTyped(json, false);
}
export function ShoppingListEntrySimpleCreateToJSONTyped(value?: ShoppingListEntrySimpleCreate | null, ignoreDiscriminator: boolean = false): any {
if (value == null) {
return value;
}
return {
'amount': value['amount'],
'unit_id': value['unitId'],
'food_id': value['foodId'],
};
}

View File

@@ -36,7 +36,7 @@ export interface ShoppingListRecipe {
* @type {string} * @type {string}
* @memberof ShoppingListRecipe * @memberof ShoppingListRecipe
*/ */
readonly name: string; name?: string;
/** /**
* *
* @type {number} * @type {number}
@@ -80,7 +80,6 @@ export interface ShoppingListRecipe {
*/ */
export function instanceOfShoppingListRecipe(value: object): value is ShoppingListRecipe { export function instanceOfShoppingListRecipe(value: object): value is ShoppingListRecipe {
if (!('recipeName' in value) || value['recipeName'] === undefined) return false; if (!('recipeName' in value) || value['recipeName'] === undefined) return false;
if (!('name' in value) || value['name'] === undefined) return false;
if (!('servings' in value) || value['servings'] === undefined) return false; if (!('servings' in value) || value['servings'] === undefined) return false;
if (!('mealplanNote' in value) || value['mealplanNote'] === undefined) return false; if (!('mealplanNote' in value) || value['mealplanNote'] === undefined) return false;
if (!('mealplanFromDate' in value) || value['mealplanFromDate'] === undefined) return false; if (!('mealplanFromDate' in value) || value['mealplanFromDate'] === undefined) return false;
@@ -100,7 +99,7 @@ export function ShoppingListRecipeFromJSONTyped(json: any, ignoreDiscriminator:
'id': json['id'] == null ? undefined : json['id'], 'id': json['id'] == null ? undefined : json['id'],
'recipeName': json['recipe_name'], 'recipeName': json['recipe_name'],
'name': json['name'], 'name': json['name'] == null ? undefined : json['name'],
'recipe': json['recipe'] == null ? undefined : json['recipe'], 'recipe': json['recipe'] == null ? undefined : json['recipe'],
'mealplan': json['mealplan'] == null ? undefined : json['mealplan'], 'mealplan': json['mealplan'] == null ? undefined : json['mealplan'],
'servings': json['servings'], 'servings': json['servings'],
@@ -114,7 +113,7 @@ export function ShoppingListRecipeToJSON(json: any): ShoppingListRecipe {
return ShoppingListRecipeToJSONTyped(json, false); return ShoppingListRecipeToJSONTyped(json, false);
} }
export function ShoppingListRecipeToJSONTyped(value?: Omit<ShoppingListRecipe, 'recipe_name'|'name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'> | null, ignoreDiscriminator: boolean = false): any { export function ShoppingListRecipeToJSONTyped(value?: Omit<ShoppingListRecipe, 'recipe_name'|'mealplan_note'|'mealplan_from_date'|'mealplan_type'> | null, ignoreDiscriminator: boolean = false): any {
if (value == null) { if (value == null) {
return value; return value;
} }
@@ -122,6 +121,7 @@ export function ShoppingListRecipeToJSONTyped(value?: Omit<ShoppingListRecipe, '
return { return {
'id': value['id'], 'id': value['id'],
'name': value['name'],
'recipe': value['recipe'], 'recipe': value['recipe'],
'mealplan': value['mealplan'], 'mealplan': value['mealplan'],
'servings': value['servings'], 'servings': value['servings'],

View File

@@ -130,6 +130,8 @@ export * from './ServerSettings';
export * from './ShareLink'; export * from './ShareLink';
export * from './ShoppingListEntry'; export * from './ShoppingListEntry';
export * from './ShoppingListEntryBulk'; export * from './ShoppingListEntryBulk';
export * from './ShoppingListEntryBulkCreate';
export * from './ShoppingListEntrySimpleCreate';
export * from './ShoppingListRecipe'; export * from './ShoppingListRecipe';
export * from './SourceImportDuplicate'; export * from './SourceImportDuplicate';
export * from './SourceImportFood'; export * from './SourceImportFood';