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 ContactEmail, ContactPhone, EstablishmentEmployee from gallery.models import Image from location.serializers import AddressDetailSerializer, TranslatedField from main.models import Currency from main.serializers import AwardSerializer from utils.decorators import with_base_attributes from utils.serializers import ImageBaseSerializer, ProjectModelSerializer, TimeZoneChoiceField def phones_handler(phones_list, establishment): """ create or update phones for establishment """ ContactPhone.objects.filter(establishment=establishment).delete() for new_phone in phones_list: ContactPhone.objects.create(establishment=establishment, phone=new_phone) def emails_handler(emails_list, establishment): """ create or update emails for establishment """ ContactEmail.objects.filter(establishment=establishment).delete() for new_email in emails_list: ContactEmail.objects.create(establishment=establishment, email=new_email) class EstablishmentListCreateSerializer(model_serializers.EstablishmentBaseSerializer): """Establishment create serializer""" type_id = serializers.PrimaryKeyRelatedField( source='establishment_type', queryset=models.EstablishmentType.objects.all(), write_only=True ) address_id = serializers.PrimaryKeyRelatedField( source='address', queryset=models.Address.objects.all(), write_only=True ) socials = model_serializers.SocialNetworkRelatedSerializers( read_only=True, many=True, ) type = model_serializers.EstablishmentTypeBaseSerializer( source='establishment_type', read_only=True, ) tz = TimeZoneChoiceField() phones = serializers.ListField( source='contact_phones', allow_null=True, allow_empty=True, child=serializers.CharField(max_length=128), required=False, ) emails = serializers.ListField( source='contact_emails', allow_null=True, allow_empty=True, child=serializers.CharField(max_length=128), required=False, ) class Meta: model = models.Establishment fields = [ 'id', 'name', 'transliterated_name', 'index_name', 'website', 'phones', 'emails', 'price_level', 'toque_number', 'type_id', 'type', 'socials', 'image_url', 'slug', 'facebook', 'twitter', 'instagram', # TODO: check in admin filters 'is_publish', 'guestonline_id', 'lastable_id', 'tags', 'tz', 'address_id', ] def create(self, validated_data): phones_list = [] if 'contact_phones' in validated_data: phones_list = validated_data.pop('contact_phones') emails_list = [] if 'contact_emails' in validated_data: emails_list = validated_data.pop('contact_emails') instance = super().create(validated_data) phones_handler(phones_list, instance) emails_handler(emails_list, instance) return instance class EstablishmentPositionListSerializer(model_serializers.EstablishmentBaseSerializer): """Establishment position serializer""" class Meta: model = models.Establishment fields = [ 'id', 'name', 'transliterated_name', 'index_name', ] class EstablishmentRUDSerializer(model_serializers.EstablishmentBaseSerializer): """Establishment create serializer""" type_id = serializers.PrimaryKeyRelatedField( source='establishment_type', queryset=models.EstablishmentType.objects.all(), write_only=True ) address = AddressDetailSerializer(read_only=True) emails = serializers.ListField( source='contact_emails', allow_null=True, allow_empty=True, child=serializers.CharField(max_length=128), required=False, ) socials = model_serializers.SocialNetworkRelatedSerializers(read_only=True, many=True, ) type = model_serializers.EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True) phones = serializers.ListField( source='contact_phones', allow_null=True, allow_empty=True, child=serializers.CharField(max_length=128), required=False, ) class Meta: model = models.Establishment fields = [ 'id', 'slug', 'transliterated_name', 'name', 'website', 'phones', 'emails', 'price_level', 'toque_number', 'type_id', 'type', 'socials', 'image_url', 'facebook', 'twitter', 'instagram', # TODO: check in admin filters 'is_publish', 'address', 'transportation', 'tags', ] def update(self, instance, validated_data): phones_list = [] if 'contact_phones' in validated_data: phones_list = validated_data.pop('contact_phones') emails_list = [] if 'contact_emails' in validated_data: emails_list = validated_data.pop('contact_emails') instance = super().update(instance, validated_data) phones_handler(phones_list, instance) emails_handler(emails_list, instance) return instance class SocialChoiceSerializers(serializers.ModelSerializer): """SocialChoice serializers.""" class Meta: model = models.SocialChoice fields = '__all__' class SocialNetworkSerializers(serializers.ModelSerializer): """Social network serializers.""" class Meta: model = models.SocialNetwork fields = [ 'id', 'establishment', 'network', 'url', ] class PlatesSerializers(model_serializers.PlateSerializer): """Plates serializers.""" currency_id = serializers.PrimaryKeyRelatedField( source='currency', queryset=Currency.objects.all(), write_only=True ) class Meta: """Meta class.""" model = models.Plate fields = model_serializers.PlateSerializer.Meta.fields + [ 'name', 'currency_id', 'menu' ] class ContactPhoneBackSerializers(model_serializers.PlateSerializer): """ContactPhone serializers.""" country_calling_code = serializers.CharField(read_only=True, allow_null=True) national_calling_number = serializers.CharField(read_only=True, allow_null=True) class Meta: model = models.ContactPhone fields = [ 'id', 'establishment', 'phone', 'country_calling_code', 'national_calling_number', ] class ContactEmailBackSerializers(model_serializers.PlateSerializer): """ContactEmail serializers.""" class Meta: model = models.ContactEmail fields = [ 'id', 'establishment', 'email' ] class PositionBackSerializer(serializers.ModelSerializer): """Position Back serializer.""" name_translated = TranslatedField() class Meta: model = models.Position fields = [ 'id', 'name_translated', 'priority', 'index_name', ] # TODO: test decorator @with_base_attributes class EmployeeBackSerializers(serializers.ModelSerializer): """Employee serializers.""" public_mark = serializers.SerializerMethodField() positions = serializers.SerializerMethodField() establishment = serializers.SerializerMethodField() awards = AwardSerializer(many=True, read_only=True) toque_number = serializers.SerializerMethodField() photo = ImageBaseSerializer(source='crop_image', read_only=True) photo_id = serializers.PrimaryKeyRelatedField(source='photo', write_only=True, required=False, queryset=Image.objects.all(), allow_null=True) @staticmethod @lru_cache(maxsize=32) def get_qs(obj): return obj.establishmentemployee_set.actual().annotate( public_mark=F('establishment__public_mark'), est_id=F('establishment__id'), est_slug=F('establishment__slug'), toque_number=F('establishment__toque_number'), ).order_by('-from_date').first() def get_public_mark(self, obj): """Get last list actual public_mark""" if hasattr(obj, 'prefetched_establishment_employee'): return obj.prefetched_establishment_employee[0].establishment.public_mark if len( obj.prefetched_establishment_employee) else None qs = self.get_qs(obj) if qs: return qs.public_mark return None def get_toque_number(self, obj): if hasattr(obj, 'prefetched_establishment_employee'): return obj.prefetched_establishment_employee[0].establishment.toque_number if len( obj.prefetched_establishment_employee) else None qs = self.get_qs(obj) if qs: return qs.toque_number return None def get_positions(self, obj): """Get last list actual positions""" if hasattr(obj, 'prefetched_establishment_employee'): if not len(obj.prefetched_establishment_employee): return [] return [PositionBackSerializer(ee.position).data for ee in obj.prefetched_establishment_employee] est_id = self.get_qs(obj) if not est_id: return None qs = obj.establishmentemployee_set.actual() \ .filter(establishment_id=est_id.establishment_id) \ .prefetch_related('position').values('position') positions = models.Position.objects.filter(id__in=[q['position'] for q in qs]) return [PositionBackSerializer(p).data for p in positions] def get_establishment(self, obj): """Get last actual establishment""" if hasattr(obj, 'prefetched_establishment_employee'): return { 'id': obj.prefetched_establishment_employee[0].establishment.pk, 'slug': obj.prefetched_establishment_employee[0].establishment.slug, } if len(obj.prefetched_establishment_employee) else None est = self.get_qs(obj) if not est: return None return { "id": est.est_id, "slug": est.est_slug } class Meta: model = models.Employee fields = [ 'id', 'name', 'last_name', 'user', 'public_mark', 'positions', 'awards', 'establishment', 'sex', 'birth_date', 'email', 'phone', 'toque_number', 'available_for_events', 'photo', 'photo_id', ] class EstablishmentEmployeeBackSerializer(serializers.ModelSerializer): """Establishment Employee serializer.""" employee = EmployeeBackSerializers() position = PositionBackSerializer() class Meta: model = models.EstablishmentEmployee fields = [ 'id', 'employee', 'from_date', 'to_date', 'position', 'status', ] class EstEmployeeBackSerializer(EmployeeBackSerializers): @property def request_kwargs(self): """Get url kwargs from request.""" return self.context.get('request').parser_context.get('kwargs') def get_positions(self, obj): establishment_id = self.request_kwargs.get('establishment_id') es_emp = EstablishmentEmployee.objects.filter( employee=obj, establishment_id=establishment_id, ).distinct().order_by('position_id') result = [] for item in es_emp: result.append({ 'id': item.id, 'from_date': item.from_date, 'to_date': item.to_date, 'status': item.status, 'position_id': item.position_id, 'position_priority': item.position.priority, 'position_index_name': item.position.index_name, 'position_name_translated': item.position.name_translated, }) return result class Meta: model = models.Employee fields = [ 'id', 'name', 'last_name', 'user', 'public_mark', 'positions', 'awards', 'sex', 'birth_date', 'email', 'phone', 'toque_number', 'available_for_events', 'photo', ] class EstablishmentBackOfficeGallerySerializer(serializers.ModelSerializer): """Serializer class for model EstablishmentGallery.""" class Meta: """Meta class""" model = models.EstablishmentGallery fields = [ 'id', 'is_main', ] @property def request_kwargs(self): """Get url kwargs from request.""" return self.context.get('request').parser_context.get('kwargs') def validate(self, attrs): """Override validate method.""" establishment_pk = self.request_kwargs.get('pk') establishment_slug = self.request_kwargs.get('slug') search_kwargs = {'pk': establishment_pk} if establishment_pk else {'slug': establishment_slug} image_id = self.request_kwargs.get('image_id') establishment_qs = models.Establishment.objects.filter(**search_kwargs) image_qs = Image.objects.filter(id=image_id) if not establishment_qs.exists(): raise serializers.ValidationError({'detail': _('Establishment not found')}) if not image_qs.exists(): raise serializers.ValidationError({'detail': _('Image not found')}) establishment = establishment_qs.first() image = image_qs.first() if image in establishment.gallery.all(): raise serializers.ValidationError({'detail': _('Image is already added.')}) attrs['establishment'] = establishment attrs['image'] = image return attrs class EstablishmentCompanyListCreateSerializer(model_serializers.CompanyBaseSerializer): """Serializer for linking page w/ advertisement.""" class Meta(model_serializers.CompanyBaseSerializer.Meta): """Meta class.""" model_serializers.CompanyBaseSerializer.Meta.extra_kwargs.update({ 'establishment': {'required': False} }) def create(self, validated_data): """Overridden create method.""" validated_data['establishment'] = self.context.get('view').get_object() return super().create(validated_data) class EstablishmentNoteBaseSerializer(serializers.ModelSerializer): """Serializer for model EstablishmentNote.""" user_detail = UserShortSerializer(read_only=True, source='user') class Meta: """Meta class.""" model = models.EstablishmentNote fields = [ 'id', 'created', 'modified', 'text', 'user', 'user_detail', 'establishment', ] extra_kwargs = { 'created': {'read_only': True}, 'modified': {'read_only': True}, 'establishment': {'required': False, 'write_only': True}, 'user': {'required': False, 'write_only': True}, } @property def serializer_view(self): """Return view instance.""" return self.context.get('view') class EstablishmentNoteListCreateSerializer(EstablishmentNoteBaseSerializer): """Serializer for List|Create action for model EstablishmentNote.""" def create(self, validated_data): """Overridden create method.""" validated_data['user'] = self.user validated_data['establishment'] = self.establishment return super().create(validated_data) @property def user(self): """Return user instance from view.""" if self.serializer_view: return self.serializer_view.request.user @property def establishment(self): """Return establishment instance from view.""" if self.serializer_view: return self.serializer_view.get_object() class EstablishmentAdminListSerializer(UserShortSerializer): """Establishment admin serializer.""" class Meta: model = UserShortSerializer.Meta.model fields = [ 'id', 'username', 'email' ] class EstablishmentEmployeePositionsSerializer(serializers.ModelSerializer): """Establishments from employee serializer""" restaurant_name = serializers.CharField(read_only=True, source='establishment.name') position = PositionBackSerializer(read_only=True) state = serializers.CharField(read_only=True, source='status') start = serializers.DateTimeField(read_only=True, source='from_date') end = serializers.DateTimeField(read_only=True, source='to_date') class Meta: model = models.EstablishmentEmployee fields = [ 'restaurant_name', 'position', 'state', 'start', 'end', ] 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) class Meta: model = models.Menu fields = [ 'id', 'category', 'category_translated', 'establishment', 'is_drinks_included', 'schedule', 'plates', 'last_update', 'gallery', ] 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) class Meta: model = models.Menu fields = [ 'id', 'category', 'plates', 'establishment', 'is_drinks_included', 'schedule', 'gallery', ] 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 class MenuFilesSerializers(ProjectModelSerializer): class Meta: model = models.MenuFiles fields = [ 'id', 'name', 'type', 'file', ] def create(self, validated_data): instance = models.MenuFiles.objects.create(**validated_data) return instance class MenuBackOfficeSerializer(serializers.ModelSerializer): name = serializers.CharField(source='category_translated') drinks_included = serializers.BooleanField(source='is_drinks_included') establishment_id = serializers.IntegerField(source='establishment.id') establishment_slug = serializers.CharField(source='establishment.slug', read_only=True) uploads = MenuFilesSerializers(many=True) class Meta: model = models.Menu fields = [ 'id', 'name', 'establishment_id', 'establishment_slug', 'price', 'drinks_included', 'diner', 'lunch', 'last_update', 'uploads', ] extra_kwargs = { 'last_update': {'read_only': True} } def validate(self, attrs): attrs['category'] = {'en-GB': attrs.pop('category_translated')} return attrs def update(self, instance, validated_data): uploaded_files_ids = validated_data.pop('uploads') if len(uploaded_files_ids): uploaded_files = list(models.MenuFiles.objects.filter(id__in=uploaded_files_ids)) establishment_kwargs = validated_data.pop('establishment') establishment = get_object_or_404(models.Establishment, **establishment_kwargs) validated_data['establishment'] = establishment instance = super().update(instance, validated_data) instance.uploads.set(uploaded_files) return instance def create(self, validated_data): uploaded_files_ids = validated_data.pop('uploads') if len(uploaded_files_ids): uploaded_files = list(models.MenuFiles.objects.filter(id__in=uploaded_files_ids)) establishment_kwargs = validated_data.pop('establishment') establishment = get_object_or_404(models.Establishment, **establishment_kwargs) validated_data['establishment'] = establishment instance = models.Menu.objects.create(**validated_data) instance.uploads.set(uploaded_files) return instance