added guide type field, added tasks, refactored GuideElement model, added endpoint,
This commit is contained in:
parent
65746b1cd0
commit
d9df886b98
14
apps/collection/migrations/0029_merge_20191225_1819.py
Normal file
14
apps/collection/migrations/0029_merge_20191225_1819.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Generated by Django 2.2.7 on 2019-12-25 18:19
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('collection', '0028_merge_20191223_1415'),
|
||||||
|
('collection', '0028_guide_initial_objects_counter'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
]
|
||||||
18
apps/collection/migrations/0030_guidefilter_guide_type.py
Normal file
18
apps/collection/migrations/0030_guidefilter_guide_type.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.2.7 on 2019-12-25 18:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('collection', '0029_merge_20191225_1819'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='guidefilter',
|
||||||
|
name='guide_type',
|
||||||
|
field=models.PositiveSmallIntegerField(choices=[(0, 'Restaurant'), (1, 'Artisan'), (2, 'Wine')], default=0, verbose_name='guide type'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -5,17 +5,19 @@ from django.contrib.postgres.fields import JSONField
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from mptt.models import MPTTModel
|
||||||
from mptt.models import MPTTModel, TreeForeignKey
|
from mptt.models import MPTTModel, TreeForeignKey
|
||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
|
|
||||||
from location.models import Country, Region, WineRegion, WineSubRegion
|
from location.models import Country, Region, WineRegion, WineSubRegion, City
|
||||||
from review.models import Review
|
from review.models import Review
|
||||||
|
from product.models import Product
|
||||||
from translation.models import Language
|
from translation.models import Language
|
||||||
from utils.models import IntermediateGalleryModelMixin
|
|
||||||
from utils.models import (
|
from utils.models import (
|
||||||
ProjectBaseMixin, TJSONField, TranslatedFieldsMixin,
|
ProjectBaseMixin, TJSONField, TranslatedFieldsMixin,
|
||||||
URLImageMixin, IntermediateGalleryModelMixin
|
URLImageMixin, IntermediateGalleryModelMixin
|
||||||
)
|
)
|
||||||
|
from utils.methods import slug_into_section_name
|
||||||
from utils.querysets import RelatedObjectsCountMixin
|
from utils.querysets import RelatedObjectsCountMixin
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -381,6 +383,20 @@ class GuideFilterQuerySet(models.QuerySet):
|
||||||
|
|
||||||
class GuideFilter(ProjectBaseMixin):
|
class GuideFilter(ProjectBaseMixin):
|
||||||
"""Guide filter model."""
|
"""Guide filter model."""
|
||||||
|
|
||||||
|
RESTAURANT = 0
|
||||||
|
ARTISAN = 1
|
||||||
|
WINE = 2
|
||||||
|
|
||||||
|
GUIDE_TYPES = (
|
||||||
|
(RESTAURANT, _('Restaurant')),
|
||||||
|
(ARTISAN, _('Artisan')),
|
||||||
|
(WINE, _('Wine')),
|
||||||
|
)
|
||||||
|
|
||||||
|
guide_type = models.PositiveSmallIntegerField(choices=GUIDE_TYPES,
|
||||||
|
default=RESTAURANT,
|
||||||
|
verbose_name=_('guide type'))
|
||||||
establishment_type_json = JSONField(blank=True, null=True,
|
establishment_type_json = JSONField(blank=True, null=True,
|
||||||
verbose_name='establishment types')
|
verbose_name='establishment types')
|
||||||
country_json = JSONField(blank=True, null=True,
|
country_json = JSONField(blank=True, null=True,
|
||||||
|
|
@ -568,13 +584,73 @@ class GuideFilter(ProjectBaseMixin):
|
||||||
'public_mark__isnull': False,
|
'public_mark__isnull': False,
|
||||||
})
|
})
|
||||||
|
|
||||||
if self.locale_json:
|
if self.locales:
|
||||||
filters.update({
|
filters.update({
|
||||||
'reviews__text__has_any_keys': self.locales,
|
'reviews__text__has_any_keys': self.locales,
|
||||||
})
|
})
|
||||||
|
|
||||||
return filters
|
return filters
|
||||||
|
|
||||||
|
@property
|
||||||
|
def product_filter_set(self):
|
||||||
|
filters = {
|
||||||
|
# establishment.Establishment
|
||||||
|
'establishment__public_mark__in': [self.min_mark, self.max_mark],
|
||||||
|
# review.Reviews
|
||||||
|
'reviews__vintage__in': self.review_vintages,
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.establishment_type_ids:
|
||||||
|
filters.update({
|
||||||
|
# establishment.EstablishmentType
|
||||||
|
'establishment__establishment_type_id__in': self.establishment_type_ids,
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.country_ids:
|
||||||
|
filters.update({
|
||||||
|
# location.Country
|
||||||
|
'establishment__address__city__country_id__in': self.country_ids,
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.region_ids:
|
||||||
|
filters.update({
|
||||||
|
# location.Region
|
||||||
|
'establishment__address__city__region__parent_id__in': self.region_ids,
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.sub_region_ids:
|
||||||
|
filters.update({
|
||||||
|
# location.Region
|
||||||
|
'establishment__address__city__region__parent_id__in': self.region_ids,
|
||||||
|
'establishment__address__city__region_id__in': self.sub_region_ids,
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.wine_region_ids:
|
||||||
|
filters.update({
|
||||||
|
# location.WineRegion
|
||||||
|
'wine_origins__wine_region_id__in': self.wine_region_ids,
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.with_mark:
|
||||||
|
filters.update({
|
||||||
|
# establishment.Establishment
|
||||||
|
'establishment__public_mark__isnull': False,
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.locales:
|
||||||
|
filters.update({
|
||||||
|
'reviews__text__has_any_keys': self.locales,
|
||||||
|
})
|
||||||
|
|
||||||
|
return filters
|
||||||
|
|
||||||
|
@property
|
||||||
|
def filter_set(self):
|
||||||
|
if self.guide_type in [self.RESTAURANT, self.ARTISAN]:
|
||||||
|
return self.establishment_filter_set
|
||||||
|
elif self.guide_type == self.WINE:
|
||||||
|
return self.product_filter_set
|
||||||
|
|
||||||
|
|
||||||
class GuideElementType(models.Model):
|
class GuideElementType(models.Model):
|
||||||
"""Model for type of guide elements."""
|
"""Model for type of guide elements."""
|
||||||
|
|
@ -657,6 +733,138 @@ class GuideElementManager(models.Manager):
|
||||||
if qs.exists():
|
if qs.exists():
|
||||||
return qs.first()
|
return qs.first()
|
||||||
|
|
||||||
|
def get_or_create_root_node(self, guide_id: int):
|
||||||
|
"""Get or Create RootNode."""
|
||||||
|
guide_element_type_qs = GuideElementType.objects.filter(name='Root')
|
||||||
|
guide_qs = Guide.objects.filter(id=guide_id)
|
||||||
|
|
||||||
|
if guide_element_type_qs.exists() and guide_qs.exists():
|
||||||
|
guide = guide_qs.first()
|
||||||
|
return self.get_or_create(guide_id=guide_qs.first(),
|
||||||
|
guide_element_type=guide_element_type_qs.first(),
|
||||||
|
defaults={
|
||||||
|
'guide_id': guide.id,
|
||||||
|
'guide_element_type': guide_element_type_qs.first()})
|
||||||
|
return None, False
|
||||||
|
|
||||||
|
def get_or_create_city_node(self, root_node_id: int, city_id: int):
|
||||||
|
"""Get or Create CityNode."""
|
||||||
|
parent_node_qs = GuideElement.objects.filter(id=root_node_id)
|
||||||
|
guide_element_type_qs = GuideElementType.objects.filter(name='CityNode')
|
||||||
|
city_qs = City.objects.filter(id=city_id)
|
||||||
|
|
||||||
|
if parent_node_qs.exists() and city_qs.exists() and guide_element_type_qs.exists():
|
||||||
|
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||||
|
parent=parent_node_qs.first(),
|
||||||
|
city=city_qs.first())
|
||||||
|
return None, False
|
||||||
|
|
||||||
|
def get_or_create_establishment_section_node(self, city_node: int, establishment_node_name: str):
|
||||||
|
"""Get or Create (Restaurant|Shop...)SectionNode."""
|
||||||
|
parent_node_qs = GuideElement.objects.filter(id=city_node)
|
||||||
|
guide_element_type_qs = GuideElementType.objects.filter(name__iexact=establishment_node_name)
|
||||||
|
|
||||||
|
if parent_node_qs.exists() and guide_element_type_qs.exists():
|
||||||
|
parent_node = parent_node_qs.first()
|
||||||
|
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||||
|
parent=parent_node,
|
||||||
|
guide=parent_node.get_root().guide)
|
||||||
|
return None, False
|
||||||
|
|
||||||
|
def get_or_create_establishment_node(self, restaurant_section_node_id: int,
|
||||||
|
establishment_id: int, review_id: int = None):
|
||||||
|
"""Get or Create EstablishmentNode."""
|
||||||
|
from establishment.models import Establishment
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
guide_element_type_qs = GuideElementType.objects.filter(name='EstablishmentNode')
|
||||||
|
parent_node_qs = GuideElement.objects.filter(id=restaurant_section_node_id)
|
||||||
|
establishment_qs = Establishment.objects.filter(id=establishment_id)
|
||||||
|
|
||||||
|
if parent_node_qs.exists() and establishment_qs.exists() and guide_element_type_qs.exists():
|
||||||
|
establishment = establishment_qs.first()
|
||||||
|
parent_node = parent_node_qs.first()
|
||||||
|
data.update({
|
||||||
|
'guide_element_type': guide_element_type_qs.first(),
|
||||||
|
'parent': parent_node,
|
||||||
|
'guide': parent_node.get_root().guide,
|
||||||
|
'establishment': establishment
|
||||||
|
})
|
||||||
|
if review_id:
|
||||||
|
review_qs = Review.objects.filter(id=review_id)
|
||||||
|
if review_qs.exists(): data.update({'review_id': review_qs.first().id})
|
||||||
|
return self.get_or_create(**data)
|
||||||
|
return None, False
|
||||||
|
|
||||||
|
def get_or_create_wine_region_node(self, root_node_id: int, wine_region_id: int):
|
||||||
|
"""Get or Create WineRegionNode."""
|
||||||
|
guide_element_type_qs = GuideElementType.objects.filter(name='RegionNode')
|
||||||
|
parent_node_qs = GuideElement.objects.filter(id=root_node_id)
|
||||||
|
wine_region_qs = WineRegion.objects.filter(id=wine_region_id)
|
||||||
|
|
||||||
|
if parent_node_qs.exists() and parent_node_qs.first().guide and wine_region_qs.exists() and guide_element_type_qs.exists():
|
||||||
|
root_node = parent_node_qs.first()
|
||||||
|
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||||
|
parent=root_node,
|
||||||
|
guide=root_node.guide,
|
||||||
|
wine_region=wine_region_qs.first())
|
||||||
|
return None, False
|
||||||
|
|
||||||
|
def get_or_create_yard_node(self, product_id: int, wine_region_node_id: int):
|
||||||
|
"""Make YardNode."""
|
||||||
|
from establishment.models import Establishment
|
||||||
|
|
||||||
|
guide_element_type_qs = GuideElementType.objects.filter(name='YardNode')
|
||||||
|
wine_region_node_qs = GuideElement.objects.filter(id=wine_region_node_id)
|
||||||
|
product_qs = Product.objects.filter(id=product_id)
|
||||||
|
|
||||||
|
if product_qs.exists() and wine_region_node_qs.exists():
|
||||||
|
wine_region_node = wine_region_node_qs.first()
|
||||||
|
root_node = wine_region_node.get_root()
|
||||||
|
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||||
|
parent=wine_region_node,
|
||||||
|
guide=root_node.guide,
|
||||||
|
product=product_qs.first())
|
||||||
|
return None, False
|
||||||
|
|
||||||
|
def get_or_create_color_wine_section_node(self, wine_color_name: str, yard_node_id: int):
|
||||||
|
"""Get or Create WineSectionNode."""
|
||||||
|
guide_element_type_qs = GuideElementType.objects.filter(name='ColorWineSectionNode')
|
||||||
|
parent_node_qs = GuideElement.objects.filter(id=yard_node_id)
|
||||||
|
|
||||||
|
if not wine_color_name.endswith('SectionNode'):
|
||||||
|
wine_color_name = slug_into_section_name(wine_color_name)
|
||||||
|
|
||||||
|
wine_color_section, _ = GuideWineColorSection.objects.get_or_create(
|
||||||
|
name=wine_color_name,
|
||||||
|
defaults={
|
||||||
|
'name': wine_color_name
|
||||||
|
})
|
||||||
|
|
||||||
|
if parent_node_qs.exists() and guide_element_type_qs.exists():
|
||||||
|
root_node = parent_node_qs.first().get_root()
|
||||||
|
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||||
|
parent=root_node,
|
||||||
|
wine_color_section=wine_color_section,
|
||||||
|
guide=root_node.guide)
|
||||||
|
return None, False
|
||||||
|
|
||||||
|
def get_or_create_wine_node(self, color_wine_section_node_id: int, wine_id: int, review_id: int):
|
||||||
|
"""Get or Create WineNode."""
|
||||||
|
guide_element_type_qs = GuideElementType.objects.filter(name='WineNode')
|
||||||
|
parent_node_qs = GuideElement.objects.filter(id=color_wine_section_node_id)
|
||||||
|
wine_qs = Product.objects.wines().filter(id=wine_id)
|
||||||
|
review_qs = Review.objects.filter(id=review_id)
|
||||||
|
|
||||||
|
if parent_node_qs.exists() and wine_qs.exists() and review_qs.exists() and guide_element_type_qs.exists():
|
||||||
|
root_node = parent_node_qs.first().get_root()
|
||||||
|
return self.get_or_create(guide_element_type=guide_element_type_qs.first(),
|
||||||
|
parent=root_node,
|
||||||
|
product=wine_qs.first(),
|
||||||
|
guide=root_node.guide,
|
||||||
|
review=review_qs.first())
|
||||||
|
return None, False
|
||||||
|
|
||||||
|
|
||||||
class GuideElementQuerySet(models.QuerySet):
|
class GuideElementQuerySet(models.QuerySet):
|
||||||
"""QuerySet for model Guide elements."""
|
"""QuerySet for model Guide elements."""
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ from rest_framework_recursive.fields import RecursiveField
|
||||||
from establishment.serializers import EstablishmentGuideElementSerializer
|
from establishment.serializers import EstablishmentGuideElementSerializer
|
||||||
from product.serializers import ProductGuideElementSerializer
|
from product.serializers import ProductGuideElementSerializer
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
from utils import exceptions
|
||||||
|
|
||||||
|
|
||||||
class CollectionBaseSerializer(serializers.ModelSerializer):
|
class CollectionBaseSerializer(serializers.ModelSerializer):
|
||||||
|
|
@ -203,3 +204,40 @@ class GuideElementBaseSerializer(serializers.ModelSerializer):
|
||||||
'parent',
|
'parent',
|
||||||
'label_photo',
|
'label_photo',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class AdvertorialBaseSerializer(serializers.ModelSerializer):
|
||||||
|
"""Serializer for model Advertorial."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta class."""
|
||||||
|
model = models.Advertorial
|
||||||
|
fields = [
|
||||||
|
'number_of_pages',
|
||||||
|
'right_pages',
|
||||||
|
'guide_element',
|
||||||
|
]
|
||||||
|
extra_kwargs = {
|
||||||
|
'guide_element': {'required': False}
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def request_kwargs(self):
|
||||||
|
return self.context.get('request').parser_context.get('kwargs')
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
# check existence in guide
|
||||||
|
guide = get_object_or_404(models.Guide.objects.all(),
|
||||||
|
pk=self.request_kwargs.get('pk'))
|
||||||
|
root_node = models.GuideElement.objects.get_root_node(guide)
|
||||||
|
guide_element_qs = root_node.get_children().filter(pk=self.request_kwargs.get('element_pk'))
|
||||||
|
guide_element = guide_element_qs.first()
|
||||||
|
|
||||||
|
if not guide_element_qs.exists():
|
||||||
|
raise exceptions.GuideElementError()
|
||||||
|
|
||||||
|
if models.Advertorial.objects.filter(guide_element=guide_element).exists():
|
||||||
|
raise exceptions.AdvertorialError()
|
||||||
|
|
||||||
|
attrs['guide_element'] = guide_element
|
||||||
|
return attrs
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,133 @@
|
||||||
"""Collectoin app celery tasks."""
|
"""Collection app celery tasks."""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from collection import models as collection_models
|
|
||||||
|
|
||||||
logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO)
|
logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# todo: finish this
|
def get_additional_establishment_data(section_node, establishment):
|
||||||
|
data = [
|
||||||
|
section_node.id,
|
||||||
|
establishment.id,
|
||||||
|
]
|
||||||
|
if establishment.last_published_review:
|
||||||
|
data.append(establishment.last_published_review.id)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def get_additional_product_data(section_node, product):
|
||||||
|
data = [
|
||||||
|
section_node.id,
|
||||||
|
product.id,
|
||||||
|
]
|
||||||
|
if product.last_published_review:
|
||||||
|
data.append(product.last_published_review.id)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def generate_guide_elements(guide_id: int):
|
def generate_establishment_guide_elements(guide_id: int, queryset_values: dict, section_node_name: str):
|
||||||
"""Send verification email to user."""
|
"""Generate guide elements."""
|
||||||
|
from collection.models import GuideElement, Guide
|
||||||
|
from establishment.models import Establishment
|
||||||
|
|
||||||
|
guide = Guide.objects.get(id=guide_id)
|
||||||
try:
|
try:
|
||||||
obj = collection_models.Guide.objects.get(id=guide_id)
|
for instance in queryset_values:
|
||||||
|
establishment_id = instance.get('id')
|
||||||
|
establishment_qs = Establishment.objects.filter(id=establishment_id)
|
||||||
|
if establishment_qs.exists():
|
||||||
|
establishment = establishment_qs.first()
|
||||||
|
root_node, _ = GuideElement.objects.get_or_create_root_node(guide_id)
|
||||||
|
if root_node:
|
||||||
|
city_node, _ = GuideElement.objects.get_or_create_city_node(root_node.id,
|
||||||
|
establishment.address.city_id)
|
||||||
|
if city_node:
|
||||||
|
section_node, _ = GuideElement.objects.get_or_create_establishment_section_node(
|
||||||
|
city_node.id,
|
||||||
|
section_node_name,
|
||||||
|
)
|
||||||
|
if section_node:
|
||||||
|
GuideElement.objects.get_or_create_establishment_node(
|
||||||
|
*get_additional_establishment_data(section_node=section_node,
|
||||||
|
establishment=establishment))
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
|
||||||
|
f'DETAIL: Guide ID {guide_id} - SectionNode is not exists.')
|
||||||
|
else:
|
||||||
|
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
|
||||||
|
f'DETAIL: Guide ID {guide_id} - CityNode is not exists.')
|
||||||
|
else:
|
||||||
|
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
|
||||||
|
f'DETAIL: Guide ID {guide_id} - RootNode is not exists.')
|
||||||
|
else:
|
||||||
|
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
|
||||||
|
f'DETAIL: Guide ID {guide_id} - Establishment {establishment_id} id is not exists.')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f'METHOD_NAME: {generate_guide_elements.__name__}\n'
|
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
|
||||||
f'DETAIL: guide {guide_id}, - {e}')
|
f'DETAIL: Guide ID {guide_id} - {e}')
|
||||||
|
else:
|
||||||
|
guide.update_count_related_objects()
|
||||||
|
# Update tree indexes
|
||||||
|
GuideElement._tree_manager.rebuild()
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def generate_product_guide_elements(guide_id: int, queryset_values: dict):
|
||||||
|
"""Generate guide elements."""
|
||||||
|
from collection.models import GuideElement, Guide
|
||||||
|
from product.models import Product
|
||||||
|
|
||||||
|
guide = Guide.objects.get(id=guide_id)
|
||||||
|
try:
|
||||||
|
for instance in queryset_values:
|
||||||
|
wine_id = instance.get('id')
|
||||||
|
wine_qs = Product.objects.filter(id=wine_id)
|
||||||
|
if wine_qs.exists():
|
||||||
|
wine = wine_qs.first()
|
||||||
|
root_node, _ = GuideElement.objects.get_or_create_root_node(guide_id)
|
||||||
|
if root_node:
|
||||||
|
wine_region_node, _ = GuideElement.objects.get_or_create_wine_region_node(
|
||||||
|
root_node.id,
|
||||||
|
wine.wine_region.id)
|
||||||
|
if wine_region_node:
|
||||||
|
yard_node, _ = GuideElement.objects.get_or_create_yard_node(
|
||||||
|
product_id=wine.id,
|
||||||
|
wine_region_node_id=wine_region_node.id
|
||||||
|
)
|
||||||
|
if yard_node:
|
||||||
|
wine_color_qs = wine.wine_colors
|
||||||
|
if wine_color_qs.exists():
|
||||||
|
wine_color_section, _ = GuideElement.objects.get_or_create_color_wine_section_node(
|
||||||
|
wine_color_name=wine_color_qs.first().value,
|
||||||
|
yard_node_id=yard_node.id
|
||||||
|
)
|
||||||
|
if wine_color_section:
|
||||||
|
GuideElement.objects.get_or_create_wine_node(
|
||||||
|
*get_additional_product_data(
|
||||||
|
section_node=wine_color_section,
|
||||||
|
product=wine))
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
||||||
|
f'DETAIL: Guide ID {guide_id} - Wine {wine.id} has not colors.')
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
||||||
|
f'DETAIL: Guide ID {guide_id} - WineRegionNode is not exists.')
|
||||||
|
else:
|
||||||
|
logger.error(f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
||||||
|
f'DETAIL: Guide ID {guide_id} - RootNode is not exists.')
|
||||||
|
else:
|
||||||
|
logger.error(f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
||||||
|
f'DETAIL: Guide ID {guide_id} - Product {wine_id} id is not exists.')
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
||||||
|
f'DETAIL: Guide ID {guide_id} - {e}')
|
||||||
|
else:
|
||||||
|
guide.update_count_related_objects()
|
||||||
|
# Update tree indexes
|
||||||
|
GuideElement._tree_manager.rebuild()
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ urlpatterns = [
|
||||||
name='guide-list-create'),
|
name='guide-list-create'),
|
||||||
path('guides/<int:pk>/', views.GuideElementListView.as_view(),
|
path('guides/<int:pk>/', views.GuideElementListView.as_view(),
|
||||||
name='guide-element-list'),
|
name='guide-element-list'),
|
||||||
|
path('guides/<int:pk>/element/<int:element_pk>/advertorial/', views.AdvertorialCreateDestroyView.as_view(),
|
||||||
|
name='guide-advertorial-create-destroy'),
|
||||||
path('guides/<int:pk>/filters/', views.GuideFilterCreateView.as_view(),
|
path('guides/<int:pk>/filters/', views.GuideFilterCreateView.as_view(),
|
||||||
name='guide-filter-list-create'),
|
name='guide-filter-list-create'),
|
||||||
] + router.urls
|
] + router.urls
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,14 @@ class GuideElementBaseView(generics.GenericAPIView):
|
||||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||||
|
|
||||||
|
|
||||||
|
class AdvertorialBaseView(generics.GenericAPIView):
|
||||||
|
"""Base view for Advertorial model."""
|
||||||
|
pagination_class = None
|
||||||
|
queryset = models.Advertorial.objects.all()
|
||||||
|
serializer_class = serializers.AdvertorialBaseSerializer
|
||||||
|
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||||
|
|
||||||
|
|
||||||
class CollectionBackOfficeViewSet(mixins.CreateModelMixin,
|
class CollectionBackOfficeViewSet(mixins.CreateModelMixin,
|
||||||
mixins.UpdateModelMixin,
|
mixins.UpdateModelMixin,
|
||||||
mixins.DestroyModelMixin,
|
mixins.DestroyModelMixin,
|
||||||
|
|
@ -118,3 +126,9 @@ class GuideElementListView(GuideElementBaseView,
|
||||||
guide = get_object_or_404(models.Guide.objects.all(), pk=self.kwargs.get('pk'))
|
guide = get_object_or_404(models.Guide.objects.all(), pk=self.kwargs.get('pk'))
|
||||||
return models.GuideElement.objects.get_root_node(guide) \
|
return models.GuideElement.objects.get_root_node(guide) \
|
||||||
.get_descendants()
|
.get_descendants()
|
||||||
|
|
||||||
|
|
||||||
|
class AdvertorialCreateDestroyView(AdvertorialBaseView,
|
||||||
|
generics.CreateAPIView,
|
||||||
|
generics.DestroyAPIView):
|
||||||
|
"""View for model Advertorial for back office users."""
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ class ProductQuerySet(models.QuerySet):
|
||||||
return self.filter(category=self.model.ONLINE)
|
return self.filter(category=self.model.ONLINE)
|
||||||
|
|
||||||
def wines(self):
|
def wines(self):
|
||||||
return self.filter(type__index_name__icontains=ProductType.WINE)
|
return self.filter(product_type__index_name__icontains=ProductType.WINE)
|
||||||
|
|
||||||
def without_current_product(self, current_product: str):
|
def without_current_product(self, current_product: str):
|
||||||
"""Exclude by current product."""
|
"""Exclude by current product."""
|
||||||
|
|
@ -383,6 +383,11 @@ class Product(GalleryMixin, TranslatedFieldsMixin, BaseAttributes,
|
||||||
if self.main_image:
|
if self.main_image:
|
||||||
return self.main_image.get_image_url(thumbnail_key='product_preview')
|
return self.main_image.get_image_url(thumbnail_key='product_preview')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def wine_region(self):
|
||||||
|
if self.wine_origins.exists():
|
||||||
|
return self.wine_origins.first().wine_region
|
||||||
|
|
||||||
|
|
||||||
class OnlineProductManager(ProductManager):
|
class OnlineProductManager(ProductManager):
|
||||||
"""Extended manger for OnlineProduct model."""
|
"""Extended manger for OnlineProduct model."""
|
||||||
|
|
|
||||||
|
|
@ -179,3 +179,21 @@ class UnprocessableEntityError(exceptions.APIException):
|
||||||
"""
|
"""
|
||||||
status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
|
status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
|
||||||
default_detail = _('Unprocessable entity valid.')
|
default_detail = _('Unprocessable entity valid.')
|
||||||
|
|
||||||
|
|
||||||
|
class GuideElementError(exceptions.APIException):
|
||||||
|
"""
|
||||||
|
The exception should be raised when user tries send guide element that doesn't
|
||||||
|
valid for guide.
|
||||||
|
"""
|
||||||
|
status_code = status.HTTP_400_BAD_REQUEST
|
||||||
|
default_detail = _('Guide element not valid for Guide.')
|
||||||
|
|
||||||
|
|
||||||
|
class AdvertorialError(exceptions.APIException):
|
||||||
|
"""
|
||||||
|
The exception should be raised when user tries create advertorial for guide element
|
||||||
|
that already exists.
|
||||||
|
"""
|
||||||
|
status_code = status.HTTP_400_BAD_REQUEST
|
||||||
|
default_detail = _('Advertorial already exists for this guide element.')
|
||||||
|
|
|
||||||
|
|
@ -140,4 +140,18 @@ def dictfetchall(cursor):
|
||||||
return [
|
return [
|
||||||
dict(zip(columns, row))
|
dict(zip(columns, row))
|
||||||
for row in cursor.fetchall()
|
for row in cursor.fetchall()
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def slug_into_section_name(slug: str, postfix: str = 'SectionNode'):
|
||||||
|
"""
|
||||||
|
Transform slug into section name, i.e:
|
||||||
|
like
|
||||||
|
"EffervescentRoseDeSaigneeSectionNode"
|
||||||
|
from
|
||||||
|
"effervescent-rose-de-saignee"
|
||||||
|
"""
|
||||||
|
re_exp = r'[\w]+'
|
||||||
|
result = re.findall(re_exp, slug)
|
||||||
|
if result:
|
||||||
|
return f"{''.join([i.capitalize() for i in result])}{postfix}"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user