Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
littlewolf 2019-12-20 13:04:30 +03:00
commit aa7ebf4d4c
4 changed files with 108 additions and 70 deletions

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.7 on 2019-12-20 09:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('establishment', '0067_auto_20191122_1244'),
]
operations = [
migrations.AlterField(
model_name='establishment',
name='schedule',
field=models.ManyToManyField(blank=True, related_name='schedule', to='timetable.Timetable', verbose_name='Establishment schedule'),
),
]

View File

@ -221,15 +221,16 @@ class EstablishmentQuerySet(models.QuerySet):
Return filtered QuerySet by base filters. Return filtered QuerySet by base filters.
Filters including: Filters including:
1 Filter by type (and subtype) establishment. 1 Filter by type (and subtype) establishment.
2 Filter by published Review. 2 With annotated distance.
3 With annotated distance. 3 By country
""" """
filters = { filters = {
'reviews__status': Review.READY,
'establishment_type': establishment.establishment_type, 'establishment_type': establishment.establishment_type,
'address__city__country': establishment.address.city.country
} }
if establishment.establishment_subtypes.exists(): if establishment.establishment_subtypes.exists():
filters.update({'establishment_subtypes__in': establishment.establishment_subtypes.all()}) filters.update({'establishment_subtypes__in': establishment.establishment_subtypes.all()})
return self.exclude(id=establishment.id) \ return self.exclude(id=establishment.id) \
.filter(**filters) \ .filter(**filters) \
.annotate_distance(point=establishment.location) .annotate_distance(point=establishment.location)
@ -248,17 +249,16 @@ class EstablishmentQuerySet(models.QuerySet):
.values('id') .values('id')
) )
def similar_restaurants(self, slug): def similar_restaurants(self, restaurant):
""" """
Return QuerySet with objects that similar to Restaurant. Return QuerySet with objects that similar to Restaurant.
:param slug: str restaurant slug :param restaurant: Establishment instance.
""" """
restaurant_qs = self.filter(slug=slug)
if restaurant_qs.exists():
restaurant = restaurant_qs.first()
ids_by_subquery = self.similar_base_subquery( ids_by_subquery = self.similar_base_subquery(
establishment=restaurant, establishment=restaurant,
filters={ filters={
'reviews__status': Review.READY,
'public_mark__gte': 10, 'public_mark__gte': 10,
'establishment_gallery__is_main': True, 'establishment_gallery__is_main': True,
} }
@ -268,8 +268,6 @@ class EstablishmentQuerySet(models.QuerySet):
.annotate_mark_similarity(mark=restaurant.public_mark) \ .annotate_mark_similarity(mark=restaurant.public_mark) \
.order_by('mark_similarity') \ .order_by('mark_similarity') \
.distinct('mark_similarity', 'id') .distinct('mark_similarity', 'id')
else:
return self.none()
def same_subtype(self, establishment): def same_subtype(self, establishment):
"""Annotate flag same subtype.""" """Annotate flag same subtype."""
@ -282,21 +280,17 @@ class EstablishmentQuerySet(models.QuerySet):
output_field=models.BooleanField(default=False) output_field=models.BooleanField(default=False)
)) ))
def similar_artisans_producers(self, slug): def similar_artisans_producers(self, establishment):
""" """
Return QuerySet with objects that similar to Artisan/Producer(s). Return QuerySet with objects that similar to Artisan/Producer(s).
:param slug: str artisan/producer slug :param establishment: Establishment instance
""" """
establishment_qs = self.filter(slug=slug)
if establishment_qs.exists():
establishment = establishment_qs.first()
return self.similar_base(establishment) \ return self.similar_base(establishment) \
.same_subtype(establishment) \ .same_subtype(establishment) \
.has_published_reviews() \
.order_by(F('same_subtype').desc(), .order_by(F('same_subtype').desc(),
F('distance').asc()) \ F('distance').asc()) \
.distinct('same_subtype', 'distance', 'id') .distinct('same_subtype', 'distance', 'id')
else:
return self.none()
def by_wine_region(self, wine_region): def by_wine_region(self, wine_region):
""" """
@ -312,23 +306,19 @@ class EstablishmentQuerySet(models.QuerySet):
""" """
return self.filter(wine_origin__wine_sub_region=wine_sub_region).distinct() return self.filter(wine_origin__wine_sub_region=wine_sub_region).distinct()
def similar_wineries(self, slug: str): def similar_wineries(self, winery):
""" """
Return QuerySet with objects that similar to Winery. Return QuerySet with objects that similar to Winery.
:param establishment_slug: str Establishment slug :param winery: Establishment instance
""" """
winery_qs = self.filter(slug=slug)
if winery_qs.exists():
winery = winery_qs.first()
return self.similar_base(winery) \ return self.similar_base(winery) \
.order_by(F('wine_origins__wine_region').asc(), .order_by(F('wine_origins__wine_region').asc(),
F('wine_origins__wine_sub_region').asc()) \ F('wine_origins__wine_sub_region').asc(),
.annotate_distance(point=winery.location) \ F('distance').asc()) \
.order_by('distance') \ .distinct('wine_origins__wine_region',
.distinct('distance', 'wine_origins__wine_region', 'wine_origins__wine_sub_region',
'wine_origins__wine_sub_region', 'id') 'distance',
else: 'id')
return self.none()
def last_reviewed(self, point: Point): def last_reviewed(self, point: Point):
""" """
@ -483,7 +473,7 @@ class Establishment(GalleryModelMixin, ProjectBaseMixin, URLImageMixin,
booking = models.URLField(blank=True, null=True, default=None, max_length=255, booking = models.URLField(blank=True, null=True, default=None, max_length=255,
verbose_name=_('Booking URL')) verbose_name=_('Booking URL'))
is_publish = models.BooleanField(default=False, verbose_name=_('Publish status')) is_publish = models.BooleanField(default=False, verbose_name=_('Publish status'))
schedule = models.ManyToManyField(to='timetable.Timetable', schedule = models.ManyToManyField(to='timetable.Timetable', blank=True,
verbose_name=_('Establishment schedule'), verbose_name=_('Establishment schedule'),
related_name='schedule') related_name='schedule')
# holidays_from = models.DateTimeField(verbose_name=_('Holidays from'), # holidays_from = models.DateTimeField(verbose_name=_('Holidays from'),
@ -532,12 +522,6 @@ class Establishment(GalleryModelMixin, ProjectBaseMixin, URLImageMixin,
def __str__(self): def __str__(self):
return f'id:{self.id}-{self.name}' return f'id:{self.id}-{self.name}'
def clean_fields(self, exclude=None):
super().clean_fields(exclude)
if self.purchased_products.filter(product_type__index_name='souvenir').exists():
raise ValidationError(
_('Only souvenirs.'))
def delete(self, using=None, keep_parents=False): def delete(self, using=None, keep_parents=False):
"""Overridden delete method""" """Overridden delete method"""
# Delete all related companies # Delete all related companies

View File

@ -46,6 +46,19 @@ class EstablishmentSimilarView(EstablishmentListView):
serializer_class = serializers.EstablishmentSimilarSerializer serializer_class = serializers.EstablishmentSimilarSerializer
pagination_class = PortionPagination pagination_class = PortionPagination
def get_base_object(self):
"""
Return base establishment instance for a getting list of similar establishments.
"""
establishment = get_object_or_404(models.Establishment.objects.all(),
slug=self.kwargs.get('slug'))
return establishment
def get_queryset(self):
"""Overridden get_queryset method."""
return EstablishmentMixinView.get_queryset(self) \
.has_location()
class EstablishmentRetrieveView(EstablishmentMixinView, generics.RetrieveAPIView): class EstablishmentRetrieveView(EstablishmentMixinView, generics.RetrieveAPIView):
"""Resource for getting a establishment.""" """Resource for getting a establishment."""
@ -88,9 +101,14 @@ class RestaurantSimilarListView(EstablishmentSimilarView):
def get_queryset(self): def get_queryset(self):
"""Overridden get_queryset method""" """Overridden get_queryset method"""
qs = super(RestaurantSimilarListView, self).get_queryset()
base_establishment = self.get_base_object()
if base_establishment:
return qs.similar_restaurants(base_establishment)
else:
return EstablishmentMixinView.get_queryset(self) \ return EstablishmentMixinView.get_queryset(self) \
.has_location() \ .none()
.similar_restaurants(slug=self.kwargs.get('slug'))
class WinerySimilarListView(EstablishmentSimilarView): class WinerySimilarListView(EstablishmentSimilarView):
@ -98,9 +116,13 @@ class WinerySimilarListView(EstablishmentSimilarView):
def get_queryset(self): def get_queryset(self):
"""Overridden get_queryset method""" """Overridden get_queryset method"""
return EstablishmentMixinView.get_queryset(self) \ qs = EstablishmentSimilarView.get_queryset(self)
.has_location() \ base_establishment = self.get_base_object()
.similar_wineries(slug=self.kwargs.get('slug'))
if base_establishment:
return qs.similar_wineries(base_establishment)
else:
return qs.none()
class ArtisanProducerSimilarListView(EstablishmentSimilarView): class ArtisanProducerSimilarListView(EstablishmentSimilarView):
@ -108,9 +130,13 @@ class ArtisanProducerSimilarListView(EstablishmentSimilarView):
def get_queryset(self): def get_queryset(self):
"""Overridden get_queryset method""" """Overridden get_queryset method"""
return EstablishmentMixinView.get_queryset(self) \ qs = super(ArtisanProducerSimilarListView, self).get_queryset()
.has_location() \ base_establishment = self.get_base_object()
.similar_artisans_producers(slug=self.kwargs.get('slug'))
if base_establishment:
return qs.similar_artisans_producers(base_establishment)
else:
return qs.none()
class EstablishmentTypeListView(generics.ListAPIView): class EstablishmentTypeListView(generics.ListAPIView):

View File

@ -35,6 +35,22 @@ class Timetable(ProjectBaseMixin):
opening_at = models.TimeField(verbose_name=_('Opening time'), null=True) opening_at = models.TimeField(verbose_name=_('Opening time'), null=True)
closed_at = models.TimeField(verbose_name=_('Closed time'), null=True) closed_at = models.TimeField(verbose_name=_('Closed time'), null=True)
class Meta:
"""Meta class."""
verbose_name = _('Timetable')
verbose_name_plural = _('Timetables')
ordering = ['weekday']
def __str__(self):
"""Overridden str dunder."""
return f'{self.get_weekday_display()} ' \
f'(closed_at - {self.closed_at_str}, ' \
f'opening_at - {self.opening_at_str}, ' \
f'opening_time - {self.opening_time}, ' \
f'ending_time - {self.ending_time}, ' \
f'works_at_noon - {self.works_at_noon}, ' \
f'works_at_afternoon: {self.works_at_afternoon})'
@property @property
def closed_at_str(self): def closed_at_str(self):
return str(self.closed_at) if self.closed_at else None return str(self.closed_at) if self.closed_at else None
@ -58,9 +74,3 @@ class Timetable(ProjectBaseMixin):
@property @property
def works_at_afternoon(self): def works_at_afternoon(self):
return bool(self.ending_time and self.ending_time > self.NOON) return bool(self.ending_time and self.ending_time > self.NOON)
class Meta:
"""Meta class."""
verbose_name = _('Timetable')
verbose_name_plural = _('Timetables')
ordering = ['weekday']