intermediate commit

This commit is contained in:
Anatoly 2019-12-30 13:22:57 +03:00
parent e2d71fca8f
commit 2827374911
8 changed files with 133 additions and 55 deletions

View File

@ -343,13 +343,15 @@ class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
def update_count_related_objects(self, nodes: set = ('EstablishmentNode', 'WineNode')):
"""Update count of related guide element objects."""
descendants = GuideElement.objects.get_root_node(self) \
.get_descendants()
if descendants:
updated_count = descendants.filter(
guide_element_type__name__in=nodes).count()
self.count_related_objects = updated_count
self.save()
root_node = GuideElement.objects.get_root_node(self)
if root_node:
descendants = root_node.get_descendants()
if descendants:
updated_count = descendants.filter(
guide_element_type__name__in=nodes).count()
self.count_related_objects = updated_count
self.save()
def generate_elements(self):
if self.guidefilter:
@ -390,6 +392,12 @@ class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
self.state = state
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):
"""QuerySet for model Advertorial."""

View File

@ -217,7 +217,7 @@ class AdvertorialBaseSerializer(serializers.ModelSerializer):
class GuideElementExportSerializer(GuideElementBaseSerializer):
"""GuideElement export serializer."""
# establishment = EstablishmentGuideElementSerializer(read_only=True,)
# ESTABLISHMENT
name = serializers.CharField(source='establishment.name', default=None)
public_mark = serializers.CharField(source='establishment.public_mark_display', default=None)
toque_number = serializers.IntegerField(source='establishment.toque_number', default=None)
@ -239,6 +239,18 @@ class GuideElementExportSerializer(GuideElementBaseSerializer):
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:
model = models.GuideElement
fields = [
@ -254,7 +266,7 @@ class GuideElementExportSerializer(GuideElementBaseSerializer):
# 'section_name',
# 'wine_color_section_name',
# 'children',
# 'label_photo',
'label_photo_url',
'name',
'public_mark',
'toque_number',

View File

@ -86,7 +86,7 @@ def generate_product_guide_elements(guide_id: int, filter_set: dict):
guide = Guide.objects.get(id=guide_id)
guide.change_state(Guide.BUILDING)
queryset_values = Product.objects.filter(**filter_set).values()[0]
queryset_values = Product.objects.filter(**filter_set).values()
try:
for instance in queryset_values:
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)
logger.error(f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
f'DETAIL: Guide ID {guide_id} - {e}')
else:
guide.update_count_related_objects()
guide.change_state(Guide.BUILT)

View File

@ -205,7 +205,6 @@ class GuideElementExportDOCView(generics.ListAPIView):
"""Overridden get_queryset method."""
guide = get_object_or_404(
models.Guide.objects.all(), pk=self.kwargs.get('pk'))
# todo: put in GuideElement model
tasks.export_guide(guide_id=guide.id, user_id=request.user.id, file_type='doc')
guide.export_to_file(user_id=request.user.id, file_type='doc')
return Response({"success": _('The file will be sent to your email.')},
status=status.HTTP_200_OK)

View File

@ -247,13 +247,13 @@ class EstablishmentQuerySet(models.QuerySet):
'establishment_type': establishment.establishment_type,
'address__city__country': establishment.address.city.country
}
qs = self.exclude(id=establishment.id).filter(**filters)
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)
if establishment.address and establishment.address.coordinates:
qs = qs.annotate_distance(point=establishment.location)
return qs
def similar_base_subquery(self, establishment, filters: dict) -> Subquery:
"""
@ -262,12 +262,16 @@ class EstablishmentQuerySet(models.QuerySet):
1 Filter by transmitted filters.
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(
self.similar_base(establishment)
.filter(**filters)
.order_by('distance')
.distinct()
.values_list('id', flat=True)[:settings.LIMITING_QUERY_OBJECTS]
qs.values_list('id', flat=True)[:settings.LIMITING_QUERY_OBJECTS]
)
def similar_restaurants(self, restaurant):
@ -284,7 +288,6 @@ class EstablishmentQuerySet(models.QuerySet):
'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) \
.annotate_intermediate_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).
: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) \
.same_subtype(establishment) \
.has_published_reviews() \
.order_by(F('same_subtype').desc(),
F('distance').asc()) \
.distinct('same_subtype', 'distance', 'id')
.order_by(similarity_rules['ordering']) \
.distinct(similarity_rules['distinctions'],
'id')
def by_wine_region(self, wine_region):
"""
@ -333,14 +344,20 @@ class EstablishmentQuerySet(models.QuerySet):
Return QuerySet with objects that similar to Winery.
: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) \
.order_by(F('wine_origins__wine_region').asc(),
F('wine_origins__wine_sub_region').asc(),
F('distance').asc()) \
.distinct('wine_origins__wine_region',
'wine_origins__wine_sub_region',
'distance',
'id')
.order_by(*similarity_rules['ordering']) \
.distinct(*similarity_rules['distinctions'],
'id')
def last_reviewed(self, point: Point):
"""

View File

@ -16,6 +16,7 @@ from utils.models import (BaseAttributes, ProjectBaseMixin, HasTagsMixin,
TranslatedFieldsMixin, TJSONField, FavoritesMixin,
GalleryMixin, IntermediateGalleryModelMixin,
TypeDefaultImageMixin)
from utils.methods import transform_into_readable_str
class ProductType(TypeDefaultImageMixin, TranslatedFieldsMixin, ProjectBaseMixin):
@ -188,28 +189,32 @@ class ProductQuerySet(models.QuerySet):
'reviews__status': Review.READY,
'product_type': product.product_type,
}
qs = self.exclude(id=product.id) \
.filter(**filters)
if product.subtypes.exists():
filters.update(
{'subtypes__in': product.subtypes.all()})
return self.exclude(id=product.id) \
.filter(**filters) \
.annotate_distance(point=product.establishment.location)
if product.establishment.address and product.establishment.location:
qs = qs.annotate_distance(point=product.establishment.location)
return qs
def similar(self, slug):
def similar(self, 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)
if product_qs.exists():
product = product_qs.first()
return self.similar_base(product) \
.same_subtype(product) \
.order_by(F('same_subtype').desc(),
F('distance').asc()) \
.distinct('same_subtype', 'distance', 'id')
else:
return self.none()
similarity_rules = {
'ordering': [F('same_subtype').desc(), ],
'distinction': ['same_subtype', ]
}
if product.establishment.address and product.establishment.location:
similarity_rules['ordering'].append(F('distance').asc())
similarity_rules['distinction'].append('distance')
return self.similar_base(product) \
.same_subtype(product) \
.order_by(*similarity_rules['ordering']) \
.distinct(*similarity_rules['distinction'],
'id')
class Product(GalleryMixin, TranslatedFieldsMixin, BaseAttributes,
@ -389,6 +394,22 @@ class Product(GalleryMixin, TranslatedFieldsMixin, BaseAttributes,
if self.wine_origins.exists():
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):
"""Extended manger for OnlineProduct model."""

View File

@ -110,6 +110,6 @@ class SimilarListView(ProductSimilarView):
base_product = self.get_base_object()
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:
return qs.none()

View File

@ -388,12 +388,6 @@ class SendGuideExport(SendExportBase):
document.document.save(self.file_path)
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):
return self.data
@ -432,6 +426,34 @@ class SendGuideExport(SendExportBase):
print(f'ok: {self.file_path}')
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):
file_header = self.get_headers()
if not self.success: