gault-millau/apps/transfer/serializers/establishment.py

304 lines
11 KiB
Python

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