intermediate commit
This commit is contained in:
parent
e2d71fca8f
commit
2827374911
|
|
@ -343,13 +343,15 @@ class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
|
||||||
|
|
||||||
def update_count_related_objects(self, nodes: set = ('EstablishmentNode', 'WineNode')):
|
def update_count_related_objects(self, nodes: set = ('EstablishmentNode', 'WineNode')):
|
||||||
"""Update count of related guide element objects."""
|
"""Update count of related guide element objects."""
|
||||||
descendants = GuideElement.objects.get_root_node(self) \
|
root_node = GuideElement.objects.get_root_node(self)
|
||||||
.get_descendants()
|
if root_node:
|
||||||
if descendants:
|
descendants = root_node.get_descendants()
|
||||||
updated_count = descendants.filter(
|
|
||||||
guide_element_type__name__in=nodes).count()
|
if descendants:
|
||||||
self.count_related_objects = updated_count
|
updated_count = descendants.filter(
|
||||||
self.save()
|
guide_element_type__name__in=nodes).count()
|
||||||
|
self.count_related_objects = updated_count
|
||||||
|
self.save()
|
||||||
|
|
||||||
def generate_elements(self):
|
def generate_elements(self):
|
||||||
if self.guidefilter:
|
if self.guidefilter:
|
||||||
|
|
@ -390,6 +392,12 @@ class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
|
||||||
self.state = state
|
self.state = state
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
def export_to_file(self, user_id: int, file_type: str):
|
||||||
|
if settings.USE_CELERY:
|
||||||
|
tasks.export_guide.delay(guide_id=self.id, user_id=user_id, file_type=file_type)
|
||||||
|
else:
|
||||||
|
tasks.export_guide(guide_id=self.id, user_id=user_id, file_type=file_type)
|
||||||
|
|
||||||
|
|
||||||
class AdvertorialQuerySet(models.QuerySet):
|
class AdvertorialQuerySet(models.QuerySet):
|
||||||
"""QuerySet for model Advertorial."""
|
"""QuerySet for model Advertorial."""
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ class AdvertorialBaseSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
class GuideElementExportSerializer(GuideElementBaseSerializer):
|
class GuideElementExportSerializer(GuideElementBaseSerializer):
|
||||||
"""GuideElement export serializer."""
|
"""GuideElement export serializer."""
|
||||||
# establishment = EstablishmentGuideElementSerializer(read_only=True,)
|
# ESTABLISHMENT
|
||||||
name = serializers.CharField(source='establishment.name', default=None)
|
name = serializers.CharField(source='establishment.name', default=None)
|
||||||
public_mark = serializers.CharField(source='establishment.public_mark_display', default=None)
|
public_mark = serializers.CharField(source='establishment.public_mark_display', default=None)
|
||||||
toque_number = serializers.IntegerField(source='establishment.toque_number', default=None)
|
toque_number = serializers.IntegerField(source='establishment.toque_number', default=None)
|
||||||
|
|
@ -239,6 +239,18 @@ class GuideElementExportSerializer(GuideElementBaseSerializer):
|
||||||
default=None)
|
default=None)
|
||||||
advertorial_page = serializers.IntegerField(default=None)
|
advertorial_page = serializers.IntegerField(default=None)
|
||||||
|
|
||||||
|
# PRODUCT
|
||||||
|
product_name = serializers.CharField(source='product.name',
|
||||||
|
default=None)
|
||||||
|
product_review = serializers.DictField(source='product.establishment.last_published_review_data',
|
||||||
|
default=None)
|
||||||
|
product_type = serializers.CharField(source='product.product_type_label',
|
||||||
|
default=None)
|
||||||
|
product_subtypes = serializers.CharField(source='product.product_subtype_labels',
|
||||||
|
default=None)
|
||||||
|
product_address = serializers.CharField(source='product.establishment.address.full_address',
|
||||||
|
default=None)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.GuideElement
|
model = models.GuideElement
|
||||||
fields = [
|
fields = [
|
||||||
|
|
@ -254,7 +266,7 @@ class GuideElementExportSerializer(GuideElementBaseSerializer):
|
||||||
# 'section_name',
|
# 'section_name',
|
||||||
# 'wine_color_section_name',
|
# 'wine_color_section_name',
|
||||||
# 'children',
|
# 'children',
|
||||||
# 'label_photo',
|
'label_photo_url',
|
||||||
'name',
|
'name',
|
||||||
'public_mark',
|
'public_mark',
|
||||||
'toque_number',
|
'toque_number',
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ def generate_product_guide_elements(guide_id: int, filter_set: dict):
|
||||||
|
|
||||||
guide = Guide.objects.get(id=guide_id)
|
guide = Guide.objects.get(id=guide_id)
|
||||||
guide.change_state(Guide.BUILDING)
|
guide.change_state(Guide.BUILDING)
|
||||||
queryset_values = Product.objects.filter(**filter_set).values()[0]
|
queryset_values = Product.objects.filter(**filter_set).values()
|
||||||
try:
|
try:
|
||||||
for instance in queryset_values:
|
for instance in queryset_values:
|
||||||
wine_id = instance.get('id')
|
wine_id = instance.get('id')
|
||||||
|
|
@ -133,7 +133,6 @@ def generate_product_guide_elements(guide_id: int, filter_set: dict):
|
||||||
guide.change_state(Guide.WAITING)
|
guide.change_state(Guide.WAITING)
|
||||||
logger.error(f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
logger.error(f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
||||||
f'DETAIL: Guide ID {guide_id} - {e}')
|
f'DETAIL: Guide ID {guide_id} - {e}')
|
||||||
else:
|
|
||||||
guide.update_count_related_objects()
|
guide.update_count_related_objects()
|
||||||
guide.change_state(Guide.BUILT)
|
guide.change_state(Guide.BUILT)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,6 @@ class GuideElementExportDOCView(generics.ListAPIView):
|
||||||
"""Overridden get_queryset method."""
|
"""Overridden get_queryset method."""
|
||||||
guide = get_object_or_404(
|
guide = get_object_or_404(
|
||||||
models.Guide.objects.all(), pk=self.kwargs.get('pk'))
|
models.Guide.objects.all(), pk=self.kwargs.get('pk'))
|
||||||
# todo: put in GuideElement model
|
guide.export_to_file(user_id=request.user.id, file_type='doc')
|
||||||
tasks.export_guide(guide_id=guide.id, user_id=request.user.id, file_type='doc')
|
|
||||||
return Response({"success": _('The file will be sent to your email.')},
|
return Response({"success": _('The file will be sent to your email.')},
|
||||||
status=status.HTTP_200_OK)
|
status=status.HTTP_200_OK)
|
||||||
|
|
|
||||||
|
|
@ -247,13 +247,13 @@ class EstablishmentQuerySet(models.QuerySet):
|
||||||
'establishment_type': establishment.establishment_type,
|
'establishment_type': establishment.establishment_type,
|
||||||
'address__city__country': establishment.address.city.country
|
'address__city__country': establishment.address.city.country
|
||||||
}
|
}
|
||||||
|
qs = self.exclude(id=establishment.id).filter(**filters)
|
||||||
if establishment.establishment_subtypes.exists():
|
if establishment.establishment_subtypes.exists():
|
||||||
filters.update(
|
filters.update(
|
||||||
{'establishment_subtypes__in': establishment.establishment_subtypes.all()})
|
{'establishment_subtypes__in': establishment.establishment_subtypes.all()})
|
||||||
|
if establishment.address and establishment.address.coordinates:
|
||||||
return self.exclude(id=establishment.id) \
|
qs = qs.annotate_distance(point=establishment.location)
|
||||||
.filter(**filters) \
|
return qs
|
||||||
.annotate_distance(point=establishment.location)
|
|
||||||
|
|
||||||
def similar_base_subquery(self, establishment, filters: dict) -> Subquery:
|
def similar_base_subquery(self, establishment, filters: dict) -> Subquery:
|
||||||
"""
|
"""
|
||||||
|
|
@ -262,12 +262,16 @@ class EstablishmentQuerySet(models.QuerySet):
|
||||||
1 Filter by transmitted filters.
|
1 Filter by transmitted filters.
|
||||||
2 With ordering by distance.
|
2 With ordering by distance.
|
||||||
"""
|
"""
|
||||||
|
qs = self.similar_base(establishment) \
|
||||||
|
.filter(**filters)
|
||||||
|
if establishment.address and establishment.address.coordinates:
|
||||||
|
return Subquery(
|
||||||
|
qs.order_by('distance')
|
||||||
|
.distinct()
|
||||||
|
.values_list('id', flat=True)[:settings.LIMITING_QUERY_OBJECTS]
|
||||||
|
)
|
||||||
return Subquery(
|
return Subquery(
|
||||||
self.similar_base(establishment)
|
qs.values_list('id', flat=True)[:settings.LIMITING_QUERY_OBJECTS]
|
||||||
.filter(**filters)
|
|
||||||
.order_by('distance')
|
|
||||||
.distinct()
|
|
||||||
.values_list('id', flat=True)[:settings.LIMITING_QUERY_OBJECTS]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def similar_restaurants(self, restaurant):
|
def similar_restaurants(self, restaurant):
|
||||||
|
|
@ -284,7 +288,6 @@ class EstablishmentQuerySet(models.QuerySet):
|
||||||
'establishment_gallery__is_main': True,
|
'establishment_gallery__is_main': True,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
# todo: fix this - replace ids_by_subquery.queryset on ids_by_subquery
|
|
||||||
return self.filter(id__in=ids_by_subquery.queryset) \
|
return self.filter(id__in=ids_by_subquery.queryset) \
|
||||||
.annotate_intermediate_public_mark() \
|
.annotate_intermediate_public_mark() \
|
||||||
.annotate_mark_similarity(mark=restaurant.public_mark) \
|
.annotate_mark_similarity(mark=restaurant.public_mark) \
|
||||||
|
|
@ -307,12 +310,20 @@ class EstablishmentQuerySet(models.QuerySet):
|
||||||
Return QuerySet with objects that similar to Artisan/Producer(s).
|
Return QuerySet with objects that similar to Artisan/Producer(s).
|
||||||
:param establishment: Establishment instance
|
:param establishment: Establishment instance
|
||||||
"""
|
"""
|
||||||
|
similarity_rules = {
|
||||||
|
'ordering': [F('same_subtype').desc(), ],
|
||||||
|
'distinctions': ['same_subtype', ]
|
||||||
|
}
|
||||||
|
if establishment.address and establishment.address.coordinates:
|
||||||
|
similarity_rules['ordering'].append(F('distance').asc())
|
||||||
|
similarity_rules['distinctions'].append('distance')
|
||||||
|
|
||||||
return self.similar_base(establishment) \
|
return self.similar_base(establishment) \
|
||||||
.same_subtype(establishment) \
|
.same_subtype(establishment) \
|
||||||
.has_published_reviews() \
|
.has_published_reviews() \
|
||||||
.order_by(F('same_subtype').desc(),
|
.order_by(similarity_rules['ordering']) \
|
||||||
F('distance').asc()) \
|
.distinct(similarity_rules['distinctions'],
|
||||||
.distinct('same_subtype', 'distance', 'id')
|
'id')
|
||||||
|
|
||||||
def by_wine_region(self, wine_region):
|
def by_wine_region(self, wine_region):
|
||||||
"""
|
"""
|
||||||
|
|
@ -333,14 +344,20 @@ class EstablishmentQuerySet(models.QuerySet):
|
||||||
Return QuerySet with objects that similar to Winery.
|
Return QuerySet with objects that similar to Winery.
|
||||||
:param winery: Establishment instance
|
:param winery: Establishment instance
|
||||||
"""
|
"""
|
||||||
|
similarity_rules = {
|
||||||
|
'ordering': [F('wine_origins__wine_region').asc(),
|
||||||
|
F('wine_origins__wine_sub_region').asc()],
|
||||||
|
'distinctions': ['wine_origins__wine_region',
|
||||||
|
'wine_origins__wine_sub_region']
|
||||||
|
}
|
||||||
|
if winery.address and winery.address.coordinates:
|
||||||
|
similarity_rules['ordering'].append(F('distance').asc())
|
||||||
|
similarity_rules['distinctions'].append('distance')
|
||||||
|
|
||||||
return self.similar_base(winery) \
|
return self.similar_base(winery) \
|
||||||
.order_by(F('wine_origins__wine_region').asc(),
|
.order_by(*similarity_rules['ordering']) \
|
||||||
F('wine_origins__wine_sub_region').asc(),
|
.distinct(*similarity_rules['distinctions'],
|
||||||
F('distance').asc()) \
|
'id')
|
||||||
.distinct('wine_origins__wine_region',
|
|
||||||
'wine_origins__wine_sub_region',
|
|
||||||
'distance',
|
|
||||||
'id')
|
|
||||||
|
|
||||||
def last_reviewed(self, point: Point):
|
def last_reviewed(self, point: Point):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ from utils.models import (BaseAttributes, ProjectBaseMixin, HasTagsMixin,
|
||||||
TranslatedFieldsMixin, TJSONField, FavoritesMixin,
|
TranslatedFieldsMixin, TJSONField, FavoritesMixin,
|
||||||
GalleryMixin, IntermediateGalleryModelMixin,
|
GalleryMixin, IntermediateGalleryModelMixin,
|
||||||
TypeDefaultImageMixin)
|
TypeDefaultImageMixin)
|
||||||
|
from utils.methods import transform_into_readable_str
|
||||||
|
|
||||||
|
|
||||||
class ProductType(TypeDefaultImageMixin, TranslatedFieldsMixin, ProjectBaseMixin):
|
class ProductType(TypeDefaultImageMixin, TranslatedFieldsMixin, ProjectBaseMixin):
|
||||||
|
|
@ -188,28 +189,32 @@ class ProductQuerySet(models.QuerySet):
|
||||||
'reviews__status': Review.READY,
|
'reviews__status': Review.READY,
|
||||||
'product_type': product.product_type,
|
'product_type': product.product_type,
|
||||||
}
|
}
|
||||||
|
qs = self.exclude(id=product.id) \
|
||||||
|
.filter(**filters)
|
||||||
if product.subtypes.exists():
|
if product.subtypes.exists():
|
||||||
filters.update(
|
filters.update(
|
||||||
{'subtypes__in': product.subtypes.all()})
|
{'subtypes__in': product.subtypes.all()})
|
||||||
return self.exclude(id=product.id) \
|
if product.establishment.address and product.establishment.location:
|
||||||
.filter(**filters) \
|
qs = qs.annotate_distance(point=product.establishment.location)
|
||||||
.annotate_distance(point=product.establishment.location)
|
return qs
|
||||||
|
|
||||||
def similar(self, slug):
|
def similar(self, product):
|
||||||
"""
|
"""
|
||||||
Return QuerySet with objects that similar to Product.
|
Return QuerySet with objects that similar to Product.
|
||||||
:param slug: str product slug
|
:param product: instance of Product model.
|
||||||
"""
|
"""
|
||||||
product_qs = self.filter(slug=slug)
|
similarity_rules = {
|
||||||
if product_qs.exists():
|
'ordering': [F('same_subtype').desc(), ],
|
||||||
product = product_qs.first()
|
'distinction': ['same_subtype', ]
|
||||||
return self.similar_base(product) \
|
}
|
||||||
.same_subtype(product) \
|
if product.establishment.address and product.establishment.location:
|
||||||
.order_by(F('same_subtype').desc(),
|
similarity_rules['ordering'].append(F('distance').asc())
|
||||||
F('distance').asc()) \
|
similarity_rules['distinction'].append('distance')
|
||||||
.distinct('same_subtype', 'distance', 'id')
|
return self.similar_base(product) \
|
||||||
else:
|
.same_subtype(product) \
|
||||||
return self.none()
|
.order_by(*similarity_rules['ordering']) \
|
||||||
|
.distinct(*similarity_rules['distinction'],
|
||||||
|
'id')
|
||||||
|
|
||||||
|
|
||||||
class Product(GalleryMixin, TranslatedFieldsMixin, BaseAttributes,
|
class Product(GalleryMixin, TranslatedFieldsMixin, BaseAttributes,
|
||||||
|
|
@ -389,6 +394,22 @@ class Product(GalleryMixin, TranslatedFieldsMixin, BaseAttributes,
|
||||||
if self.wine_origins.exists():
|
if self.wine_origins.exists():
|
||||||
return self.wine_origins.first().wine_region
|
return self.wine_origins.first().wine_region
|
||||||
|
|
||||||
|
@property
|
||||||
|
def last_published_review_data(self):
|
||||||
|
if self.last_published_review:
|
||||||
|
return self.last_published_review.text
|
||||||
|
|
||||||
|
@property
|
||||||
|
def establishment_subtype_labels(self):
|
||||||
|
if self.subtypes:
|
||||||
|
return [transform_into_readable_str(label)
|
||||||
|
for label in self.subtypes.all().values_list('index_name', flat=True)]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def wine_region_label(self):
|
||||||
|
if self.wine_region:
|
||||||
|
return self.wine_region.name
|
||||||
|
|
||||||
|
|
||||||
class OnlineProductManager(ProductManager):
|
class OnlineProductManager(ProductManager):
|
||||||
"""Extended manger for OnlineProduct model."""
|
"""Extended manger for OnlineProduct model."""
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,6 @@ class SimilarListView(ProductSimilarView):
|
||||||
base_product = self.get_base_object()
|
base_product = self.get_base_object()
|
||||||
|
|
||||||
if base_product:
|
if base_product:
|
||||||
return qs.has_location().similar(slug=self.kwargs.get('slug'))[:settings.QUERY_OUTPUT_OBJECTS]
|
return qs.has_location().similar(base_product)[:settings.QUERY_OUTPUT_OBJECTS]
|
||||||
else:
|
else:
|
||||||
return qs.none()
|
return qs.none()
|
||||||
|
|
|
||||||
|
|
@ -388,12 +388,6 @@ class SendGuideExport(SendExportBase):
|
||||||
document.document.save(self.file_path)
|
document.document.save(self.file_path)
|
||||||
self.success = True
|
self.success = True
|
||||||
|
|
||||||
def get_headers(self):
|
|
||||||
headers = list(self.data[0].keys())
|
|
||||||
headers.pop(headers.index('node_name'))
|
|
||||||
self.success = True
|
|
||||||
return headers
|
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return self.data
|
return self.data
|
||||||
|
|
||||||
|
|
@ -432,6 +426,34 @@ class SendGuideExport(SendExportBase):
|
||||||
print(f'ok: {self.file_path}')
|
print(f'ok: {self.file_path}')
|
||||||
self.send_email()
|
self.send_email()
|
||||||
|
|
||||||
|
def get_headers(self):
|
||||||
|
"""Get headers for model Establishment."""
|
||||||
|
exclude_headers = ['node_name', ]
|
||||||
|
headers = list(self.data[0].keys())
|
||||||
|
|
||||||
|
if self.guide.RESTAURANT or self.guide.ARTISAN:
|
||||||
|
exclude_headers.append('product_name', )
|
||||||
|
if self.guide.WINE:
|
||||||
|
exclude_headers.extend([
|
||||||
|
'name',
|
||||||
|
'public_mark',
|
||||||
|
'toque_number',
|
||||||
|
'schedule',
|
||||||
|
'address',
|
||||||
|
'phones',
|
||||||
|
'establishment_type',
|
||||||
|
'establishment_subtypes',
|
||||||
|
'review',
|
||||||
|
'price_level',
|
||||||
|
'metadata',
|
||||||
|
])
|
||||||
|
|
||||||
|
for name in set(exclude_headers):
|
||||||
|
headers.pop(headers.index(name))
|
||||||
|
else:
|
||||||
|
self.success = True
|
||||||
|
return headers
|
||||||
|
|
||||||
def make_csv_file(self):
|
def make_csv_file(self):
|
||||||
file_header = self.get_headers()
|
file_header = self.get_headers()
|
||||||
if not self.success:
|
if not self.success:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user