Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
aa7ebf4d4c
18
apps/establishment/migrations/0068_auto_20191220_0914.py
Normal file
18
apps/establishment/migrations/0068_auto_20191220_0914.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -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,28 +249,25 @@ 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():
|
ids_by_subquery = self.similar_base_subquery(
|
||||||
restaurant = restaurant_qs.first()
|
establishment=restaurant,
|
||||||
ids_by_subquery = self.similar_base_subquery(
|
filters={
|
||||||
establishment=restaurant,
|
'reviews__status': Review.READY,
|
||||||
filters={
|
'public_mark__gte': 10,
|
||||||
'public_mark__gte': 10,
|
'establishment_gallery__is_main': True,
|
||||||
'establishment_gallery__is_main': True,
|
}
|
||||||
}
|
)
|
||||||
)
|
return self.filter(id__in=ids_by_subquery) \
|
||||||
return self.filter(id__in=ids_by_subquery) \
|
.annotate_intermediate_public_mark() \
|
||||||
.annotate_intermediate_public_mark() \
|
.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)
|
return self.similar_base(establishment) \
|
||||||
if establishment_qs.exists():
|
.same_subtype(establishment) \
|
||||||
establishment = establishment_qs.first()
|
.has_published_reviews() \
|
||||||
return self.similar_base(establishment) \
|
.order_by(F('same_subtype').desc(),
|
||||||
.same_subtype(establishment) \
|
F('distance').asc()) \
|
||||||
.order_by(F('same_subtype').desc(),
|
.distinct('same_subtype', 'distance', 'id')
|
||||||
F('distance').asc()) \
|
|
||||||
.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)
|
return self.similar_base(winery) \
|
||||||
if winery_qs.exists():
|
.order_by(F('wine_origins__wine_region').asc(),
|
||||||
winery = winery_qs.first()
|
F('wine_origins__wine_sub_region').asc(),
|
||||||
return self.similar_base(winery) \
|
F('distance').asc()) \
|
||||||
.order_by(F('wine_origins__wine_region').asc(),
|
.distinct('wine_origins__wine_region',
|
||||||
F('wine_origins__wine_sub_region').asc()) \
|
'wine_origins__wine_sub_region',
|
||||||
.annotate_distance(point=winery.location) \
|
'distance',
|
||||||
.order_by('distance') \
|
'id')
|
||||||
.distinct('distance', 'wine_origins__wine_region',
|
|
||||||
'wine_origins__wine_sub_region', 'id')
|
|
||||||
else:
|
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -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"""
|
||||||
return EstablishmentMixinView.get_queryset(self) \
|
qs = super(RestaurantSimilarListView, self).get_queryset()
|
||||||
.has_location() \
|
base_establishment = self.get_base_object()
|
||||||
.similar_restaurants(slug=self.kwargs.get('slug'))
|
|
||||||
|
if base_establishment:
|
||||||
|
return qs.similar_restaurants(base_establishment)
|
||||||
|
else:
|
||||||
|
return EstablishmentMixinView.get_queryset(self) \
|
||||||
|
.none()
|
||||||
|
|
||||||
|
|
||||||
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):
|
||||||
|
|
|
||||||
|
|
@ -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']
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user