diff --git a/cookbook/helper/recipe_html_import.py b/cookbook/helper/recipe_html_import.py
index acf72917b..7fa7beaf2 100644
--- a/cookbook/helper/recipe_html_import.py
+++ b/cookbook/helper/recipe_html_import.py
@@ -58,18 +58,6 @@ def get_recipe_from_source(text, url, request):
})
return kid_list
- recipe_json = {
- 'name': '',
- 'url': '',
- 'description': '',
- 'image': '',
- 'keywords': [],
- 'recipeIngredient': [],
- 'recipeInstructions': '',
- 'servings': '',
- 'prepTime': '',
- 'cookTime': ''
- }
recipe_tree = []
parse_list = []
html_data = []
diff --git a/cookbook/helper/recipe_url_import.py b/cookbook/helper/recipe_url_import.py
index dd6c24353..3a39b403f 100644
--- a/cookbook/helper/recipe_url_import.py
+++ b/cookbook/helper/recipe_url_import.py
@@ -131,9 +131,7 @@ def get_from_scraper(scrape, request):
recipe_json['steps'][0]['ingredients'].append(
{
'amount': 0,
- 'unit': {
- 'name': '',
- },
+ 'unit': None,
'food': {
'name': x,
},
@@ -275,9 +273,9 @@ def parse_keywords(keyword_json, space):
kw = normalize_string(kw)
if len(kw) != 0:
if k := Keyword.objects.filter(name=kw, space=space).first():
- keywords.append({'name': str(k)})
+ keywords.append({'label': str(k), 'name': k.name, 'id': k.id})
else:
- keywords.append({'name': kw})
+ keywords.append({'label': kw, 'name': kw})
return keywords
diff --git a/cookbook/serializer.py b/cookbook/serializer.py
index d41a47be1..3c19b7be0 100644
--- a/cookbook/serializer.py
+++ b/cookbook/serializer.py
@@ -621,9 +621,12 @@ class RecipeSerializer(RecipeBaseSerializer):
class RecipeImageSerializer(WritableNestedModelSerializer):
+ image = serializers.ImageField(required=False, allow_null=True)
+ image_url = serializers.CharField(max_length=4096, required=False, allow_null=True)
+
class Meta:
model = Recipe
- fields = ['image', ]
+ fields = ['image', 'image_url', ]
class RecipeImportSerializer(SpacedModelSerializer):
diff --git a/cookbook/views/api.py b/cookbook/views/api.py
index 9e62bbdf2..39df3b076 100644
--- a/cookbook/views/api.py
+++ b/cookbook/views/api.py
@@ -5,6 +5,7 @@ import uuid
from collections import OrderedDict
import requests
+from PIL import UnidentifiedImageError
from annoying.decorators import ajax_request
from annoying.functions import get_object_or_None
from django.contrib import messages
@@ -23,6 +24,7 @@ from django.utils.translation import gettext as _
from django_scopes import scopes_disabled
from icalendar import Calendar, Event
from recipe_scrapers import NoSchemaFoundInWildMode, WebsiteNotImplementedError, scrape_me
+from requests.exceptions import MissingSchema
from rest_framework import decorators, status, viewsets
from rest_framework.exceptions import APIException, PermissionDenied
from rest_framework.pagination import PageNumberPagination
@@ -706,20 +708,33 @@ class RecipeViewSet(viewsets.ModelViewSet):
serializer = self.serializer_class(obj, data=request.data, partial=True)
- if self.request.space.demo:
- raise PermissionDenied(detail='Not available in demo', code=None)
-
if serializer.is_valid():
serializer.save()
+ image = None
- if serializer.validated_data == {}:
- obj.image = None
- else:
- img, filetype = handle_image(request, obj.image)
+ if 'image' in serializer.validated_data:
+ image = obj.image
+ elif 'image_url' in serializer.validated_data:
+ try:
+ response = requests.get(serializer.validated_data['image_url'])
+ image = File(io.BytesIO(response.content))
+ print('test')
+ except UnidentifiedImageError as e:
+ print(e)
+ pass
+ except MissingSchema as e:
+ print(e)
+ pass
+ except Exception as e:
+ print(e)
+ pass
+
+ if image is not None:
+ img, filetype = handle_image(request, image)
obj.image = File(img, name=f'{uuid.uuid4()}_{obj.pk}{filetype}')
- obj.save()
+ obj.save()
+ return Response(serializer.data)
- return Response(serializer.data)
return Response(serializer.errors, 400)
# TODO: refactor API to use post/put/delete or leave as put and change VUE to use list_recipe after creating
diff --git a/openapitools.json b/openapitools.json
new file mode 100644
index 000000000..e69de29bb
diff --git a/vue/src/apps/ImportView/ImportView.vue b/vue/src/apps/ImportView/ImportView.vue
index adacc222d..c0d7b8d65 100644
--- a/vue/src/apps/ImportView/ImportView.vue
+++ b/vue/src/apps/ImportView/ImportView.vue
@@ -34,13 +34,41 @@
Images
+
+
+
+
+
+ Click the image you want to import for this
+ recipe
+
+
+
+
+
+
+
Keywords
- - {{k}}
+ - {{ k.label }}
+
+ unused
+
Steps
@@ -119,8 +147,14 @@ export default {
importRecipe: function () {
let apiFactory = new ApiApiFactory()
apiFactory.createRecipe(this.recipe_json).then(response => {
- StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE)
- window.location = resolveDjangoUrl('edit_recipe', response.data.id)
+ let recipe = response.data
+ apiFactory.imageRecipe(response.data.id, undefined, this.recipe_json.image).then(response => {
+ StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE)
+ window.location = resolveDjangoUrl('edit_recipe', recipe.id)
+ }).catch(e => {
+ StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE)
+ window.location = resolveDjangoUrl('edit_recipe', recipe.id)
+ })
}).catch(err => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE)
})
@@ -154,6 +188,10 @@ export default {
'mode': this.mode
},).then((response) => {
this.recipe_json = response.data['recipe_json'];
+
+ this.$set(this.recipe_json, 'unused_keywords', this.recipe_json.keywords.filter(k => k.id === undefined))
+ this.$set(this.recipe_json, 'keywords', this.recipe_json.keywords.filter(k => k.id !== undefined))
+
this.recipe_tree = response.data['recipe_tree'];
this.recipe_html = response.data['recipe_html'];
this.recipe_images = response.data['recipe_images']; //todo change on backend as well after old view is deprecated
diff --git a/vue/src/utils/openapi/api.ts b/vue/src/utils/openapi/api.ts
index dae44015d..c88cb5e21 100644
--- a/vue/src/utils/openapi/api.ts
+++ b/vue/src/utils/openapi/api.ts
@@ -1856,6 +1856,12 @@ export interface RecipeImage {
* @memberof RecipeImage
*/
image?: any | null;
+ /**
+ *
+ * @type {string}
+ * @memberof RecipeImage
+ */
+ image_url?: string | null;
}
/**
*
@@ -5227,10 +5233,11 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
*
* @param {string} id A unique integer value identifying this recipe.
* @param {any} [image]
+ * @param {string} [imageUrl]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- imageRecipe: async (id: string, image?: any, options: any = {}): Promise => {
+ imageRecipe: async (id: string, image?: any, imageUrl?: string, options: any = {}): Promise => {
// verify required parameter 'id' is not null or undefined
assertParamExists('imageRecipe', 'id', id)
const localVarPath = `/api/recipe/{id}/image/`
@@ -5252,6 +5259,10 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
localVarFormParams.append('image', image as any);
}
+ if (imageUrl !== undefined) {
+ localVarFormParams.append('image_url', imageUrl as any);
+ }
+
localVarHeaderParameter['Content-Type'] = 'multipart/form-data';
@@ -10341,11 +10352,12 @@ export const ApiApiFp = function(configuration?: Configuration) {
*
* @param {string} id A unique integer value identifying this recipe.
* @param {any} [image]
+ * @param {string} [imageUrl]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- async imageRecipe(id: string, image?: any, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
- const localVarAxiosArgs = await localVarAxiosParamCreator.imageRecipe(id, image, options);
+ async imageRecipe(id: string, image?: any, imageUrl?: string, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> {
+ const localVarAxiosArgs = await localVarAxiosParamCreator.imageRecipe(id, image, imageUrl, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
@@ -12174,11 +12186,12 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
*
* @param {string} id A unique integer value identifying this recipe.
* @param {any} [image]
+ * @param {string} [imageUrl]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
- imageRecipe(id: string, image?: any, options?: any): AxiosPromise {
- return localVarFp.imageRecipe(id, image, options).then((request) => request(axios, basePath));
+ imageRecipe(id: string, image?: any, imageUrl?: string, options?: any): AxiosPromise {
+ return localVarFp.imageRecipe(id, image, imageUrl, options).then((request) => request(axios, basePath));
},
/**
*
@@ -13992,12 +14005,13 @@ export class ApiApi extends BaseAPI {
*
* @param {string} id A unique integer value identifying this recipe.
* @param {any} [image]
+ * @param {string} [imageUrl]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof ApiApi
*/
- public imageRecipe(id: string, image?: any, options?: any) {
- return ApiApiFp(this.configuration).imageRecipe(id, image, options).then((request) => request(this.axios, this.basePath));
+ public imageRecipe(id: string, image?: any, imageUrl?: string, options?: any) {
+ return ApiApiFp(this.configuration).imageRecipe(id, image, imageUrl, options).then((request) => request(this.axios, this.basePath));
}
/**