304 lines
11 KiB
Python
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()
|