Merge branch 'develop' into feature/permission_liquor

This commit is contained in:
Виктор Гладких 2019-12-10 18:37:36 +03:00
commit e3cf808e3d
8 changed files with 125 additions and 27 deletions

View File

@ -24,8 +24,8 @@ from collection.models import Collection
from location.models import Address from location.models import Address
from location.models import WineOriginAddressMixin from location.models import WineOriginAddressMixin
from main.models import Award, Currency from main.models import Award, Currency
from tag.models import Tag
from review.models import Review from review.models import Review
from tag.models import Tag
from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin, from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin,
TranslatedFieldsMixin, BaseAttributes, GalleryModelMixin, TranslatedFieldsMixin, BaseAttributes, GalleryModelMixin,
IntermediateGalleryModelMixin, HasTagsMixin, IntermediateGalleryModelMixin, HasTagsMixin,
@ -209,23 +209,34 @@ class EstablishmentQuerySet(models.QuerySet):
""" """
return self.annotate(mark_similarity=ExpressionWrapper( return self.annotate(mark_similarity=ExpressionWrapper(
mark - F('intermediate_public_mark'), mark - F('intermediate_public_mark'),
output_field=models.FloatField() output_field=models.FloatField(default=0)
)) ))
def similar(self, establishment_slug: str): def similar_base(self, establishment):
filters = {
'reviews__status': Review.READY,
'establishment_type': establishment.establishment_type,
}
if establishment.establishment_subtypes.exists():
filters.update({'establishment_subtypes__in': establishment.establishment_subtypes.all()})
return self.exclude(id=establishment.id) \
.filter(**filters) \
.annotate_distance(point=establishment.location)
def similar_restaurants(self, slug):
""" """
Return QuerySet with objects that similar to Establishment. Return QuerySet with objects that similar to Restaurant.
:param establishment_slug: str Establishment slug :param restaurant_slug: str Establishment slug
""" """
establishment_qs = self.filter(slug=establishment_slug, restaurant_qs = self.filter(slug=slug,
public_mark__isnull=False) public_mark__isnull=False)
if establishment_qs.exists(): if restaurant_qs.exists():
establishment = establishment_qs.first() establishment = restaurant_qs.first()
subquery_filter_by_distance = Subquery( subquery_filter_by_distance = Subquery(
self.exclude(slug=establishment_slug) self.similar_base(establishment)
.filter(image_url__isnull=False, public_mark__gte=10) .filter(public_mark__gte=10,
.has_published_reviews() establishment_gallery__is_main=True)
.annotate_distance(point=establishment.location)
.order_by('distance')[:settings.LIMITING_QUERY_OBJECTS] .order_by('distance')[:settings.LIMITING_QUERY_OBJECTS]
.values('id') .values('id')
) )
@ -234,6 +245,36 @@ class EstablishmentQuerySet(models.QuerySet):
.annotate_mark_similarity(mark=establishment.public_mark) \ .annotate_mark_similarity(mark=establishment.public_mark) \
.order_by('mark_similarity') \ .order_by('mark_similarity') \
.distinct('mark_similarity', 'id') .distinct('mark_similarity', 'id')
def by_wine_region(self, wine_region):
"""
Return filtered QuerySet by wine region in wine origin.
:param wine_region: wine region.
"""
return self.filter(wine_origin__wine_region=wine_region).distinct()
def by_wine_sub_region(self, wine_sub_region):
"""
Return filtered QuerySet by wine region in wine origin.
:param wine_sub_region: wine sub region.
"""
return self.filter(wine_origin__wine_sub_region=wine_sub_region).distinct()
def similar_wineries(self, slug: str):
"""
Return QuerySet with objects that similar to Winery.
:param establishment_slug: str Establishment slug
"""
winery_qs = self.filter(slug=slug)
if winery_qs.exists():
winery = winery_qs.first()
return self.similar_base(winery) \
.order_by(F('wine_origins__wine_region').asc(),
F('wine_origins__wine_sub_region').asc()) \
.annotate_distance(point=winery.location) \
.order_by('distance') \
.distinct('distance', 'wine_origins__wine_region',
'wine_origins__wine_sub_region', 'id')
else: else:
return self.none() return self.none()
@ -457,15 +498,9 @@ class Establishment(GalleryModelMixin, ProjectBaseMixin, URLImageMixin,
def visible_tags(self): def visible_tags(self):
return super().visible_tags \ return super().visible_tags \
.exclude(category__index_name__in=['guide', 'collection', 'purchased_item', .exclude(category__index_name__in=['guide', 'collection', 'purchased_item',
'business_tag', 'business_tags_de']) \ 'business_tag', 'business_tags_de', 'tag'])
.exclude(value__in=['rss', 'rss_selection'])
# todo: recalculate toque_number # todo: recalculate toque_number
@property
def visible_tags_detail(self):
"""Removes some tags from detail Establishment representation"""
return self.visible_tags.exclude(category__index_name__in=['tag'])
def recalculate_toque_number(self): def recalculate_toque_number(self):
toque_number = 0 toque_number = 0
if self.address and self.public_mark: if self.address and self.public_mark:
@ -830,6 +865,25 @@ class ContactEmail(models.Model):
return f'{self.email}' return f'{self.email}'
#
# class Wine(TranslatedFieldsMixin, models.Model):
# """Wine model."""
# establishment = models.ForeignKey(
# 'establishment.Establishment', verbose_name=_('establishment'),
# on_delete=models.CASCADE)
# bottles = models.IntegerField(_('bottles'))
# price_min = models.DecimalField(
# _('price min'), max_digits=14, decimal_places=2)
# price_max = models.DecimalField(
# _('price max'), max_digits=14, decimal_places=2)
# by_glass = models.BooleanField(_('by glass'))
# price_glass_min = models.DecimalField(
# _('price min'), max_digits=14, decimal_places=2)
# price_glass_max = models.DecimalField(
# _('price max'), max_digits=14, decimal_places=2)
#
class Plate(TranslatedFieldsMixin, models.Model): class Plate(TranslatedFieldsMixin, models.Model):
"""Plate model.""" """Plate model."""
STR_FIELD_NAME = 'name' STR_FIELD_NAME = 'name'

View File

@ -9,7 +9,6 @@ urlpatterns = [
path('', views.EstablishmentListView.as_view(), name='list'), path('', views.EstablishmentListView.as_view(), name='list'),
path('recent-reviews/', views.EstablishmentRecentReviewListView.as_view(), path('recent-reviews/', views.EstablishmentRecentReviewListView.as_view(),
name='recent-reviews'), name='recent-reviews'),
path('slug/<slug:slug>/similar/', views.EstablishmentSimilarListView.as_view(), name='similar'),
path('slug/<slug:slug>/comments/', views.EstablishmentCommentListView.as_view(), name='list-comments'), path('slug/<slug:slug>/comments/', views.EstablishmentCommentListView.as_view(), name='list-comments'),
path('slug/<slug:slug>/comments/create/', views.EstablishmentCommentCreateView.as_view(), path('slug/<slug:slug>/comments/create/', views.EstablishmentCommentCreateView.as_view(),
name='create-comment'), name='create-comment'),
@ -17,4 +16,11 @@ urlpatterns = [
name='rud-comment'), name='rud-comment'),
path('slug/<slug:slug>/favorites/', views.EstablishmentFavoritesCreateDestroyView.as_view(), path('slug/<slug:slug>/favorites/', views.EstablishmentFavoritesCreateDestroyView.as_view(),
name='create-destroy-favorites'), name='create-destroy-favorites'),
# similar establishments
path('slug/<slug:slug>/similar/', views.RestaurantSimilarListView.as_view(),
name='similar-restaurants'),
path('slug/<slug:slug>/similar/wineries/', views.WinerySimilarListView.as_view(),
name='similar-restaurants'),
] ]

View File

@ -77,16 +77,28 @@ class EstablishmentRecentReviewListView(EstablishmentListView):
return qs.last_reviewed(point=point) return qs.last_reviewed(point=point)
class EstablishmentSimilarListView(EstablishmentListView): class EstablishmentSimilarList(EstablishmentListView):
"""Resource for getting a list of establishments.""" """Resource for getting a list of similar establishments."""
serializer_class = serializers.EstablishmentSimilarSerializer serializer_class = serializers.EstablishmentSimilarSerializer
pagination_class = EstablishmentPortionPagination pagination_class = EstablishmentPortionPagination
class RestaurantSimilarListView(EstablishmentSimilarList):
"""Resource for getting a list of similar restaurants."""
def get_queryset(self): def get_queryset(self):
"""Override get_queryset method""" """Override get_queryset method"""
qs = super().get_queryset() return EstablishmentMixinView.get_queryset(self) \
return qs.similar(establishment_slug=self.kwargs.get('slug')) .similar_restaurants(slug=self.kwargs.get('slug'))
class WinerySimilarListView(EstablishmentSimilarList):
"""Resource for getting a list of similar wineries."""
def get_queryset(self):
"""Override get_queryset method"""
return EstablishmentMixinView.get_queryset(self) \
.similar_wineries(slug=self.kwargs.get('slug'))
class EstablishmentTypeListView(generics.ListAPIView): class EstablishmentTypeListView(generics.ListAPIView):

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.7 on 2019-12-10 12:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('news', '0037_auto_20191129_1320'),
]
operations = [
migrations.AddField(
model_name='news',
name='backoffice_title',
field=models.TextField(default=None, null=True, verbose_name='Title for searching via BO'),
),
]

View File

@ -168,6 +168,8 @@ class News(GalleryModelMixin, BaseAttributes, TranslatedFieldsMixin, HasTagsMixi
title = TJSONField(blank=True, null=True, default=None, title = TJSONField(blank=True, null=True, default=None,
verbose_name=_('title'), verbose_name=_('title'),
help_text='{"en-GB":"some text"}') help_text='{"en-GB":"some text"}')
backoffice_title = models.TextField(null=True, default=None,
verbose_name=_('Title for searching via BO'))
subtitle = TJSONField(blank=True, null=True, default=None, subtitle = TJSONField(blank=True, null=True, default=None,
verbose_name=_('subtitle'), verbose_name=_('subtitle'),
help_text='{"en-GB":"some text"}') help_text='{"en-GB":"some text"}')

View File

@ -169,9 +169,13 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer):
fields = NewsBaseSerializer.Meta.fields + ( fields = NewsBaseSerializer.Meta.fields + (
'title', 'title',
'backoffice_title',
'subtitle', 'subtitle',
'is_published', 'is_published',
) )
extra_kwargs = {
'backoffice_title': {'allow_null': False},
}
class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer, class NewsBackOfficeDetailSerializer(NewsBackOfficeBaseSerializer,

View File

@ -17,6 +17,7 @@ class NewsDocument(Document):
'name': fields.KeywordField()}) 'name': fields.KeywordField()})
title = fields.ObjectField(attr='title_indexing', title = fields.ObjectField(attr='title_indexing',
properties=OBJECT_FIELD_PROPERTIES) properties=OBJECT_FIELD_PROPERTIES)
backoffice_title = fields.TextField(analyzer='english')
subtitle = fields.ObjectField(attr='subtitle_indexing', subtitle = fields.ObjectField(attr='subtitle_indexing',
properties=OBJECT_FIELD_PROPERTIES) properties=OBJECT_FIELD_PROPERTIES)
description = fields.ObjectField(attr='description_indexing', description = fields.ObjectField(attr='description_indexing',

View File

@ -314,7 +314,8 @@ class MobileEstablishmentDocumentViewSet(EstablishmentDocumentViewSet):
filter_backends = [ filter_backends = [
FilteringFilterBackend, FilteringFilterBackend,
filters.CustomSearchFilterBackend, filters.CustomSearchFilterBackend,
GeoSpatialFilteringFilterBackend, filters.CustomGeoSpatialFilteringFilterBackend,
GeoSpatialOrderingFilterBackend,
] ]