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
from django.conf import settings
from django.contrib.contenttypes.fields import ContentType
from django.contrib.postgres.fields import JSONField
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from mptt.models import MPTTModel, TreeForeignKey
from slugify import slugify
from collection import tasks
from establishment.models import Establishment
from location.models import Country, Region, WineRegion, WineSubRegion, City
from product.models import Product
from review.models import Review
from collection import tasks
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 (
ProjectBaseMixin, TJSONField, TranslatedFieldsMixin,
URLImageMixin, IntermediateGalleryModelMixin
@ -177,9 +177,13 @@ class GuideQuerySet(models.QuerySet):
return self.with_base_related().prefetch_related('guideelement_set')
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)
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):
"""Annotate flag if GuideElement in RestaurantSectionNode."""
restaurant_guides = models.Subquery(
@ -420,6 +424,32 @@ class Guide(ProjectBaseMixin, CollectionNameMixin, CollectionDateMixin):
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):
# get Root node
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)
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(
name=wine_color_name,
@ -972,6 +1002,14 @@ class GuideElementQuerySet(models.QuerySet):
"""Return QuerySet with descendants."""
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):
"""Frozen state of elements of guide instance."""

View File

@ -2,7 +2,8 @@
import logging
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)
logger = logging.getLogger(__name__)
@ -31,7 +32,7 @@ def get_additional_product_data(section_node, product):
@shared_task
def generate_establishment_guide_elements(guide_id: int, filter_set: dict):
"""Generate guide elements."""
from collection.models import GuideElement, Guide
from collection.models import Guide
from establishment.models import Establishment
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()
try:
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,
transform_into_readable_str(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:
logger.error(
f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
f'DETAIL: Guide ID {guide_id} - SectionNode is not exists.')
populate_establishment_guide(guide_id, instance.get('id'))
except Exception as e:
guide.change_state(Guide.WAITING)
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
f'DETAIL: Guide ID {guide_id} - {e}')
else:
guide.update_count_related_objects()
guide.change_state(Guide.BUILT)
@shared_task
def populate_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.BUILDING)
try:
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,
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:
logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
f'DETAIL: Guide ID {guide_id} - CityNode is not exists.')
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} - RootNode is not exists.')
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:
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)
@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
def generate_product_guide_elements(guide_id: int, filter_set: dict):
"""Generate guide elements."""

View File

@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from account.serializers.common import UserShortSerializer
from collection.models import Guide
from establishment import models, serializers as model_serializers
from establishment.models import ContactEmail, ContactPhone, EstablishmentEmployee
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):
"""Establishments from employee serializer"""

View File

@ -30,6 +30,8 @@ urlpatterns = [
name='note-rud'),
path('slug/<slug:slug>/admin/', views.EstablishmentAdminView.as_view(),
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/<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'),

View File

@ -1,6 +1,6 @@
"""Establishment app views."""
from django.db.models.query_utils import Q
from django.db import transaction
from django.db.models.query_utils import Q
from django.http import Http404
from django.shortcuts import get_object_or_404
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 account.models import User
from collection.models import Guide
from establishment import filters, models, serializers
from establishment.models import EstablishmentEmployee, Menu
from timetable.models import Timetable
@ -616,6 +617,82 @@ class EstablishmentAdminView(generics.ListAPIView):
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):
"""Menu (dessert, main_course, starter) list create view."""
serializer_class = serializers.MenuDishesSerializer

View File

@ -1,12 +1,13 @@
"""Utils app method."""
import logging
import pathlib
import random
import re
import string
from collections import namedtuple
from functools import reduce
from io import BytesIO
import pathlib
import requests
from PIL import Image
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:
like
"EffervescentRoseDeSaigneeSectionNode"
"EffervescentRoseDeSaignee"
from
"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])}"
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):
"""
Transform str, i.e: