"""Establishment serializers.""" import logging from django.conf import settings from django.utils.translation import ugettext_lazy as _ from phonenumber_field.phonenumber import to_python as str_to_phonenumber from rest_framework import serializers from comment import models as comment_models from comment.serializers import common as comment_serializers from establishment import models from location.serializers import ( AddressBaseSerializer, AddressDetailSerializer, CityBaseSerializer, CityShortSerializer, EstablishmentWineOriginBaseSerializer, EstablishmentWineRegionBaseSerializer, ) from main.serializers import AwardSerializer, CurrencySerializer from review.serializers import ReviewShortSerializer, ReviewBaseSerializer from tag.serializers import TagBaseSerializer from timetable.serialziers import ScheduleRUDSerializer from utils import exceptions as utils_exceptions from utils.serializers import ( CarouselCreateSerializer, FavoritesCreateSerializer, ImageBaseSerializer, ProjectModelSerializer, TranslatedField, PhoneMixinSerializer ) logger = logging.getLogger(__name__) class ContactPhonesSerializer(PhoneMixinSerializer, serializers.ModelSerializer): """Contact phone serializer""" class Meta: model = models.ContactPhone fields = [ 'phone', 'country_calling_code', 'national_calling_number', ] class ContactEmailsSerializer(serializers.ModelSerializer): """Contact email serializer""" class Meta: model = models.ContactEmail fields = [ 'email' ] class SocialNetworkRelatedSerializers(serializers.ModelSerializer): """Social network serializers.""" class Meta: model = models.SocialNetwork fields = [ 'id', 'network', 'url', ] class PlateSerializer(ProjectModelSerializer): name_translated = TranslatedField() class Meta: model = models.Plate fields = [ 'name_translated', 'currency', 'price', 'is_signature_plate', ] class MenuSerializers(ProjectModelSerializer): plates = PlateSerializer(read_only=True, many=True, source='plate_set') category_translated = serializers.CharField(read_only=True) class Meta: model = models.Menu fields = [ 'id', 'category', 'category_translated', 'plates', 'establishment' ] class MenuRUDSerializers(ProjectModelSerializer): plates = PlateSerializer(read_only=True, many=True, source='plate_set') class Meta: model = models.Menu fields = [ 'id', 'category', 'plates', 'establishment' ] class EstablishmentTypeBaseSerializer(serializers.ModelSerializer): """Serializer for EstablishmentType model.""" name_translated = TranslatedField() default_image_url = serializers.ImageField(source='default_image.image', allow_null=True) preview_image = serializers.URLField(source='preview_image_url', allow_null=True, read_only=True) class Meta: """Meta class.""" model = models.EstablishmentType fields = [ 'id', 'name', 'name_translated', 'use_subtypes', 'index_name', 'default_image_url', 'preview_image', ] extra_kwargs = { 'name': {'write_only': True}, 'use_subtypes': {'write_only': True}, } class EstablishmentTypeGeoSerializer(EstablishmentTypeBaseSerializer): """Serializer for EstablishmentType model w/ index_name.""" class Meta(EstablishmentTypeBaseSerializer.Meta): fields = EstablishmentTypeBaseSerializer.Meta.fields + [ 'index_name' ] extra_kwargs = { **EstablishmentTypeBaseSerializer.Meta.extra_kwargs, 'index_name': {'read_only': True}, } class EstablishmentSubTypeBaseSerializer(serializers.ModelSerializer): """Serializer for EstablishmentSubType models.""" name_translated = TranslatedField() default_image_url = serializers.ImageField(source='default_image.image', allow_null=True) preview_image = serializers.URLField(source='preview_image_url', allow_null=True, read_only=True) class Meta: """Meta class.""" model = models.EstablishmentSubType fields = [ 'id', 'name', 'name_translated', 'establishment_type', 'index_name', 'default_image_url', 'preview_image', ] extra_kwargs = { 'name': {'write_only': True}, 'establishment_type': {'write_only': True} } class EstablishmentSubTypeGeoSerializer(EstablishmentSubTypeBaseSerializer): """Serializer for EstablishmentSuType model w/ index_name.""" class Meta(EstablishmentSubTypeBaseSerializer.Meta): fields = EstablishmentSubTypeBaseSerializer.Meta.fields + [ 'index_name' ] extra_kwargs = { **EstablishmentSubTypeBaseSerializer.Meta.extra_kwargs, 'index_name': {'read_only': True}, } class EstablishmentEmployeeSerializer(serializers.ModelSerializer): """Serializer for actual employees.""" id = serializers.IntegerField(source='employee.id') name = serializers.CharField(source='employee.name') position_translated = serializers.CharField(source='position.name_translated') awards = AwardSerializer(source='employee.awards', many=True) priority = serializers.IntegerField(source='position.priority') position_index_name = serializers.CharField(source='position.index_name') status = serializers.CharField() class Meta: """Meta class.""" model = models.Employee fields = ('id', 'name', 'position_translated', 'awards', 'priority', 'position_index_name', 'status') class EstablishmentEmployeeCreateSerializer(serializers.ModelSerializer): """Serializer for establishment employee relation.""" class Meta: """Meta class.""" model = models.EstablishmentEmployee fields = ('id', 'from_date', 'to_date') def _validate_entity(self, entity_id_param: str, entity_class): entity_id = self.context.get('request').parser_context.get('kwargs').get(entity_id_param) entity_qs = entity_class.objects.filter(id=entity_id) if not entity_qs.exists(): raise serializers.ValidationError({'detail': _(f'{entity_class.__name__} not found.')}) return entity_qs.first() def validate(self, attrs): """Override validate method""" establishment = self._validate_entity("establishment_id", models.Establishment) employee = self._validate_entity("employee_id", models.Employee) position = self._validate_entity("position_id", models.Position) attrs['establishment'] = establishment attrs['employee'] = employee attrs['position'] = position return attrs def create(self, validated_data, *args, **kwargs): """Override create method""" validated_data.update({ 'employee': validated_data.pop('employee'), 'establishment': validated_data.pop('establishment'), 'position': validated_data.pop("position") }) return super().create(validated_data) class EstablishmentShortSerializer(serializers.ModelSerializer): """Short serializer for establishment.""" city = CityBaseSerializer(source='address.city', allow_null=True) establishment_type = EstablishmentTypeGeoSerializer() establishment_subtypes = EstablishmentSubTypeBaseSerializer(many=True) currency = CurrencySerializer(read_only=True) class Meta: """Meta class.""" model = models.Establishment fields = [ 'id', 'name', 'index_name', 'slug', 'city', 'establishment_type', 'establishment_subtypes', 'currency', ] class _EstablishmentAddressShortSerializer(serializers.ModelSerializer): """Short serializer for establishment.""" city = CityBaseSerializer(source='address.city', allow_null=True) establishment_type = EstablishmentTypeGeoSerializer() establishment_subtypes = EstablishmentSubTypeBaseSerializer(many=True) address = AddressBaseSerializer(read_only=True) class Meta: """Meta class.""" model = models.Establishment fields = [ 'id', 'name', 'index_name', 'slug', 'city', 'establishment_type', 'establishment_subtypes', 'currency', 'address', ] class EstablishmentProductShortSerializer(serializers.ModelSerializer): """SHORT Serializer for displaying info about an establishment on product page.""" establishment_type = EstablishmentTypeGeoSerializer() establishment_subtypes = EstablishmentSubTypeBaseSerializer(many=True) address = AddressBaseSerializer() city = CityShortSerializer(source='address.city', allow_null=True) currency_detail = CurrencySerializer(source='currency', read_only=True) class Meta: """Meta class.""" model = models.Establishment fields = [ 'id', 'name', 'index_name', 'slug', 'city', 'establishment_type', 'establishment_subtypes', 'address', 'currency_detail', ] class EstablishmentProductSerializer(EstablishmentShortSerializer): """Serializer for displaying info about an establishment on product page.""" address = AddressBaseSerializer() class Meta(EstablishmentShortSerializer.Meta): """Meta class.""" fields = EstablishmentShortSerializer.Meta.fields + [ 'address', ] class EstablishmentBaseSerializer(ProjectModelSerializer): """Base serializer for Establishment model.""" address = AddressBaseSerializer() in_favorites = serializers.BooleanField(allow_null=True) tags = TagBaseSerializer(read_only=True, many=True, source='visible_tags') currency = CurrencySerializer() type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True) subtypes = EstablishmentSubTypeBaseSerializer(many=True, source='establishment_subtypes') image = serializers.SerializerMethodField(read_only=True) wine_regions = EstablishmentWineRegionBaseSerializer(many=True, source='wine_origins_unique', read_only=True, allow_null=True) preview_image = serializers.URLField(source='preview_image_url', allow_null=True, read_only=True) tz = serializers.CharField(read_only=True, source='timezone_as_str') new_image = serializers.SerializerMethodField(allow_null=True, read_only=True) distillery_type = TagBaseSerializer(read_only=True, many=True, allow_null=True) status_display = serializers.CharField(read_only=True, source='get_status_display') def get_image(self, obj): if obj.main_image: return obj.main_image[0].image.image.url if len(obj.main_image) else None logging.info('Possibly not optimal image reading') return obj._main_image # backwards compatibility def get_new_image(self, obj): if hasattr(self, 'main_image') and hasattr(self, '_meta'): if obj.main_image and len(obj.main_image): main_image = obj.main_image[0].image else: logging.info('Possibly not optimal image reading') main_image = obj._main_image if main_image: image = main_image image_property = { 'id': image.id, 'title': image.title, 'original_url': image.image.url, 'orientation_display': image.get_orientation_display(), 'auto_crop_images': {}, } crop_parameters = [p for p in settings.SORL_THUMBNAIL_ALIASES if p.startswith(self._meta.model_name.lower())] for crop in crop_parameters: image_property['auto_crop_images'].update( {crop: image.get_image_url(crop)} ) return image_property class Meta: """Meta class.""" model = models.Establishment fields = [ 'id', 'name', 'transliterated_name', 'index_name', 'price_level', 'toque_number', 'public_mark', 'slug', 'in_favorites', 'address', 'tags', 'currency', 'type', 'subtypes', 'image', 'preview_image', 'new_image', 'tz', 'wine_regions', 'distillery_type', 'status_display', 'status', ] class EstablishmentListRetrieveSerializer(EstablishmentBaseSerializer): """Establishment with city serializer.""" address = AddressDetailSerializer() schedule = ScheduleRUDSerializer(many=True, allow_null=True) restaurant_category = TagBaseSerializer(read_only=True, many=True, allow_null=True) restaurant_cuisine = TagBaseSerializer(read_only=True, many=True, allow_null=True) artisan_category = TagBaseSerializer(read_only=True, many=True, allow_null=True) distillery_type = TagBaseSerializer(read_only=True, many=True, allow_null=True) food_producer = TagBaseSerializer(read_only=True, many=True, allow_null=True) reviews = ReviewBaseSerializer(read_only=True, many=True) class Meta(EstablishmentBaseSerializer.Meta): """Meta class.""" fields = EstablishmentBaseSerializer.Meta.fields + [ 'schedule', 'restaurant_category', 'restaurant_cuisine', 'artisan_category', 'distillery_type', 'food_producer', 'vintage_year', 'reviews', 'contact_phones', 'public_mark' ] class EstablishmentGeoSerializer(EstablishmentBaseSerializer): """Serializer for Geo view.""" type = EstablishmentTypeGeoSerializer(source='establishment_type', read_only=True) subtypes = EstablishmentSubTypeGeoSerializer(many=True, source='establishment_subtypes') class Meta(EstablishmentBaseSerializer.Meta): """Meta class.""" fields = EstablishmentBaseSerializer.Meta.fields class RangePriceSerializer(serializers.Serializer): max = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True, default=0) min = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True, default=0) class EstablishmentDetailSerializer(EstablishmentBaseSerializer): """Serializer for Establishment model.""" description_translated = TranslatedField() awards = AwardSerializer(many=True) schedule = ScheduleRUDSerializer(many=True, allow_null=True) phones = ContactPhonesSerializer(read_only=True, many=True) emails = ContactEmailsSerializer(read_only=True, many=True) review = ReviewShortSerializer(source='last_published_review', allow_null=True) employees = EstablishmentEmployeeSerializer(source='actual_establishment_employees', many=True) address = AddressDetailSerializer(read_only=True) tags = TagBaseSerializer(read_only=True, many=True, source='visible_tags_detail') menu = MenuSerializers(source='menu_set', many=True, read_only=True) best_price_menu = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) best_price_carte = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) range_price_menu = RangePriceSerializer(read_only=True) range_price_carte = RangePriceSerializer(read_only=True) vintage_year = serializers.ReadOnlyField() gallery = ImageBaseSerializer(read_only=True, source='crop_gallery', many=True) wine_origins = EstablishmentWineOriginBaseSerializer(many=True, read_only=True) class Meta(EstablishmentBaseSerializer.Meta): """Meta class.""" fields = EstablishmentBaseSerializer.Meta.fields + [ 'description_translated', 'image', 'awards', 'schedule', 'website', 'facebook', 'twitter', 'instagram', 'lafourchette', 'booking', 'phones', 'emails', 'review', 'employees', 'menu', 'best_price_menu', 'best_price_carte', 'range_price_menu', 'range_price_carte', 'transportation', 'vintage_year', 'gallery', 'wine_origins', ] class MobileEstablishmentDetailSerializer(EstablishmentDetailSerializer): """Serializer for Establishment model for mobiles.""" last_comment = comment_serializers.CommentBaseSerializer(allow_null=True) class Meta(EstablishmentDetailSerializer.Meta): """Meta class.""" fields = EstablishmentDetailSerializer.Meta.fields + [ 'last_comment', ] class EstablishmentSimilarSerializer(EstablishmentBaseSerializer): """Serializer for Establishment model.""" address = AddressDetailSerializer(read_only=True) schedule = ScheduleRUDSerializer(many=True, allow_null=True) type = EstablishmentTypeGeoSerializer(source='establishment_type') artisan_category = TagBaseSerializer(many=True, allow_null=True, read_only=True) restaurant_category = TagBaseSerializer(many=True, allow_null=True, read_only=True) restaurant_cuisine = TagBaseSerializer(many=True, allow_null=True, read_only=True) distillery_type = TagBaseSerializer(many=True, allow_null=True, read_only=True) class Meta(EstablishmentBaseSerializer.Meta): fields = EstablishmentBaseSerializer.Meta.fields + [ 'schedule', 'type', 'artisan_category', 'restaurant_category', 'restaurant_cuisine', 'distillery_type', ] class EstablishmentCommentBaseSerializer(comment_serializers.CommentBaseSerializer): """Create comment serializer""" class Meta(comment_serializers.CommentBaseSerializer.Meta): """Serializer for model Comment""" fields = [ 'id', 'created', 'text', 'mark', # 'nickname', 'profile_pic', 'status', 'status_display', ] class EstablishmentCommentCreateSerializer(EstablishmentCommentBaseSerializer): """Extended EstablishmentCommentBaseSerializer.""" def validate(self, attrs): """Override validate method""" # Check establishment object establishment_slug = self.context.get('request').parser_context.get('kwargs').get('slug') establishment_qs = models.Establishment.objects.filter(slug=establishment_slug) if not establishment_qs.exists(): raise serializers.ValidationError({'detail': _('Establishment not found.')}) attrs['establishment'] = establishment_qs.first() return attrs def create(self, validated_data, *args, **kwargs): """Override create method""" validated_data.update({ 'user': self.context.get('request').user, 'content_object': validated_data.pop('establishment') }) return super().create(validated_data) class EstablishmentCommentRUDSerializer(comment_serializers.CommentBaseSerializer): """Retrieve/Update/Destroy comment serializer.""" class Meta: """Meta class.""" model = comment_models.Comment fields = [ 'id', 'created', 'text', 'mark', # 'nickname', 'profile_pic', ] class EstablishmentFavoritesCreateSerializer(FavoritesCreateSerializer): """Serializer to favorite object w/ model Establishment.""" def validate(self, attrs): """Overridden validate method""" # Check establishment object establishment_qs = models.Establishment.objects.filter(slug=self.slug) # Check establishment obj by slug from lookup_kwarg if not establishment_qs.exists(): raise serializers.ValidationError({'detail': _('Object not found.')}) else: establishment = establishment_qs.first() # Check existence in favorites if establishment.favorites.filter(user=self.user).exists(): raise utils_exceptions.FavoritesError() attrs['establishment'] = establishment return attrs def create(self, validated_data, *args, **kwargs): """Overridden create method""" validated_data.update({ 'user': self.user, 'content_object': validated_data.pop('establishment') }) return super().create(validated_data) class EstablishmentCarouselCreateSerializer(CarouselCreateSerializer): """Serializer to carousel object w/ model News.""" def validate(self, attrs): search_kwargs = {'pk': self.pk} if self.pk else {'slug': self.slug} establishment = models.Establishment.objects.filter(**search_kwargs).first() if not establishment: raise serializers.ValidationError({'detail': _('Object not found.')}) if establishment.carousels.exists(): raise utils_exceptions.CarouselError() attrs['establishment'] = establishment return attrs def create(self, validated_data, *args, **kwargs): validated_data.update({ 'content_object': validated_data.pop('establishment') }) return super().create(validated_data) class CompanyBaseSerializer(serializers.ModelSerializer): """Company base serializer""" phone_list = serializers.ListField(source='phones_array', read_only=True) fax_list = serializers.ListField(source='faxes_array', read_only=True) address_detail = AddressDetailSerializer(source='address', read_only=True) class Meta: """Meta class.""" model = models.Company fields = [ 'id', 'establishment', 'name', 'phones', 'faxes', 'legal_entity', 'registry_number', 'vat_number', 'sic_code', 'address', 'phone_list', 'fax_list', 'address_detail', ] extra_kwargs = { 'establishment': {'write_only': True}, 'address': {'write_only': True} } def validate(self, attrs): """Overridden validate method""" phones = [str_to_phonenumber(phone).as_national for phone in attrs.get('phones')] faxes = [str_to_phonenumber(fax).as_national for fax in attrs.get('faxes')] if faxes: if models.Company.objects.filter(faxes__overlap=faxes).exists(): raise serializers.ValidationError({'detail': _('Fax is already reserved.')}) if phones: if models.Company.objects.filter(phones__overlap=phones).exists(): raise serializers.ValidationError({'detail': _('Phones is already reserved.')}) return attrs class EstablishmentGuideElementSerializer(serializers.ModelSerializer): """Serializer for Guide serializer.""" type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True) subtypes = EstablishmentSubTypeBaseSerializer(many=True, source='establishment_subtypes') address = AddressBaseSerializer() tz = serializers.CharField(read_only=True, source='timezone_as_str') schedule = ScheduleRUDSerializer(many=True, allow_null=True) best_price_menu = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) best_price_carte = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True) range_price_menu = RangePriceSerializer(read_only=True) range_price_carte = RangePriceSerializer(read_only=True) currency = CurrencySerializer() class Meta(EstablishmentBaseSerializer.Meta): """Meta class.""" fields = [ 'id', 'type', 'name', 'subtypes', 'address', 'tz', 'schedule', 'best_price_menu', 'best_price_carte', 'range_price_menu', 'range_price_carte', 'currency', ] class EstablishmentStatusesSerializer(serializers.Serializer): value = serializers.IntegerField() state_translated = serializers.CharField()