mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 04:10:06 -05:00
Merge branch 'search_and_keywords' of https://github.com/smilerz/recipes into smilerz-search_and_keywords
# Conflicts: # cookbook/admin.py # cookbook/helper/recipe_search.py # cookbook/integration/chowdown.py # cookbook/integration/integration.py # cookbook/management/commands/rebuildindex.py # cookbook/managers.py # cookbook/migrations/0142_build_full_text_index.py # cookbook/models.py # cookbook/schemas.py # cookbook/serializer.py # cookbook/static/vue/css/keyword_list_view.css # cookbook/static/vue/js/chunk-vendors.js # cookbook/static/vue/js/keyword_list_view.js # cookbook/tests/api/test_api_keyword.py # cookbook/views/api.py # cookbook/views/data.py # cookbook/views/views.py # recipes/settings.py # vue/package.json # vue/src/apps/KeywordListView/KeywordListView.vue # vue/src/components/KeywordCard.vue # vue/src/locales/en.json # vue/src/utils/openapi/api.ts # vue/yarn.lock
This commit is contained in:
@@ -4,9 +4,9 @@ import re
|
||||
import uuid
|
||||
|
||||
import requests
|
||||
from PIL import Image
|
||||
from annoying.decorators import ajax_request
|
||||
from annoying.functions import get_object_or_None
|
||||
from collections import OrderedDict
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.postgres.search import TrigramSimilarity
|
||||
@@ -37,13 +37,13 @@ from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest,
|
||||
group_required)
|
||||
from cookbook.helper.recipe_html_import import get_recipe_from_source
|
||||
|
||||
from cookbook.helper.recipe_search import search_recipes
|
||||
from cookbook.helper.recipe_search import search_recipes, get_facet
|
||||
from cookbook.helper.recipe_url_import import get_from_scraper
|
||||
from cookbook.models import (CookLog, Food, Ingredient, Keyword, MealPlan,
|
||||
MealType, Recipe, RecipeBook, ShoppingList,
|
||||
ShoppingListEntry, ShoppingListRecipe, Step,
|
||||
Storage, Sync, SyncLog, Unit, UserPreference,
|
||||
ViewLog, RecipeBookEntry, Supermarket, ImportLog, BookmarkletImport, SupermarketCategory, UserFile, ShareLink)
|
||||
ViewLog, RecipeBookEntry, Supermarket, ImportLog, BookmarkletImport, SupermarketCategory, UserFile, ShareLink, SupermarketCategoryRelation)
|
||||
from cookbook.provider.dropbox import Dropbox
|
||||
from cookbook.provider.local import Local
|
||||
from cookbook.provider.nextcloud import Nextcloud
|
||||
@@ -61,7 +61,7 @@ from cookbook.serializer import (FoodSerializer, IngredientSerializer,
|
||||
UserNameSerializer, UserPreferenceSerializer,
|
||||
ViewLogSerializer, CookLogSerializer, RecipeBookEntrySerializer,
|
||||
RecipeOverviewSerializer, SupermarketSerializer, ImportLogSerializer,
|
||||
BookmarkletImportSerializer, SupermarketCategorySerializer, UserFileSerializer)
|
||||
BookmarkletImportSerializer, SupermarketCategorySerializer, UserFileSerializer, SupermarketCategoryRelationSerializer)
|
||||
|
||||
|
||||
class StandardFilterMixin(ViewSetMixin):
|
||||
@@ -144,18 +144,18 @@ class TreeMixin(FuzzyFilterMixin):
|
||||
except self.model.DoesNotExist:
|
||||
self.queryset = self.model.objects.none()
|
||||
if root == 0:
|
||||
self.queryset = self.model.get_root_nodes().filter(space=self.request.space)
|
||||
self.queryset = self.model.get_root_nodes() | self.model.objects.filter(depth=0)
|
||||
else:
|
||||
self.queryset = self.model.objects.get(id=root).get_children().filter(space=self.request.space)
|
||||
self.queryset = self.model.objects.get(id=root).get_children()
|
||||
elif tree:
|
||||
if tree.isnumeric():
|
||||
try:
|
||||
self.queryset = self.model.objects.get(id=int(tree)).get_descendants_and_self().filter(space=self.request.space)
|
||||
self.queryset = self.model.objects.get(id=int(tree)).get_descendants_and_self()
|
||||
except Keyword.DoesNotExist:
|
||||
self.queryset = self.model.objects.none()
|
||||
else:
|
||||
return super().get_queryset()
|
||||
return self.queryset
|
||||
return self.queryset.filter(space=self.request.space)
|
||||
|
||||
@decorators.action(detail=True, url_path='move/(?P<parent>[^/.]+)', methods=['PUT'],)
|
||||
@decorators.renderer_classes((TemplateHTMLRenderer, JSONRenderer))
|
||||
@@ -166,7 +166,7 @@ class TreeMixin(FuzzyFilterMixin):
|
||||
child = self.model.objects.get(pk=pk, space=self.request.space)
|
||||
except (self.model.DoesNotExist):
|
||||
content = {'error': True, 'msg': _(f'No {self.basename} with id {child} exists')}
|
||||
return Response(content, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(content, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
parent = int(parent)
|
||||
# parent 0 is root of the tree
|
||||
@@ -184,7 +184,7 @@ class TreeMixin(FuzzyFilterMixin):
|
||||
parent = self.model.objects.get(pk=parent, space=self.request.space)
|
||||
except (self.model.DoesNotExist):
|
||||
content = {'error': True, 'msg': _(f'No {self.basename} with id {parent} exists')}
|
||||
return Response(content, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(content, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
try:
|
||||
with scopes_disabled():
|
||||
@@ -204,7 +204,7 @@ class TreeMixin(FuzzyFilterMixin):
|
||||
source = self.model.objects.get(pk=pk, space=self.request.space)
|
||||
except (self.model.DoesNotExist):
|
||||
content = {'error': True, 'msg': _(f'No {self.basename} with id {pk} exists')}
|
||||
return Response(content, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(content, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
if int(target) == source.id:
|
||||
content = {'error': True, 'msg': _('Cannot merge with the same object!')}
|
||||
@@ -215,14 +215,14 @@ class TreeMixin(FuzzyFilterMixin):
|
||||
target = self.model.objects.get(pk=target, space=self.request.space)
|
||||
except (self.model.DoesNotExist):
|
||||
content = {'error': True, 'msg': _(f'No {self.basename} with id {target} exists')}
|
||||
return Response(content, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(content, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
try:
|
||||
if target in source.get_descendants_and_self():
|
||||
content = {'error': True, 'msg': _('Cannot merge with child object!')}
|
||||
return Response(content, status=status.HTTP_403_FORBIDDEN)
|
||||
########################################################################
|
||||
# this needs abstracted to update steps instead of recipes for food merge
|
||||
# TODO this needs abstracted to update steps instead of recipes for food merge
|
||||
########################################################################
|
||||
recipes = Recipe.objects.filter(**{"%ss" % self.basename: source}, space=self.request.space)
|
||||
|
||||
@@ -322,9 +322,18 @@ class SupermarketCategoryViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||
return super().get_queryset()
|
||||
|
||||
|
||||
class KeywordViewSet(viewsets.ModelViewSet, TreeMixin):
|
||||
# TODO check if fuzzyfilter is conflicting - may also need to create 'tree filter' mixin
|
||||
class SupermarketCategoryRelationViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||
queryset = SupermarketCategoryRelation.objects
|
||||
serializer_class = SupermarketCategoryRelationSerializer
|
||||
permission_classes = [CustomIsUser]
|
||||
pagination_class = DefaultPagination
|
||||
|
||||
def get_queryset(self):
|
||||
self.queryset = self.queryset.filter(supermarket__space=self.request.space)
|
||||
return super().get_queryset()
|
||||
|
||||
|
||||
class KeywordViewSet(viewsets.ModelViewSet, TreeMixin):
|
||||
queryset = Keyword.objects
|
||||
model = Keyword
|
||||
serializer_class = KeywordSerializer
|
||||
@@ -438,6 +447,19 @@ class RecipePagination(PageNumberPagination):
|
||||
page_size_query_param = 'page_size'
|
||||
max_page_size = 100
|
||||
|
||||
def paginate_queryset(self, queryset, request, view=None):
|
||||
self.facets = get_facet(queryset, request.query_params)
|
||||
return super().paginate_queryset(queryset, request, view)
|
||||
|
||||
def get_paginated_response(self, data):
|
||||
return Response(OrderedDict([
|
||||
('count', self.page.paginator.count),
|
||||
('next', self.get_next_link()),
|
||||
('previous', self.get_previous_link()),
|
||||
('results', data),
|
||||
('facets', self.facets)
|
||||
]))
|
||||
|
||||
|
||||
class RecipeViewSet(viewsets.ModelViewSet):
|
||||
queryset = Recipe.objects
|
||||
@@ -679,7 +701,7 @@ def share_link(request, pk):
|
||||
def log_cooking(request, recipe_id):
|
||||
recipe = get_object_or_None(Recipe, id=recipe_id)
|
||||
if recipe:
|
||||
log = CookLog.objects.create(created_by=request.user, recipe=recipe)
|
||||
log = CookLog.objects.create(created_by=request.user, recipe=recipe, space=request.space)
|
||||
servings = request.GET['s'] if 's' in request.GET else None
|
||||
if servings and re.match(r'^([1-9])+$', servings):
|
||||
log.servings = int(servings)
|
||||
|
||||
@@ -13,7 +13,7 @@ from django.urls import reverse
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import ngettext
|
||||
from django_tables2 import RequestConfig
|
||||
from PIL import Image, UnidentifiedImageError
|
||||
from PIL import UnidentifiedImageError
|
||||
from requests.exceptions import MissingSchema
|
||||
|
||||
from cookbook.forms import BatchEditForm, SyncForm
|
||||
@@ -150,12 +150,8 @@ def import_url(request):
|
||||
|
||||
all_keywords = Keyword.get_tree()
|
||||
for kw in data['keywords']:
|
||||
q = all_keywords.filter(name=kw['text'], space=request.space)
|
||||
if len(q) != 0:
|
||||
recipe.keywords.add(q[0])
|
||||
elif data['all_keywords']:
|
||||
k = Keyword.add_root(name=kw['text'], space=request.space)
|
||||
recipe.keywords.add(k)
|
||||
k, created = Keyword.objects.get_or_create(name=kw['text'], space=request.space)
|
||||
recipe.keywords.add(k)
|
||||
|
||||
for ing in data['recipeIngredient']:
|
||||
ingredient = Ingredient(space=request.space,)
|
||||
|
||||
@@ -63,7 +63,7 @@ def get_integration(request, export_type):
|
||||
|
||||
@group_required('user')
|
||||
def import_recipe(request):
|
||||
if request.space.max_recipes != 0 and Recipe.objects.filter(space=request.space).count() >= request.space.max_recipes: # TODO move to central helper function
|
||||
if request.space.max_recipes != 0 and Recipe.objects.filter(space=request.space).count() >= request.space.max_recipes: # TODO move to central helper function
|
||||
messages.add_message(request, messages.WARNING, _('You have reached the maximum number of recipes for your space.'))
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@ import json
|
||||
import requests
|
||||
from django.db.models import Q
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from cookbook.helper.ingredient_parser import parse, get_unit, get_food
|
||||
from cookbook.helper.permission_helper import group_required
|
||||
from cookbook.models import TelegramBot, ShoppingList, ShoppingListEntry, Food, Unit
|
||||
from cookbook.models import TelegramBot, ShoppingList, ShoppingListEntry
|
||||
|
||||
|
||||
@group_required('user')
|
||||
@@ -58,7 +58,7 @@ def hook(request, token):
|
||||
)
|
||||
)
|
||||
return JsonResponse({'data': data['message']['text']})
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return JsonResponse({})
|
||||
|
||||
@@ -15,6 +15,8 @@ from django.db.models import Avg, Q
|
||||
from django.db.models import Sum
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.http import JsonResponse
|
||||
from django.db.models import Avg, Q, Sum
|
||||
from django.http import HttpResponseRedirect, JsonResponse
|
||||
from django.shortcuts import get_object_or_404, render, redirect
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone
|
||||
@@ -27,6 +29,8 @@ from cookbook.filters import RecipeFilter
|
||||
from cookbook.forms import (CommentForm, Recipe, User,
|
||||
UserCreateForm, UserNameForm, UserPreference,
|
||||
UserPreferenceForm, SpaceJoinForm, SpaceCreateForm, SearchPreferenceForm)
|
||||
UserPreferenceForm, SpaceJoinForm, SpaceCreateForm,
|
||||
SearchPreferenceForm)
|
||||
from cookbook.helper.ingredient_parser import parse
|
||||
from cookbook.helper.permission_helper import group_required, share_link_valid, has_group_permission
|
||||
from cookbook.models import (Comment, CookLog, InviteLink, MealPlan,
|
||||
@@ -122,6 +126,7 @@ def no_space(request):
|
||||
max_users=settings.SPACE_DEFAULT_MAX_USERS,
|
||||
allow_sharing=settings.SPACE_DEFAULT_ALLOW_SHARING,
|
||||
)
|
||||
|
||||
request.user.userpreference.space = created_space
|
||||
request.user.userpreference.save()
|
||||
request.user.groups.add(Group.objects.filter(name='admin').get())
|
||||
@@ -141,7 +146,7 @@ def no_space(request):
|
||||
if 'signup_token' in request.session:
|
||||
return HttpResponseRedirect(reverse('view_invite', args=[request.session.pop('signup_token', '')]))
|
||||
|
||||
create_form = SpaceCreateForm()
|
||||
create_form = SpaceCreateForm(initial={'name': f'{request.user.username}\'s Space'})
|
||||
join_form = SpaceJoinForm()
|
||||
|
||||
return render(request, 'no_space_info.html', {'create_form': create_form, 'join_form': join_form})
|
||||
|
||||
Reference in New Issue
Block a user