diff --git a/cookbook/apps.py b/cookbook/apps.py
index c785e5536..e551319db 100644
--- a/cookbook/apps.py
+++ b/cookbook/apps.py
@@ -35,4 +35,3 @@ class CookbookConfig(AppConfig):
# if DEBUG:
# traceback.print_exc()
# pass # dont break startup just because fix could not run, need to investigate cases when this happens
-
diff --git a/cookbook/helper/scope_middleware.py b/cookbook/helper/scope_middleware.py
index 7f0676c7e..08b7b3d33 100644
--- a/cookbook/helper/scope_middleware.py
+++ b/cookbook/helper/scope_middleware.py
@@ -5,6 +5,7 @@ from rest_framework.authtoken.models import Token
from rest_framework.exceptions import AuthenticationFailed
from cookbook.views import views
+from recipes import settings
class ScopeMiddleware:
@@ -14,14 +15,15 @@ class ScopeMiddleware:
def __call__(self, request):
if request.user.is_authenticated:
- if request.path.startswith('/admin/'):
+ prefix = settings.JS_REVERSE_SCRIPT_PREFIX or ''
+ if request.path.startswith(prefix + '/admin/'):
with scopes_disabled():
return self.get_response(request)
- if request.path.startswith('/signup/') or request.path.startswith('/invite/'):
+ if request.path.startswith(prefix + '/signup/') or request.path.startswith(prefix + '/invite/'):
return self.get_response(request)
- if request.path.startswith('/accounts/'):
+ if request.path.startswith(prefix + '/accounts/'):
return self.get_response(request)
with scopes_disabled():
diff --git a/cookbook/helper/shopping_helper.py b/cookbook/helper/shopping_helper.py
index 75a70d36b..4f725ffa0 100644
--- a/cookbook/helper/shopping_helper.py
+++ b/cookbook/helper/shopping_helper.py
@@ -17,8 +17,7 @@ def shopping_helper(qs, request):
supermarket = request.query_params.get('supermarket', None)
checked = request.query_params.get('checked', 'recent')
user = request.user
-
- supermarket_order = ['food__supermarket_category__name', 'food__name']
+ supermarket_order = [F('food__supermarket_category__name').asc(nulls_first=True), 'food__name']
# TODO created either scheduled task or startup task to delete very old shopping list entries
# TODO create user preference to define 'very old'
diff --git a/cookbook/tests/api/test_api_related_recipe.py b/cookbook/tests/api/test_api_related_recipe.py
index ce6221880..1a381ed5c 100644
--- a/cookbook/tests/api/test_api_related_recipe.py
+++ b/cookbook/tests/api/test_api_related_recipe.py
@@ -65,7 +65,6 @@ def test_related_mixed_space(request, recipe, u1_s2):
reverse(RELATED_URL, args={recipe.id})).content)) == 0
-# TODO add tests for mealplan related when thats added
# TODO if/when related recipes includes multiple levels (related recipes of related recipes) add the following tests
# -- step recipes included in step recipes
# -- step recipes included in food recipes
diff --git a/cookbook/tests/api/test_api_shopping_recipe.py b/cookbook/tests/api/test_api_shopping_recipe.py
index 438de046e..d564bf2ab 100644
--- a/cookbook/tests/api/test_api_shopping_recipe.py
+++ b/cookbook/tests/api/test_api_shopping_recipe.py
@@ -233,7 +233,6 @@ def test_shopping_recipe_mixed_authors(u1_s1, u2_s1):
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0
-# TODO test adding recipe with ingredients that are not food
@pytest.mark.parametrize("recipe", [{'steps__ingredients__header': 1}], indirect=['recipe'])
def test_shopping_with_header_ingredient(u1_s1, recipe):
# with scope(space=recipe.space):
diff --git a/cookbook/views/api.py b/cookbook/views/api.py
index 8b3ff8fb9..b5006752d 100644
--- a/cookbook/views/api.py
+++ b/cookbook/views/api.py
@@ -689,8 +689,11 @@ class RecipeViewSet(viewsets.ModelViewSet):
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)
- qs = obj.get_related_recipes(levels=1) # TODO: make levels a user setting, included in request data?, keep solely in the backend?
- # mealplans= TODO get todays mealplans
+ try:
+ levels = int(request.query_params.get('levels', 1))
+ except (ValueError, TypeError):
+ levels = 1
+ qs = obj.get_related_recipes(levels=levels) # TODO: make levels a user setting, included in request data?, keep solely in the backend?
return Response(self.serializer_class(qs, many=True).data)
diff --git a/vue/src/apps/ModelListView/ModelListView.vue b/vue/src/apps/ModelListView/ModelListView.vue
index d889267c7..1d90667de 100644
--- a/vue/src/apps/ModelListView/ModelListView.vue
+++ b/vue/src/apps/ModelListView/ModelListView.vue
@@ -7,7 +7,7 @@
@@ -594,6 +606,7 @@ export default {
// if a supermarket is selected and filtered to only supermarket categories filter out everything else
if (this.selected_supermarket && this.supermarket_categories_only) {
let shopping_categories = this.supermarkets // category IDs configured on supermarket
+ .filter((x) => x.id === this.selected_supermarket)
.map((x) => x.category_to_supermarket)
.flat()
.map((x) => x.category.id)
@@ -603,7 +616,7 @@ export default {
shopping_list = shopping_list.filter((x) => x?.food?.supermarket_category)
}
- let groups = { false: {}, true: {} } // force unchecked to always be first
+ var groups = { false: {}, true: {} } // force unchecked to always be first
if (this.selected_supermarket) {
let super_cats = this.supermarkets
.filter((x) => x.id === this.selected_supermarket)
@@ -611,10 +624,13 @@ export default {
.flat()
.map((x) => x.category.name)
new Set([...super_cats, ...this.shopping_categories.map((x) => x.name)]).forEach((cat) => {
- groups["false"][cat.name] = {}
- groups["true"][cat.name] = {}
+ groups["false"][cat] = {}
+ groups["true"][cat] = {}
})
} else {
+ // TODO: make nulls_first a user setting
+ groups.false[this.$t("Undefined")] = {}
+ groups.true[this.$t("Undefined")] = {}
this.shopping_categories.forEach((cat) => {
groups.false[cat.name] = {}
groups.true[cat.name] = {}
@@ -917,8 +933,17 @@ export default {
// TODO make decision - should inheritance always be set manually or give user a choice at front-end or make it a setting?
let food = this.items.filter((x) => x.food.id == item?.[0]?.food.id ?? item.food.id)[0].food
- food.supermarket_category = this.shopping_categories.filter((x) => x?.id === this.shopcat)?.[0]
- this.updateFood(food, "supermarket_category")
+ let supermarket_category = this.shopping_categories.filter((x) => x?.id === this.shopcat)?.[0]
+ food.supermarket_category = supermarket_category
+ this.updateFood(food, "supermarket_category").then((result) => {
+ this.items = this.items.map((x) => {
+ if (x.food.id === food.id) {
+ return { ...x, food: { ...x.food, supermarket_category: supermarket_category } }
+ } else {
+ return x
+ }
+ })
+ })
this.shopcat = null
},
onHand: function (item) {
@@ -940,8 +965,13 @@ export default {
})
})
},
- openContextMenu(e, value) {
- this.shopcat = value?.food?.supermarket_category?.id ?? value?.[0]?.food?.supermarket_category?.id ?? undefined
+ openContextMenu(e, value, section = false) {
+ if (section) {
+ value = Object.values(value).flat()
+ } else {
+ this.shopcat = value?.food?.supermarket_category?.id ?? value?.[0]?.food?.supermarket_category?.id ?? undefined
+ }
+
this.$refs.menu.open(e, value)
},
saveSettings: function () {
@@ -1010,24 +1040,15 @@ export default {
},
updateFood: function (food, field) {
let api = new ApiApiFactory()
- let ignore_category
if (field) {
- ignore_category = food.inherit_fields
- .map((x) => food.inherit_fields.fields)
- .flat()
- .includes(field)
- } else {
- ignore_category = true
+ // assume if field is changing it should no longer be inheritted
+ food.inherit_fields = food.inherit_fields.filter((x) => x.field !== field)
}
return api
.partialUpdateFood(food.id, food)
.then((result) => {
- if (food.supermarket_category && !ignore_category && food.parent) {
- makeToast(this.$t("Warning"), this.$t("InheritWarning", { food: food.name }), "warning")
- } else {
- StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE)
- }
+ StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE)
if (food?.numchild > 0) {
this.getShoppingList() // if food has children, just get the whole list. probably could be more efficient
}
diff --git a/vue/src/components/GenericHorizontalCard.vue b/vue/src/components/GenericHorizontalCard.vue
index 0635702e2..39a31d907 100644
--- a/vue/src/components/GenericHorizontalCard.vue
+++ b/vue/src/components/GenericHorizontalCard.vue
@@ -26,7 +26,7 @@
{{ item[subtitle] }}
{{ getFullname }}
-
+
diff --git a/vue/src/components/GenericPill.vue b/vue/src/components/GenericPill.vue
index 183d83ec8..276042523 100644
--- a/vue/src/components/GenericPill.vue
+++ b/vue/src/components/GenericPill.vue
@@ -10,7 +10,7 @@
export default {
name: "GenericPill",
props: {
- item_list: { type: Object },
+ item_list: { type: Array },
label: { type: String, default: "name" },
color: { type: String, default: "light" },
},
diff --git a/vue/src/components/IngredientComponent.vue b/vue/src/components/IngredientComponent.vue
index 184765c97..595db720b 100644
--- a/vue/src/components/IngredientComponent.vue
+++ b/vue/src/components/IngredientComponent.vue
@@ -108,7 +108,7 @@ export default {
this.shop = false // don't check any boxes until user selects a shopping list to edit
if (count_shopping_ingredient >= 1) {
this.shopping_status = true // ingredient is in the shopping list - probably (but not definitely, this ingredient)
- } else if (this.ingredient.food.shopping) {
+ } else if (this.ingredient?.food?.shopping) {
this.shopping_status = null // food is in the shopping list, just not for this ingredient/recipe
} else {
// food is not in any shopping list
@@ -123,7 +123,7 @@ export default {
if (count_shopping_ingredient >= 1) {
// ingredient is in this shopping list (not entirely sure how this could happen?)
this.shopping_status = true
- } else if (count_shopping_ingredient == 0 && this.ingredient.food.shopping) {
+ } else if (count_shopping_ingredient == 0 && this.ingredient?.food?.shopping) {
// food is in the shopping list, just not for this ingredient/recipe
this.shopping_status = null
} else {