from itertools import chain import yaml from django.utils.text import slugify from pycountry import countries, subdivisions from rest_framework import serializers from collection import models from establishment.models import EstablishmentType from location.models import Country, Region, WineRegion from main.models import SiteSettings from review.models import Review from transfer.mixins import TransferSerializerMixin from translation.models import Language class GuideSerializer(TransferSerializerMixin): id = serializers.IntegerField() title = serializers.CharField() vintage = serializers.IntegerField() slug = serializers.CharField() state = serializers.CharField() site_id = serializers.IntegerField() inserter_field = serializers.CharField() class Meta: model = models.Guide fields = ( 'id', 'title', 'vintage', 'slug', 'state', 'site_id', 'inserter_field', ) def validate(self, attrs): """Overridden validate method.""" attrs['old_id'] = attrs.pop('id') attrs['name'] = attrs.pop('title') attrs['vintage'] = int(attrs.pop('vintage')) attrs['state'] = self.get_state(attrs.pop('state')) attrs['site'] = self.get_site(attrs.pop('site_id')) attrs['guide_type'] = self.get_guide_type(attrs.pop('inserter_field')) return attrs def get_state(self, state: str): if state == 'built': return models.Guide.BUILT elif state == 'removing': return models.Guide.REMOVING elif state == 'building': return models.Guide.BUILDING else: return models.Guide.WAITING def get_site(self, site_id): qs = SiteSettings.objects.filter(old_id=site_id) if qs.exists(): return qs.first() def get_guide_type(self, inserter_field): guide_type, _ = models.GuideType.objects.get_or_create(name=inserter_field) return guide_type class GuideFilterSerializer(TransferSerializerMixin): id = serializers.IntegerField() year = serializers.CharField(allow_null=True) establishment_type = serializers.CharField(allow_null=True) countries = serializers.CharField(allow_null=True) regions = serializers.CharField(allow_null=True) subregions = serializers.CharField(allow_null=True) wine_regions = serializers.CharField(allow_null=True) locales = serializers.CharField(allow_null=True) states = serializers.CharField(allow_null=True) max_mark = serializers.FloatField(allow_null=True) min_mark = serializers.FloatField(allow_null=True) marks_only = serializers.NullBooleanField() guide_id = serializers.IntegerField() class Meta: model = models.GuideFilter fields = ( 'id', 'year', 'establishment_type', 'countries', 'regions', 'subregions', 'wine_regions', 'max_mark', 'min_mark', 'marks_only', 'locales', 'states', 'guide_id', ) def create(self, validated_data): qs = self.Meta.model.objects.filter(guide=validated_data.get('guide')) if not qs.exists(): return super().create(validated_data) @staticmethod def parse_ruby_helper(raw_value: str): """Parse RubyActiveSupport records""" def convert_entry(loader, node): return {e[0]: e[1] for e in loader.construct_pairs(node)} loader = yaml.Loader loader.add_constructor('!ruby/hash:ActiveSupport::HashWithIndifferentAccess', convert_entry) return yaml.load(raw_value, Loader=loader) if raw_value else None @staticmethod def parse_dictionary(dictionary: dict): """ Exclude root values from dictionary. Convert {key_2: [value_1, value_2]} into [value_1, value_2] """ return list(chain.from_iterable(dictionary.values())) @staticmethod def parse_nested_dictionary(dictionary: dict): """ Exclude root values from dictionary. Convert {key_1: {key_2: [value_1, value_2]}} into [value_1, value_2] """ l = [] for i in dictionary: l.append(list(chain.from_iterable(list(dictionary[i].values())))) return list(chain.from_iterable(l)) def get_country(self, code_alpha_3: str) -> Country: country = countries.get(alpha_3=code_alpha_3.upper()) if country: country_name = country.name country_code = country.alpha_2 if country_name and country_code: country, _ = Country.objects.get_or_create( code__icontains=country_code, name__contains={'en-GB': country_name}, defaults={ 'code': country_code, 'name': {'en-GB': country_name} } ) return country def get_region(self, region_code_alpha_3: str, country_code_alpha_3: str, sub_region_code_alpha_3: str = None): country = self.get_country(country_code_alpha_3) if country: country_code_alpha_2 = country.code.upper() region_qs = Region.objects.filter(code__iexact=region_code_alpha_3, country__code__iexact=country_code_alpha_2) if region_qs.exists(): return region_qs.first() # If region isn't existed, check sub region for parent_code (region code) if sub_region_code_alpha_3: # sub region subdivision = subdivisions.get( code=f"{country_code_alpha_2}-{sub_region_code_alpha_3}") if subdivision: # try with parent code subdivision_region = subdivisions.get(code=subdivision.__dict__.get('_fields') .get('parent_code')) if not subdivision_region: # try with parent subdivision_region = subdivisions.get(code=subdivision.__dict__.get('_fields') .get('parent')) if subdivision_region: obj = Region.objects.create( name=subdivision_region.name, code=subdivision_region.code, country=country) return obj def validate_year(self, value): return self.parse_ruby_helper(value) def validate_establishment_type(self, value): return self.parse_ruby_helper(value) def validate_countries(self, value): return self.parse_ruby_helper(value) def validate_regions(self, value): return self.parse_ruby_helper(value) def validate_subregions(self, value): return self.parse_ruby_helper(value) def validate_wine_regions(self, value): return self.parse_ruby_helper(value) def validate_wine_classifications(self, value): return self.parse_ruby_helper(value) def validate_wine_colors(self, value): return self.parse_ruby_helper(value) def validate_wine_types(self, value): return self.parse_ruby_helper(value) def validate_locales(self, value): return self.parse_ruby_helper(value) def validate_states(self, value): return self.parse_ruby_helper(value) def validate(self, attrs): sub_regions = attrs.pop('subregions') regions = attrs.pop('regions') attrs['old_id'] = attrs.pop('id') attrs['review_vintage_json'] = self.get_review_vintage(attrs.pop('year')) attrs['establishment_type_json'] = self.get_establishment_type_ids( attrs.pop('establishment_type')) attrs['country_json'] = self.get_country_ids(attrs.pop('countries')) attrs['region_json'] = self.get_region_ids(regions=regions, sub_regions=sub_regions) attrs['sub_region_json'] = self.get_sub_region_ids(sub_regions) attrs['wine_region_json'] = self.get_wine_region_ids(attrs.pop('wine_regions')) attrs['with_mark'] = attrs.pop('marks_only') or True attrs['locale_json'] = self.get_locale_ids(attrs.pop('locales')) attrs['review_state_json'] = self.get_review_state(attrs.pop('states')) attrs['guide'] = self.get_guide(attrs.pop('guide_id')) return attrs def get_review_vintage(self, year): if hasattr(year, '__iter__'): return {'vintage': list(set(int(i) for i in set(year) if i.isdigit()))} return {'vintage': [year, ]} def get_establishment_type_ids(self, establishment_types): establishment_type_ids = [] if establishment_types: for establishment_type in establishment_types: establishment_type_qs = EstablishmentType.objects.filter(index_name__iexact=establishment_type) if not establishment_type_qs.exists(): obj = EstablishmentType.objects.create( name={'en-GB': establishment_type.capitalize()}, index_name=slugify(establishment_type)) else: obj = establishment_type_qs.first() establishment_type_ids.append(obj.id) return {'id': list(set(establishment_type_ids))} def get_country_ids(self, country_codes_alpha_3): country_ids = [] if country_codes_alpha_3: for code_alpha_3 in country_codes_alpha_3: # Code can be an empty string. if code_alpha_3 and not code_alpha_3 == 'AAA': country = self.get_country(code_alpha_3) if not country: raise serializers.ValidationError({'detail': f'Country with alpha code 3 -' f'{code_alpha_3}, is not found.'}) country_ids.append(country.id) return {'id': list(set(country_ids))} def get_region_ids(self, regions, sub_regions): region_ids = [] if regions: for country_code_alpha_3 in regions: for region_code_alpha_3 in regions[country_code_alpha_3]: # Get region from sub region code. if sub_regions and country_code_alpha_3 in sub_regions: if region_code_alpha_3 in sub_regions[country_code_alpha_3]: for sub_region_code_alpha_3 in sub_regions[country_code_alpha_3][region_code_alpha_3]: region = self.get_region( region_code_alpha_3=region_code_alpha_3, country_code_alpha_3=country_code_alpha_3, sub_region_code_alpha_3=sub_region_code_alpha_3) if region: region_ids.append(region.id) return {'id': list(set(region_ids))} def get_sub_region_ids(self, sub_regions): sub_region_ids = [] if sub_regions: for country_code_alpha_3 in sub_regions: # FRA etc. if country_code_alpha_3 in sub_regions: for region_code_alpha_3 in sub_regions[country_code_alpha_3]: # B, C, A etc. if region_code_alpha_3 in sub_regions[country_code_alpha_3]: for sub_region_code_alpha_3 in sub_regions[country_code_alpha_3][region_code_alpha_3]: # 24, 32 etc. # Get parent region region = self.get_region( region_code_alpha_3=region_code_alpha_3, country_code_alpha_3=country_code_alpha_3, sub_region_code_alpha_3=sub_region_code_alpha_3) if region: sub_region_qs = Region.objects.filter(parent_region__code=region.code) if sub_region_qs.exists(): sub_region_ids.append(sub_region_qs.first().id) else: subdivision = subdivisions.get(code=region.code.upper()) if subdivision: sub_region, _ = Region.objects.get_or_create( name=subdivision.name, code=subdivision.code, parent_region=region, country=region.country) sub_region_ids.append(sub_region.id) return {'id': list(set(sub_region_ids))} def get_wine_region_ids(self, wine_regions): wine_region_ids = [] if wine_regions: for wine_region in wine_regions: qs = WineRegion.objects.filter(name__iexact=wine_region) if not qs.exists(): raise serializers.ValidationError({ 'detail': f'Wine region - {wine_region}, is not found.'}) wine_region_ids.append(qs.first().id) return {'id': list(set(wine_region_ids))} def get_locale_ids(self, locales): locale_ids = [] if locales: for locale in [locale for locale in locales if locale]: if len(locale) == 2: qs = Language.objects.filter(locale__startswith=locale) else: qs = Language.objects.filter(locale=locale) if not qs.exists(): raise serializers.ValidationError({ 'detail': f'Language with locale - {locale}, is not found.'}) locale_ids.extend(qs.values_list('id', flat=True)) return {'id': list(set(locale_ids))} def get_review_state(self, states): review_states = [] if states: for state in [state for state in states if state]: if state == 'published': review_states.append(Review.READY) else: review_states.append(Review.TO_INVESTIGATE) return {'state': list(set(review_states))} def get_guide(self, old_guide_id: int): qs = models.Guide.objects.filter(old_id=old_guide_id) if qs.exists(): return qs.first()