added endpoint to add/remove establishment from guide

This commit is contained in:
Anatoly 2020-01-31 18:25:06 +03:00
parent 202b8c8d96
commit 8df96716cc
6 changed files with 231 additions and 35 deletions

View File

@ -1,21 +1,21 @@
import re import re
from django.conf import settings
from django.contrib.contenttypes.fields import ContentType from django.contrib.contenttypes.fields import ContentType
from django.contrib.postgres.fields import JSONField 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.conf import settings
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from mptt.models import MPTTModel, TreeForeignKey from mptt.models import MPTTModel, TreeForeignKey
from slugify import slugify from slugify import slugify
from collection import tasks
from establishment.models import Establishment from establishment.models import Establishment
from location.models import Country, Region, WineRegion, WineSubRegion, City from location.models import Country, Region, WineRegion, WineSubRegion, City
from product.models import Product from product.models import Product
from review.models import Review from review.models import Review
from collection import tasks
from translation.models import Language from translation.models import Language
from utils.methods import transform_into_readable_str from utils.methods import transform_into_section_name
from utils.models import ( from utils.models import (
ProjectBaseMixin, TJSONField, TranslatedFieldsMixin, ProjectBaseMixin, TJSONField, TranslatedFieldsMixin,
URLImageMixin, IntermediateGalleryModelMixin URLImageMixin, IntermediateGalleryModelMixin
@ -177,9 +177,13 @@ class GuideQuerySet(models.QuerySet):
return self.with_base_related().prefetch_related('guideelement_set') return self.with_base_related().prefetch_related('guideelement_set')
def by_country_id(self, country_id): def by_country_id(self, country_id):
"""Return QuerySet filtered by country code.""" """Return QuerySet filtered by country id."""
return self.filter(country_json__id__contains=country_id) return self.filter(country_json__id__contains=country_id)
def by_country_code(self, country_code):
"""Return QuerySet filtered by country code."""
return self.filter(site__country__code=country_code)
def annotate_in_restaurant_section(self): def annotate_in_restaurant_section(self):
"""Annotate flag if GuideElement in RestaurantSectionNode.""" """Annotate flag if GuideElement in RestaurantSectionNode."""
restaurant_guides = models.Subquery( restaurant_guides = models.Subquery(
@ -420,6 +424,32 @@ class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
filter_set=self.guidefilter.product_filter_set, filter_set=self.guidefilter.product_filter_set,
) )
def extend_establishment_guide(self, establishment_id: int):
if self.guide_type in [self.ARTISAN, self.RESTAURANT]:
if settings.USE_CELERY:
tasks.populate_establishment_guide.delay(
guide_id=self.id,
establishment_id=establishment_id,
)
else:
tasks.populate_establishment_guide(
guide_id=self.id,
establishment_id=establishment_id,
)
def remove_establishment(self, establishment_id: int):
if self.guide_type in [self.ARTISAN, self.RESTAURANT]:
if settings.USE_CELERY:
tasks.remove_establishment_guide.delay(
guide_id=self.id,
establishment_id=establishment_id,
)
else:
tasks.remove_establishment_guide(
guide_id=self.id,
establishment_id=establishment_id,
)
def regenerate_elements(self): def regenerate_elements(self):
# get Root node # get Root node
root_node = GuideElement.objects.get_root_node(self) root_node = GuideElement.objects.get_root_node(self)
@ -905,7 +935,7 @@ class GuideElementManager(models.Manager):
parent_node_qs = GuideElement.objects.filter(id=yard_node_id) parent_node_qs = GuideElement.objects.filter(id=yard_node_id)
if not wine_color_name.endswith('SectionNode'): if not wine_color_name.endswith('SectionNode'):
wine_color_name = transform_into_readable_str(wine_color_name) wine_color_name = transform_into_section_name(wine_color_name)
wine_color_section, _ = GuideWineColorSection.objects.get_or_create( wine_color_section, _ = GuideWineColorSection.objects.get_or_create(
name=wine_color_name, name=wine_color_name,
@ -972,6 +1002,14 @@ class GuideElementQuerySet(models.QuerySet):
"""Return QuerySet with descendants.""" """Return QuerySet with descendants."""
return self.exclude(guide_element_type__name='Root') return self.exclude(guide_element_type__name='Root')
def by_establishment_type(self, establishment_type_index_name: str):
"""Return QuerySet by establishment_type."""
return self.filter(
guide_element_type__name__iexact=transform_into_section_name(
establishment_type_index_name
)
)
class GuideElement(ProjectBaseMixin, MPTTModel): class GuideElement(ProjectBaseMixin, MPTTModel):
"""Frozen state of elements of guide instance.""" """Frozen state of elements of guide instance."""

View File

@ -2,7 +2,8 @@
import logging import logging
from celery import shared_task from celery import shared_task
from utils.methods import transform_into_readable_str
from utils.methods import transform_into_section_name
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__)
@ -31,7 +32,7 @@ def get_additional_product_data(section_node, product):
@shared_task @shared_task
def generate_establishment_guide_elements(guide_id: int, filter_set: dict): def generate_establishment_guide_elements(guide_id: int, filter_set: dict):
"""Generate guide elements.""" """Generate guide elements."""
from collection.models import GuideElement, Guide from collection.models import Guide
from establishment.models import Establishment from establishment.models import Establishment
guide = Guide.objects.get(id=guide_id) guide = Guide.objects.get(id=guide_id)
@ -39,35 +40,53 @@ def generate_establishment_guide_elements(guide_id: int, filter_set: dict):
queryset_values = Establishment.objects.filter(**filter_set).values() queryset_values = Establishment.objects.filter(**filter_set).values()
try: try:
for instance in queryset_values: for instance in queryset_values:
establishment_id = instance.get('id') populate_establishment_guide(guide_id, instance.get('id'))
establishment_qs = Establishment.objects.filter(id=establishment_id) except Exception as e:
if establishment_qs.exists(): guide.change_state(Guide.WAITING)
establishment = establishment_qs.first() logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
root_node, _ = GuideElement.objects.get_or_create_root_node(guide_id) f'DETAIL: Guide ID {guide_id} - {e}')
if root_node: else:
city_node, _ = GuideElement.objects.get_or_create_city_node(root_node.id, guide.update_count_related_objects()
establishment.address.city_id) guide.change_state(Guide.BUILT)
if city_node:
section_node, _ = GuideElement.objects.get_or_create_establishment_section_node(
city_node.id, @shared_task
transform_into_readable_str(establishment.establishment_type.index_name), def populate_establishment_guide(guide_id: int, establishment_id: int):
) """Extend guide."""
if section_node: from collection.models import GuideElement, Guide
GuideElement.objects.get_or_create_establishment_node( from establishment.models import Establishment
*get_additional_establishment_data(section_node=section_node,
establishment=establishment)) guide = Guide.objects.get(id=guide_id)
else: guide.change_state(Guide.BUILDING)
logger.error( try:
f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n' establishment_qs = Establishment.objects.filter(id=establishment_id)
f'DETAIL: Guide ID {guide_id} - SectionNode is not exists.') 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,
transform_into_section_name(establishment.establishment_type.index_name),
)
if section_node:
GuideElement.objects.get_or_create_establishment_node(
*get_additional_establishment_data(section_node=section_node,
establishment=establishment))
else: else:
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n' logger.error(
f'DETAIL: Guide ID {guide_id} - CityNode is not exists.') f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
f'DETAIL: Guide ID {guide_id} - SectionNode is not exists.')
else: else:
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n' logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
f'DETAIL: Guide ID {guide_id} - RootNode is not exists.') f'DETAIL: Guide ID {guide_id} - CityNode is not exists.')
else: else:
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n' 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.') f'DETAIL: Guide ID {guide_id} - Establishment {establishment_id} id is not exists.')
except Exception as e: except Exception as e:
guide.change_state(Guide.WAITING) guide.change_state(Guide.WAITING)
@ -78,6 +97,42 @@ def generate_establishment_guide_elements(guide_id: int, filter_set: dict):
guide.change_state(Guide.BUILT) guide.change_state(Guide.BUILT)
@shared_task
def remove_establishment_guide(guide_id: int, establishment_id: int):
"""Extend guide."""
from collection.models import GuideElement, Guide
from establishment.models import Establishment
guide = Guide.objects.get(id=guide_id)
guide.change_state(Guide.REMOVING)
try:
establishment_qs = Establishment.objects.filter(id=establishment_id)
if establishment_qs.exists():
establishment = establishment_qs.first()
if hasattr(establishment, 'establishment_type') and establishment.establishment_type.index_name:
establishment_node_qs = GuideElement.objects.filter(establishment=establishment,
guide=guide)
if establishment_node_qs.exists():
establishment_node_qs.first().delete()
else:
logger.error(f'METHOD_NAME: {remove_establishment_guide.__name__}\n'
f'DETAIL: Guide ID {guide_id} - EstablishmentNode {establishment_id} id is not exists.')
else:
logger.error(f'METHOD_NAME: {remove_establishment_guide.__name__}\n'
f'DETAIL: Guide ID {guide_id} - Establishment {establishment_id} '
f'has not establishment type or establishment type index name.')
else:
logger.error(f'METHOD_NAME: {remove_establishment_guide.__name__}\n'
f'DETAIL: Guide ID {guide_id} - Establishment {establishment_id} id is not exists.')
except Exception as e:
guide.change_state(Guide.WAITING)
logger.error(f'METHOD_NAME: {remove_establishment_guide.__name__}\n'
f'DETAIL: Guide ID {guide_id} - {e}')
else:
guide.update_count_related_objects()
guide.change_state(Guide.BUILT)
@shared_task @shared_task
def generate_product_guide_elements(guide_id: int, filter_set: dict): def generate_product_guide_elements(guide_id: int, filter_set: dict):
"""Generate guide elements.""" """Generate guide elements."""

View File

@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from account.serializers.common import UserShortSerializer from account.serializers.common import UserShortSerializer
from collection.models import Guide
from establishment import models, serializers as model_serializers from establishment import models, serializers as model_serializers
from establishment.models import ContactEmail, ContactPhone, EstablishmentEmployee from establishment.models import ContactEmail, ContactPhone, EstablishmentEmployee
from gallery.models import Image from gallery.models import Image
@ -582,6 +583,17 @@ class EstablishmentAdminListSerializer(UserShortSerializer):
] ]
class EstablishmentGuideSerializer(serializers.ModelSerializer):
"""Serializer for model Guide. """
class Meta:
"""Meta class."""
model = Guide
fields = [
'id',
]
class EstablishmentEmployeePositionsSerializer(serializers.ModelSerializer): class EstablishmentEmployeePositionsSerializer(serializers.ModelSerializer):
"""Establishments from employee serializer""" """Establishments from employee serializer"""

View File

@ -30,6 +30,8 @@ urlpatterns = [
name='note-rud'), name='note-rud'),
path('slug/<slug:slug>/admin/', views.EstablishmentAdminView.as_view(), path('slug/<slug:slug>/admin/', views.EstablishmentAdminView.as_view(),
name='establishment-admin-list'), name='establishment-admin-list'),
path('slug/<slug:slug>/guides/<int:guide_id>/', views.EstablishmentGuideCreateDestroyView.as_view(),
name='guide-list-create'),
path('menus/dishes/', views.MenuDishesListCreateView.as_view(), name='menu-dishes-list'), path('menus/dishes/', views.MenuDishesListCreateView.as_view(), name='menu-dishes-list'),
path('menus/dishes/<int:pk>/', views.MenuDishesRUDView.as_view(), name='menu-dishes-rud'), path('menus/dishes/<int:pk>/', views.MenuDishesRUDView.as_view(), name='menu-dishes-rud'),
path('menus/dishes/<int:pk>/gallery/', views.MenuGalleryListView.as_view(), name='menu-dishes-gallery-list'), path('menus/dishes/<int:pk>/gallery/', views.MenuGalleryListView.as_view(), name='menu-dishes-gallery-list'),

View File

@ -1,6 +1,6 @@
"""Establishment app views.""" """Establishment app views."""
from django.db.models.query_utils import Q
from django.db import transaction from django.db import transaction
from django.db.models.query_utils import Q
from django.http import Http404 from django.http import Http404
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
@ -8,6 +8,7 @@ from rest_framework import generics, response, status
from rest_framework.response import Response from rest_framework.response import Response
from account.models import User from account.models import User
from collection.models import Guide
from establishment import filters, models, serializers from establishment import filters, models, serializers
from establishment.models import EstablishmentEmployee, Menu from establishment.models import EstablishmentEmployee, Menu
from timetable.models import Timetable from timetable.models import Timetable
@ -616,6 +617,82 @@ class EstablishmentAdminView(generics.ListAPIView):
return User.objects.establishment_admin(establishment).distinct() return User.objects.establishment_admin(establishment).distinct()
class EstablishmentGuideCreateDestroyView(generics.GenericAPIView):
"""Add/Remove establishment from guide."""
establishment_lookup_url_kwarg = 'slug'
guide_lookup_url_kwarg = 'guide_id'
permission_classes = get_permission_classes(
IsEstablishmentManager,
IsEstablishmentAdministrator
)
def get_establishment_queryset(self):
"""Get Establishment queryset."""
return EstablishmentMixinViews.get_queryset(self)
def get_guide_queryset(self):
"""Get Guide queryset."""
queryset = Guide.objects
if hasattr(self, 'request') and hasattr(self.request, 'country_code'):
return queryset.by_country_code(self.request.country_code)
return queryset.none()
def get_establishment(self):
queryset = self.get_establishment_queryset()
# Perform the lookup filtering.
lookup_url_kwarg = getattr(self, 'establishment_lookup_url_kwarg', None)
assert lookup_url_kwarg and lookup_url_kwarg in self.kwargs, (
'Expected view %s to be called with a URL keyword argument '
'named "%s". Fix your URL conf, or set the `.lookup_field` '
'attribute on the view correctly.' %
(self.__class__.__name__, lookup_url_kwarg)
)
filters = {'klass': queryset, lookup_url_kwarg: self.kwargs.get(lookup_url_kwarg)}
obj = get_object_or_404(**filters)
# May raise a permission denied
self.check_object_permissions(self.request, obj)
return obj
def get_guide(self):
queryset = self.get_guide_queryset()
# Perform the lookup filtering.
lookup_url_kwarg = getattr(self, 'guide_lookup_url_kwarg', None)
assert lookup_url_kwarg and lookup_url_kwarg in self.kwargs, (
'Expected view %s to be called with a URL keyword argument '
'named "%s". Fix your URL conf, or set the `.lookup_field` '
'attribute on the view correctly.' %
(self.__class__.__name__, lookup_url_kwarg)
)
obj = get_object_or_404(klass=queryset, id=self.kwargs.get(lookup_url_kwarg))
# May raise a permission denied
self.check_object_permissions(self.request, obj)
return obj
def post(self, request, *args, **kwargs):
"""Implement GET-method."""
establishment = self.get_establishment()
guide = self.get_guide()
guide.extend_establishment_guide(establishment.id)
return Response(status=status.HTTP_200_OK)
def delete(self, request, *args, **kwargs):
"""Implement DELETE-method."""
establishment = self.get_establishment()
guide = self.get_guide()
guide.remove_establishment(establishment.id)
return Response(status=status.HTTP_204_NO_CONTENT)
class MenuDishesListCreateView(generics.ListCreateAPIView): class MenuDishesListCreateView(generics.ListCreateAPIView):
"""Menu (dessert, main_course, starter) list create view.""" """Menu (dessert, main_course, starter) list create view."""
serializer_class = serializers.MenuDishesSerializer serializer_class = serializers.MenuDishesSerializer

View File

@ -1,12 +1,13 @@
"""Utils app method.""" """Utils app method."""
import logging import logging
import pathlib
import random import random
import re import re
import string import string
from collections import namedtuple from collections import namedtuple
from functools import reduce from functools import reduce
from io import BytesIO from io import BytesIO
import pathlib
import requests import requests
from PIL import Image from PIL import Image
from django.conf import settings from django.conf import settings
@ -169,11 +170,11 @@ def dictfetchall(cursor):
] ]
def transform_into_readable_str(raw_string: str, postfix: str = 'SectionNode'): def transform_into_readable_str(raw_string: str):
""" """
Transform slug into section name, i.e: Transform slug into section name, i.e:
like like
"EffervescentRoseDeSaigneeSectionNode" "EffervescentRoseDeSaignee"
from from
"effervescent-rose-de-saignee" "effervescent-rose-de-saignee"
""" """
@ -183,6 +184,17 @@ def transform_into_readable_str(raw_string: str, postfix: str = 'SectionNode'):
return f"{''.join([i.capitalize() for i in result])}" return f"{''.join([i.capitalize() for i in result])}"
def transform_into_section_name(raw_string: str, postfix: str = 'SectionNode'):
"""
Transform slug into section name, i.e:
like
"EffervescentRoseDeSaigneeSectionNode"
from
"effervescent-rose-de-saignee"
"""
return f'{transform_into_readable_str(raw_string)}{postfix}'
def transform_camelcase_to_underscore(raw_string: str): def transform_camelcase_to_underscore(raw_string: str):
""" """
Transform str, i.e: Transform str, i.e: