from rest_framework import serializers from django.utils.text import slugify from product import models from location import models as location_models from tag import models as tag_models from establishment import models as establishment_models from transfer import models as transfer_models from utils.methods import get_point_from_coordinates from transfer.mixins import TransferSerializerMixin from django.conf import settings from functools import reduce from gallery.models import Image from translation.models import SiteInterfaceDictionary from main.models import SiteSettings class WineColorSerializer(TransferSerializerMixin): CATEGORY_LABEL = 'Wine color' CATEGORY_INDEX_NAME = slugify(CATEGORY_LABEL) id = serializers.IntegerField() name = serializers.CharField(allow_null=True) order_number = serializers.IntegerField(allow_null=True) class Meta: model = tag_models.Tag fields = ( 'id', 'name', 'order_number', ) def validate(self, attrs): value = attrs.pop('name') translation = SiteInterfaceDictionary(text={settings.FALLBACK_LOCALE: value}, keywords=f'tag.{self.CATEGORY_INDEX_NAME}.{slugify(value)}') translation.save() attrs['old_id'] = attrs.pop('id', None) attrs['translation'] = translation attrs['value'] = slugify(value) attrs['priority'] = attrs.pop('order_number') attrs['category'] = self.tag_category return attrs class WineTypeSerializer(TransferSerializerMixin): CATEGORY_LABEL = 'Sugar content' CATEGORY_INDEX_NAME = slugify(CATEGORY_LABEL) id = serializers.IntegerField() name = serializers.CharField() class Meta: model = tag_models.Tag fields = ( 'id', 'name', ) def validate(self, attrs): value = attrs.pop('name') translation = SiteInterfaceDictionary(text={settings.FALLBACK_LOCALE: value}, keywords=f'tag.{self.CATEGORY_INDEX_NAME}.{slugify(value)}') translation.save() attrs['old_id'] = attrs.pop('id', None) attrs['translation'] = translation attrs['value'] = slugify(value) attrs['category'] = self.tag_category return attrs class WineBottlesProducedSerializer(TransferSerializerMixin): CATEGORY_LABEL = 'Bottles produced' CATEGORY_INDEX_NAME = slugify(CATEGORY_LABEL) bottles_produced = serializers.CharField(allow_null=True) class Meta: model = tag_models.Tag fields = ( 'bottles_produced', ) def validate(self, attrs): value = attrs.pop('bottles_produced') translation = SiteInterfaceDictionary(text={settings.FALLBACK_LOCALE: value}, keywords=f'tag.{self.CATEGORY_INDEX_NAME}.{slugify(value)}') translation.save() parsed_value = self.parsed_value(value) attrs['translation'] = translation attrs['value'] = slugify(parsed_value) attrs['category'] = self.tag_category return attrs def parsed_value(self, value): if value.isdigit(): return int(value) parted = value.split(' ') if len(parted) > 1: values = [int(i) for i in parted if i.isdigit()] return reduce(lambda a, b: a + b, values) lowered = value.lower() if 'magnum' in lowered: return 1 return 0 class ProductStandardSerializer(TransferSerializerMixin): id = serializers.IntegerField() name = serializers.CharField() type = serializers.CharField(allow_null=True) longitude = serializers.FloatField(allow_null=True) latitude = serializers.FloatField(allow_null=True) class Meta: model = models.ProductStandard fields = ( 'id', 'name', 'type', 'longitude', 'latitude', ) def validate(self, attrs): latitude = attrs.pop('latitude', None) longitude = attrs.pop('longitude', None) standard_type = attrs.pop('type', None) attrs['coordinates'] = get_point_from_coordinates(latitude, longitude) attrs['old_id'] = attrs.get('id') attrs['standard_type'] = self.get_standard_type(standard_type) return attrs def get_standard_type(self, type: str): if type == 'Appellation': return models.ProductStandard.APPELLATION elif type == 'YardClassification': return models.ProductStandard.WINEQUALITY elif type == 'WineQuality': return models.ProductStandard.YARDCLASSIFICATION class WineClassificationTypeSerializer(TransferSerializerMixin): name = serializers.CharField() class Meta: model = models.ProductClassificationType fields = ( 'name', ) def validate(self, attrs): attrs['product_type'] = self.wine_type return attrs @property def wine_type(self): qs = models.ProductType.objects.filter(index_name=models.ProductType.WINE) if qs.exists(): return qs.first() class ProductClassificationSerializer(TransferSerializerMixin): id = serializers.IntegerField() name = serializers.CharField() possible_type_id = serializers.IntegerField(allow_null=True) possible_color_id = serializers.IntegerField(allow_null=True) parent_id = serializers.IntegerField(allow_null=True) class Meta: model = models.ProductClassification fields = ( 'id', 'name', 'possible_type_id', 'possible_color_id', 'parent_id', ) def validate(self, attrs): classification_name = attrs.pop('name') possible_type_id = attrs.pop('possible_type_id', None) possible_color_id = attrs.pop('possible_color_id', None) parent_id = attrs.pop('parent_id', None) attrs['old_id'] = attrs.pop('id') attrs['classification_type'] = self.get_classification_type(classification_name) attrs['possible_subtype_tag'] = self.get_possible_type_tag(possible_type_id) attrs['possible_color_tag'] = self.get_possible_color_tag(possible_color_id) attrs['standard'] = self.get_wine_standard(parent_id) return attrs def create(self, validated_data): possible_color_tag = validated_data.pop('possible_color_tag', None) possible_subtype_tag = validated_data.pop('possible_subtype_tag', None) obj, _ = self.Meta.model.objects.get_or_create(**validated_data) if possible_color_tag: obj.tags.add(possible_color_tag) if possible_subtype_tag: obj.tags.add(possible_subtype_tag) return obj def get_classification_type(self, classification_name): qs = models.ProductClassificationType.objects.filter(name__icontains=classification_name) if qs.exists(): return qs.first() def get_possible_type_tag(self, possible_type_id): if possible_type_id: qs = tag_models.Tag.objects.filter( old_id=possible_type_id, category__index_name=WineTypeSerializer.CATEGORY_INDEX_NAME) if qs.exists(): return qs.first() def get_possible_color_tag(self, possible_color_id): if possible_color_id: qs = tag_models.Tag.objects.filter( old_id=possible_color_id, category__index_name=WineColorSerializer.CATEGORY_INDEX_NAME) if qs.exists(): return qs.first() def get_wine_standard(self, parent_id): if parent_id: qs = models.ProductStandard.objects.filter(old_id=parent_id) if qs.exists(): return qs.first() class ProductSerializer(TransferSerializerMixin): establishment_id = serializers.PrimaryKeyRelatedField( queryset=transfer_models.Establishments.objects.all(), allow_null=True) classification_id = serializers.PrimaryKeyRelatedField( queryset=transfer_models.ProductClassification.objects.all(), allow_null=True) wine_region_id = serializers.PrimaryKeyRelatedField( queryset=transfer_models.WineLocations.objects.all(), allow_null=True) wine_type_id = serializers.PrimaryKeyRelatedField( queryset=transfer_models.WineType.objects.all(), allow_null=True) wine_color_id = serializers.PrimaryKeyRelatedField( queryset=transfer_models.WineColor.objects.all(), allow_null=True) appellation_id = serializers.PrimaryKeyRelatedField( queryset=transfer_models.ProductClassification.objects.all(), allow_null=True) village_id = serializers.PrimaryKeyRelatedField( queryset=transfer_models.WineLocations.objects.all(), allow_null=True) # same as wine_village # vineyard_id = serializers.PrimaryKeyRelatedField( # queryset=transfer_models.WineLocations.objects.all(), # allow_null=True) wine_quality_id = serializers.PrimaryKeyRelatedField( queryset=transfer_models.ProductClassification.objects.all(), allow_null=True) # same as establishment name id = serializers.IntegerField() brand = serializers.CharField(allow_null=True, allow_blank=True) name = serializers.CharField(allow_null=True, allow_blank=True) vintage = serializers.CharField(allow_null=True) type = serializers.CharField(allow_null=True) wine_sub_region_id = serializers.CharField(allow_null=True) state = serializers.CharField() bottles_produced = serializers.CharField(allow_null=True, allow_blank=True) unique_key = serializers.CharField(allow_null=True) price = serializers.DecimalField(max_digits=14, decimal_places=2, allow_null=True) class Meta: model = models.Product fields = ( 'id', 'establishment_id', # done 'classification_id', # done 'wine_region_id', # done 'wine_type_id', # done 'wine_color_id', # done 'appellation_id', # done 'village_id', # done # 'vineyard_id', 'wine_quality_id', # done 'brand', 'name', # done 'vintage', # done 'type', # done 'wine_sub_region_id', # done 'state', # done 'bottles_produced', # done 'unique_key', # done 'price', ) def validate(self, attrs): name = attrs.pop('name', None) establishment = attrs.pop('establishment_id', None) brand = attrs.pop('brand', None) classification = attrs.pop('classification_id', None) vintage = attrs.pop('vintage', None) product_type = attrs.pop('type') wine_region = attrs.pop('wine_region_id', None) wine_sub_region_id = attrs.pop('wine_sub_region_id', None) bottles_produced = attrs.pop('bottles_produced', None) wine_type = attrs.pop('wine_type_id', None) wine_color = attrs.pop('wine_color_id', None) appellation = attrs.pop('appellation_id', None) village = attrs.pop('village_id', None) wine_quality = attrs.pop('wine_quality_id', None) name = self.get_name(name, brand) old_id = attrs.pop('id') state = self.get_state(attrs.pop('state', None)) attrs['old_id'] = old_id attrs['name'] = name attrs['old_unique_key'] = attrs.pop('unique_key') attrs['establishment'] = self.get_establishment(establishment) attrs['state'] = state # attrs['brand'] = self.get_brand(brand) attrs['wine_classification'] = self.get_wine_classification(classification) attrs['wine_quality'] = self.get_wine_standard(wine_quality, models.ProductStandard.WINEQUALITY) attrs['wine_appellation'] = self.get_wine_standard(appellation, models.ProductStandard.APPELLATION) attrs['product_type'] = self.get_product_type(product_type) attrs['vintage'] = self.get_vintage_year(vintage) attrs['wine_region'] = self.get_wine_region(wine_region) attrs['wine_sub_region'] = self.get_wine_sub_region(wine_sub_region_id) attrs['bottles_produced_tag'] = self.get_tag(bottles_produced, WineBottlesProducedSerializer.CATEGORY_INDEX_NAME) attrs['wine_type_tag'] = self.get_tag(wine_type, WineTypeSerializer.CATEGORY_INDEX_NAME) attrs['wine_color_tag'] = self.get_tag(wine_color, WineColorSerializer.CATEGORY_INDEX_NAME) attrs['wine_village'] = self.get_wine_village(village) attrs['available'] = self.get_availability(state) attrs['slug'] = self.get_slug(name, old_id) attrs['average_price'] = attrs.pop('price') return attrs def create(self, validated_data): qs = self.Meta.model.objects.filter(old_id=validated_data.get('old_id')) wine_region = validated_data.pop('wine_region') wine_sub_region = validated_data.pop('wine_sub_region') # classifications classifications = [validated_data.pop('wine_classification', None)] # standards standards = [validated_data.pop('wine_quality', None), validated_data.pop('wine_appellation', None)] # tags tags = [validated_data.pop('bottles_produced_tag', None), validated_data.pop('wine_type_tag', None), validated_data.pop('wine_color_tag', None)] if not qs.exists(): obj = super().create(validated_data) else: obj = qs.first() # adding classification obj.classifications.add(*[i for i in classifications if i and i not in obj.classifications.all()]) # adding standard obj.standards.add(*[i for i in standards if i and i not in obj.standards.all()]) # adding tags obj.tags.add(*[i for i in tags if i and i not in obj.tags.all()]) # checking wine origin address wine_origin_address, _ = location_models.WineOriginAddress.objects.get_or_create( product=obj, wine_region=wine_region, wine_sub_region=wine_sub_region) return obj def get_name(self, name, brand): if name: return name if brand and not name: return brand def get_establishment(self, establishment): if establishment: qs = establishment_models.Establishment.objects.filter( old_id=establishment.id) if qs.exists(): return qs.first() def get_state(self, state): if state == 'published': return models.Product.PUBLISHED elif state == 'out_of_production': return models.Product.OUT_OF_PRODUCTION return models.Product.WAITING def get_wine_classification(self, classification): if classification: classification_qs = models.ProductClassification.objects.filter( old_id=classification.id) if classification_qs.exists(): return classification_qs.first() @staticmethod def get_wine_region(wine_region): if wine_region: old_id = wine_region if not isinstance(wine_region, transfer_models.WineLocations) \ else wine_region.id wine_region_qs = location_models.WineRegion.objects.filter( old_id=old_id) if wine_region_qs.exists(): return wine_region_qs.first() @staticmethod def get_wine_sub_region(wine_sub_region): if wine_sub_region: old_id = wine_sub_region if not isinstance(wine_sub_region, transfer_models.WineLocations) \ else wine_sub_region.id sub_region_qs = location_models.WineSubRegion.objects.filter( old_id=old_id) if sub_region_qs.exists(): return sub_region_qs.first() def get_wine_village(self, village): if village: village_qs = location_models.WineVillage.objects.filter( old_id=village.id) if village_qs.exists(): return village_qs.first() def get_wine_standard(self, standard, standard_type): if standard: if isinstance(standard, transfer_models.ProductClassification): standard = standard.id standard_qs = models.ProductStandard.objects.filter( standard_type=standard_type, old_id=standard) if standard_qs.exists(): return standard_qs.first() def get_availability(self, state: int): if state == models.Product.PUBLISHED: return True return False class WineColorTagSerializer(TransferSerializerMixin): """Serializer for fixing existing products with missing tags.""" id = serializers.IntegerField() wine_color_id = serializers.PrimaryKeyRelatedField( queryset=transfer_models.WineColor.objects.all(), allow_null=True) class Meta: model = models.Product fields = ( 'id', 'wine_color_id', ) def validate(self, attrs): attrs['old_id'] = attrs.pop('id') attrs['wine_color_tag'] = self.get_tag(attrs.pop('wine_color_id', None), WineColorSerializer.CATEGORY_INDEX_NAME) return attrs def create(self, validated_data): qs = self.Meta.model.objects.filter(old_id=validated_data.get('old_id')) # tags wine_color_tag = validated_data.pop('wine_color_tag', None) if qs.exists(): obj = qs.first() # adding missing tag if wine_color_tag in obj.tags.all(): obj.tags.add(wine_color_tag) return obj class PlateSerializer(TransferSerializerMixin): PRODUCT_TYPE_INDEX_NAME = 'souvenir' PRODUCT_SUB_TYPE_INDEX_NAME = 'plate' id = serializers.IntegerField() name = serializers.CharField() vintage = serializers.CharField() site_id = serializers.IntegerField() class Meta(ProductSerializer.Meta): fields = ( 'id', 'name', 'vintage', 'site_id', ) def validate(self, attrs): old_id = attrs.pop('id') name = attrs.get('name') product_type = self.get_product_type(self.PRODUCT_TYPE_INDEX_NAME) attrs['old_id'] = old_id attrs['vintage'] = self.get_vintage_year(attrs.pop('vintage')) attrs['product_type'] = product_type attrs['state'] = self.Meta.model.PUBLISHED attrs['subtype'] = self.get_product_sub_type(product_type, self.PRODUCT_SUB_TYPE_INDEX_NAME) attrs['slug'] = self.get_slug(name, old_id) attrs['site'] = self.get_site(attrs.pop('site_id', None)) return attrs def create(self, validated_data): qs = self.Meta.model.objects.filter(old_id=validated_data.get('old_id')) # subtypes subtypes = [validated_data.pop('subtype', None)] if not qs.exists(): obj = super().create(validated_data) else: obj = qs.first() # adding classification obj.subtypes.add(*[i for i in subtypes if i]) return obj def get_site(self, old_id: int): if old_id: site_qs = SiteSettings.objects.filter(old_id=old_id) if site_qs.exists(): return site_qs.first() class PlateImageSerializer(TransferSerializerMixin): id = serializers.IntegerField() name = serializers.CharField() attachment_suffix_url = serializers.CharField() class Meta: model = models.ProductGallery fields = ( 'id', 'name', 'attachment_suffix_url', ) def create(self, validated_data): image = self.get_image(validated_data.pop('attachment_suffix_url', None), validated_data.pop('name', None)) product = self.get_product(validated_data.pop('id')) if product and image: obj, created = models.ProductGallery.objects.get_or_create( product=product, image=image, is_main=True) return obj def get_image(self, image_url, name): if image_url: obj, created = Image.objects.get_or_create( title=name, image=image_url) return obj if created else None class ProductNoteSerializer(TransferSerializerMixin): id = serializers.IntegerField() product_id = serializers.IntegerField() text = serializers.CharField(allow_blank=True) class Meta: model = models.ProductNote fields = ( 'id', 'product_id', 'text', ) def validate(self, attrs): attrs['old_id'] = attrs['id'] attrs['product'] = self.get_product(attrs.pop('product_id')) return attrs def create(self, validated_data): qs = self.Meta.model.objects.filter(**validated_data) product = validated_data.get('product') if not qs.exists() and product: return super().create(validated_data) def get_product(self, old_id): if old_id: qs = models.Product.objects.filter(old_id=old_id) if qs.exists(): return qs.first() class AssemblageProductTagSerializer(TransferSerializerMixin): CATEGORY_LABEL = 'Grape variety' CATEGORY_INDEX_NAME = slugify(CATEGORY_LABEL) percent = serializers.FloatField() cepage_id = serializers.PrimaryKeyRelatedField( queryset=transfer_models.Cepages.objects.all()) product_id = serializers.IntegerField() class Meta(ProductSerializer.Meta): fields = ( 'percent', 'cepage_id', # tag category 'product_id', ) def validate(self, attrs): cepage = attrs.pop('cepage_id') legacy_tag = str(attrs.pop('percent')) attrs['product'] = self.get_product(attrs.pop('product_id')) attrs['tag'] = self.get_tag_object(legacy_tag, self.tag_category, cepage) return attrs def create(self, validated_data): obj = validated_data.pop('product', None) tag = validated_data.pop('tag', None) if obj and tag: if tag not in obj.tags.all(): obj.tags.add(tag) return obj def get_tag_object(self, tag, tag_category, cepage): cepage = cepage.name if isinstance(cepage, transfer_models.Cepages) else cepage if tag and tag_category and cepage: qs = tag_models.Tag.objects.filter(category=tag_category, value=slugify(f'{cepage} - {tag}')) if qs.exists(): return qs.first()