diff --git a/ofu_app/apps/food/api/v1_1/__init__.py b/ofu_app/apps/food/api/v1_1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ofu_app/apps/food/api/v1_1/urls.py b/ofu_app/apps/food/api/v1_1/urls.py index 66bcd5b..ae7cae2 100644 --- a/ofu_app/apps/food/api/v1_1/urls.py +++ b/ofu_app/apps/food/api/v1_1/urls.py @@ -14,7 +14,7 @@ Including another URLconf 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url -from apps.food.api import views as api_views +from apps.food.api.v1_1 import views as api_views from apps.food.models import Menu urlpatterns = [ diff --git a/ofu_app/apps/food/api/v1_1/views.py b/ofu_app/apps/food/api/v1_1/views.py index 5e73da7..25e8809 100644 --- a/ofu_app/apps/food/api/v1_1/views.py +++ b/ofu_app/apps/food/api/v1_1/views.py @@ -4,11 +4,11 @@ from __future__ import unicode_literals from datetime import datetime from datetime import timedelta -from apps.food.api.serializers import MenuSerializer, HappyHourSerializer +from apps.food.api.v1_1.serializers import MenuSerializer, HappyHourSerializer from apps.food.models import Menu, HappyHour from rest_framework import viewsets -from rest_framework.decorators import api_view, permission_classes +from rest_framework.decorators import permission_classes from rest_framework.permissions import AllowAny diff --git a/ofu_app/apps/food/api/v1_2/__init__.py b/ofu_app/apps/food/api/v1_2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ofu_app/apps/food/api/v1_2/serializers.py b/ofu_app/apps/food/api/v1_2/serializers.py index 98f2cfc..b5e98dd 100644 --- a/ofu_app/apps/food/api/v1_2/serializers.py +++ b/ofu_app/apps/food/api/v1_2/serializers.py @@ -1,18 +1,36 @@ from rest_framework import serializers -from apps.food.models import Menu, SingleFood, HappyHour, Allergene, UserFoodImage, FoodImage +from apps.food.models import Menu, SingleFood, HappyHour, Allergene, FoodImage, HappyHourLocation +from apps.food.models import UserFoodRating, UserFoodImage +from django.contrib.auth.models import User +from rest_framework import serializers +from rest_framework import validators -class UserFoodImageSerializer(serializers.HyperlinkedModelSerializer): - class Meta: - model = UserFoodImage - fields = ('id', 'image_image', 'image_thumb') +class DefaultFoodImageSerializer(serializers.Serializer): + """Your data serializer, define your fields here.""" + + def create(self, validated_data): + pass + + def update(self, instance, validated_data): + pass + + image = serializers.CharField() -class FoodImageSerializer(serializers.HyperlinkedModelSerializer): - class Meta: - model = FoodImage - fields = ('id', 'image', 'thumb') +class MenusLocationsSerializer(serializers.Serializer): + """Your data serializer, define your fields here.""" + + def create(self, validated_data): + pass + + def update(self, instance, validated_data): + pass + + id = serializers.CharField() + short = serializers.CharField() + name = serializers.CharField() class AllergensSerializer(serializers.HyperlinkedModelSerializer): @@ -21,18 +39,44 @@ class AllergensSerializer(serializers.HyperlinkedModelSerializer): fields = ('id', 'name') -class SingleFoodSerializer(serializers.HyperlinkedModelSerializer): - allergens = AllergensSerializer(many=True, read_only=True) - image = FoodImageSerializer(many=False, read_only=True) +class OverviewFoodImageSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = FoodImage + fields = ('id', 'thumb') + + +class DetailedFoodImageSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = FoodImage + fields = ('id', 'image', 'thumb') + + +class OverviewSingleFoodSerializer(serializers.HyperlinkedModelSerializer): + image = OverviewFoodImageSerializer(many=False, read_only=True) class Meta: model = SingleFood - fields = ('name', 'rating', 'price_student', 'price_employee', 'price_guest', 'allergens', 'image') + fields = ('id', 'name', 'rating', 'price_student', 'image') -class MenuSerializer(serializers.HyperlinkedModelSerializer): +class MinimalSingleFoodSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = SingleFood + fields = ('id', 'name') + + +class DetailedSingleFoosdSerializer(serializers.HyperlinkedModelSerializer): + allergens = AllergensSerializer(many=True, read_only=True) + image = DetailedFoodImageSerializer(many=False, read_only=True) + + class Meta: + model = SingleFood + fields = ('id', 'name', 'rating', 'price_student', 'price_employee', 'price_guest', 'allergens', 'image') + + +class OverviewMenuSerializer(serializers.HyperlinkedModelSerializer): date = serializers.DateField(format='iso-8601') - menu = SingleFoodSerializer(many=True, read_only=True) + menu = OverviewSingleFoodSerializer(many=True, read_only=True) location = serializers.ChoiceField(choices=Menu.LOCATION_CHOICES) class Meta: @@ -40,6 +84,17 @@ class MenuSerializer(serializers.HyperlinkedModelSerializer): fields = ('id', 'date', 'location', 'menu') +class DetailMenuSerializer(serializers.HyperlinkedModelSerializer): + date = serializers.DateField(format='iso-8601') + menu = DetailedSingleFoosdSerializer(many=True, read_only=True) + location = serializers.ChoiceField(choices=Menu.LOCATION_CHOICES) + + class Meta: + model = Menu + fields = ('id', 'date', 'location', 'menu') + + +# -------------------------- Happy Hour ------------------------------------ class HappyHourSerializer(serializers.HyperlinkedModelSerializer): date = serializers.DateField(format='iso-8601') starttime = serializers.TimeField() @@ -48,3 +103,53 @@ class HappyHourSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = HappyHour fields = ('id', 'date', 'starttime', 'endtime', 'location', 'description') + + +class HappyHourLocationSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = HappyHourLocation + fields = ('id', 'name') + + +# --------------------------- User -------------------------------------------- +class UserFoodImageSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = UserFoodImage + fields = ('id', 'image_image', 'image_thumb') + + +class UserFoodRatingSerializer(serializers.ModelSerializer): + # food = MinimalSingleFoodSerializer(many=False, read_only=False) + + class Meta: + model = UserFoodRating + fields = ('id', 'rating') + + def run_validators(self, value): + for validator in self.validators: + if isinstance(validator, validators.UniqueTogetherValidator): + self.validators.remove(validator) + super(UserFoodRatingSerializer, self).run_validators(value) + + def create(self, validated_data): + # TODO: Custom exception handler + rating = validated_data.pop('rating') + if rating >= 1 or rating <= 5: + food_id = self.context.get('food_id') + # user = self.context['request'].user + user = User.objects.get(id=1) + food = SingleFood.objects.get(id=food_id) + user_rating, _ = UserFoodRating.objects.get_or_create(food=food, user=user) + user_rating.rating = rating + user_rating.save() + + food_user_ratings = UserFoodRating.objects.all().filter(food=food) + sum = 0 + for food_user_rating in food_user_ratings: + sum += food_user_rating.rating + + food.rating = sum / food_user_ratings.count() + food.save() + return user_rating + else: + raise ValueError diff --git a/ofu_app/apps/food/api/v1_2/urls.py b/ofu_app/apps/food/api/v1_2/urls.py index ae7cae2..25b8669 100644 --- a/ofu_app/apps/food/api/v1_2/urls.py +++ b/ofu_app/apps/food/api/v1_2/urls.py @@ -13,21 +13,25 @@ Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ -from django.conf.urls import url -from apps.food.api.v1_1 import views as api_views -from apps.food.models import Menu +from django.urls import path +from apps.food.api.v1_2 import views as api_views urlpatterns = [ - # API Version 1.1 - url(r'^food/$', api_views.FoodViewSetV1_1.as_view({'get': 'list'}), name='api-v1_1-food-all'), - url(r'^food/(?P' + Menu.FEKI + '|' + Menu.MARKUSPLATZ + '|' + Menu.ERBA + '|' + Menu.AUSTRASSE + ')/$', - api_views.FoodViewSetV1_1.as_view({'get': 'list'}), name='api-v1_1-food-location'), - url(r'food/(?P[0-9]{4})/(?P[0-9]{2})/(?P[0-9]{2})/$', - api_views.FoodViewSetV1_1.as_view({'get': 'list'}), name='api-v1_1-food-date'), - url( - r'food/(?P' + Menu.FEKI + '|' + Menu.MARKUSPLATZ + '|' + Menu.ERBA + '|' + Menu.AUSTRASSE + ')/(?P[0-9]{4})/(?P[0-9]{2})/(?P[0-9]{2})/$', - api_views.FoodViewSetV1_1.as_view({'get': 'list'}), name='api-v1_1-food-location-date'), - url(r'food/today/$', api_views.FoodViewSetV1_1.as_view({'get': 'list'}), name='api-v1_1-food-today'), - url(r'food/week/$', api_views.FoodViewSetV1_1.as_view({'get': 'list'}), name='api-v1_1-food-week'), - url(r'happy-hour', api_views.HappyHourViewSet.as_view({'get': 'list'}), name='api-v1_1-happy-hour-all'), + # API Version 1.2 + path('food/menus/', api_views.ApiMenus.as_view(), name='menus'), + path('food/menus//', api_views.ApiMenu.as_view(), name='menu'), + path('food/menus/locations', api_views.ApiMenusLocations.as_view(), name='menus-locations'), + + path('food/meals/', api_views.ApiMeals.as_view(), name='meals'), + path('food/meals/', api_views.ApiMeal.as_view(), name='meal'), + path('food/allergens/', api_views.ApiAllergens.as_view(), name='allergens'), + path('food/meals/images/', api_views.ApiFoodImages.as_view(), name='images'), + path('food/meals/images/default', api_views.ApiFoodImagesDefault.as_view(), name='images-default'), + path('food/meals//rating', api_views.ApiFoodRatingUpload.as_view(), name='meals-rating-upload'), + # path('food/meals//image', api_views.ApiFoodImagesDefault.as_view(), name='meals-image-upload'), + # path('food/meals//comment', api_views.ApiFoodImagesDefault.as_view(), name='meals-comment-upload'), + + path('food/happy-hours/', api_views.ApiHappyHours.as_view(), name='happy-hours'), + path('food/happy-hours/', api_views.ApiHappyHours.as_view(), name='happy-hours'), + path('food/happy-hours/locations', api_views.ApiHappyHoursLocations.as_view(), name='happy-hours-locations'), ] diff --git a/ofu_app/apps/food/api/v1_2/views.py b/ofu_app/apps/food/api/v1_2/views.py index 25e8809..b5cbf63 100644 --- a/ofu_app/apps/food/api/v1_2/views.py +++ b/ofu_app/apps/food/api/v1_2/views.py @@ -1,190 +1,226 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +from django.templatetags.static import static + from datetime import datetime from datetime import timedelta -from apps.food.api.v1_1.serializers import MenuSerializer, HappyHourSerializer -from apps.food.models import Menu, HappyHour -from rest_framework import viewsets - -from rest_framework.decorators import permission_classes -from rest_framework.permissions import AllowAny +from apps.food.api.v1_2.serializers import OverviewMenuSerializer, DetailMenuSerializer, MenusLocationsSerializer +from apps.food.api.v1_2.serializers import OverviewSingleFoodSerializer, DetailedSingleFoosdSerializer, \ + AllergensSerializer, DetailedFoodImageSerializer, DefaultFoodImageSerializer, MinimalSingleFoodSerializer +from apps.food.api.v1_2.serializers import HappyHourSerializer, HappyHourLocationSerializer +from apps.food.api.v1_2.serializers import UserFoodRatingSerializer +from apps.food.models import Menu, SingleFood, Allergene, HappyHour, HappyHourLocation, FoodImage, UserFoodRating +from rest_framework import generics +from rest_framework.decorators import permission_classes, api_view, authentication_classes +from rest_framework.permissions import IsAuthenticated, AllowAny +from rest_framework import views, status +from rest_framework.response import Response +from rest_framework.authentication import TokenAuthentication +from rest_framework.exceptions import ValidationError -# @api_view(['GET']) @permission_classes((AllowAny,)) -class FoodViewSet(viewsets.ModelViewSet, ): - """ - API endpoint that allows users to be viewed or edited. - """ - # queryset = Menu.objects.all() - serializer_class = MenuSerializer +class ApiMenus(generics.ListAPIView): + serializer_class = OverviewMenuSerializer def get_queryset(self): queryset = Menu.objects.all() location = self.request.query_params.get('location') - date = self.request.query_params.get('date') - print(str(location).upper() == Menu.ERBA.upper()) + start_date = self.request.query_params.get('startdate') + end_date = self.request.query_params.get('enddate') + if location: - print(str(location).upper() == Menu.ERBA.upper()) - if str(location).upper() is Menu.ERBA.upper(): - queryset = queryset.filter(location_contains='Erba') - elif str(location).upper() is Menu.FEKI.upper(): - queryset = queryset.filter(location=Menu.FEKI) - elif str(location).upper() is Menu.AUSTRASSE.upper(): - queryset = queryset.filter(location=Menu.AUSTRASSE) - elif str(location).upper() is Menu.MARKUSPLATZ.upper(): - queryset = queryset.filter(location=Menu.MARKUSPLATZ) - if date: - if date == "week": - today = datetime.now() - weekday = today.weekday() - monday = today - timedelta(weekday) - sunday = today + (timedelta(6 - weekday)) - print("Monday: " + str(monday)) - print("Sunday: " + str(sunday)) - queryset = queryset.filter(date__gte=monday, date__lte=sunday) + if str(location).upper() == Menu.ERBA.upper(): + queryset = queryset.filter(location__contains=Menu.ERBA) + elif str(location).upper() == Menu.FEKI.upper(): + queryset = queryset.filter(location__contains=Menu.FEKI) + elif str(location).upper() == Menu.AUSTRASSE.upper(): + queryset = queryset.filter(location__contains=Menu.AUSTRASSE) + elif str(location).upper() == Menu.MARKUSPLATZ.upper(): + queryset = queryset.filter(location__contains=Menu.MARKUSPLATZ) else: - queryset = queryset.filter(date=datetime.strptime(date, "%Y-%m-%d")) + queryset = [] + if start_date: + try: + queryset = queryset.filter(date__gte=datetime.strptime(start_date, '%Y-%m-%d')) + except ValueError as e: + # TODO: return Exception + return [] - print("LOCATION: %s" % str(location)) - print("DATE: " + str(date)) - print(str(queryset)) + if end_date: + try: + queryset = queryset.filter(date__lte=datetime.strptime(end_date, '%Y-%m-%d')) + except ValueError as e: + # TODO: return Exception + return [] return queryset -# @api_view(['GET']) @permission_classes((AllowAny,)) -class FoodViewSetV1_1(viewsets.ModelViewSet, ): - """ - API endpoint that allows users to be viewed or edited. - """ - # queryset = Menu.objects.all() - serializer_class = MenuSerializer +class ApiMenu(generics.RetrieveAPIView): + serializer_class = DetailMenuSerializer + queryset = Menu.objects.all() + + +@permission_classes((AllowAny,)) +class ApiMeals(generics.ListAPIView): + serializer_class = OverviewSingleFoodSerializer def get_queryset(self): - queryset = Menu.objects.all() - location = None - if 'location' in self.kwargs: - location = self.kwargs['location'] + queryset = SingleFood.objects.all() + rating = self.request.query_params.get('rating') + max_rating = self.request.query_params.get('max-rating') + min_rating = self.request.query_params.get('min-rating') - year = None - if 'year' in self.kwargs: - year = self.kwargs['year'] - month = None - if 'month' in self.kwargs: - month = self.kwargs['month'] - day = None - if 'day' in self.kwargs: - day = self.kwargs['day'] + price = self.request.query_params.get('price') + max_price = self.request.query_params.get('max-price') + min_price = self.request.query_params.get('min-price') - if location: - if str(location).upper() == Menu.ERBA.upper(): - queryset = queryset.filter(location__contains=Menu.ERBA) - elif str(location).upper() == Menu.FEKI.upper(): - queryset = queryset.filter(location__contains=Menu.FEKI) - elif str(location).upper() == Menu.AUSTRASSE.upper(): - print("Before: " + str(queryset)) - queryset = queryset.filter(location__contains=Menu.AUSTRASSE) - elif str(location).upper() == Menu.MARKUSPLATZ.upper(): - queryset = queryset.filter(location__contains=Menu.MARKUSPLATZ) - print(queryset) - if year and month and day: - date = '%s-%s-%s' % (year, month, day) - queryset = queryset.filter(date=datetime.strptime(date, '%Y-%m-%d')) + allergens = self.request.query_params.get('allergens') - # if date == "week": - # today = datetime.now() - # weekday = today.weekday() - # monday = today - timedelta(weekday) - # sunday = today + (timedelta(6 - weekday)) - # print("Monday: " + str(monday)) - # print("Sunday: " + str(sunday)) - # queryset = queryset.filter(date__gte=monday, date__lte=sunday) - # else: - # queryset = queryset.filter(date=datetime.strptime(date, "%Y-%m-%d")) + if rating: + queryset = queryset.filter(rating=rating) - print("LOCATION: %s" % str(location)) - print(str(queryset)) + if max_rating: + queryset = queryset.filter(rating__lte=max_rating) + + if min_rating: + queryset = queryset.filter(rating__gte=min_rating) + + # TODO: Change price model to Floatfield + # if price: + # pass + # + # if max_price: + # pass + # + # if min_price: + # pass + + if allergens: + allergens = [allergen for allergen in str(allergens).strip('[]').split(',')] + queryset = queryset.filter(allergens__id__in=allergens) return queryset -# @api_view(['GET']) @permission_classes((AllowAny,)) -class FoodViewSetV1_1(viewsets.ModelViewSet, ): - """ - API endpoint that allows users to be viewed or edited. - """ - # queryset = Menu.objects.all() - serializer_class = MenuSerializer - - def get_queryset(self): - queryset = Menu.objects.all() - location = None - if 'location' in self.kwargs: - location = self.kwargs['location'] - - year = None - if 'year' in self.kwargs: - year = self.kwargs['year'] - month = None - if 'month' in self.kwargs: - month = self.kwargs['month'] - day = None - if 'day' in self.kwargs: - day = self.kwargs['day'] - - if location: - if str(location).upper() == Menu.ERBA.upper(): - queryset = queryset.filter(location__contains=Menu.ERBA) - elif str(location).upper() == Menu.FEKI.upper(): - queryset = queryset.filter(location__contains=Menu.FEKI) - elif str(location).upper() == Menu.AUSTRASSE.upper(): - print("Before: " + str(queryset)) - queryset = queryset.filter(location__contains=Menu.AUSTRASSE) - elif str(location).upper() == Menu.MARKUSPLATZ.upper(): - queryset = queryset.filter(location__contains=Menu.MARKUSPLATZ) - print(queryset) - if year and month and day: - date = '%s-%s-%s' % (year, month, day) - queryset = queryset.filter(date=datetime.strptime(date, '%Y-%m-%d')) - - # if date == "week": - # today = datetime.now() - # weekday = today.weekday() - # monday = today - timedelta(weekday) - # sunday = today + (timedelta(6 - weekday)) - # print("Monday: " + str(monday)) - # print("Sunday: " + str(sunday)) - # queryset = queryset.filter(date__gte=monday, date__lte=sunday) - # else: - # queryset = queryset.filter(date=datetime.strptime(date, "%Y-%m-%d")) - - print("LOCATION: %s" % str(location)) - print(str(queryset)) - - return queryset +class ApiMeal(generics.RetrieveAPIView): + serializer_class = DetailedSingleFoosdSerializer + queryset = SingleFood.objects.all() -# @api_view(['GET']) @permission_classes((AllowAny,)) -class HappyHourViewSet(viewsets.ModelViewSet): - """ - API endpoint that allows users to be viewed or edited. - """ - queryset = HappyHour.objects.all() +class ApiAllergens(generics.ListAPIView): + serializer_class = AllergensSerializer + queryset = Allergene.objects.all() + + +@permission_classes((AllowAny,)) +class ApiMenusLocations(views.APIView): + + def get(self, request): + locations = Menu.API_LOCATIONS + results = MenusLocationsSerializer(locations, many=True).data + return Response(results, status=status.HTTP_200_OK) + + +@permission_classes((AllowAny,)) +class ApiHappyHours(generics.ListAPIView): serializer_class = HappyHourSerializer def get_queryset(self): queryset = HappyHour.objects.all() - type = self.request.query_params.get('type') + date = self.request.query_params.get('date') + start_date = self.request.query_params.get('startdate') + end_date = self.request.query_params.get('enddate') - # if type == "food": - # queryset = HappyHour.filter(location__contains="Austraße") - # elif type == "drinks": - # queryset = HappyHour.filter(location__contains="Austraße") + start_time = self.request.query_params.get('starttime') + end_time = self.request.query_params.get('endtime') + + location = self.request.query_params.get('location') + if date: + try: + queryset = queryset.filter(date=datetime.strptime(date, '%Y-%m-%d')) + except ValueError as e: + # TODO: return Exception + return [] + + if start_date: + try: + queryset = queryset.filter(date__gte=datetime.strptime(start_date, '%Y-%m-%d')) + except ValueError as e: + # TODO: return Exception + return [] + + if end_date: + try: + queryset = queryset.filter(date__lte=datetime.strptime(start_date, '%Y-%m-%d')) + except ValueError as e: + # TODO: return Exception + return [] + + if start_time: + try: + queryset = queryset.filter(date__lte=datetime.strptime(start_time, '%H')) + except ValueError as e: + # TODO: return Exception + return [] + + if end_time: + try: + queryset = queryset.filter(date__lte=datetime.strptime(end_time, '%H')) + except ValueError as e: + # TODO: return Exception + return [] + + if location: + queryset = queryset.filter(location__id=location) return queryset + + +@permission_classes((AllowAny,)) +class ApiHappyHoursLocations(generics.RetrieveAPIView): + serializer_class = HappyHourSerializer + queryset = HappyHour.objects.all() + + +@permission_classes((AllowAny,)) +class ApiHappyHoursLocations(generics.ListAPIView): + serializer_class = HappyHourLocationSerializer + queryset = HappyHourLocation.objects.all() + + +@permission_classes((AllowAny,)) +class ApiFoodImages(generics.ListAPIView): + serializer_class = DetailedFoodImageSerializer + queryset = FoodImage.objects.all() + + +@permission_classes((AllowAny,)) +class ApiFoodImagesDefault(views.APIView): + + def get(self, request): + request.build_absolute_uri(static('img/food/default.jpg')) + default_image = {'image': request.build_absolute_uri(static('img/food/default.jpg'))} + results = DefaultFoodImageSerializer(default_image, many=False).data + return Response(results, status=status.HTTP_200_OK) + + +@authentication_classes((TokenAuthentication,)) +@permission_classes((IsAuthenticated,)) +class ApiFoodRatingUpload(generics.CreateAPIView): + serializer_class = UserFoodRatingSerializer + queryset = UserFoodRating.objects.all() + + def get_serializer_context(self): + context = super(ApiFoodRatingUpload, self).get_serializer_context() + context.update({ + "food_id": self.kwargs['pk'], + # extra data + }) + return context