Merge branch 'develop' into fix_tags

This commit is contained in:
Dmitriy Kuzmenko 2019-11-16 22:57:43 +03:00
commit ca9843482e
20 changed files with 212 additions and 65 deletions

View File

@ -0,0 +1,17 @@
# Generated by Django 2.2.7 on 2019-11-16 11:35
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('advertisement', '0007_auto_20191115_0750'),
]
operations = [
migrations.AlterModelOptions(
name='advertisement',
options={'verbose_name': 'Advertisement', 'verbose_name_plural': 'Advertisements'},
),
]

View File

@ -14,6 +14,12 @@ class CheckWhetherBookingAvailable(generics.GenericAPIView):
""" Checks which service to use if establishmend is managed by any """
_VALID_GUESTONLINE_PERIODS = {'lunch', 'dinner', 'afternoon', 'breakfast'}
_GUESTONLINE_PERIODS_TO_PRIOR = {
'breakfast': 1,
'lunch': 2,
'afternoon': 3,
'dinner': 4,
}
permission_classes = (permissions.AllowAny,)
serializer_class = CheckBookingSerializer
@ -32,7 +38,7 @@ class CheckWhetherBookingAvailable(generics.GenericAPIView):
period_template = iter(periods_by_name.values()).__next__().copy()
period_template.pop('total_left_seats')
period_template.pop('hours')
period_template['hours'] = []
period_template.pop('period')
processed_periods = [
@ -46,7 +52,8 @@ class CheckWhetherBookingAvailable(generics.GenericAPIView):
for unnamed_period in unnamed_periods:
processed_periods.append(unnamed_period)
response['periods'] = processed_periods
response['periods'] = sorted(processed_periods,
key=lambda x: self._GUESTONLINE_PERIODS_TO_PRIOR[x.get('period', 'lunch')])
return response

View File

@ -1,11 +1,11 @@
from django.shortcuts import get_object_or_404
from rest_framework import generics
from rest_framework import permissions
from collection import models
from utils.pagination import ProjectPageNumberPagination
from django.shortcuts import get_object_or_404
from establishment.serializers import EstablishmentBaseSerializer
from collection.serializers import common as serializers
from establishment.serializers import EstablishmentSimilarSerializer
from utils.pagination import ProjectPageNumberPagination
# Mixins
@ -53,7 +53,7 @@ class CollectionEstablishmentListView(CollectionListView):
"""Retrieve list of establishment for collection."""
lookup_field = 'slug'
pagination_class = ProjectPageNumberPagination
serializer_class = EstablishmentBaseSerializer
serializer_class = EstablishmentSimilarSerializer
def get_queryset(self):
"""

View File

@ -5,7 +5,7 @@ from rest_framework import serializers
from comment import models as comment_models
from comment.serializers import common as comment_serializers
from establishment import models
from location.serializers import AddressBaseSerializer, CitySerializer, AddressDetailSerializer
from location.serializers import AddressBaseSerializer, CitySerializer, AddressDetailSerializer, CityShortSerializer
from main.serializers import AwardSerializer, CurrencySerializer
from tag.serializers import TagBaseSerializer
from timetable.serialziers import ScheduleRUDSerializer
@ -17,6 +17,7 @@ from review.serializers import ReviewShortSerializer
class ContactPhonesSerializer(serializers.ModelSerializer):
"""Contact phone serializer"""
class Meta:
model = models.ContactPhone
fields = [
@ -26,6 +27,7 @@ class ContactPhonesSerializer(serializers.ModelSerializer):
class ContactEmailsSerializer(serializers.ModelSerializer):
"""Contact email serializer"""
class Meta:
model = models.ContactEmail
fields = [
@ -35,6 +37,7 @@ class ContactEmailsSerializer(serializers.ModelSerializer):
class SocialNetworkRelatedSerializers(serializers.ModelSerializer):
"""Social network serializers."""
class Meta:
model = models.SocialNetwork
fields = [
@ -45,7 +48,6 @@ class SocialNetworkRelatedSerializers(serializers.ModelSerializer):
class PlateSerializer(ProjectModelSerializer):
name_translated = TranslatedField()
currency = CurrencySerializer(read_only=True)
@ -191,6 +193,28 @@ class EstablishmentShortSerializer(serializers.ModelSerializer):
]
class EstablishmentProductShortSerializer(serializers.ModelSerializer):
"""SHORT Serializer for displaying info about an establishment on product page."""
establishment_type = EstablishmentTypeGeoSerializer()
establishment_subtypes = EstablishmentSubTypeBaseSerializer(many=True)
address = AddressBaseSerializer()
city = CityShortSerializer(source='address.city', allow_null=True)
class Meta:
"""Meta class."""
model = models.Establishment
fields = [
'id',
'name',
'index_name',
'slug',
'city',
'establishment_type',
'establishment_subtypes',
'address',
]
class EstablishmentProductSerializer(EstablishmentShortSerializer):
"""Serializer for displaying info about an establishment on product page."""
@ -316,6 +340,12 @@ class EstablishmentDetailSerializer(EstablishmentBaseSerializer):
]
class EstablishmentSimilarSerializer(EstablishmentBaseSerializer):
"""Serializer for Establishment model."""
address = AddressDetailSerializer(read_only=True)
class EstablishmentCommentCreateSerializer(comment_serializers.CommentSerializer):
"""Create comment serializer"""
mark = serializers.IntegerField()
@ -379,4 +409,3 @@ class EstablishmentFavoritesCreateSerializer(FavoritesCreateSerializer):
'content_object': validated_data.pop('establishment')
})
return super().create(validated_data)

View File

@ -72,7 +72,7 @@ class EstablishmentRecentReviewListView(EstablishmentListView):
class EstablishmentSimilarListView(EstablishmentListView):
"""Resource for getting a list of establishments."""
serializer_class = serializers.EstablishmentBaseSerializer
serializer_class = serializers.EstablishmentSimilarSerializer
pagination_class = EstablishmentPortionPagination
def get_queryset(self):

View File

@ -54,6 +54,20 @@ class RegionSerializer(serializers.ModelSerializer):
'country_id'
]
class CityShortSerializer(serializers.ModelSerializer):
"""Short city serializer"""
country = CountrySerializer(read_only=True)
class Meta:
"""Meta class"""
model = models.City
fields = (
'id',
'name',
'code',
'country',
)
class CitySerializer(serializers.ModelSerializer):
"""City serializer."""

View File

@ -1,8 +1,9 @@
"""Location app views."""
from rest_framework import generics
from rest_framework import permissions
from django.db.models.expressions import RawSQL
from location import models, serializers
from utils.models import get_current_locale
# Mixins
@ -37,7 +38,9 @@ class CountryListView(CountryViewMixin, generics.ListAPIView):
"""List view for model Country."""
pagination_class = None
def get_queryset(self):
qs = super().get_queryset().order_by(RawSQL("name->>%s", (get_current_locale(),)))
return qs
class CountryRetrieveView(CountryViewMixin, generics.RetrieveAPIView):
"""Retrieve view for model Country."""

View File

@ -0,0 +1,20 @@
# Generated by Django 2.2.7 on 2019-11-16 12:48
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('notification', '0002_subscriber_old_id'),
]
operations = [
migrations.AlterField(
model_name='subscriber',
name='user',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subscriber', to=settings.AUTH_USER_MODEL, verbose_name='User'),
),
]

View File

@ -74,7 +74,7 @@ class Subscriber(ProjectBaseMixin):
(USABLE, _('Usable')),
)
user = models.OneToOneField(
user = models.ForeignKey(
User,
blank=True,
null=True,

View File

@ -1,7 +1,5 @@
from pprint import pprint
from django.db.models import Count
from transfer.models import EmailAddresses, NewsletterSubscriber
from transfer.serializers.notification import SubscriberSerializer, NewsletterSubscriberSerializer
@ -25,14 +23,14 @@ def transfer_newsletter_subscriber():
'email_address__ip',
'email_address__country_code',
'email_address__locale',
'created_at',
'updated_at',
)
# serialized_data = NewsletterSubscriberSerializer(data=list(queryset.values()), many=True)
# if serialized_data.is_valid():
# serialized_data.save()
# else:
# pprint(f'NewsletterSubscriber serializer errors: {serialized_data.errors}')
serialized_data = NewsletterSubscriberSerializer(data=list(queryset), many=True)
if serialized_data.is_valid():
serialized_data.save()
else:
pprint(f'NewsletterSubscriber serializer errors: {serialized_data.errors}')
data_types = {

View File

@ -82,7 +82,12 @@ class ProductQuerySet(models.QuerySet):
def with_extended_related(self):
"""Returns qs with almost all related objects."""
return self.with_base_related() \
.prefetch_related('tags', 'standards', 'classifications', 'classifications__standard',
.prefetch_related('tags', 'tags__category', 'tags__category__country',
'standards', 'classifications', 'classifications__standard',
'establishment__address', 'establishment__establishment_type',
'establishment__address__city', 'establishment__address__city__country',
'establishment__establishment_subtypes', 'product_gallery',
'gallery', 'product_type', 'subtypes',
'classifications__classification_type', 'classifications__tags') \
.select_related('wine_region', 'wine_sub_region')
@ -270,15 +275,14 @@ class Product(TranslatedFieldsMixin, BaseAttributes):
@property
def related_tags(self):
return self.tags.exclude(
category__index_name__in=['sugar-content', 'wine-color', 'bottles-produced',
'serial-number', 'grape-variety'])
return self.tags.exclude(category__index_name__in=['sugar-content', 'wine-color', 'bottles-produced',
'serial-number', 'grape-variety']).prefetch_related('category')
@property
def display_name(self):
name = f'{self.name} ' \
f'({self.vintage if self.vintage else "BSA"})'
if self.establishment.name:
if self.establishment and self.establishment.name:
name = f'{self.establishment.name} - ' + name
return name

View File

@ -4,7 +4,7 @@ from rest_framework import serializers
from comment.models import Comment
from comment.serializers import CommentSerializer
from establishment.serializers import EstablishmentShortSerializer, EstablishmentProductSerializer
from establishment.serializers import EstablishmentShortSerializer, EstablishmentProductSerializer, EstablishmentProductShortSerializer
from gallery.models import Image
from product import models
from review.serializers import ReviewShortSerializer
@ -12,13 +12,13 @@ from utils import exceptions as utils_exceptions
from utils.serializers import TranslatedField, FavoritesCreateSerializer
from main.serializers import AwardSerializer
from location.serializers import WineRegionBaseSerializer, WineSubRegionBaseSerializer
from tag.serializers import TagBaseSerializer, TagCategoryShortSerializer
from tag.serializers import TagBaseSerializer, TagCategoryProductSerializer
class ProductTagSerializer(TagBaseSerializer):
"""Serializer for model Tag."""
category = TagCategoryShortSerializer(read_only=True)
category = TagCategoryProductSerializer(read_only=True)
class Meta(TagBaseSerializer.Meta):
"""Meta class."""
@ -88,10 +88,10 @@ class ProductBaseSerializer(serializers.ModelSerializer):
name = serializers.CharField(source='display_name', read_only=True)
product_type = ProductTypeBaseSerializer(read_only=True)
subtypes = ProductSubTypeBaseSerializer(many=True, read_only=True)
establishment_detail = EstablishmentShortSerializer(source='establishment', read_only=True)
establishment_detail = EstablishmentProductShortSerializer(source='establishment', read_only=True)
tags = ProductTagSerializer(source='related_tags', many=True, read_only=True)
wine_region = WineRegionBaseSerializer(read_only=True)
wine_colors = TagBaseSerializer(many=True, read_only=True)
wine_colors = ProductTagSerializer(many=True, read_only=True)
preview_image_url = serializers.URLField(source='preview_main_image_url',
allow_null=True,
read_only=True)
@ -120,6 +120,7 @@ class ProductBaseSerializer(serializers.ModelSerializer):
class ProductDetailSerializer(ProductBaseSerializer):
"""Product detail serializer."""
description_translated = TranslatedField()
establishment_detail = EstablishmentShortSerializer(source='establishment', read_only=True)
review = ReviewShortSerializer(source='last_published_review', read_only=True)
awards = AwardSerializer(many=True, read_only=True)
classifications = ProductClassificationBaseSerializer(many=True, read_only=True)

View File

@ -26,6 +26,10 @@ class ProductListView(ProductBaseView, generics.ListAPIView):
serializer_class = serializers.ProductBaseSerializer
filter_class = filters.ProductFilterSet
def get_queryset(self):
qs = super().get_queryset().with_extended_related()
return qs
class ProductDetailView(ProductBaseView, generics.RetrieveAPIView):
"""Detail view fro model Product."""

View File

@ -95,13 +95,15 @@ class ProductDocument(Document):
},
multi=True
)
name = fields.TextField(attr='display_name', analyzer='english')
name_ru = fields.TextField(attr='display_name', analyzer='russian')
name_fr = fields.TextField(attr='display_name', analyzer='french')
class Django:
model = models.Product
fields = (
'id',
'category',
'name',
'available',
'public_mark',
'slug',

View File

@ -39,7 +39,8 @@ class ProductSubtypeDocumentSerializer(serializers.Serializer):
id = serializers.IntegerField()
name_translated = serializers.SerializerMethodField()
get_name_translated = lambda obj: get_translated_value(obj.name)
def get_name_translated(self, obj):
return get_translated_value(obj.name)
class WineRegionCountryDocumentSerialzer(serializers.Serializer):
@ -64,6 +65,9 @@ class WineRegionDocumentSerializer(serializers.Serializer):
name = serializers.CharField()
country = WineRegionCountryDocumentSerialzer(allow_null=True)
def get_attribute(self, instance):
return instance.wine_region if instance and instance.wine_region else None
class WineColorDocumentSerializer(serializers.Serializer):
"""Wine color ES document serializer,"""
@ -79,6 +83,18 @@ class WineColorDocumentSerializer(serializers.Serializer):
return get_translated_value(obj.label)
class ProductTypeDocumentSerializer(serializers.Serializer):
"""Product type ES document serializer."""
id = serializers.IntegerField()
index_name = serializers.CharField()
name_translated = serializers.SerializerMethodField()
@staticmethod
def get_name_translated(obj):
return get_translated_value(obj.name)
class ProductEstablishmentDocumentSerializer(serializers.Serializer):
"""Related to Product Establishment ES document serializer."""
@ -199,16 +215,12 @@ class ProductDocumentSerializer(DocumentSerializer):
"""Product document serializer"""
tags = TagsDocumentSerializer(many=True)
subtypes = ProductSubtypeDocumentSerializer(many=True)
subtypes = ProductSubtypeDocumentSerializer(many=True, allow_null=True)
wine_region = WineRegionDocumentSerializer(allow_null=True)
wine_colors = WineColorDocumentSerializer(many=True)
product_type = serializers.SerializerMethodField()
product_type = ProductTypeDocumentSerializer(allow_null=True)
establishment_detail = ProductEstablishmentDocumentSerializer(source='establishment', allow_null=True)
@staticmethod
def get_product_type(obj):
return get_translated_value(obj.product_type.name if obj.product_type else {})
class Meta:
"""Meta class."""

View File

@ -91,8 +91,6 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet):
'boost': 4},
'transliterated_name': {'fuzziness': 'auto:2,5',
'boost': 3},
'index_name': {'fuzziness': 'auto:2,5',
'boost': 2},
'description': {'fuzziness': 'auto:2,5'},
}
translated_search_fields = (
@ -199,7 +197,7 @@ class ProductDocumentViewSet(BaseDocumentViewSet):
"""Product document ViewSet."""
document = ProductDocument
lookup_field = 'slug'
# lookup_field = 'slug'
pagination_class = ProjectMobilePagination
permission_classes = (permissions.AllowAny,)
serializer_class = serializers.ProductDocumentSerializer
@ -212,19 +210,22 @@ class ProductDocumentViewSet(BaseDocumentViewSet):
filter_backends = [
FilteringFilterBackend,
filters.CustomSearchFilterBackend,
GeoSpatialFilteringFilterBackend,
DefaultOrderingFilterBackend,
# GeoSpatialFilteringFilterBackend,
# DefaultOrderingFilterBackend,
]
search_fields = {
'name': {'fuzziness': 'auto:2,5',
'boost': 4},
'boost': 8},
'name_ru': {'fuzziness': 'auto:2,5',
'boost': 6},
'name_fr': {'fuzziness': 'auto:2,5',
'boost': 7},
'transliterated_name': {'fuzziness': 'auto:2,5',
'boost': 3},
'index_name': {'fuzziness': 'auto:2,5',
'boost': 2},
'description': {'fuzziness': 'auto:2,5'},
}
translated_search_fields = (
'description',
)
@ -248,7 +249,7 @@ class ProductDocumentViewSet(BaseDocumentViewSet):
'for_establishment': {
'field': 'establishment.slug',
},
'type': {
'product_type': {
'field': 'product_type.index_name',
},
'subtype': {
@ -259,5 +260,5 @@ class ProductDocumentViewSet(BaseDocumentViewSet):
]
}
}
geo_spatial_filter_fields = {
}
# geo_spatial_filter_fields = {
# }

View File

@ -31,13 +31,18 @@ class TagCategoryFilterSet(TagsBaseFilterSet):
"""TagCategory filterset."""
establishment_type = filters.CharFilter(method='by_establishment_type')
product_type = filters.CharFilter(method='by_product_type')
class Meta:
"""Meta class."""
model = models.TagCategory
fields = ('type',
'establishment_type', )
'establishment_type',
'product_type', )
def by_product_type(self, queryset, name, value):
return queryset.by_product_type(value)
# todo: filter by establishment type
def by_establishment_type(self, queryset, name, value):

View File

@ -101,6 +101,10 @@ class TagCategoryQuerySet(models.QuerySet):
"""Filter by establishment type index name."""
return self.filter(establishment_types__index_name=index_name)
def by_product_type(self, index_name):
"""Filter by product type index name."""
return self.filter(product_types__index_name=index_name)
def with_tags(self, switcher=True):
"""Filter by existing tags."""
return self.exclude(tags__isnull=switcher)

View File

@ -37,6 +37,21 @@ class TagBackOfficeSerializer(TagBaseSerializer):
'category'
)
class TagCategoryProductSerializer(serializers.ModelSerializer):
"""SHORT Serializer for TagCategory"""
label_translated = TranslatedField()
class Meta:
"""Meta class."""
model = models.TagCategory
fields = (
'id',
'label_translated',
'index_name',
)
class TagCategoryBaseSerializer(serializers.ModelSerializer):
"""Serializer for model TagCategory."""

View File

@ -1,3 +1,4 @@
from django.db import IntegrityError
from rest_framework import serializers
from account.models import User
@ -41,10 +42,10 @@ class NewsletterSubscriberSerializer(serializers.Serializer):
id = serializers.IntegerField()
email_address__email = serializers.CharField()
email_address__account_id = serializers.IntegerField(allow_null=True)
email_address__ip = serializers.CharField(allow_null=True)
email_address__country_code = serializers.CharField(allow_null=True)
email_address__locale = serializers.CharField(allow_null=True)
created_at = serializers.DateTimeField(format='%m-%d-%Y %H:%M:%S')
email_address__ip = serializers.CharField(allow_null=True, allow_blank=True)
email_address__country_code = serializers.CharField(allow_null=True, allow_blank=True)
email_address__locale = serializers.CharField(allow_null=True, allow_blank=True)
updated_at = serializers.DateTimeField(format='%m-%d-%Y %H:%M:%S')
def validate(self, data):
data.update({
@ -53,18 +54,28 @@ class NewsletterSubscriberSerializer(serializers.Serializer):
'ip_address': data.pop('email_address__ip'),
'country_code': data.pop('email_address__country_code'),
'locale': data.pop('email_address__locale'),
'created': data.pop('created_at'),
'created': data.pop('updated_at'),
'user_id': self.get_user(data),
})
data.pop('email_address__account_id')
return data
# def create(self, validated_data):
# obj, _ = Review.objects.update_or_create(
# old_id=validated_data['old_id'],
# defaults=validated_data,
# )
# return obj
def create(self, validated_data):
try:
obj = Subscriber.objects.get(email=validated_data['email'])
except Subscriber.DoesNotExist:
obj = Subscriber.objects.create(**validated_data)
else:
current_data = obj.created
if validated_data['created'] > current_data:
obj.ip_address = validated_data['ip_address']
obj.locale = validated_data['locale']
obj.country_code = validated_data['country_code']
obj.old_id = validated_data['old_id']
obj.created = validated_data['created']
obj.user_id = validated_data['user_id']
obj.save()
return obj
@staticmethod
def get_user(data):
@ -73,6 +84,6 @@ class NewsletterSubscriberSerializer(serializers.Serializer):
return None
user = User.objects.filter(old_id=data['email_address__account_id']).first()
if not user:
raise ValueError(f"User account not found with old_id {data['email_address__account_id']}")
return user.id
if user:
return user.id
return None