From 48c0252893aedbea8e61595496d02c08a273bf00 Mon Sep 17 00:00:00 2001 From: Drumstickx Date: Thu, 21 Apr 2022 18:51:38 +0200 Subject: [PATCH 1/8] Fix link in German locale --- cookbook/locale/de/LC_MESSAGES/django.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/locale/de/LC_MESSAGES/django.po b/cookbook/locale/de/LC_MESSAGES/django.po index 0f9c05294..ad6f1e0ab 100644 --- a/cookbook/locale/de/LC_MESSAGES/django.po +++ b/cookbook/locale/de/LC_MESSAGES/django.po @@ -2467,7 +2467,7 @@ msgid "" msgstr "" "Das direkte ausliefern von Mediendateien mit gunicorn/python ist nicht " "empfehlenswert! Bitte folge den beschriebenen Schritten hier, um Ihre " +"://github.com/vabene1111/recipes/releases/tag/0.8.1\">hier, um Ihre " "Installation zu aktualisieren.\n" " " From 8df3009cb2ee25abb20371f685251d402cb700aa Mon Sep 17 00:00:00 2001 From: Nathan Beals <850796+ndbeals@users.noreply.github.com> Date: Sat, 23 Apr 2022 00:11:04 -0400 Subject: [PATCH 2/8] Update image upload handler to be content-type aware Update handle_image: made filetype required and not optional Updated handle_image usage to reflect changes --- cookbook/helper/image_processing.py | 10 ++++++---- cookbook/integration/integration.py | 2 +- cookbook/views/api.py | 7 +++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cookbook/helper/image_processing.py b/cookbook/helper/image_processing.py index d9a334acb..552bf1312 100644 --- a/cookbook/helper/image_processing.py +++ b/cookbook/helper/image_processing.py @@ -38,10 +38,12 @@ def get_filetype(name): # TODO this whole file needs proper documentation, refactoring, and testing # TODO also add env variable to define which images sizes should be compressed -def handle_image(request, image_object, filetype='.jpeg'): +# filetype argument can not be optional, otherwise this function will treat all images as if they were a jpeg +# Because it's no longer optional, no reason to return it +def handle_image(request, image_object, filetype): if (image_object.size / 1000) > 500: # if larger than 500 kb compress if filetype == '.jpeg' or filetype == '.jpg': - return rescale_image_jpeg(image_object), filetype + return rescale_image_jpeg(image_object) if filetype == '.png': - return rescale_image_png(image_object), filetype - return image_object, filetype + return rescale_image_png(image_object) + return image_object diff --git a/cookbook/integration/integration.py b/cookbook/integration/integration.py index 6493c266d..c3dc9dfb7 100644 --- a/cookbook/integration/integration.py +++ b/cookbook/integration/integration.py @@ -253,7 +253,7 @@ class Integration: :param image_file: ByteIO stream containing the image :param filetype: type of file to write bytes to, default to .jpeg if unknown """ - recipe.image = File(handle_image(self.request, File(image_file, name='image'), filetype=filetype)[0], name=f'{uuid.uuid4()}_{recipe.pk}{filetype}') + recipe.image = File(handle_image(self.request, File(image_file, name='image'), filetype=filetype), name=f'{uuid.uuid4()}_{recipe.pk}{filetype}') recipe.save() def get_recipe_from_file(self, file): diff --git a/cookbook/views/api.py b/cookbook/views/api.py index 15e3cf299..635ee15fa 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -1,5 +1,6 @@ import io import json +import mimetypes import re import uuid from collections import OrderedDict @@ -772,14 +773,16 @@ class RecipeViewSet(viewsets.ModelViewSet): if serializer.is_valid(): serializer.save() image = None + filetype = ".jpeg" # fall-back to .jpeg, even if wrong, at least users will know it's an image and most image viewers can open it correctly anyways if 'image' in serializer.validated_data: image = obj.image + filetype = mimetypes.guess_extension(serializer.validated_data['image'].content_type) or filetype elif 'image_url' in serializer.validated_data: try: response = requests.get(serializer.validated_data['image_url']) image = File(io.BytesIO(response.content)) - print('test') + filetype = mimetypes.guess_extension(response.headers['content-type']) or filetype except UnidentifiedImageError as e: print(e) pass @@ -791,7 +794,7 @@ class RecipeViewSet(viewsets.ModelViewSet): pass if image is not None: - img, filetype = handle_image(request, image) + img = handle_image(request, image, filetype) obj.image = File(img, name=f'{uuid.uuid4()}_{obj.pk}{filetype}') obj.save() return Response(serializer.data) From 0b8dd635102c730a5e23e97e97fc8a4c347eb2c5 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 23 Apr 2022 15:27:21 +0200 Subject: [PATCH 3/8] small fixes --- vue/src/apps/ShoppingListView/ShoppingListView.vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vue/src/apps/ShoppingListView/ShoppingListView.vue b/vue/src/apps/ShoppingListView/ShoppingListView.vue index bdb6951d1..eeb709a20 100644 --- a/vue/src/apps/ShoppingListView/ShoppingListView.vue +++ b/vue/src/apps/ShoppingListView/ShoppingListView.vue @@ -1112,7 +1112,7 @@ export default { if (this.new_item.ingredient !== "" && this.new_item.ingredient !== undefined) { this.genericPostAPI("api_ingredient_from_string", {text: this.new_item.ingredient}).then((result) => { let unit = null - if (result.data.unit !== "") { + if (result.data.unit !== null) { unit = {name: result.data.unit} } @@ -1158,7 +1158,7 @@ export default { }) }, deleteCategory: function (c) { - // could be category relation or a catory + // could be category relation or a category let c_id = c?.category?.id ?? c.id let api = new ApiApiFactory() api.destroySupermarketCategory(c_id) @@ -1198,9 +1198,10 @@ export default { promises.push(this.saveThis({id: entry, delay_until: delay_date}, false)) }) Promise.all(promises).then(() => { - StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_UPDATE) this.items = this.items.filter((x) => !entries.includes(x.id)) this.delay = this.defaultDelay + }).catch(err => { + StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err) }) }, deleteRecipe: function (e, recipe) { From 1fb6f965710cbf092bd3990e413d7b0f8c153ccf Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 23 Apr 2022 16:51:04 +0200 Subject: [PATCH 4/8] minor shopping tweaks --- .../ShoppingListView/ShoppingListView.vue | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/vue/src/apps/ShoppingListView/ShoppingListView.vue b/vue/src/apps/ShoppingListView/ShoppingListView.vue index eeb709a20..69268082f 100644 --- a/vue/src/apps/ShoppingListView/ShoppingListView.vue +++ b/vue/src/apps/ShoppingListView/ShoppingListView.vue @@ -112,21 +112,21 @@
-
-
+
{{ $t("Completed") }}
-
+
-
+
-
+
category -> food group -> entries) + // ordering/sorting is definied by the order in which categories are added to the sections array (even trough the dev console does not show it like this) function getKey(item, group_by, x) { switch (group_by) { case "category": @@ -934,10 +936,12 @@ export default { } var groups = {false: {}, true: {}} // force unchecked to always be first + // TODO: make nulls_first a user setting + // add undefined group to both the checked and non checked + groups.false[this.$t("Undefined")] = {} + groups.true[this.$t("Undefined")] = {} + // category order is defined by order of insertion into groups variable if (this.ui.selected_supermarket) { - // TODO: make nulls_first a user setting - groups.false[this.$t("Undefined")] = {} - groups.true[this.$t("Undefined")] = {} let super_cats = this.supermarkets .filter((x) => x.id === this.ui.selected_supermarket) .map((x) => x.category_to_supermarket) @@ -1021,7 +1025,7 @@ export default { watch: { ui: { handler() { - this.$cookies.set(SETTINGS_COOKIE_NAME, {ui: this.ui, settings: {entrymode: this.entrymode}}) + this.$cookies.set(SETTINGS_COOKIE_NAME, {ui: this.ui, settings: {entrymode: this.entrymode}}, "100y") if (this.entrymode) { this.$nextTick(function () { this.setFocus() @@ -1032,7 +1036,7 @@ export default { }, entrymode: { handler() { - this.$cookies.set(SETTINGS_COOKIE_NAME, {ui: this.ui, settings: {entrymode: this.entrymode}}) + this.$cookies.set(SETTINGS_COOKIE_NAME, {ui: this.ui, settings: {entrymode: this.entrymode}}, "100y") if (this.entrymode) { document.getElementById('shoppinglist').scrollTop = 0 this.$nextTick(function () { From 9f90306f6c67c2bef4d581425b1036d67f379247 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 23 Apr 2022 17:13:27 +0200 Subject: [PATCH 5/8] at least basic ui for ingredient editor --- vue/src/apps/ImportView/ImportView.vue | 6 +- .../IngredientEditorView.vue | 144 +++++++++++------- vue/src/locales/en.json | 1 + 3 files changed, 96 insertions(+), 55 deletions(-) diff --git a/vue/src/apps/ImportView/ImportView.vue b/vue/src/apps/ImportView/ImportView.vue index 28993fae2..c76c1f190 100644 --- a/vue/src/apps/ImportView/ImportView.vue +++ b/vue/src/apps/ImportView/ImportView.vue @@ -218,8 +218,7 @@ :href="resolveDjangoUrl('view_recipe',r.id)">{{ r.name }} - Imported - + {{ $t('Imported') }}
@@ -230,8 +229,7 @@
{{ u }} - Failed - + {{ $t('Failure') }}
diff --git a/vue/src/apps/IngredientEditorView/IngredientEditorView.vue b/vue/src/apps/IngredientEditorView/IngredientEditorView.vue index 35b1c5ba1..3bfdeaa4b 100644 --- a/vue/src/apps/IngredientEditorView/IngredientEditorView.vue +++ b/vue/src/apps/IngredientEditorView/IngredientEditorView.vue @@ -1,23 +1,36 @@ @@ -153,7 +195,7 @@ Vue.use(BootstrapVue) export default { name: "IngredientEditorView", mixins: [ApiMixin, ResolveUrlMixin], - components: {BetaWarning, LoadingSpinner, GenericMultiselect, GenericModalForm}, + components: {LoadingSpinner, GenericMultiselect, GenericModalForm}, data() { return { ingredients: [], @@ -202,7 +244,7 @@ export default { if (this.unit !== null) { params.query.unit = this.unit.id } - apiClient.listIngredients(this.current_page, this.page_size,params).then(result => { + apiClient.listIngredients(this.current_page, this.page_size, params).then(result => { this.ingredients = result.data.results this.total_object_count = result.data.count this.loading = false diff --git a/vue/src/locales/en.json b/vue/src/locales/en.json index 98eaf3a9e..d23405869 100644 --- a/vue/src/locales/en.json +++ b/vue/src/locales/en.json @@ -368,6 +368,7 @@ "no_pinned_recipes": "You have no pinned recipes!", "Planned": "Planned", "Pinned": "Pinned", + "Imported": "Imported", "Quick actions": "Quick actions", "Ratings": "Ratings", "Internal": "Internal", From 69a23f34b41e5a7ef02b7fb260764398cd957aea Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 23 Apr 2022 18:54:23 +0200 Subject: [PATCH 6/8] updated tests ingredient pagination --- cookbook/tests/api/test_api_ingredient.py | 8 ++++---- cookbook/tests/api/test_api_shopping_recipe.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cookbook/tests/api/test_api_ingredient.py b/cookbook/tests/api/test_api_ingredient.py index cde3d65a9..c3182b1e2 100644 --- a/cookbook/tests/api/test_api_ingredient.py +++ b/cookbook/tests/api/test_api_ingredient.py @@ -23,8 +23,8 @@ def test_list_permission(arg, request): def test_list_space(recipe_1_s1, u1_s1, u1_s2, space_2): - assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 10 - assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0 + assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)['results']) == 10 + assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)['results']) == 0 with scopes_disabled(): recipe_1_s1.space = space_2 @@ -32,8 +32,8 @@ def test_list_space(recipe_1_s1, u1_s1, u1_s2, space_2): Step.objects.update(space=Subquery(Step.objects.filter(pk=OuterRef('pk')).values('recipe__space')[:1])) Ingredient.objects.update(space=Subquery(Ingredient.objects.filter(pk=OuterRef('pk')).values('step__recipe__space')[:1])) - assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 0 - assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 10 + assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)['results']) == 0 + assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)['results']) == 10 @pytest.mark.parametrize("arg", [ diff --git a/cookbook/tests/api/test_api_shopping_recipe.py b/cookbook/tests/api/test_api_shopping_recipe.py index 9732250d5..bbf3cb8fb 100644 --- a/cookbook/tests/api/test_api_shopping_recipe.py +++ b/cookbook/tests/api/test_api_shopping_recipe.py @@ -230,4 +230,4 @@ def test_shopping_with_header_ingredient(u1_s1, recipe): # recipe.step_set.first().ingredient_set.add(IngredientFactory(ingredients__header=1)) u1_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id})) assert len(json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 10 - assert len(json.loads(u1_s1.get(reverse('api:ingredient-list')).content)) == 11 + assert len(json.loads(u1_s1.get(reverse('api:ingredient-list')).content)['results']) == 11 From 825b7b7cf92e46c8346c4bc50776012bd4b76465 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 23 Apr 2022 19:58:35 +0200 Subject: [PATCH 7/8] ingredient editor and parser --- cookbook/helper/ingredient_parser.py | 2 +- .../IngredientEditorView.vue | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cookbook/helper/ingredient_parser.py b/cookbook/helper/ingredient_parser.py index 8c07635af..593dea7e3 100644 --- a/cookbook/helper/ingredient_parser.py +++ b/cookbook/helper/ingredient_parser.py @@ -221,7 +221,7 @@ class IngredientParser: # some people/languages put amount and unit at the end of the ingredient string # if something like this is detected move it to the beginning so the parser can handle it - if re.search(r'^([A-z])+(.)*[1-9](\d)*\s([A-z])+', ingredient): + if len(ingredient) < 1000 and re.search(r'^([A-z])+(.)*[1-9](\d)*\s([A-z])+', ingredient): match = re.search(r'[1-9](\d)*\s([A-z])+', ingredient) print(f'reording from {ingredient} to {ingredient[match.start():match.end()] + " " + ingredient.replace(ingredient[match.start():match.end()], "")}') ingredient = ingredient[match.start():match.end()] + ' ' + ingredient.replace(ingredient[match.start():match.end()], '') diff --git a/vue/src/apps/IngredientEditorView/IngredientEditorView.vue b/vue/src/apps/IngredientEditorView/IngredientEditorView.vue index 3bfdeaa4b..531f159ca 100644 --- a/vue/src/apps/IngredientEditorView/IngredientEditorView.vue +++ b/vue/src/apps/IngredientEditorView/IngredientEditorView.vue @@ -7,7 +7,8 @@ + :multiple="false" + style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"> @@ -39,13 +40,14 @@
- - + + From 2914c2052266a2a48f9b0372fef51e283073a4fc Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sat, 23 Apr 2022 20:18:10 +0200 Subject: [PATCH 8/8] added space tier banner --- cookbook/templates/base.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cookbook/templates/base.html b/cookbook/templates/base.html index 625a96fec..9ca099ba1 100644 --- a/cookbook/templates/base.html +++ b/cookbook/templates/base.html @@ -337,6 +337,12 @@
{% endif %} +{% if HOSTED and request.space.max_recipes == 10 %} +
+ {% trans 'You are using the free version of Tandor' %} {% trans 'Upgrade Now' %} +
+{% endif %} +