diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 9f5fa7fd..22cd9925 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -6,36 +6,32 @@ from typing import List import elasticsearch_dsl from django.conf import settings -from django.shortcuts import get_object_or_404 from django.contrib.contenttypes import fields as generic from django.contrib.gis.db.models.functions import Distance from django.contrib.gis.geos import Point from django.contrib.gis.measure import Distance as DistanceMeasure from django.contrib.postgres.fields import ArrayField -from django.contrib.postgres.search import TrigramDistance, TrigramSimilarity from django.contrib.postgres.indexes import GinIndex +from django.contrib.postgres.search import TrigramSimilarity from django.core.exceptions import ValidationError -from django.core.validators import MinValueValidator, MaxValueValidator, FileExtensionValidator +from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models -from django.db.models import When, Case, F, ExpressionWrapper, Subquery, Q, Prefetch, Sum +from django.db.models import Case, ExpressionWrapper, F, Prefetch, Q, Subquery, When +from django.shortcuts import get_object_or_404 from django.utils import timezone from django.utils.translation import gettext_lazy as _ from phonenumber_field.modelfields import PhoneNumberField from timezone_field import TimeZoneField from location.models import Address -from timetable.models import Timetable -from location.models import WineOriginAddressMixin from main.models import Award, Currency from review.models import Review from tag.models import Tag +from timetable.models import Timetable from utils.methods import transform_into_readable_str from utils.models import ( - ProjectBaseMixin, TJSONField, URLImageMixin, - TranslatedFieldsMixin, BaseAttributes, GalleryMixin, - IntermediateGalleryModelMixin, HasTagsMixin, - FavoritesMixin, TypeDefaultImageMixin, FileMixin, - ImageMixin, + BaseAttributes, FavoritesMixin, FileMixin, GalleryMixin, HasTagsMixin, IntermediateGalleryModelMixin, + ProjectBaseMixin, TJSONField, TranslatedFieldsMixin, TypeDefaultImageMixin, URLImageMixin, ) @@ -564,8 +560,8 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, verbose_name=_('Facebook URL')) twitter = models.URLField(blank=True, null=True, default=None, max_length=255, verbose_name=_('Twitter URL')) - instagram =models.URLField(blank=True, null=True, default=None, max_length=255, - verbose_name=_('Instagram URL')) + instagram = models.URLField(blank=True, null=True, default=None, max_length=255, + verbose_name=_('Instagram URL')) lafourchette = models.URLField(blank=True, null=True, default=None, max_length=255, verbose_name=_('Lafourchette URL')) guestonline_id = models.PositiveIntegerField(blank=True, verbose_name=_('guestonline id'), @@ -737,7 +733,8 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, now_at_est_tz = datetime.now(tz=self.tz) current_week = now_at_est_tz.weekday() schedule_for_today = self.schedule.filter(weekday=current_week).first() - if schedule_for_today is None or schedule_for_today.opening_time is None or schedule_for_today.ending_time is None: + if schedule_for_today is None or schedule_for_today.opening_time is None or schedule_for_today.ending_time is\ + None: return False time_at_est_tz = now_at_est_tz.time() return schedule_for_today.ending_time > time_at_est_tz > schedule_for_today.opening_time @@ -749,8 +746,10 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, @property def tags_indexing(self): - return [{'id': tag.metadata.id, - 'label': tag.metadata.label} for tag in self.tags.all()] + return [{ + 'id': tag.metadata.id, + 'label': tag.metadata.label + } for tag in self.tags.all()] @property def last_published_review(self): @@ -1244,6 +1243,24 @@ class Plate(TranslatedFieldsMixin, models.Model): verbose_name_plural = _('plates') +class MenuDish(BaseAttributes): + """Dish model.""" + + STR_FIELD_NAME = 'category' + + name = models.CharField(_('name'), max_length=255, default='') + category = TJSONField( + blank=True, null=True, default=None, verbose_name=_('category'), + help_text='{"en-GB":"some text"}') + price = models.IntegerField(blank=True, null=True, default=None, verbose_name=_('price')) + signature = models.BooleanField(_('signature'), default=False) + + class Meta: + verbose_name = _('dish') + verbose_name_plural = _('dishes') + ordering = ('-created',) + + class MenuQuerySet(models.QuerySet): def with_schedule_plates_establishment(self): return self.select_related( @@ -1270,15 +1287,10 @@ class MenuQuerySet(models.QuerySet): return self.filter(category__icontains=value) -class Menu(GalleryMixin, TranslatedFieldsMixin, BaseAttributes): +class Menu(TranslatedFieldsMixin, BaseAttributes): """Menu model.""" - STR_FIELD_NAME = 'category' - name = models.CharField(_('name'), max_length=255, default='') - category = TJSONField( - blank=True, null=True, default=None, verbose_name=_('category'), - help_text='{"en-GB":"some text"}') establishment = models.ForeignKey( 'establishment.Establishment', verbose_name=_('establishment'), on_delete=models.CASCADE) @@ -1294,6 +1306,14 @@ class Menu(GalleryMixin, TranslatedFieldsMixin, BaseAttributes): blank=True, verbose_name=_('Menu files'), ) + dishes = models.ManyToManyField( + to='MenuDish', + blank=True, + verbose_name=_('Menu dishes') + ) + category = TJSONField( + blank=True, null=True, default=None, verbose_name=_('category'), + help_text='{"en-GB":"some text"}') old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None) diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index e3adb490..4b92d985 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -1,12 +1,14 @@ from functools import lru_cache from django.db.models import F +from django.shortcuts import get_object_or_404 from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from account.serializers.common import UserShortSerializer from establishment import models, serializers as model_serializers from establishment.models import ContactPhone, EstablishmentEmployee, ContactEmail +from establishment.serializers import MenuDishSerializer from gallery.models import Image from location.models import Address from location.serializers import AddressDetailSerializer, TranslatedField @@ -580,109 +582,36 @@ class EstablishmentEmployeePositionsSerializer(serializers.ModelSerializer): ] -class _PlateSerializer(ProjectModelSerializer): - name_translated = TranslatedField() - - class Meta: - model = models.Plate - fields = [ - 'name_translated', - 'price', - ] - - -class _MenuUploadsSerializer(serializers.Serializer): - id = serializers.IntegerField() - title = serializers.CharField() - original_url = serializers.URLField() - - class MenuDishesSerializer(ProjectModelSerializer): """for dessert, main_course and starter category""" - plates = _PlateSerializer(read_only=True, many=True, source='plate_set', allow_null=True) - category_translated = serializers.CharField(read_only=True) - last_update = serializers.DateTimeField(source='created') - gallery = ImageBaseSerializer(read_only=True, source='crop_gallery', many=True) + establishment_id = serializers.PrimaryKeyRelatedField(read_only=True) + establishment_slug = serializers.CharField(read_only=True, source='establishment.slug') + dishes = MenuDishSerializer(many=True, read_only=True) class Meta: model = models.Menu fields = [ 'id', - 'category', - 'category_translated', - 'establishment', - 'is_drinks_included', - 'schedule', - 'plates', - 'last_update', - 'gallery', + 'establishment_id', + 'establishment_slug', + 'dishes', ] class MenuDishesRUDSerializers(ProjectModelSerializer): """for dessert, main_course and starter category""" - plates = _PlateSerializer(read_only=True, many=True, source='plate_set', allow_null=True) - gallery = ImageBaseSerializer(read_only=True, source='crop_gallery', many=True) + establishment_id = serializers.PrimaryKeyRelatedField(queryset=models.Establishment.objects.all()) + establishment_slug = serializers.CharField(read_only=True, source='establishment.slug') + dishes = MenuDishSerializer(many=True, read_only=True) class Meta: model = models.Menu fields = [ 'id', - 'category', - 'plates', - 'establishment', - 'is_drinks_included', - 'schedule', - 'gallery', + 'establishment_id', + 'establishment_slug', + 'dishes', ] - -# class MenuGallerySerializer(serializers.ModelSerializer): -# """Serializer class for model MenuGallery.""" -# -# class Meta: -# """Meta class""" -# -# model = models.MenuGallery -# fields = [ -# 'id', -# 'is_main', -# ] -# -# @property -# def request_kwargs(self): -# """Get url kwargs from request.""" -# return self.context.get('request').parser_context.get('kwargs') -# -# def create(self, validated_data): -# menu_pk = self.request_kwargs.get('pk') -# image_id = self.request_kwargs.get('image_id') -# qs = models.MenuGallery.objects.filter(image_id=image_id, menu_id=menu_pk) -# instance = qs.first() -# if instance: -# qs.update(**validated_data) -# return instance -# return super().create(validated_data) -# -# def validate(self, attrs): -# """Override validate method.""" -# menu_pk = self.request_kwargs.get('pk') -# image_id = self.request_kwargs.get('image_id') -# -# menu_qs = models.Menu.objects.filter(pk=menu_pk) -# image_qs = Image.objects.filter(id=image_id) -# -# if not menu_qs.exists(): -# raise serializers.ValidationError({'detail': _('Menu not found')}) -# if not image_qs.exists(): -# raise serializers.ValidationError({'detail': _('Image not found')}) -# -# menu = menu_qs.first() -# image = image_qs.first() -# -# attrs['menu'] = menu -# attrs['image'] = image -# -# return attrs diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index f861d593..3c58f476 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -72,6 +72,19 @@ class PlateSerializer(ProjectModelSerializer): ] +class MenuDishSerializer(ProjectModelSerializer): + class Meta: + model = models.MenuDish + fields = [ + 'id', + 'name', + 'category', + 'category_translated', + 'price', + 'signature' + ] + + class MenuFilesSerializers(ProjectModelSerializer): menu_id = serializers.IntegerField(write_only=True) @@ -113,7 +126,6 @@ class MenuSerializers(ProjectModelSerializer): 'drinks_included', 'schedules', 'uploads', - 'category', ] def create(self, validated_data): @@ -142,7 +154,6 @@ class MenuRUDSerializers(ProjectModelSerializer): 'drinks_included', 'schedules', 'uploads', - 'category', ] diff --git a/apps/establishment/urls/back.py b/apps/establishment/urls/back.py index b046cf85..053d47fd 100644 --- a/apps/establishment/urls/back.py +++ b/apps/establishment/urls/back.py @@ -30,13 +30,11 @@ urlpatterns = [ name='note-rud'), path('slug//admin/', views.EstablishmentAdminView.as_view(), name='establishment-admin-list'), - path('menus/dishes/', views.MenuDishesListCreateView.as_view(), name='menu-dishes-list'), + path('menus/dishes/', views.MenuDishesListView.as_view(), name='menu-dishes-list'), path('menus/dishes//', views.MenuDishesRUDView.as_view(), name='menu-dishes-rud'), - # path('menus/dishes//gallery/', views.MenuGalleryListView.as_view(), name='menu-dishes-gallery-list'), - # path('menus/dishes//gallery//', views.MenuGalleryCreateDestroyView.as_view(), - # name='menu-dishes-gallery-create-destroy'), path('menus/', views.MenuListCreateView.as_view(), name='menu-list'), path('menus//', views.MenuRUDView.as_view(), name='menu-rud'), + path('menus/slug//', views.MenuRUDView.as_view(), name='menu-rud'), path('menus/uploads/', views.MenuFilesListCreateView.as_view(), name='menu-files-list'), path('menus/uploads//', views.MenuFilesRUDView.as_view(), name='menu-files-rud'), path('plates/', views.PlateListCreateView.as_view(), name='plates'), diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index e7e2ad44..d1384b1e 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -466,10 +466,10 @@ class EstablishmentAdminView(generics.ListAPIView): return User.objects.establishment_admin(establishment).distinct() -class MenuDishesListCreateView(generics.ListCreateAPIView): +class MenuDishesListView(generics.ListAPIView): """Menu (dessert, main_course, starter) list create view.""" serializer_class = serializers.MenuDishesSerializer - queryset = models.Menu.objects.with_schedule_plates_establishment().dishes().distinct() + queryset = models.Menu.objects.all() permission_classes = [IsWineryReviewer | IsEstablishmentManager] filter_class = filters.MenuDishesBackFilter @@ -477,57 +477,5 @@ class MenuDishesListCreateView(generics.ListCreateAPIView): class MenuDishesRUDView(generics.RetrieveUpdateDestroyAPIView): """Menu (dessert, main_course, starter) RUD view.""" serializer_class = serializers.MenuDishesRUDSerializers - queryset = models.Menu.objects.dishes().distinct() + queryset = models.Menu.objects.all() permission_classes = [IsWineryReviewer | IsEstablishmentManager] - -# class MenuGalleryListView(generics.ListAPIView): -# """Resource for returning gallery for menu for back-office users.""" -# serializer_class = serializers.ImageBaseSerializer -# permission_classes = [IsWineryReviewer | IsEstablishmentManager] -# queryset = models.Menu.objects.with_schedule_plates_establishment().with_gallery().dishes() -# -# def get_object(self): -# """Override get_object method.""" -# qs = super(MenuGalleryListView, self).get_queryset() -# menu = get_object_or_404(qs, pk=self.kwargs.get('pk')) -# -# # May raise a permission denied -# # self.check_object_permissions(self.request, menu) -# -# return menu -# -# def get_queryset(self): -# """Override get_queryset method.""" -# return self.get_object().crop_gallery -# -# -# class MenuGalleryCreateDestroyView(CreateDestroyGalleryViewMixin): -# """Resource for a create gallery for menu for back-office users.""" -# serializer_class = serializers.MenuGallerySerializer -# permission_classes = [IsWineryReviewer | IsEstablishmentManager] -# -# def get_queryset(self): -# """Override get_queryset method.""" -# qs = models.Menu.objects.with_schedule_plates_establishment().with_gallery().dishes() -# return qs -# -# def create(self, request, *args, **kwargs): -# _ = super().create(request, *args, **kwargs) -# news_qs = self.filter_queryset(self.get_queryset()) -# return response.Response( -# data=serializers.MenuDishesRUDSerializers(get_object_or_404(news_qs, pk=kwargs.get('pk'))).data -# ) -# -# def get_object(self): -# """ -# Returns the object the view is displaying. -# """ -# menu_qs = self.filter_queryset(self.get_queryset()) -# -# menu = get_object_or_404(menu_qs, pk=self.kwargs.get('pk')) -# gallery = get_object_or_404(menu.menu_gallery, image_id=self.kwargs.get('image_id')) -# -# # May raise a permission denied -# self.check_object_permissions(self.request, gallery) -# -# return gallery