gm-127: finished

This commit is contained in:
Anatoly 2019-09-26 14:19:01 +03:00
parent 0e0d6e318e
commit c4d8b89a8d
5 changed files with 90 additions and 21 deletions

View File

@ -93,15 +93,20 @@ class EstablishmentQuerySet(models.QuerySet):
"""
return self.filter(is_publish=True)
def annotate_distance(self, point: Point):
def has_published_reviews(self):
"""
Return QuerySet establishments with published reviews.
"""
return self.filter(reviews__status=Review.READY,)
def annotate_distance(self, point: Point = None):
"""
Return QuerySet with annotated field - distance
Description:
"""
return self.annotate(distance=models.Value(
DistanceMeasure(Distance('address__coordinates', point, srid=4236)).m,
output_field=models.FloatField()))
return self.annotate(distance=Distance('address__coordinates', point,
srid=settings.GEO_DEFAULT_SRID))
def annotate_intermediate_public_mark(self):
"""
@ -110,8 +115,8 @@ class EstablishmentQuerySet(models.QuerySet):
If establishments in collection POP and its mark is null, then
intermediate_mark is set to 10;
"""
return self.annotate(intermediate_public_mark=models.Case(
models.When(
return self.annotate(intermediate_public_mark=Case(
When(
collections__collection_type=Collection.POP,
public_mark__isnull=True,
then=settings.DEFAULT_ESTABLISHMENT_PUBLIC_MARK
@ -125,8 +130,8 @@ class EstablishmentQuerySet(models.QuerySet):
Description:
Similarity mark determined by comparison with compared establishment mark
"""
return self.annotate(mark_similarity=models.ExpressionWrapper(
mark - models.F('intermediate_public_mark'),
return self.annotate(mark_similarity=ExpressionWrapper(
mark - F('intermediate_public_mark'),
output_field=models.FloatField()
))
@ -154,6 +159,21 @@ class EstablishmentQuerySet(models.QuerySet):
else:
return self.none()
def last_reviewed(self, point: Point):
"""
Return QuerySet with last reviewed establishments.
:param point: location Point object, needs to ordering
"""
subquery_filter_by_distance = Subquery(
self.filter(image_url__isnull=False, public_mark__gte=10)
.has_published_reviews()
.annotate_distance(point=point)
.order_by('distance')[:settings.LIMITING_QUERY_NUMBER]
.values('id')
)
return self.filter(id__in=subquery_filter_by_distance) \
.order_by('-reviews__published_at')
def prefetch_actual_employees(self):
"""Prefetch actual employees."""
return self.prefetch_related(
@ -169,8 +189,8 @@ class EstablishmentQuerySet(models.QuerySet):
favorite_establishments = user.favorites.by_content_type(app_label='establishment',
model='establishment') \
.values_list('object_id', flat=True)
return self.annotate(in_favorites=models.Case(
models.When(
return self.annotate(in_favorites=Case(
When(
id__in=favorite_establishments,
then=True),
default=False,
@ -194,7 +214,7 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
name = models.CharField(_('name'), max_length=255, default='')
name_translated = models.CharField(_('Transliterated name'),
max_length=255, default='')
max_length=255, default='')
description = TJSONField(blank=True, null=True, default=None,
verbose_name=_('description'),
help_text='{"en-GB":"some text"}')
@ -308,6 +328,19 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
return [{'id': tag.metadata.id,
'label': tag.metadata.label} for tag in self.tags.all()]
@property
def last_published_review(self):
"""Return last published review"""
return self.reviews.published()\
.order_by('-published_at').first()
@property
def location(self):
"""
Return Point object of establishment location
"""
return self.address.coordinates
class Position(BaseAttributes, TranslatedFieldsMixin):
"""Position model."""

View File

@ -8,6 +8,8 @@ app_name = 'establishment'
urlpatterns = [
path('', views.EstablishmentListView.as_view(), name='list'),
path('tags/', views.EstablishmentTagListView.as_view(), name='tags'),
path('recent-reviews/', views.EstablishmentRecentReviewListView.as_view(),
name='recent-reviews'),
path('<slug:slug>/', views.EstablishmentRetrieveView.as_view(), name='detail'),
path('<slug:slug>/similar/', views.EstablishmentSimilarListView.as_view(), name='similar'),
path('<slug:slug>/comments/', views.EstablishmentCommentListView.as_view(), name='list-comments'),

View File

@ -11,6 +11,6 @@ class EstablishmentMixin:
permission_classes = (permissions.AllowAny,)
def get_queryset(self):
"""Overrided method 'get_queryset'."""
"""Overridden method 'get_queryset'."""
return models.Establishment.objects.published() \
.prefetch_actual_employees()

View File

@ -27,6 +27,25 @@ class EstablishmentListView(EstablishmentMixin, generics.ListAPIView):
.annotate_in_favorites(user=self.request.user)
class EstablishmentRecentReviewListView(EstablishmentListView):
"""List view for last reviewed establishments."""
pagination_class = EstablishmentPortionPagination
def get_queryset(self):
"""Overridden method 'get_queryset'."""
qs = super().get_queryset()
user_ip = methods.get_user_ip(self.request)
query_params = self.request.query_params
if 'longitude' in query_params and 'latitude' in query_params:
longitude, latitude = query_params.get('longitude'), query_params.get('latitude')
else:
longitude, latitude = methods.determine_coordinates(user_ip)
if not longitude or not latitude:
return qs.none()
point = Point(x=float(longitude), y=float(latitude), srid=settings.GEO_DEFAULT_SRID)
return qs.last_reviewed(point=point)
class EstablishmentSimilarListView(EstablishmentListView):
"""Resource for getting a list of establishments."""
serializer_class = serializers.EstablishmentListSerializer

View File

@ -327,15 +327,30 @@ FCM_DJANGO_SETTINGS = {
# Thumbnail settings
THUMBNAIL_ALIASES = {
'': {
'tiny': {'size': (100, 0), },
'small': {'size': (480, 0), },
'middle': {'size': (700, 0), },
'large': {'size': (1500, 0), },
'default': {'size': (300, 200), 'crop': True},
'gallery': {'size': (240, 160), 'crop': True},
'establishment_preview': {'size': (300, 280), 'crop': True},
}
'news_preview': {
'web': {'size': (300, 260), }
},
'news_promo_horizontal': {
'web': {'size': (1900, 600), },
'mobile': {'size': (375, 260), },
},
'news_tile_horizontal': {
'web': {'size': (300, 275), },
'mobile': {'size': (343, 180), },
},
'news_tile_vertical': {
'web': {'size': (300, 380), },
},
'news_highlight_vertical': {
'web': {'size': (460, 630), },
},
'news_editor': {
'web': {'size': (940, 430), }, # при загрузке через контент эдитор
'mobile': {'size': (343, 260), }, # через контент эдитор в мобильном браузерe
},
'avatar_comments': {
'web': {'size': (116, 116), },
},
}
# Password reset