diff --git a/cookbook/helper/recipe_search.py b/cookbook/helper/recipe_search.py
index 074d4ea96..b80b1e8e7 100644
--- a/cookbook/helper/recipe_search.py
+++ b/cookbook/helper/recipe_search.py
@@ -82,12 +82,25 @@ class RecipeSearch():
self._num_recent = int(self._params.get('num_recent', 0))
self._include_children = str2bool(
self._params.get('include_children', None))
- self._timescooked = self._params.get('timescooked', None)
- self._cookedon = self._params.get('cookedon', None)
+ self._timescooked = self._params.get('timescooked_gte', None)
+ self._timescooked_gte = self._params.get('timescooked_gte', None)
+ self._timescooked_lte = self._params.get('timescooked_lte', None)
+
self._createdon = self._params.get('createdon', None)
- self._createdby = self._params.get('createdby', None)
+ self._createdon_gte = self._params.get('createdon_gte', None)
+ self._createdon_lte = self._params.get('createdon_lte', None)
+
self._updatedon = self._params.get('updatedon', None)
- self._viewedon = self._params.get('viewedon', None)
+ self._updatedon_gte = self._params.get('updatedon_gte', None)
+ self._updatedon_lte = self._params.get('updatedon_lte', None)
+
+ self._viewedon_gte = self._params.get('viewedon_gte', None)
+ self._viewedon_lte = self._params.get('viewedon_lte', None)
+
+ self._cookedon_gte = self._params.get('cookedon_gte', None)
+ self._cookedon_lte = self._params.get('cookedon_lte', None)
+
+ self._createdby = self._params.get('createdby', None)
self._makenow = self._params.get('makenow', None)
# this supports hidden feature to find recipes missing X ingredients
if isinstance(self._makenow, bool) and self._makenow == True:
@@ -134,12 +147,14 @@ class RecipeSearch():
self._build_sort_order()
self._recently_viewed(num_recent=self._num_recent)
- self._cooked_on_filter(cooked_date=self._cookedon)
- self._created_on_filter(created_date=self._createdon)
+
+ self._cooked_on_filter()
+ self._created_on_filter()
+ self._updated_on_filter()
+ self._viewed_on_filter()
+
self._created_by_filter(created_by_user_id=self._createdby)
- self._updated_on_filter(updated_date=self._updatedon)
- self._viewed_on_filter(viewed_date=self._viewedon)
- self._favorite_recipes(times_cooked=self._timescooked)
+ self._favorite_recipes()
self._new_recipes()
self.keyword_filters(**self._keywords)
self.food_filters(**self._foods)
@@ -232,9 +247,9 @@ class RecipeSearch():
query_filter |= Q(**{"%s" % f: self._string})
self._queryset = self._queryset.filter(query_filter).distinct()
- def _cooked_on_filter(self, cooked_date=None):
- if self._sort_includes('lastcooked') or cooked_date:
- lessthan = self._sort_includes('-lastcooked') or '-' in (cooked_date or [])[:1]
+ def _cooked_on_filter(self):
+ if self._sort_includes('lastcooked') or self._cookedon_gte or self._cookedon_lte:
+ lessthan = self._sort_includes('-lastcooked') or self._cookedon_lte
if lessthan:
default = timezone.now() - timedelta(days=100000)
else:
@@ -242,15 +257,11 @@ class RecipeSearch():
self._queryset = self._queryset.annotate(
lastcooked=Coalesce(Max(Case(When(cooklog__created_by=self._request.user, cooklog__space=self._request.space, then='cooklog__created_at'))), Value(default))
)
- if cooked_date is None:
- return
- cooked_date = date(*[int(x)for x in cooked_date.split('-') if x != ''])
-
- if lessthan:
- self._queryset = self._queryset.filter(lastcooked__date__lte=cooked_date).exclude(lastcooked=default)
- else:
- self._queryset = self._queryset.filter(lastcooked__date__gte=cooked_date).exclude(lastcooked=default)
+ if self._cookedon_lte:
+ self._queryset = self._queryset.filter(lastcooked__date__lte=self._cookedon_lte).exclude(lastcooked=default)
+ elif self._cookedon_gte:
+ self._queryset = self._queryset.filter(lastcooked__date__gte=self._cookedon_gte ).exclude(lastcooked=default)
def _created_on_filter(self, created_date=None):
if created_date is None:
@@ -317,9 +328,9 @@ class RecipeSearch():
)
self._queryset = self._queryset.annotate(recent=Coalesce(Max(Case(When(pk__in=num_recent_recipes.values('recipe'), then='viewlog__pk'))), Value(0)))
- def _favorite_recipes(self, times_cooked=None):
- if self._sort_includes('favorite') or times_cooked:
- less_than = '-' in (str(times_cooked) or []) and not self._sort_includes('-favorite')
+ def _favorite_recipes(self):
+ if self._sort_includes('favorite') or self._timescooked_gte or self._timescooked_lte:
+ less_than = self._timescooked_lte and not self._sort_includes('-favorite')
if less_than:
default = 1000
else:
@@ -331,15 +342,13 @@ class RecipeSearch():
.values('count')
)
self._queryset = self._queryset.annotate(favorite=Coalesce(Subquery(favorite_recipes), default))
- if times_cooked is None:
- return
- if times_cooked == '0':
+ if (self._timescooked_lte == 0 and self._timescooked_gte is None) or (self._timescooked_gte == 0 and self._timescooked_lte is None):
self._queryset = self._queryset.filter(favorite=0)
- elif less_than:
- self._queryset = self._queryset.filter(favorite__lte=int(times_cooked.replace('-', ''))).exclude(favorite=0)
- else:
- self._queryset = self._queryset.filter(favorite__gte=int(times_cooked))
+ elif self._timescooked_lte:
+ self._queryset = self._queryset.filter(favorite__lte=int(self._timescooked_lte)).exclude(favorite=0)
+ elif self._timescooked_gte:
+ self._queryset = self._queryset.filter(favorite__gte=int(self._timescooked_gte))
def keyword_filters(self, **kwargs):
if all([kwargs[x] is None for x in kwargs]):
diff --git a/cookbook/views/api.py b/cookbook/views/api.py
index 21dbc2f31..ba09cd095 100644
--- a/cookbook/views/api.py
+++ b/cookbook/views/api.py
@@ -1079,101 +1079,55 @@ class RecipePagination(PageNumberPagination):
@extend_schema_view(retrieve=extend_schema(parameters=[
OpenApiParameter(name='share', type=str),
]), list=extend_schema(parameters=[
- OpenApiParameter(name='query', description=_(
- 'Query string matched (fuzzy) against recipe name. In the future also fulltext search.'), type=str),
- OpenApiParameter(name='keywords', description=_(
- 'ID of keyword a recipe should have. For multiple repeat parameter. Equivalent to keywords_or'), type=int,
- many=True),
- OpenApiParameter(name='keywords_or',
- description=_('Keyword IDs, repeat for multiple. Return recipes with any of the keywords'),
- type=int, many=True),
- OpenApiParameter(name='keywords_and',
- description=_('Keyword IDs, repeat for multiple. Return recipes with all of the keywords.'),
- type=int, many=True),
- OpenApiParameter(name='keywords_or_not',
- description=_('Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords.'),
- type=int, many=True),
- OpenApiParameter(name='keywords_and_not',
- description=_('Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords.'),
- type=int, many=True),
- OpenApiParameter(name='foods', description=_('ID of food a recipe should have. For multiple repeat parameter.'),
- type=int, many=True),
- OpenApiParameter(name='foods_or',
- description=_('Food IDs, repeat for multiple. Return recipes with any of the foods'), type=int,
- many=True),
- OpenApiParameter(name='foods_and',
- description=_('Food IDs, repeat for multiple. Return recipes with all of the foods.'), type=int,
- many=True),
- OpenApiParameter(name='foods_or_not',
- description=_('Food IDs, repeat for multiple. Exclude recipes with any of the foods.'), type=int,
- many=True),
- OpenApiParameter(name='foods_and_not',
- description=_('Food IDs, repeat for multiple. Exclude recipes with all of the foods.'), type=int,
- many=True),
- OpenApiParameter(name='books', description=_('ID of book a recipe should be in. For multiple repeat parameter.'),
- type=int, many=True),
- OpenApiParameter(name='books_or',
- description=_('Book IDs, repeat for multiple. Return recipes with any of the books'), type=int,
- many=True),
- OpenApiParameter(name='books_and',
- description=_('Book IDs, repeat for multiple. Return recipes with all of the books.'), type=int,
- many=True),
- OpenApiParameter(name='books_or_not',
- description=_('Book IDs, repeat for multiple. Exclude recipes with any of the books.'), type=int,
- many=True),
- OpenApiParameter(name='books_and_not',
- description=_('Book IDs, repeat for multiple. Exclude recipes with all of the books.'), type=int,
- many=True),
+ OpenApiParameter(name='query', description=_('Query string matched (fuzzy) against recipe name. In the future also fulltext search.'), type=str),
+
+ OpenApiParameter(name='keywords', description=_('ID of keyword a recipe should have. For multiple repeat parameter. Equivalent to keywords_or'), type=int, many=True),
+ OpenApiParameter(name='keywords_or', description=_('Keyword IDs, repeat for multiple. Return recipes with any of the keywords'), type=int, many=True),
+ OpenApiParameter(name='keywords_and', description=_('Keyword IDs, repeat for multiple. Return recipes with all of the keywords.'), type=int, many=True),
+ OpenApiParameter(name='keywords_or_not', description=_('Keyword IDs, repeat for multiple. Exclude recipes with any of the keywords.'), type=int, many=True),
+ OpenApiParameter(name='keywords_and_not', description=_('Keyword IDs, repeat for multiple. Exclude recipes with all of the keywords.'), type=int, many=True),
+
+ OpenApiParameter(name='foods', description=_('ID of food a recipe should have. For multiple repeat parameter.'), type=int, many=True),
+ OpenApiParameter(name='foods_or', description=_('Food IDs, repeat for multiple. Return recipes with any of the foods'), type=int, many=True),
+ OpenApiParameter(name='foods_and', description=_('Food IDs, repeat for multiple. Return recipes with all of the foods.'), type=int, many=True),
+ OpenApiParameter(name='foods_or_not', description=_('Food IDs, repeat for multiple. Exclude recipes with any of the foods.'), type=int, many=True),
+ OpenApiParameter(name='foods_and_not', description=_('Food IDs, repeat for multiple. Exclude recipes with all of the foods.'), type=int, many=True),
+
+ OpenApiParameter(name='books', description=_('ID of book a recipe should be in. For multiple repeat parameter.'), type=int, many=True),
+ OpenApiParameter(name='books_or', description=_('Book IDs, repeat for multiple. Return recipes with any of the books'), type=int, many=True),
+ OpenApiParameter(name='books_and', description=_('Book IDs, repeat for multiple. Return recipes with all of the books.'), type=int, many=True),
+ OpenApiParameter(name='books_or_not', description=_('Book IDs, repeat for multiple. Exclude recipes with any of the books.'), type=int, many=True),
+ OpenApiParameter(name='books_and_not', description=_('Book IDs, repeat for multiple. Exclude recipes with all of the books.'), type=int, many=True),
+
OpenApiParameter(name='units', description=_('ID of unit a recipe should have.'), type=int),
- OpenApiParameter(name='internal',
- description=_('If only internal recipes should be returned. [''true''/''false'']'),
- type=bool),
- OpenApiParameter(name='rating', description=_( 'Exact rating of recipe'), type=int),
- OpenApiParameter(name='rating_gte', description=_( 'Rating a recipe should have or greater. '), type=int),
- OpenApiParameter(name='rating_lte', description=_( 'Rating a recipe should have or smaller.'), type=int),
+ OpenApiParameter(name='rating', description=_('Exact rating of recipe'), type=int),
+ OpenApiParameter(name='rating_gte', description=_('Rating a recipe should have or greater.'), type=int),
+ OpenApiParameter(name='rating_lte', description=_('Rating a recipe should have or smaller.'), type=int),
- OpenApiParameter(name='random',
- description=_('Returns the results in randomized order. [''true''/''false'']')),
- OpenApiParameter(name='new',
- description=_('Returns new results first in search results. [''true''/''false'']')),
- OpenApiParameter(name='num_recent', description=_(
- 'Returns the given number of recently viewed recipes before search results (if given)'), type=int),
- OpenApiParameter(name='timescooked', description=_(
- 'Filter recipes cooked X times or more. Negative values returns cooked less than X times'), type=int),
- OpenApiParameter(
- name='cookedon',
- description=_('Filter recipes last cooked on or after YYYY-MM-DD. Prepending ''-'' filters on or before date.'),
- type=str,
- examples=[DateExample, BeforeDateExample]
- ),
- OpenApiParameter(
- name='createdon',
- description=_('Filter recipes created on or after YYYY-MM-DD. Prepending ''-'' filters on or before date.'),
- type=str,
- examples=[DateExample, BeforeDateExample]
- ),
- OpenApiParameter(
- name='createdby',
- description=_('Filter recipes for ones created by the given user ID'),
- type=int,
- ),
- OpenApiParameter(
- name='updatedon',
- description=_('Filter recipes updated on or after YYYY-MM-DD. Prepending ''-'' filters on or before date.'),
- type=str,
- examples=[DateExample, BeforeDateExample]
- ),
- OpenApiParameter(
- name='viewedon',
- description=_(
- 'Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending ''-'' filters on or before date.'),
- type=str,
- examples=[DateExample, BeforeDateExample]
- ),
- OpenApiParameter(name='makenow',
- description=_('Filter recipes that can be made with OnHand food. [''true''/''false'']'),
- type=bool),
+ OpenApiParameter(name='timescooked_gte', description=_('Filter recipes cooked X times or more.'), type=int),
+ OpenApiParameter(name='timescooked_lte', description=_('Filter recipes cooked X times or less.'), type=int),
+
+ OpenApiParameter(name='createdon', description=_('Filter recipes created on the given date.'), type=OpenApiTypes.DATE, ),
+ OpenApiParameter(name='createdon_gte', description=_('Filter recipes created on the given date or after.'), type=OpenApiTypes.DATE, ),
+ OpenApiParameter(name='createdon_lte', description=_('Filter recipes created on the given date or before.'), type=OpenApiTypes.DATE, ),
+
+ OpenApiParameter(name='updatedon', description=_('Filter recipes updated on the given date.'), type=OpenApiTypes.DATE, ),
+ OpenApiParameter(name='updatedon_gte', description=_('Filter recipes updated on the given date.'), type=OpenApiTypes.DATE, ),
+ OpenApiParameter(name='updatedon_lte', description=_('Filter recipes updated on the given date.'), type=OpenApiTypes.DATE, ),
+
+ OpenApiParameter(name='cookedon_gte', description=_('Filter recipes last cooked on the given date or after.'), type=OpenApiTypes.DATE),
+ OpenApiParameter(name='cookedon_lte', description=_('Filter recipes last cooked on the given date or before.'), type=OpenApiTypes.DATE),
+
+ OpenApiParameter(name='viewedon_gte', description=_('Filter recipes lasts viewed on the given date.'), type=OpenApiTypes.DATE, ),
+ OpenApiParameter(name='viewedon_lte', description=_('Filter recipes lasts viewed on the given date.'), type=OpenApiTypes.DATE, ),
+
+ OpenApiParameter(name='createdby', description=_('Filter recipes for ones created by the given user ID'), type=int),
+ OpenApiParameter(name='internal', description=_('If only internal recipes should be returned. [''true''/''false'']'), type=bool),
+ OpenApiParameter(name='random', description=_('Returns the results in randomized order. [''true''/''false'']')),
+ OpenApiParameter(name='new', description=_('Returns new results first in search results. [''true''/''false'']')),
+ OpenApiParameter(name='num_recent', description=_('Returns the given number of recently viewed recipes before search results (if given)'), type=int),
+ OpenApiParameter(name='makenow', description=_('Filter recipes that can be made with OnHand food. [''true''/''false'']'), type=bool),
]))
class RecipeViewSet(LoggingMixin, viewsets.ModelViewSet):
queryset = Recipe.objects
@@ -1944,7 +1898,8 @@ class FdcSearchView(APIView):
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
@extend_schema(responses=FdcQuerySerializer(many=False),
- parameters=[OpenApiParameter(name='query', type=str), OpenApiParameter(name='dataType', description='options: Branded,Foundation,Survey (FNDDS),SR Legacy', type=str, many=True)])
+ parameters=[OpenApiParameter(name='query', type=str),
+ OpenApiParameter(name='dataType', description='options: Branded,Foundation,Survey (FNDDS),SR Legacy', type=str, many=True)])
def get(self, request, format=None):
query = self.request.query_params.get('query', None)
if query is not None:
@@ -1952,7 +1907,6 @@ class FdcSearchView(APIView):
response = requests.get(f'https://api.nal.usda.gov/fdc/v1/foods/search?api_key={FDC_API_KEY}&query={query}&dataType={",".join(data_types)}')
-
if response.status_code == 429:
return JsonResponse(
{
diff --git a/vue3/src/openapi/apis/ApiApi.ts b/vue3/src/openapi/apis/ApiApi.ts
index 65a7f59cb..b048c2bdf 100644
--- a/vue3/src/openapi/apis/ApiApi.ts
+++ b/vue3/src/openapi/apis/ApiApi.ts
@@ -1195,9 +1195,12 @@ export interface ApiRecipeListRequest {
booksAndNot?: Array;
booksOr?: Array;
booksOrNot?: Array;
- cookedon?: string;
+ cookedonGte?: Date;
+ cookedonLte?: Date;
createdby?: number;
- createdon?: string;
+ createdon?: Date;
+ createdonGte?: Date;
+ createdonLte?: Date;
foods?: Array;
foodsAnd?: Array;
foodsAndNot?: Array;
@@ -1219,10 +1222,14 @@ export interface ApiRecipeListRequest {
rating?: number;
ratingGte?: number;
ratingLte?: number;
- timescooked?: number;
+ timescookedGte?: number;
+ timescookedLte?: number;
units?: number;
- updatedon?: string;
- viewedon?: string;
+ updatedon?: Date;
+ updatedonGte?: Date;
+ updatedonLte?: Date;
+ viewedonGte?: Date;
+ viewedonLte?: Date;
}
export interface ApiRecipePartialUpdateRequest {
@@ -8794,8 +8801,12 @@ export class ApiApi extends runtime.BaseAPI {
queryParameters['books_or_not'] = requestParameters['booksOrNot'];
}
- if (requestParameters['cookedon'] != null) {
- queryParameters['cookedon'] = requestParameters['cookedon'];
+ if (requestParameters['cookedonGte'] != null) {
+ queryParameters['cookedon_gte'] = (requestParameters['cookedonGte'] as any).toISOString().substring(0,10);
+ }
+
+ if (requestParameters['cookedonLte'] != null) {
+ queryParameters['cookedon_lte'] = (requestParameters['cookedonLte'] as any).toISOString().substring(0,10);
}
if (requestParameters['createdby'] != null) {
@@ -8803,7 +8814,15 @@ export class ApiApi extends runtime.BaseAPI {
}
if (requestParameters['createdon'] != null) {
- queryParameters['createdon'] = requestParameters['createdon'];
+ queryParameters['createdon'] = (requestParameters['createdon'] as any).toISOString().substring(0,10);
+ }
+
+ if (requestParameters['createdonGte'] != null) {
+ queryParameters['createdon_gte'] = (requestParameters['createdonGte'] as any).toISOString().substring(0,10);
+ }
+
+ if (requestParameters['createdonLte'] != null) {
+ queryParameters['createdon_lte'] = (requestParameters['createdonLte'] as any).toISOString().substring(0,10);
}
if (requestParameters['foods'] != null) {
@@ -8890,8 +8909,12 @@ export class ApiApi extends runtime.BaseAPI {
queryParameters['rating_lte'] = requestParameters['ratingLte'];
}
- if (requestParameters['timescooked'] != null) {
- queryParameters['timescooked'] = requestParameters['timescooked'];
+ if (requestParameters['timescookedGte'] != null) {
+ queryParameters['timescooked_gte'] = requestParameters['timescookedGte'];
+ }
+
+ if (requestParameters['timescookedLte'] != null) {
+ queryParameters['timescooked_lte'] = requestParameters['timescookedLte'];
}
if (requestParameters['units'] != null) {
@@ -8899,11 +8922,23 @@ export class ApiApi extends runtime.BaseAPI {
}
if (requestParameters['updatedon'] != null) {
- queryParameters['updatedon'] = requestParameters['updatedon'];
+ queryParameters['updatedon'] = (requestParameters['updatedon'] as any).toISOString().substring(0,10);
}
- if (requestParameters['viewedon'] != null) {
- queryParameters['viewedon'] = requestParameters['viewedon'];
+ if (requestParameters['updatedonGte'] != null) {
+ queryParameters['updatedon_gte'] = (requestParameters['updatedonGte'] as any).toISOString().substring(0,10);
+ }
+
+ if (requestParameters['updatedonLte'] != null) {
+ queryParameters['updatedon_lte'] = (requestParameters['updatedonLte'] as any).toISOString().substring(0,10);
+ }
+
+ if (requestParameters['viewedonGte'] != null) {
+ queryParameters['viewedon_gte'] = (requestParameters['viewedonGte'] as any).toISOString().substring(0,10);
+ }
+
+ if (requestParameters['viewedonLte'] != null) {
+ queryParameters['viewedon_lte'] = (requestParameters['viewedonLte'] as any).toISOString().substring(0,10);
}
const headerParameters: runtime.HTTPHeaders = {};
diff --git a/vue3/src/pages/SearchPage.vue b/vue3/src/pages/SearchPage.vue
index 516e7282c..7e8ec5fba 100644
--- a/vue3/src/pages/SearchPage.vue
+++ b/vue3/src/pages/SearchPage.vue
@@ -23,14 +23,16 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
@@ -87,7 +89,9 @@
>
-
+
+
+
@@ -136,7 +140,7 @@