360 lines
15 KiB
Python
360 lines
15 KiB
Python
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):
|
|
if inserter_field:
|
|
if inserter_field.startswith('restaurant'):
|
|
return models.Guide.RESTAURANT
|
|
elif inserter_field.startswith('wine'):
|
|
return models.Guide.WINE
|
|
|
|
|
|
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()
|