from django.conf import settings from django.core.exceptions import MultipleObjectsReturned, ValidationError from django.db import transaction from django.utils.text import slugify from phonenumber_field.phonenumber import PhoneNumber from rest_framework import serializers from account.models import User from establishment.models import Establishment, ContactEmail, ContactPhone, \ EstablishmentType, EstablishmentSubType, EstablishmentNote, Menu from location.models import Address from timetable.models import Timetable from utils.legacy_parser import parse_legacy_schedule_content from utils.serializers import TimeZoneChoiceField from utils.slug_generator import generate_unique_slug class EstablishmentSerializer(serializers.ModelSerializer): slug = serializers.CharField(allow_null=True, allow_blank=True) type = serializers.CharField() description = serializers.DictField( allow_null=True, child=serializers.CharField(allow_null=True), ) schedules = serializers.CharField(allow_null=True, allow_blank=True) location = serializers.IntegerField(allow_null=True) email = serializers.CharField(allow_null=True, allow_blank=True) phone = serializers.CharField(allow_null=True, allow_blank=True) website = serializers.CharField(allow_null=True, allow_blank=True) facebook = serializers.CharField(allow_null=True, allow_blank=True) twitter = serializers.CharField(allow_null=True, allow_blank=True) booking = serializers.CharField(allow_null=True, allow_blank=True) state = serializers.CharField(allow_null=True) tz = TimeZoneChoiceField() created = serializers.DateTimeField(format='%m-%d-%Y %H:%M:%S') class Meta: model = Establishment fields = ( 'created', 'old_id', # + 'name', # + 'transliterated_name', # + 'tz', # + 'website', # + 'facebook', # + 'twitter', # + 'lafourchette', # + 'booking', # + 'type', # + см в JIRA 'slug', # + сгенерировать уникальный слаг 'description', # + (разобрал в transfer_data) 'schedules', # + разобрать RUBY словать (2 варианта) 'location', # + получить новые объекты Address по old_id 'email', # + создать объект для ContactEmail 'phone', # + создать объект для ContactPhone 'state', # + создать объект для ContactPhone ) def validate(self, data): old_type = data.pop('type', None) phone = data.pop('phone', None) data.update({ 'slug': generate_unique_slug(Establishment, data['slug'] if data['slug'] else data['name']), 'address_id': self.get_address(data['location']), 'establishment_type_id': self.get_type(old_type), 'status': self.get_status(data.pop('state', None)), 'subtype': self.get_subtype(old_type), 'phone': self.get_phone(phone), }) data.pop('location') return data @transaction.atomic def create(self, validated_data): email = validated_data.pop('email') phone = validated_data.pop('phone') schedules = validated_data.pop('schedules') subtypes = [validated_data.pop('subtype', None)] # establishment = Establishment.objects.create(**validated_data) establishment, _ = Establishment.objects.update_or_create( old_id=validated_data['old_id'], defaults=validated_data, ) if email: ContactEmail.objects.get_or_create( email=email, establishment=establishment, ) if phone: ContactPhone.objects.get_or_create( phone=phone, establishment=establishment, ) if schedules: new_schedules = self.get_schedules(schedules) for schedule in new_schedules: establishment.schedule.add(schedule) establishment.save() if subtypes: establishment.establishment_subtypes.add(*[i for i in subtypes if i]) return establishment @staticmethod def get_address(address): address = Address.objects.filter(old_id=address).first() if address: return address.id return None @staticmethod def get_type(old_type): types = { 'Restaurant': EstablishmentType.RESTAURANT, 'Shop': EstablishmentType.ARTISAN, 'Wineyard': EstablishmentType.PRODUCER, } index_name = types.get(old_type) if index_name: obj, _ = EstablishmentType.objects.get_or_create(index_name=index_name) return obj.id @staticmethod def get_schedules(schedules): result = [] legacy_dict = parse_legacy_schedule_content(schedules) weekdays = { 'su': Timetable.SUNDAY, 'mo': Timetable.MONDAY, 'tu': Timetable.TUESDAY, 'we': Timetable.WEDNESDAY, 'th': Timetable.THURSDAY, 'fr': Timetable.FRIDAY, 'sa': Timetable.SATURDAY, } for key, val in legacy_dict.items(): payload = { 'weekday': weekdays[key], 'lunch_start': None, 'lunch_end': None, 'dinner_start': None, 'dinner_end': None, 'opening_at': None, 'closed_at': None, } if val.get('morning'): payload.update({ 'lunch_start': val['morning'].get('start'), 'lunch_end': val['morning'].get('end'), }) if val.get('afternoon'): payload.update({ 'dinner_start': val['afternoon'].get('start'), 'dinner_end': val['afternoon'].get('end'), }) try: obj, _ = Timetable.objects.get_or_create(**payload) except ValidationError: obj = None except MultipleObjectsReturned: obj = Timetable.objects.filter(**payload).first() if obj: result.append(obj) return result def get_subtype(self, old_type): if old_type == 'Wineyard': subtype_name = 'Winery' establishment_type_id = self.get_type(old_type) subtype, _ = EstablishmentSubType.objects.get_or_create( name={settings.FALLBACK_LOCALE: subtype_name}, index_name=slugify(subtype_name), establishment_type_id=establishment_type_id) return subtype @staticmethod def get_phone(phone: str): phone_obj = PhoneNumber.from_string(phone_number=phone, region='FR') if phone_obj.is_valid(): fmt = PhoneNumber.format_map[getattr(settings, 'PHONENUMBER_DB_FORMAT', 'INTERNATIONAL')] return phone_obj.format_as(fmt) @staticmethod def get_status(state: str): if state: state = state.lower() if state == 'abandoned': return Establishment.ABANDONED elif state == 'closed': return Establishment.CLOSED elif state == 'published': return Establishment.PUBLISHED elif state == 'unpicked': return Establishment.UNPICKED return Establishment.WAITING class EstablishmentNoteSerializer(serializers.ModelSerializer): id = serializers.IntegerField() establishment_id = serializers.IntegerField() account_id = serializers.IntegerField(allow_null=True) text = serializers.CharField(allow_blank=True, allow_null=True) class Meta: model = EstablishmentNote fields = ( 'id', 'establishment_id', 'account_id', 'text', ) def validate(self, attrs): attrs['old_id'] = attrs['id'] attrs['establishment'] = self.get_establishment(attrs.pop('establishment_id')) attrs['user'] = self.get_user(attrs.pop('account_id')) return attrs def create(self, validated_data): qs = self.Meta.model.objects.filter(**validated_data) establishment = validated_data.get('establishment') if not qs.exists() and establishment: obj = super().create(validated_data) return obj def get_establishment(self, old_id): if old_id: qs = Establishment.objects.filter(old_id=old_id) if qs.exists(): return qs.first() def get_user(self, old_id): qs = User.objects.exclude(old_id__isnull=True).filter(old_id=old_id) if qs.exists(): return qs.first() class ALaCartesSerializer(serializers.ModelSerializer): """Serializer for model Menu.""" establishment_id = serializers.IntegerField() renewal_per_year = serializers.IntegerField(allow_null=True) nb_wine = serializers.IntegerField(allow_null=True) lowest_price = serializers.FloatField(allow_null=True) highest_price = serializers.FloatField(allow_null=True) by_glass = serializers.CharField(allow_null=True) price_min_by_glass = serializers.FloatField(allow_null=True) price_max_by_glass = serializers.FloatField(allow_null=True) class Meta: model = Menu fields = ( 'establishment_id', 'renewal_per_year', 'nb_wine', 'lowest_price', 'highest_price', 'by_glass', 'price_min_by_glass', 'price_max_by_glass', ) def validate(self, attrs): return { 'establishment': self.get_establishment(attrs.pop('establishment_id')), 'renewal_per_year': attrs.pop('renewal_per_year', None), 'nb_wine': attrs.pop('nb_wine', None), 'lowest_price': attrs.pop('lowest_price', None), 'highest_price': attrs.pop('highest_price', None), 'served_by_glasses': self.get_served_by_glasses(attrs.pop('by_glass', None)), 'price_min_by_glass': attrs.pop('price_min_by_glass', None), 'price_max_by_glass': attrs.pop('price_max_by_glass', None), } def get_establishment(self, old_id): establishment_qs = Establishment.objects.filter(old_id=old_id) if establishment_qs.exists(): return establishment_qs.first() def get_served_by_glasses(self, value): if value: value = value.lower() if value == 'true': return True elif value == 'false': return False def create(self, validated_data): menu_qs = Menu.objects.filter(establishment=validated_data.pop('establishment', None)) if menu_qs.exists(): menu = menu_qs.first() for attr_name, attr_value in validated_data.items(): setattr(menu, attr_name, attr_value) menu.save()