fixed test + added api doc for new search
This commit is contained in:
parent
e676b4bac3
commit
4ad5d6ef2f
@ -17,7 +17,6 @@ def search_recipes(queryset, params):
|
|||||||
search_books_or = params.get('books_or', True)
|
search_books_or = params.get('books_or', True)
|
||||||
|
|
||||||
search_internal = params.get('internal', None)
|
search_internal = params.get('internal', None)
|
||||||
search_limit = params.get('limit', None)
|
|
||||||
|
|
||||||
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']:
|
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']:
|
||||||
queryset = queryset.annotate(similarity=TrigramSimilarity('name', search_string), ).filter(Q(similarity__gt=0.1) | Q(name__unaccent__icontains=search_string)).order_by('-similarity')
|
queryset = queryset.annotate(similarity=TrigramSimilarity('name', search_string), ).filter(Q(similarity__gt=0.1) | Q(name__unaccent__icontains=search_string)).order_by('-similarity')
|
||||||
@ -50,7 +49,4 @@ def search_recipes(queryset, params):
|
|||||||
if search_internal == 'true':
|
if search_internal == 'true':
|
||||||
queryset = queryset.filter(internal=True)
|
queryset = queryset.filter(internal=True)
|
||||||
|
|
||||||
# if search_limit:
|
|
||||||
# queryset = queryset[:int(search_limit)]
|
|
||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
@ -517,7 +517,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.$http.get(url).then((response) => {
|
this.$http.get(url).then((response) => {
|
||||||
this.recipes = response.data;
|
this.recipes = response.data.results;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.log("getRecipes error: ", err);
|
console.log("getRecipes error: ", err);
|
||||||
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
|
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
|
||||||
@ -530,7 +530,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.$http.get(url).then((response) => {
|
this.$http.get(url).then((response) => {
|
||||||
this.recipes = response.data;
|
this.recipes = response.data.results;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.log("getRecipes error: ", err);
|
console.log("getRecipes error: ", err);
|
||||||
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
|
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
|
||||||
|
@ -834,7 +834,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.$http.get(url).then((response) => {
|
this.$http.get(url).then((response) => {
|
||||||
this.recipes = response.data;
|
this.recipes = response.data.results;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.log("getRecipes error: ", err);
|
console.log("getRecipes error: ", err);
|
||||||
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
|
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
|
||||||
|
@ -22,15 +22,15 @@ def test_list_permission(arg, request):
|
|||||||
|
|
||||||
|
|
||||||
def test_list_space(recipe_1_s1, u1_s1, u1_s2, space_2):
|
def test_list_space(recipe_1_s1, u1_s1, u1_s2, space_2):
|
||||||
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 1
|
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)['results']) == 1
|
||||||
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0
|
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)['results']) == 0
|
||||||
|
|
||||||
with scopes_disabled():
|
with scopes_disabled():
|
||||||
recipe_1_s1.space = space_2
|
recipe_1_s1.space = space_2
|
||||||
recipe_1_s1.save()
|
recipe_1_s1.save()
|
||||||
|
|
||||||
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 0
|
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)['results']) == 0
|
||||||
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 1
|
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)['results']) == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", [
|
@pytest.mark.parametrize("arg", [
|
||||||
|
@ -4,13 +4,11 @@ import re
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import yaml
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from annoying.decorators import ajax_request
|
from annoying.decorators import ajax_request
|
||||||
from annoying.functions import get_object_or_None
|
from annoying.functions import get_object_or_None
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core import management
|
|
||||||
from django.core.exceptions import FieldError, ValidationError
|
from django.core.exceptions import FieldError, ValidationError
|
||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
@ -18,19 +16,21 @@ from django.http import FileResponse, HttpResponse, JsonResponse
|
|||||||
from django.shortcuts import redirect, get_object_or_404
|
from django.shortcuts import redirect, get_object_or_404
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from icalendar import Calendar, Event
|
from icalendar import Calendar, Event
|
||||||
from rest_framework import decorators, viewsets, status
|
from recipe_scrapers import scrape_me, WebsiteNotImplementedError, NoSchemaFoundInWildMode
|
||||||
from rest_framework.exceptions import APIException, PermissionDenied, NotFound, MethodNotAllowed
|
from rest_framework import decorators, viewsets
|
||||||
from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination
|
from rest_framework.exceptions import APIException, PermissionDenied
|
||||||
|
from rest_framework.pagination import PageNumberPagination
|
||||||
from rest_framework.parsers import MultiPartParser
|
from rest_framework.parsers import MultiPartParser
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.schemas import AutoSchema
|
from rest_framework.schemas.openapi import AutoSchema
|
||||||
|
from rest_framework.schemas.utils import is_list_view
|
||||||
from rest_framework.viewsets import ViewSetMixin
|
from rest_framework.viewsets import ViewSetMixin
|
||||||
|
|
||||||
from cookbook.helper.ingredient_parser import parse
|
from cookbook.helper.ingredient_parser import parse
|
||||||
from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest,
|
from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest,
|
||||||
CustomIsOwner, CustomIsShare,
|
CustomIsOwner, CustomIsShare,
|
||||||
CustomIsShared, CustomIsUser,
|
CustomIsShared, CustomIsUser,
|
||||||
group_required, share_link_valid)
|
group_required)
|
||||||
from cookbook.helper.recipe_search import search_recipes
|
from cookbook.helper.recipe_search import search_recipes
|
||||||
from cookbook.helper.recipe_url_import import get_from_html, get_from_scraper, find_recipe_json
|
from cookbook.helper.recipe_url_import import get_from_html, get_from_scraper, find_recipe_json
|
||||||
from cookbook.models import (CookLog, Food, Ingredient, Keyword, MealPlan,
|
from cookbook.models import (CookLog, Food, Ingredient, Keyword, MealPlan,
|
||||||
@ -55,7 +55,6 @@ from cookbook.serializer import (FoodSerializer, IngredientSerializer,
|
|||||||
ViewLogSerializer, CookLogSerializer, RecipeBookEntrySerializer,
|
ViewLogSerializer, CookLogSerializer, RecipeBookEntrySerializer,
|
||||||
RecipeOverviewSerializer, SupermarketSerializer, ImportLogSerializer)
|
RecipeOverviewSerializer, SupermarketSerializer, ImportLogSerializer)
|
||||||
from recipes.settings import DEMO
|
from recipes.settings import DEMO
|
||||||
from recipe_scrapers import scrape_me, WebsiteNotImplementedError, NoSchemaFoundInWildMode
|
|
||||||
|
|
||||||
|
|
||||||
class StandardFilterMixin(ViewSetMixin):
|
class StandardFilterMixin(ViewSetMixin):
|
||||||
@ -281,34 +280,58 @@ class RecipePagination(PageNumberPagination):
|
|||||||
max_page_size = 100
|
max_page_size = 100
|
||||||
|
|
||||||
|
|
||||||
from rest_framework.schemas.openapi import AutoSchema, SchemaGenerator
|
# TODO move to separate class to cleanup
|
||||||
|
|
||||||
|
|
||||||
class RecipeSchema(AutoSchema):
|
class RecipeSchema(AutoSchema):
|
||||||
|
|
||||||
def get_path_parameters(self, path, method):
|
def get_path_parameters(self, path, method):
|
||||||
|
if not is_list_view(path, method, self.view):
|
||||||
|
return []
|
||||||
|
|
||||||
parameters = super().get_path_parameters(path, method)
|
parameters = super().get_path_parameters(path, method)
|
||||||
parameters.append(
|
parameters.append({
|
||||||
{
|
"name": 'query', "in": "query", "required": False,
|
||||||
"name": 'query',
|
"description": 'Query string matched (fuzzy) against recipe name. In the future also fulltext search.',
|
||||||
"in": "path",
|
'schema': {'type': 'string', },
|
||||||
"required": True,
|
})
|
||||||
"description": 'description',
|
parameters.append({
|
||||||
'schema': {
|
"name": 'keywords', "in": "query", "required": False,
|
||||||
'type': 'string', # TODO: integer, pattern, ...
|
"description": 'Id of keyword a recipe should have. For multiple repeat parameter.',
|
||||||
},
|
'schema': {'type': 'string', },
|
||||||
}
|
})
|
||||||
)
|
parameters.append({
|
||||||
|
"name": 'foods', "in": "query", "required": False,
|
||||||
|
"description": 'Id of food a recipe should have. For multiple repeat parameter.',
|
||||||
|
'schema': {'type': 'string', },
|
||||||
|
})
|
||||||
|
parameters.append({
|
||||||
|
"name": 'books', "in": "query", "required": False,
|
||||||
|
"description": 'Id of book a recipe should have. For multiple repeat parameter.',
|
||||||
|
'schema': {'type': 'string', },
|
||||||
|
})
|
||||||
|
parameters.append({
|
||||||
|
"name": 'keywords_or', "in": "query", "required": False,
|
||||||
|
"description": 'If recipe should have all (AND) or any (OR) of the provided keywords.',
|
||||||
|
'schema': {'type': 'string', },
|
||||||
|
})
|
||||||
|
parameters.append({
|
||||||
|
"name": 'foods_or', "in": "query", "required": False,
|
||||||
|
"description": 'If recipe should have all (AND) or any (OR) any of the provided foods.',
|
||||||
|
'schema': {'type': 'string', },
|
||||||
|
})
|
||||||
|
parameters.append({
|
||||||
|
"name": 'books_or', "in": "query", "required": False,
|
||||||
|
"description": 'If recipe should be in all (AND) or any (OR) any of the provided books.',
|
||||||
|
'schema': {'type': 'string', },
|
||||||
|
})
|
||||||
|
parameters.append({
|
||||||
|
"name": 'internal', "in": "query", "required": False,
|
||||||
|
"description": 'true or false. If only internal recipes should be returned or not.',
|
||||||
|
'schema': {'type': 'string', },
|
||||||
|
})
|
||||||
return parameters
|
return parameters
|
||||||
|
|
||||||
|
|
||||||
class RecipeViewSet(viewsets.ModelViewSet):
|
class RecipeViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
|
||||||
list:
|
|
||||||
optional parameters
|
|
||||||
- **query**: search recipes for a string contained in the recipe name (case in-sensitive)
|
|
||||||
- **limit**: limits the amount of returned results
|
|
||||||
"""
|
|
||||||
queryset = Recipe.objects
|
queryset = Recipe.objects
|
||||||
serializer_class = RecipeSerializer
|
serializer_class = RecipeSerializer
|
||||||
# TODO split read and write permission for meal plan guest
|
# TODO split read and write permission for meal plan guest
|
||||||
@ -316,7 +339,7 @@ class RecipeViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
pagination_class = RecipePagination
|
pagination_class = RecipePagination
|
||||||
|
|
||||||
# schema = RecipeSchema
|
schema = RecipeSchema()
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
share = self.request.query_params.get('share', None)
|
share = self.request.query_params.get('share', None)
|
||||||
|
Loading…
Reference in New Issue
Block a user