gault-millau/apps/transfer/serializers/product.py
2020-01-24 17:49:42 +03:00

653 lines
23 KiB
Python

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()