"""Tag views.""" from django.conf import settings from rest_framework import generics, mixins, permissions, status, viewsets from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.serializers import ValidationError from django.utils.translation import gettext_lazy as _ from search_indexes import views as search_views from location.models import WineRegion from product.models import ProductType from tag import filters, models, serializers class ChosenTagsView(generics.ListAPIView, viewsets.GenericViewSet): pagination_class = None permission_classes = (permissions.AllowAny,) serializer_class = serializers.TagBaseSerializer filterset_class = filters.TagsFilterSet queryset = models.Tag.objects.all() def get_queryset(self): result_tags_ids = models.ChosenTagSettings.objects \ .by_country_code(self.request.country_code) \ .values('tag_id') return models.Tag.objects \ .filter(id__in=result_tags_ids) \ .order_by_priority() def list(self, request, *args, **kwargs): # TMP TODO remove it later # Временный хардкод для демонстрации > 15 ноября, потом удалить! queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) result_list = serializer.data if request.query_params.get('type') and (settings.ESTABLISHMENT_CHOSEN_TAGS or settings.NEWS_CHOSEN_TAGS): ordered_list = settings.ESTABLISHMENT_CHOSEN_TAGS if request.query_params.get( 'type') == 'establishment' else settings.NEWS_CHOSEN_TAGS result_list = sorted(result_list, key=lambda x: ordered_list.index(x['index_name'])) return Response(result_list) # User`s views & viewsets class TagCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): """ViewSet for TagCategory model.""" filterset_class = filters.TagCategoryFilterSet pagination_class = None permission_classes = (permissions.AllowAny,) queryset = models.TagCategory.objects.with_tags().with_base_related(). \ distinct() serializer_class = serializers.TagCategoryBaseSerializer # User`s views & viewsets class FiltersTagCategoryViewSet(TagCategoryViewSet): """ViewSet for TagCategory model.""" serializer_class = serializers.FiltersTagCategoryBaseSerializer def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) serializer = self.get_serializer(queryset, many=True) result_list = serializer.data query_params = request.query_params params_type = query_params.get('type') if query_params.get('establishment_type'): params_type = query_params.get('establishment_type') elif query_params.get('product_type'): params_type = query_params.get('product_type') week_days = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday") flags = ('toque_number', 'wine_region', 'works_noon', 'works_evening', 'works_now', 'works_at_weekday') filter_flags = {flag_name: False for flag_name in flags} additional_flags = [] if params_type == 'restaurant': additional_flags += ['toque_number', 'works_noon', 'works_evening', 'works_now'] elif params_type in ['winery', 'wine']: additional_flags += ['wine_region'] elif params_type == 'artisan': additional_flags += ['works_now', 'works_at_weekday'] for flag_name in additional_flags: filter_flags[flag_name] = True if filter_flags['toque_number']: toques = { "index_name": "toque_number", "label_translated": "Toques", "param_name": "toque_number__in", "filters": [{ "id": toque_id, "index_name": "toque_%d" % toque_id, "label_translated": "Toque %d" % toque_id } for toque_id in range(6)] } result_list.append(toques) if request.query_params.get('product_type') == ProductType.WINE: wine_region_id = query_params.get('wine_region_id__in') if str(wine_region_id).isdigit(): queryset = WineRegion.objects.filter(id=int(wine_region_id)) else: queryset = WineRegion.objects.all() wine_regions = { "index_name": "wine_region", "label_translated": "Wine region", "param_name": "wine_region_id__in", "filters": [{ "id": obj.id, "index_name": obj.name.lower().replace(' ', '_'), "label_translated": obj.name } for obj in queryset] } result_list.append(wine_regions) if filter_flags['works_noon']: works_noon = { "index_name": "works_noon", "label_translated": "Open noon", "param_name": "works_noon__in", "filters": [{ "id": weekday, "index_name": week_days[weekday].lower(), "label_translated": week_days[weekday] } for weekday in range(7)] } result_list.append(works_noon) if filter_flags['works_evening']: works_evening = { "index_name": "works_evening", "label_translated": "Open evening", "param_name": "works_evening__in", "filters": [{ "id": weekday, "index_name": week_days[weekday].lower(), "label_translated": week_days[weekday] } for weekday in range(7)] } result_list.append(works_evening) if filter_flags['works_now']: works_now = { "index_name": "open_now", "label_translated": "Open now", "param_name": "open_now", "type": True } result_list.append(works_now) if filter_flags['works_at_weekday']: works_at_weekday = { "index_name": "works_at_weekday", "label_translated": "Works at weekday", "param_name": "works_at_weekday__in", "filters": [{ "id": weekday, "index_name": week_days[weekday].lower(), "label_translated": week_days[weekday] } for weekday in range(7)] } result_list.append(works_at_weekday) search_view_class = self.define_search_view_by_request(request) facets = search_view_class.as_view({'get': 'list'})(self.mutate_request(self.request)).data['facets'] return Response(self.remove_empty_filters(result_list, facets)) @staticmethod def mutate_request(request): """Remove all filtering get params and remove s_ from the rest of them""" request.GET._mutable = True for name in request.query_params.copy().keys(): value = request.query_params.pop(name) if name.startswith('s_'): request.query_params[name[2:]] = value[0] request.GET._mutable = False return request._request @staticmethod def define_search_view_by_request(request): request.GET._mutable = True if request.query_params.get('items'): items = request.query_params.pop('items')[0] else: raise ValidationError({'detail': _('Missing required "items" parameter')}) item_to_class = { 'news': search_views.NewsDocumentViewSet, 'establishments': search_views.EstablishmentDocumentViewSet, 'products': search_views.ProductDocumentViewSet, } klass = item_to_class.get(items) if klass is None: raise ValidationError({'detail': _('news/establishments/products')}) request.GET._mutable = False return klass @staticmethod def remove_empty_filters(filters, facets): return filters # BackOffice user`s views & viewsets class BindObjectMixin: """Bind object mixin.""" def get_serializer_class(self): if self.action == 'bind_object': return self.bind_object_serializer_class return self.serializer_class def perform_binding(self, serializer): raise NotImplemented def perform_unbinding(self, serializer): raise NotImplemented @action(methods=['post', 'delete'], detail=True, url_path='bind-object') def bind_object(self, request, pk=None): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) if request.method == 'POST': self.perform_binding(serializer) return Response(serializer.data, status=status.HTTP_201_CREATED) elif request.method == 'DELETE': self.perform_unbinding(serializer) return Response(status=status.HTTP_204_NO_CONTENT) class TagBackOfficeViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, BindObjectMixin, viewsets.GenericViewSet): """List/create tag view.""" pagination_class = None permission_classes = (permissions.IsAuthenticated,) queryset = models.Tag.objects.all() serializer_class = serializers.TagBackOfficeSerializer bind_object_serializer_class = serializers.TagBindObjectSerializer def perform_binding(self, serializer): data = serializer.validated_data tag = data.pop('tag') obj_type = data.get('type') related_object = data.get('related_object') if obj_type == self.bind_object_serializer_class.ESTABLISHMENT: tag.establishments.add(related_object) elif obj_type == self.bind_object_serializer_class.NEWS: tag.news.add(related_object) def perform_unbinding(self, serializer): data = serializer.validated_data tag = data.pop('tag') obj_type = data.get('type') related_object = data.get('related_object') if obj_type == self.bind_object_serializer_class.ESTABLISHMENT: tag.establishments.remove(related_object) elif obj_type == self.bind_object_serializer_class.NEWS: tag.news.remove(related_object) class TagCategoryBackOfficeViewSet(mixins.CreateModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.RetrieveModelMixin, BindObjectMixin, TagCategoryViewSet): """ViewSet for TagCategory model for BackOffice users.""" permission_classes = (permissions.IsAuthenticated,) queryset = TagCategoryViewSet.queryset.with_extended_related() serializer_class = serializers.TagCategoryBackOfficeDetailSerializer bind_object_serializer_class = serializers.TagCategoryBindObjectSerializer def perform_binding(self, serializer): data = serializer.validated_data tag_category = data.pop('tag_category') obj_type = data.get('type') related_object = data.get('related_object') if obj_type == self.bind_object_serializer_class.ESTABLISHMENT_TYPE: tag_category.establishment_types.add(related_object) elif obj_type == self.bind_object_serializer_class.NEWS_TYPE: tag_category.news_types.add(related_object) def perform_unbinding(self, serializer): data = serializer.validated_data tag_category = data.pop('tag_category') obj_type = data.get('type') related_object = data.get('related_object') if obj_type == self.bind_object_serializer_class.ESTABLISHMENT_TYPE: tag_category.establishment_types.remove(related_object) elif obj_type == self.bind_object_serializer_class.NEWS_TYPE: tag_category.news_types.remove(related_object)