From 04c4182b24c6211a298edad58094951e5d9bfa5a Mon Sep 17 00:00:00 2001 From: Chris Scoggins Date: Tue, 1 Feb 2022 16:12:50 -0600 Subject: [PATCH] WIP --- cookbook/helper/recipe_search.py | 53 ++++++++++++------- cookbook/views/api.py | 7 +-- .../RecipeSearchView/RecipeSearchView.vue | 24 +++++++-- vue/src/locales/en.json | 3 +- vue/src/utils/models.js | 2 +- 5 files changed, 59 insertions(+), 30 deletions(-) diff --git a/cookbook/helper/recipe_search.py b/cookbook/helper/recipe_search.py index b1032a4d1..45d5b2380 100644 --- a/cookbook/helper/recipe_search.py +++ b/cookbook/helper/recipe_search.py @@ -33,12 +33,11 @@ class RecipeSearch(): self._params = {**(params or {})} else: self._params = {**(params or {})} - self._query = self._params.get('query', {}) or {} if self._request.user.is_authenticated: self._search_prefs = request.user.searchpreference else: self._search_prefs = SearchPreference() - self._string = params.get('string').strip() if params.get('string', None) else None + self._string = params.get('query').strip() if params.get('query', None) else None self._rating = self._params.get('rating', None) self._keywords = { 'or': self._params.get('keywords_or', None) or self._params.get('keywords', None), @@ -62,14 +61,15 @@ class RecipeSearch(): self._units = self._params.get('units', None) # TODO add created by # TODO image exists - self._sort_order = self._params.get('sort_order', None) or self._query.get('sort_order', 0) + self._sort_order = self._params.get('sort_order', None) self._internal = str2bool(self._params.get('internal', False)) self._random = str2bool(self._params.get('random', False)) self._new = str2bool(self._params.get('new', False)) - self._last_viewed = int(self._params.get('last_viewed', 0) or self._query.get('last_viewed', 0)) + self._last_viewed = int(self._params.get('last_viewed', 0)) + self._include_children = str2bool(self._params.get('include_children', None)) self._timescooked = self._params.get('timescooked', None) self._lastcooked = self._params.get('lastcooked', None) - self._makenow = self._params.get('makenow', None) + self._makenow = str2bool(self._params.get('makenow', None)) self._search_type = self._search_prefs.search or 'plain' if self._string: @@ -254,18 +254,25 @@ class RecipeSearch(): keywords = Keyword.objects.filter(pk__in=kwargs[kw_filter]) if 'or' in kw_filter: - f = Q(keywords__in=Keyword.include_descendants(keywords)) - if 'not' in kw_filter: - self._queryset = self._queryset.exclude(f) + if self._include_children: + f_or = Q(keywords__in=Keyword.include_descendants(keywords)) else: - self._queryset = self._queryset.filter(f) + f_or = Q(keywords__in=keywords) + if 'not' in kw_filter: + self._queryset = self._queryset.exclude(f_or) + else: + self._queryset = self._queryset.filter(f_or) elif 'and' in kw_filter: recipes = Recipe.objects.all() for kw in keywords: - if 'not' in kw_filter: - recipes = recipes.filter(keywords__in=kw.get_descendants_and_self()) + if self._include_children: + f_and = Q(keywords__in=kw.get_descendants_and_self()) else: - self._queryset = self._queryset.filter(keywords__in=kw.get_descendants_and_self()) + f_and = Q(keywords=kw) + if 'not' in kw_filter: + recipes = recipes.filter(f_and) + else: + self._queryset = self._queryset.filter(f_and) if 'not' in kw_filter: self._queryset = self._queryset.exclude(id__in=recipes.values('id')) @@ -280,18 +287,26 @@ class RecipeSearch(): foods = Food.objects.filter(pk__in=kwargs[fd_filter]) if 'or' in fd_filter: - f = Q(steps__ingredients__food__in=Food.include_descendants(foods)) - if 'not' in fd_filter: - self._queryset = self._queryset.exclude(f) + if self._include_children: + f_or = Q(steps__ingredients__food__in=Food.include_descendants(foods)) else: - self._queryset = self._queryset.filter(f) + f_or = Q(steps__ingredients__food__in=foods) + + if 'not' in fd_filter: + self._queryset = self._queryset.exclude(f_or) + else: + self._queryset = self._queryset.filter(f_or) elif 'and' in fd_filter: recipes = Recipe.objects.all() for food in foods: - if 'not' in fd_filter: - recipes = recipes.filter(steps__ingredients__food__in=food.get_descendants_and_self()) + if self._include_children: + f_and = Q(steps__ingredients__food__in=food.get_descendants_and_self()) else: - self._queryset = self._queryset.filter(steps__ingredients__food__in=food.get_descendants_and_self()) + f_and = Q(steps__ingredients__food=food) + if 'not' in fd_filter: + recipes = recipes.filter(f_and) + else: + self._queryset = self._queryset.filter(f_and) if 'not' in fd_filter: self._queryset = self._queryset.exclude(id__in=recipes.values('id')) diff --git a/cookbook/views/api.py b/cookbook/views/api.py index f25251536..71cfaa374 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -632,7 +632,7 @@ class RecipeViewSet(viewsets.ModelViewSet): pagination_class = RecipePagination query_params = [ - QueryParam(name='string', description=_('Query string matched (fuzzy) against recipe name. In the future also fulltext search.')), + QueryParam(name='query', description=_('Query string matched (fuzzy) against recipe name. In the future also fulltext search.')), QueryParam(name='keywords', description=_('ID of keyword a recipe should have. For multiple repeat parameter. Equivalent to keywords_or'), qtype='int'), QueryParam(name='keywords_or', description=_('Keyword IDs, repeat for multiple. Return recipes with any of the keywords'), qtype='int'), QueryParam(name='keywords_and', description=_('Keyword IDs, repeat for multiple. Return recipes with all of the keywords.'), qtype='int'), @@ -668,10 +668,7 @@ class RecipeViewSet(viewsets.ModelViewSet): if not (share and self.detail): self.queryset = self.queryset.filter(space=self.request.space) - if filter := (self.request.GET.get('query', {}) or {}).get('filter', None): - params = {'filter': filter} - else: - params = {x: self.request.GET.get(x) if len({**self.request.GET}[x]) == 1 else self.request.GET.getlist(x) for x in list(self.request.GET)} + params = {x: self.request.GET.get(x) if len({**self.request.GET}[x]) == 1 else self.request.GET.getlist(x) for x in list(self.request.GET)} search = RecipeSearch(self.request, **params) self.queryset = search.get_queryset(self.queryset).prefetch_related('cooklog_set') return self.queryset diff --git a/vue/src/apps/RecipeSearchView/RecipeSearchView.vue b/vue/src/apps/RecipeSearchView/RecipeSearchView.vue index daf75b3da..cf5173fd7 100644 --- a/vue/src/apps/RecipeSearchView/RecipeSearchView.vue +++ b/vue/src/apps/RecipeSearchView/RecipeSearchView.vue @@ -474,7 +474,7 @@
- +
{{ $t("Page") }} {{ search.pagination_page }}/{{ Math.ceil(pagination_count / ui.page_size) }} - {{ $t("Reset") }} + {{ $t("Reset") }}
@@ -616,6 +616,7 @@ export default { show_timescooked: false, show_makenow: false, show_lastcooked: false, + include_children: true, }, pagination_count: 0, random_search: false, @@ -799,6 +800,19 @@ export default { "ui.page_size": _debounce(function () { this.refreshData(false) }, 300), + expertMode: function (newVal, oldVal) { + if (!newVal) { + this.search.search_keywords = this.search.search_keywords.map((x) => { + return { ...x, not: false } + }) + this.search.search_foods = this.search.search_foods.map((x) => { + return { ...x, not: false } + }) + this.search.search_books = this.search.search_books.map((x) => { + return { ...x, not: false } + }) + } + }, }, methods: { // this.genericAPI inherited from ApiMixin @@ -817,7 +831,6 @@ export default { this.meal_plans.forEach((x) => mealPlans.push(x.recipe.id)) this.recipes = this.recipes.filter((recipe) => !mealPlans.includes(recipe.id)) } - console.log(result.data) }) .then(() => { this.$nextTick(function () { @@ -997,7 +1010,7 @@ export default { ...this.addFields("foods"), ...this.addFields("books"), units: this.search.search_units, - string: this.search.search_input, + query: this.search.search_input, rating: rating, internal: this.search.search_internal, random: this.random_search, @@ -1011,6 +1024,7 @@ export default { params.options.query = { sort_order: this.search.sort_order.map((x) => x.value), + include_children: this.ui.include_children, } if (!this.searchFiltered()) { params.options.query.last_viewed = this.ui.recently_viewed @@ -1068,10 +1082,12 @@ export default { ;["page", "pageSize"].forEach((key) => { delete search[key] }) + search = { ...search, ...search.options.query } let params = { name: filtername, search: JSON.stringify(search), } + console.log("saved search", search) this.genericAPI(this.Models.CUSTOM_FILTER, this.Actions.CREATE, params) .then((result) => { diff --git a/vue/src/locales/en.json b/vue/src/locales/en.json index dc5740dca..766baff38 100644 --- a/vue/src/locales/en.json +++ b/vue/src/locales/en.json @@ -325,5 +325,6 @@ "search_rank": "Search Rank", "make_now": "Make Now", "recipe_filter": "Recipe Filter", - "book_filter_help": "Include recipes from recipe filter instead of assigning each recipe" + "book_filter_help": "Include recipes from recipe filter instead of assigning each recipe", + "filter": "Filter" } diff --git a/vue/src/utils/models.js b/vue/src/utils/models.js index 602649a0e..c381536fc 100644 --- a/vue/src/utils/models.js +++ b/vue/src/utils/models.js @@ -441,7 +441,7 @@ export class Models { apiName: "Recipe", list: { params: [ - "string", + "query", "keywords", "keywords_or", "keywords_and",