diff --git a/cookbook/schemas.py b/cookbook/schemas.py deleted file mode 100644 index 66bab3a8f..000000000 --- a/cookbook/schemas.py +++ /dev/null @@ -1,72 +0,0 @@ -from rest_framework.schemas.openapi import AutoSchema -from rest_framework.schemas.utils import is_list_view - - -class QueryParam(object): - def __init__(self, name, description=None, qtype='string', required=False): - self.name = name - self.description = description - self.qtype = qtype - self.required = required - - def __str__(self): - return f'{self.name}, {self.qtype}, {self.description}' - - -class QueryParamAutoSchema(AutoSchema): - def is_query(self, path, method): - return is_list_view(path, method, self.view) - - def get_path_parameters(self, path, method): - if not self.is_query(path, method): - return super().get_path_parameters(path, method) - parameters = super().get_path_parameters(path, method) - for q in self.view.query_params: - parameters.append({ - "name": q.name, "in": "query", "required": q.required, - "description": q.description, - 'schema': {'type': q.qtype, }, - }) - - return parameters - - -class TreeSchema(AutoSchema): - def get_path_parameters(self, path, method): - if not is_list_view(path, method, self.view): - return super(TreeSchema, self).get_path_parameters(path, method) - - api_name = path.split('/')[2] - parameters = super().get_path_parameters(path, method) - parameters.append({ - "name": 'query', "in": "query", "required": False, - "description": 'Query string matched against {} name.'.format(api_name), - 'schema': {'type': 'string', }, - }) - parameters.append({ - "name": 'root', "in": "query", "required": False, - "description": 'Return first level children of {obj} with ID [int]. Integer 0 will return root {obj}s.'.format( - obj=api_name), - 'schema': {'type': 'integer', }, - }) - parameters.append({ - "name": 'tree', "in": "query", "required": False, - "description": 'Return all self and children of {} with ID [int].'.format(api_name), - 'schema': {'type': 'integer', }, - }) - return parameters - - -class FilterSchema(AutoSchema): - def get_path_parameters(self, path, method): - if not is_list_view(path, method, self.view): - return super(FilterSchema, self).get_path_parameters(path, method) - - api_name = path.split('/')[2] - parameters = super().get_path_parameters(path, method) - parameters.append({ - "name": 'query', "in": "query", "required": False, - "description": 'Query string matched against {} name.'.format(api_name), - 'schema': {'type': 'string', }, - }) - return parameters diff --git a/cookbook/views/api.py b/cookbook/views/api.py index 66aee94e4..d36c9f593 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -77,7 +77,6 @@ from cookbook.models import (Automation, BookmarkletImport, CookLog, CustomFilte from cookbook.provider.dropbox import Dropbox from cookbook.provider.local import Local from cookbook.provider.nextcloud import Nextcloud -from cookbook.schemas import FilterSchema, QueryParam, QueryParamAutoSchema, TreeSchema from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer, AutoMealPlanSerializer, BookmarkletImportListSerializer, BookmarkletImportSerializer, CookLogSerializer, @@ -617,25 +616,26 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin): return self.serializer_class # TODO I could not find any usage of this and it causes schema generation issues, so commenting it for now - # @decorators.action(detail=True, methods=['PUT'], serializer_class=FoodShoppingUpdateSerializer, ) + # this is used on the Shopping Badge + @decorators.action(detail=True, methods=['PUT'], serializer_class=FoodShoppingUpdateSerializer, ) # # TODO DRF only allows one action in a decorator action without overriding get_operation_id_base() this should be PUT and DELETE probably - # def shopping(self, request, pk): - # if self.request.space.demo: - # raise PermissionDenied(detail='Not available in demo', code=None) - # obj = self.get_object() - # shared_users = list(self.request.user.get_shopping_share()) - # shared_users.append(request.user) - # if request.data.get('_delete', False) == 'true': - # ShoppingListEntry.objects.filter(food=obj, checked=False, space=request.space, created_by__in=shared_users).delete() - # content = {'msg': _(f'{obj.name} was removed from the shopping list.')} - # return Response(content, status=status.HTTP_204_NO_CONTENT) - # - # amount = request.data.get('amount', 1) - # unit = request.data.get('unit', None) - # content = {'msg': _(f'{obj.name} was added to the shopping list.')} - # - # ShoppingListEntry.objects.create(food=obj, amount=amount, unit=unit, space=request.space, created_by=request.user) - # return Response(content, status=status.HTTP_204_NO_CONTENT) + def shopping(self, request, pk): + if self.request.space.demo: + raise PermissionDenied(detail='Not available in demo', code=None) + obj = self.get_object() + shared_users = list(self.request.user.get_shopping_share()) + shared_users.append(request.user) + if request.data.get('_delete', False) == 'true': + ShoppingListEntry.objects.filter(food=obj, checked=False, space=request.space, created_by__in=shared_users).delete() + content = {'msg': _(f'{obj.name} was removed from the shopping list.')} + return Response(content, status=status.HTTP_204_NO_CONTENT) + + amount = request.data.get('amount', 1) + unit = request.data.get('unit', None) + content = {'msg': _(f'{obj.name} was added to the shopping list.')} + + ShoppingListEntry.objects.create(food=obj, amount=amount, unit=unit, space=request.space, created_by=request.user) + return Response(content, status=status.HTTP_204_NO_CONTENT) @decorators.action(detail=True, methods=['POST'], ) def fdc(self, request, pk): @@ -663,13 +663,11 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin): json_dumps_params={'indent': 4}) food.properties_food_amount = 100 - food.properties_food_unit = Unit.objects.get_or_create(base_unit__iexact='g', - space=self.request.space, - defaults={ - 'name': 'g', - 'base_unit': 'g', - 'space': self.request.space - })[0] + food.properties_food_unit = Unit.objects.get_or_create( + base_unit__iexact='g', + space=self.request.space, + defaults={ 'name': 'g', 'base_unit': 'g', 'space': self.request.space} + )[0] food.save() @@ -772,9 +770,10 @@ MealPlanViewQueryParameters = [ OpenApiParameter(name='meal_type', description=_('Filter meal plans with MealType ID. For multiple repeat parameter.'), type=str), ] + @extend_schema_view( list=extend_schema( - parameters= MealPlanViewQueryParameters + parameters=MealPlanViewQueryParameters ), ical=extend_schema( parameters=MealPlanViewQueryParameters, @@ -811,7 +810,7 @@ class MealPlanViewSet(viewsets.ModelViewSet): queryset = queryset.filter(meal_type__in=meal_type) return queryset - + @action(detail=False) def ical(self, request): from_date = self.request.query_params.get('from_date', None) @@ -819,7 +818,6 @@ class MealPlanViewSet(viewsets.ModelViewSet): return meal_plans_to_ical(self.get_queryset(), f'meal_plan_{from_date}-{to_date}.ics') - class AutoPlanViewSet(viewsets.ViewSet): def create(self, request): @@ -1136,8 +1134,8 @@ class RecipeViewSet(viewsets.ModelViewSet): serializer_class=RecipeFlatSerializer, ) def flat(self, request): - qs = Recipe.objects.filter( - space=request.space).filter(Q(private=False) | (Q(private=True) & (Q(created_by=self.request.user) | Q(shared=self.request.user)))).all() # TODO limit fields retrieved but .values() kills image + # TODO limit fields retrieved but .values() kills image + qs = Recipe.objects.filter(space=request.space).filter(Q(private=False) | (Q(private=True) & (Q(created_by=self.request.user) | Q(shared=self.request.user)))).all() return Response(self.serializer_class(qs, many=True).data) @@ -1284,14 +1282,18 @@ class ViewLogViewSet(viewsets.ModelViewSet): return self.queryset.filter(created_by=self.request.user).filter(space=self.request.space) +@extend_schema_view( + list=extend_schema( + parameters=[ + OpenApiParameter(name='recipe', description='Filter for entries with the given recipe', type=int), + ] + ) +) class CookLogViewSet(viewsets.ModelViewSet): queryset = CookLog.objects serializer_class = CookLogSerializer permission_classes = [CustomIsOwner & CustomTokenHasReadWriteScope] pagination_class = DefaultPagination - query_params = [ - QueryParam(name='recipe', description=_('Filter for entries with the given recipe'), qtype='integer'), - ] def get_queryset(self): if self.request.query_params.get('recipe', None): @@ -1802,6 +1804,7 @@ def get_plan_ical(request, from_date=datetime.date.today(), to_date=None): return meal_plans_to_ical(queryset, f'meal_plan_{from_date}-{to_date}.ics') + def meal_plans_to_ical(queryset, filename): cal = Calendar()