From 7ba391300049824f3957b3af02bf5f611772abf5 Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 20 Nov 2019 16:17:49 +0300 Subject: [PATCH 01/27] award filter --- apps/main/filters.py | 37 +++++++++++++++++++++++++++++++++++++ apps/main/models.py | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 apps/main/filters.py diff --git a/apps/main/filters.py b/apps/main/filters.py new file mode 100644 index 00000000..e3a05a71 --- /dev/null +++ b/apps/main/filters.py @@ -0,0 +1,37 @@ +from django.core.validators import EMPTY_VALUES +from django_filters import rest_framework as filters + +from review import models + + +class AwardFilter(filters.FilterSet): + """Award filter set.""" + + establishment_id = filters.NumberFilter(field_name='object_id', ) + product_id = filters.NumberFilter(field_name='object_id', ) + employee_id = filters.NumberFilter(field_name='object_id', ) + + class Meta: + """Meta class.""" + + model = models.Review + fields = ( + 'establishment_id', + 'product_id', + 'employee_id', + ) + + def by_establishment_id(self, queryset, name, value): + if value not in EMPTY_VALUES: + return queryset.by_establishment_id(value, content_type='establishment') + return queryset + + def by_product_id(self, queryset, name, value): + if value not in EMPTY_VALUES: + return queryset.by_product_id(value, content_type='product') + return queryset + + def by_employee_id(self, queryset, name, value): + if value not in EMPTY_VALUES: + return queryset.by_employee_id(value, content_type='establishmentemployee') + return queryset diff --git a/apps/main/models.py b/apps/main/models.py index 1bd39a6d..61a4d447 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -153,7 +153,7 @@ class Award(TranslatedFieldsMixin, URLImageMixin, models.Model): PUBLISHED = 1 STATE_CHOICES = ( - (WAITING,'waiting'), + (WAITING, 'waiting'), (PUBLISHED, 'published') ) From 2e17d7265d2d071926b853da7fa5daf329622d38 Mon Sep 17 00:00:00 2001 From: Dmitriy Kuzmenko Date: Wed, 20 Nov 2019 16:29:46 +0300 Subject: [PATCH 02/27] add back api for collection --- apps/collection/serializers/back.py | 61 ++++++++++++++++++++++++++++- apps/collection/urls/back.py | 9 ++--- apps/collection/views/back.py | 54 +++++++++++++++++++------ apps/utils/views.py | 28 +++++++++++++ 4 files changed, 133 insertions(+), 19 deletions(-) diff --git a/apps/collection/serializers/back.py b/apps/collection/serializers/back.py index 4cc76b2f..bb88a778 100644 --- a/apps/collection/serializers/back.py +++ b/apps/collection/serializers/back.py @@ -1,8 +1,14 @@ from rest_framework import serializers + +from collection import models +from collection.serializers.common import CollectionBaseSerializer +from establishment.models import Establishment from location.models import Country from location.serializers import CountrySimpleSerializer -from collection.serializers.common import CollectionBaseSerializer -from collection import models +from product.models import Product +from utils.exceptions import ( + BindingObjectNotFound, RemovedBindingObjectNotFound, ObjectAlreadyAdded +) class CollectionBackOfficeSerializer(CollectionBaseSerializer): @@ -31,3 +37,54 @@ class CollectionBackOfficeSerializer(CollectionBaseSerializer): 'start', 'end', ] + + +class CollectionBindObjectSerializer(serializers.Serializer): + """Serializer for binding collection and objects""" + + ESTABLISHMENT = 'establishment' + PRODUCT = 'product' + + TYPE_CHOICES = ( + (ESTABLISHMENT, 'Establishment'), + (PRODUCT, 'Product'), + ) + + type = serializers.ChoiceField(TYPE_CHOICES) + object_id = serializers.IntegerField() + + def validate(self, attrs): + view = self.context.get('view') + request = self.context.get('request') + + obj_type = attrs.get('type') + obj_id = attrs.get('object_id') + + collection = view.get_object() + attrs['collection'] = collection + + if obj_type == self.ESTABLISHMENT: + establishment = Establishment.objects.filter(pk=obj_id).\ + first() + if not establishment: + raise BindingObjectNotFound() + if request.method == 'POST' and collection.establishments.\ + filter(pk=establishment.pk).exists(): + raise ObjectAlreadyAdded() + if request.method == 'DELETE' and not collection.\ + establishments.filter(pk=establishment.pk).\ + exists(): + raise RemovedBindingObjectNotFound() + attrs['related_object'] = establishment + elif obj_type == self.PRODUCT: + product = Product.objects.filter(pk=obj_id).first() + if not product: + raise BindingObjectNotFound() + if request.method == 'POST' and collection.products.\ + filter(pk=product.pk).exists(): + raise ObjectAlreadyAdded() + if request.method == 'DELETE' and not collection.products.\ + filter(pk=product.pk).exists(): + raise RemovedBindingObjectNotFound() + attrs['related_object'] = product + return attrs diff --git a/apps/collection/urls/back.py b/apps/collection/urls/back.py index eee40327..6a6dbd54 100644 --- a/apps/collection/urls/back.py +++ b/apps/collection/urls/back.py @@ -1,11 +1,10 @@ """Collection common urlpaths.""" -from django.urls import path +from rest_framework.routers import SimpleRouter from collection.views import back as views app_name = 'collection' +router = SimpleRouter() +router.register(r'', views.CollectionBackOfficeViewSet) -urlpatterns = [ - path('', views.CollectionListCreateView.as_view(), name='list-create'), - path('/', views.CollectionRUDView.as_view(), name='rud-collection'), -] +urlpatterns = router.urls diff --git a/apps/collection/views/back.py b/apps/collection/views/back.py index 78a2dcb0..a989ec56 100644 --- a/apps/collection/views/back.py +++ b/apps/collection/views/back.py @@ -1,19 +1,49 @@ -from rest_framework import generics, permissions +from rest_framework import permissions +from rest_framework import viewsets, mixins + from collection import models -from collection.serializers import back +from collection.serializers import back as serializers +from utils.views import BindObjectMixin -class CollectionListCreateView(generics.ListCreateAPIView): - """Collection list-create view.""" +class CollectionViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): + """ViewSet for Collection model.""" + + pagination_class = None + permission_classes = (permissions.AllowAny,) queryset = models.Collection.objects.all() - serializer_class = back.CollectionBackOfficeSerializer - # todo: conf. permissions by TT - permission_classes = (permissions.IsAuthenticated, ) + serializer_class = serializers.CollectionBackOfficeSerializer -class CollectionRUDView(generics.RetrieveUpdateDestroyAPIView): - """Collection list-create view.""" +class CollectionBackOfficeViewSet(mixins.CreateModelMixin, + mixins.UpdateModelMixin, + mixins.DestroyModelMixin, + mixins.RetrieveModelMixin, + BindObjectMixin, + CollectionViewSet): + """ViewSet for Collection model for BackOffice users.""" + + permission_classes = (permissions.IsAuthenticated,) queryset = models.Collection.objects.all() - serializer_class = back.CollectionBackOfficeSerializer - # todo: conf. permissions by TT - permission_classes = (permissions.IsAuthenticated, ) + serializer_class = serializers.CollectionBackOfficeSerializer + bind_object_serializer_class = serializers.CollectionBindObjectSerializer + + def perform_binding(self, serializer): + data = serializer.validated_data + collection = data.pop('collection') + obj_type = data.get('type') + related_object = data.get('related_object') + if obj_type == self.bind_object_serializer_class.ESTABLISHMENT: + collection.establishments.add(related_object) + elif obj_type == self.bind_object_serializer_class.PRODUCT: + collection.products.add(related_object) + + def perform_unbinding(self, serializer): + data = serializer.validated_data + collection = data.pop('collection') + obj_type = data.get('type') + related_object = data.get('related_object') + if obj_type == self.bind_object_serializer_class.ESTABLISHMENT: + collection.establishments.remove(related_object) + elif obj_type == self.bind_object_serializer_class.PRODUCT: + collection.products.remove(related_object) diff --git a/apps/utils/views.py b/apps/utils/views.py index d3d09079..870e132f 100644 --- a/apps/utils/views.py +++ b/apps/utils/views.py @@ -4,6 +4,7 @@ from django.conf import settings from django.db.transaction import on_commit from rest_framework import generics from rest_framework import status +from rest_framework.decorators import action from rest_framework.response import Response from gallery.tasks import delete_image @@ -121,3 +122,30 @@ class CreateDestroyGalleryViewMixin(generics.CreateAPIView, # Delete an instances of Gallery model gallery_obj.delete() return Response(status=status.HTTP_204_NO_CONTENT) + + +# 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) \ No newline at end of file From a17e83ebe5001d7a22460c8406062f875c8c4fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Wed, 20 Nov 2019 16:36:37 +0300 Subject: [PATCH 03/27] On sync elasticsearch --- project/settings/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/settings/base.py b/project/settings/base.py index 9c365b52..fba7f4b4 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -503,4 +503,4 @@ ESTABLISHMENT_CHOSEN_TAGS = ['gastronomic', 'en_vogue', 'terrace', 'streetfood', NEWS_CHOSEN_TAGS = ['eat', 'drink', 'cook', 'style', 'international', 'event', 'partnership'] INTERNATIONAL_COUNTRY_CODES = ['www', 'main', 'next'] -ELASTICSEARCH_DSL_AUTOSYNC = False \ No newline at end of file +#ELASTICSEARCH_DSL_AUTOSYNC = False From 824cbe203b06454b896988d0847c89b5b21426c1 Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 20 Nov 2019 16:38:11 +0300 Subject: [PATCH 04/27] award back api --- apps/main/filters.py | 7 +++++-- apps/main/serializers.py | 20 ++++++++++++++++++-- apps/main/urls/back.py | 11 +++++++++++ apps/main/views/back.py | 21 +++++++++++++++++++++ apps/review/views/back.py | 4 +--- project/urls/back.py | 2 +- 6 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 apps/main/urls/back.py create mode 100644 apps/main/views/back.py diff --git a/apps/main/filters.py b/apps/main/filters.py index e3a05a71..72636d9e 100644 --- a/apps/main/filters.py +++ b/apps/main/filters.py @@ -1,7 +1,7 @@ from django.core.validators import EMPTY_VALUES from django_filters import rest_framework as filters -from review import models +from main import models class AwardFilter(filters.FilterSet): @@ -14,11 +14,14 @@ class AwardFilter(filters.FilterSet): class Meta: """Meta class.""" - model = models.Review + model = models.Award fields = ( 'establishment_id', 'product_id', 'employee_id', + 'state', + 'award_type', + 'vintage_year', ) def by_establishment_id(self, queryset, name, value): diff --git a/apps/main/serializers.py b/apps/main/serializers.py index 6b8e2ba4..732cfbad 100644 --- a/apps/main/serializers.py +++ b/apps/main/serializers.py @@ -38,7 +38,7 @@ class SiteFeatureSerializer(serializers.ModelSerializer): 'route', 'source', 'nested', - ) + ) class CurrencySerializer(ProjectModelSerializer): @@ -145,6 +145,22 @@ class AwardSerializer(AwardBaseSerializer): fields = AwardBaseSerializer.Meta.fields + ['award_type', ] +class BackAwardSerializer(serializers.ModelSerializer): + """Award serializer.""" + + title_translated = serializers.CharField(read_only=True, allow_null=True) + + class Meta: + model = models.Award + fields = [ + 'id', + 'title_translated', + 'vintage_year', + 'award_type', + 'state', + ] + + class CarouselListSerializer(serializers.ModelSerializer): """Serializer for retrieving list of carousel items.""" @@ -202,4 +218,4 @@ class PageTypeBaseSerializer(serializers.ModelSerializer): fields = [ 'id', 'name', - ] \ No newline at end of file + ] diff --git a/apps/main/urls/back.py b/apps/main/urls/back.py new file mode 100644 index 00000000..93debb5e --- /dev/null +++ b/apps/main/urls/back.py @@ -0,0 +1,11 @@ +"""Back main URLs""" +from django.urls import path + +from main.views import back as views + +app_name = 'main' + +urlpatterns = [ + path('awards/', views.AwardLstView.as_view(), name='awards-list-create'), + path('awards//', views.AwardRUDView.as_view(), name='awards-rud'), +] diff --git a/apps/main/views/back.py b/apps/main/views/back.py new file mode 100644 index 00000000..bbbfad53 --- /dev/null +++ b/apps/main/views/back.py @@ -0,0 +1,21 @@ +from rest_framework import generics, permissions + +from main import serializers +from main.filters import AwardFilter +from main.models import Award + + +class AwardLstView(generics.ListCreateAPIView): + """Award list create view.""" + queryset = Award.objects.all() + serializer_class = serializers.BackAwardSerializer + permission_classes = (permissions.IsAdminUser,) + filterset_class = AwardFilter + + +class AwardRUDView(generics.RetrieveUpdateDestroyAPIView): + """Award RUD view.""" + queryset = Award.objects.all() + serializer_class = serializers.BackAwardSerializer + permission_classes = (permissions.IsAdminUser,) + lookup_field = 'id' diff --git a/apps/review/views/back.py b/apps/review/views/back.py index 5e38f7ab..c5d89e11 100644 --- a/apps/review/views/back.py +++ b/apps/review/views/back.py @@ -1,9 +1,8 @@ -from django_filters.rest_framework import DjangoFilterBackend from rest_framework import generics, permissions +from review import filters from review import models from review import serializers -from review import filters from utils.permissions import IsReviewerManager, IsRestaurantReviewer @@ -12,7 +11,6 @@ class ReviewLstView(generics.ListCreateAPIView): serializer_class = serializers.ReviewBaseSerializer queryset = models.Review.objects.all() permission_classes = [permissions.IsAuthenticatedOrReadOnly, ] - filter_backends = (DjangoFilterBackend,) filterset_class = filters.ReviewFilter diff --git a/project/urls/back.py b/project/urls/back.py index 04af4a53..fdd3d10a 100644 --- a/project/urls/back.py +++ b/project/urls/back.py @@ -15,5 +15,5 @@ urlpatterns = [ path('products/', include(('product.urls.back', 'product'), namespace='product')), path('re_blocks/', include(('advertisement.urls.back', 'advertisement'), namespace='advertisement')), + path('main/', include('main.urls.back')), ] - From cd328186e10dc3644cbf1ebad882e813ba36b650 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 20 Nov 2019 17:01:17 +0300 Subject: [PATCH 05/27] wildcard boosting --- apps/search_indexes/filters.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/apps/search_indexes/filters.py b/apps/search_indexes/filters.py index 28341502..3ef74978 100644 --- a/apps/search_indexes/filters.py +++ b/apps/search_indexes/filters.py @@ -71,14 +71,21 @@ class CustomSearchFilterBackend(SearchFilterBackend): Q("match", **{k: v}) ) __queries.append( - Q('wildcard', **{k: f'*{search_term.lower()}*'}) + Q('wildcard', + **{k: { + 'value': f'*{search_term.lower()}*', + 'boost': v.get('boost', 1) + 0.1 + } + } + ) ) else: __queries.append( Q("match", **field_kwargs) ) __queries.append( - Q('wildcard', **{field: f'*{search_term.lower()}*'}) + Q('wildcard', **{field: {'value': f'*{search_term.lower()}*', + 'boost': field_kwargs[field].get('boost', 1) + 0.1}}) ) else: for field in view.search_fields: @@ -99,13 +106,20 @@ class CustomSearchFilterBackend(SearchFilterBackend): Q("match", **{k: v}) ) __queries.append( - Q('wildcard', **{k: f'*{search_term.lower()}*'}) + Q('wildcard', + **{k: { + 'value': f'*{search_term.lower()}*', + 'boost': v.get('boost', 1) + 0.1 + } + } + ) ) else: __queries.append( Q("match", **field_kwargs) ) __queries.append( - Q('wildcard', **{field: f'*{search_term.lower()}*'}) + Q('wildcard', **{field: {'value': f'*{search_term.lower()}*', + 'boost': field_kwargs[field].get('boost', 1) + 0.1}}) ) return __queries \ No newline at end of file From 280da2d7de322b892d7c996b24af3d0ce9d37dd5 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 20 Nov 2019 19:31:43 +0300 Subject: [PATCH 06/27] Estabishment_type field --- apps/establishment/serializers/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 0c183477..3f0ca169 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -241,7 +241,7 @@ class EstablishmentBaseSerializer(ProjectModelSerializer): in_favorites = serializers.BooleanField(allow_null=True) tags = TagBaseSerializer(read_only=True, many=True, source='visible_tags') currency = CurrencySerializer() - type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True) + establishment_type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True) subtypes = EstablishmentSubTypeBaseSerializer(many=True, source='establishment_subtypes') image = serializers.URLField(source='image_url', read_only=True) preview_image = serializers.URLField(source='preview_image_url', @@ -267,7 +267,7 @@ class EstablishmentBaseSerializer(ProjectModelSerializer): 'address', 'tags', 'currency', - 'type', + 'establishment_type', 'subtypes', 'image', 'preview_image', From b60dd870f9896cf141b432d201cf352d5f97c55f Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 20 Nov 2019 19:32:45 +0300 Subject: [PATCH 07/27] Estabishment_type field #2 --- apps/establishment/serializers/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 3f0ca169..2fd7d9eb 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -241,7 +241,7 @@ class EstablishmentBaseSerializer(ProjectModelSerializer): in_favorites = serializers.BooleanField(allow_null=True) tags = TagBaseSerializer(read_only=True, many=True, source='visible_tags') currency = CurrencySerializer() - establishment_type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True) + establishment_type = EstablishmentTypeBaseSerializer(read_only=True) subtypes = EstablishmentSubTypeBaseSerializer(many=True, source='establishment_subtypes') image = serializers.URLField(source='image_url', read_only=True) preview_image = serializers.URLField(source='preview_image_url', From 49940ebab4b04a37359ba5a967d9d547caaf7620 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 20 Nov 2019 19:41:20 +0300 Subject: [PATCH 08/27] Revert "Estabishment_type field #2" This reverts commit b60dd87 --- apps/establishment/serializers/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 2fd7d9eb..3f0ca169 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -241,7 +241,7 @@ class EstablishmentBaseSerializer(ProjectModelSerializer): in_favorites = serializers.BooleanField(allow_null=True) tags = TagBaseSerializer(read_only=True, many=True, source='visible_tags') currency = CurrencySerializer() - establishment_type = EstablishmentTypeBaseSerializer(read_only=True) + establishment_type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True) subtypes = EstablishmentSubTypeBaseSerializer(many=True, source='establishment_subtypes') image = serializers.URLField(source='image_url', read_only=True) preview_image = serializers.URLField(source='preview_image_url', From 06b8582976a01fd302fb54497c466ae63620e812 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 20 Nov 2019 19:41:22 +0300 Subject: [PATCH 09/27] Revert "Estabishment_type field" This reverts commit 280da2d --- apps/establishment/serializers/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 3f0ca169..0c183477 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -241,7 +241,7 @@ class EstablishmentBaseSerializer(ProjectModelSerializer): in_favorites = serializers.BooleanField(allow_null=True) tags = TagBaseSerializer(read_only=True, many=True, source='visible_tags') currency = CurrencySerializer() - establishment_type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True) + type = EstablishmentTypeBaseSerializer(source='establishment_type', read_only=True) subtypes = EstablishmentSubTypeBaseSerializer(many=True, source='establishment_subtypes') image = serializers.URLField(source='image_url', read_only=True) preview_image = serializers.URLField(source='preview_image_url', @@ -267,7 +267,7 @@ class EstablishmentBaseSerializer(ProjectModelSerializer): 'address', 'tags', 'currency', - 'establishment_type', + 'type', 'subtypes', 'image', 'preview_image', From 2dc7f1e89de7e06e1916b4222aa49e5248310c34 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 20 Nov 2019 20:35:56 +0300 Subject: [PATCH 10/27] Boost the tempo --- apps/search_indexes/filters.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/search_indexes/filters.py b/apps/search_indexes/filters.py index 3ef74978..ab47ef84 100644 --- a/apps/search_indexes/filters.py +++ b/apps/search_indexes/filters.py @@ -74,7 +74,7 @@ class CustomSearchFilterBackend(SearchFilterBackend): Q('wildcard', **{k: { 'value': f'*{search_term.lower()}*', - 'boost': v.get('boost', 1) + 0.1 + 'boost': v.get('boost', 1) + 30 } } ) @@ -85,7 +85,7 @@ class CustomSearchFilterBackend(SearchFilterBackend): ) __queries.append( Q('wildcard', **{field: {'value': f'*{search_term.lower()}*', - 'boost': field_kwargs[field].get('boost', 1) + 0.1}}) + 'boost': field_kwargs[field].get('boost', 1) + 30}}) ) else: for field in view.search_fields: @@ -109,7 +109,7 @@ class CustomSearchFilterBackend(SearchFilterBackend): Q('wildcard', **{k: { 'value': f'*{search_term.lower()}*', - 'boost': v.get('boost', 1) + 0.1 + 'boost': v.get('boost', 1) + 30 } } ) @@ -120,6 +120,6 @@ class CustomSearchFilterBackend(SearchFilterBackend): ) __queries.append( Q('wildcard', **{field: {'value': f'*{search_term.lower()}*', - 'boost': field_kwargs[field].get('boost', 1) + 0.1}}) + 'boost': field_kwargs[field].get('boost', 1) + 30}}) ) return __queries \ No newline at end of file From 74c1b7069a22a3ce6c42f09a79622dbd617246bb Mon Sep 17 00:00:00 2001 From: Dmitriy Kuzmenko Date: Thu, 21 Nov 2019 00:23:17 +0300 Subject: [PATCH 11/27] fix product tags --- apps/product/management/commands/add_product_tag.py | 9 ++++----- project/settings/base.py | 2 +- project/settings/local.py | 10 +++++----- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/apps/product/management/commands/add_product_tag.py b/apps/product/management/commands/add_product_tag.py index dc54f35c..6377fcac 100644 --- a/apps/product/management/commands/add_product_tag.py +++ b/apps/product/management/commands/add_product_tag.py @@ -40,7 +40,6 @@ class Command(BaseCommand): TagCategory.objects.bulk_create(objects) self.stdout.write(self.style.WARNING(f'Add or get tag category objects.')) - def product_type_category_sql(self): with connections['legacy'].cursor() as cursor: cursor.execute(''' @@ -56,9 +55,9 @@ class Command(BaseCommand): def add_type_product_category(self): for c in tqdm(self.product_type_category_sql(), desc='Add type product category'): - type = ProductType.objects.get(index_name='wine') + type = ProductType.objects.get(index_name=ProductType.WINE) category = TagCategory.objects.get(index_name=c.tag_category) - if type and category not in type.tag_categories.all(): + if category not in type.tag_categories.all(): type.tag_categories.add(category) self.stdout.write(self.style.WARNING(f'Add type product category objects.')) @@ -116,7 +115,7 @@ class Command(BaseCommand): select DISTINCT m.product_id, - lower(trim(CONVERT(m.value USING utf8))) as tag_value, + trim(CONVERT(m.value USING utf8)) as tag_value, trim(CONVERT(v.key_name USING utf8)) as tag_category FROM product_metadata m JOIN product_key_value_metadata v on v.id = m.product_key_value_metadatum_id @@ -133,7 +132,7 @@ class Command(BaseCommand): ) product = Product.objects.get(old_id=t.product_id) for tag in tags: - if product not in tag.products.all(): + if tag not in product.tags.all(): product.tags.add(tag) self.stdout.write(self.style.WARNING(f'Add or get tag objects.')) diff --git a/project/settings/base.py b/project/settings/base.py index fba7f4b4..1772c2c8 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -503,4 +503,4 @@ ESTABLISHMENT_CHOSEN_TAGS = ['gastronomic', 'en_vogue', 'terrace', 'streetfood', NEWS_CHOSEN_TAGS = ['eat', 'drink', 'cook', 'style', 'international', 'event', 'partnership'] INTERNATIONAL_COUNTRY_CODES = ['www', 'main', 'next'] -#ELASTICSEARCH_DSL_AUTOSYNC = False +ELASTICSEARCH_DSL_AUTOSYNC = False diff --git a/project/settings/local.py b/project/settings/local.py index 959e6149..3d0c9981 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -80,11 +80,11 @@ LOGGING = { 'py.warnings': { 'handlers': ['console'], }, - 'django.db.backends': { - 'handlers': ['console', ], - 'level': 'DEBUG', - 'propagate': False, - }, + # 'django.db.backends': { + # 'handlers': ['console', ], + # 'level': 'DEBUG', + # 'propagate': False, + # }, } } From 7b2e8374a0d66c9dcc78f5b9e42ff90b0c45dc27 Mon Sep 17 00:00:00 2001 From: Dmitriy Kuzmenko Date: Thu, 21 Nov 2019 00:45:48 +0300 Subject: [PATCH 12/27] change settings --- project/settings/base.py | 2 +- project/settings/local.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/project/settings/base.py b/project/settings/base.py index 1772c2c8..0a20bdeb 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -503,4 +503,4 @@ ESTABLISHMENT_CHOSEN_TAGS = ['gastronomic', 'en_vogue', 'terrace', 'streetfood', NEWS_CHOSEN_TAGS = ['eat', 'drink', 'cook', 'style', 'international', 'event', 'partnership'] INTERNATIONAL_COUNTRY_CODES = ['www', 'main', 'next'] -ELASTICSEARCH_DSL_AUTOSYNC = False +# ELASTICSEARCH_DSL_AUTOSYNC = False diff --git a/project/settings/local.py b/project/settings/local.py index 3d0c9981..959e6149 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -80,11 +80,11 @@ LOGGING = { 'py.warnings': { 'handlers': ['console'], }, - # 'django.db.backends': { - # 'handlers': ['console', ], - # 'level': 'DEBUG', - # 'propagate': False, - # }, + 'django.db.backends': { + 'handlers': ['console', ], + 'level': 'DEBUG', + 'propagate': False, + }, } } From 009ecf1973d40b49f74e8bbc66c39e3dfe1928b6 Mon Sep 17 00:00:00 2001 From: Dmitriy Kuzmenko Date: Thu, 21 Nov 2019 08:42:05 +0300 Subject: [PATCH 13/27] fix filter for tag_category --- apps/tag/filters.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/tag/filters.py b/apps/tag/filters.py index 8d2343ec..7bd22ec2 100644 --- a/apps/tag/filters.py +++ b/apps/tag/filters.py @@ -43,8 +43,8 @@ class TagCategoryFilterSet(TagsBaseFilterSet): 'product_type', ) def by_product_type(self, queryset, name, value): - if value == product_models.ProductType.WINE: - queryset = queryset.filter(index_name='wine-color').filter(tags__products__isnull=False) + # if value == product_models.ProductType.WINE: + # queryset = queryset.filter(index_name='wine-color').filter(tags__products__isnull=False) queryset = queryset.by_product_type(value) return queryset From cad0f8df0a08170ea83cf76ea87b4e3e406553bf Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 21 Nov 2019 09:11:52 +0300 Subject: [PATCH 14/27] award test --- apps/main/serializers.py | 11 ++---- apps/main/tests.py | 3 -- apps/main/tests/__init__.py | 0 apps/main/tests/tests_back.py | 73 +++++++++++++++++++++++++++++++++++ apps/main/urls/back.py | 2 +- 5 files changed, 78 insertions(+), 11 deletions(-) delete mode 100644 apps/main/tests.py create mode 100644 apps/main/tests/__init__.py create mode 100644 apps/main/tests/tests_back.py diff --git a/apps/main/serializers.py b/apps/main/serializers.py index 732cfbad..572aff31 100644 --- a/apps/main/serializers.py +++ b/apps/main/serializers.py @@ -145,19 +145,16 @@ class AwardSerializer(AwardBaseSerializer): fields = AwardBaseSerializer.Meta.fields + ['award_type', ] -class BackAwardSerializer(serializers.ModelSerializer): +class BackAwardSerializer(AwardBaseSerializer): """Award serializer.""" - title_translated = serializers.CharField(read_only=True, allow_null=True) - class Meta: model = models.Award - fields = [ - 'id', - 'title_translated', - 'vintage_year', + fields = AwardBaseSerializer.Meta.fields + [ 'award_type', 'state', + 'content_type', + 'object_id', ] diff --git a/apps/main/tests.py b/apps/main/tests.py deleted file mode 100644 index 7ce503c2..00000000 --- a/apps/main/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/apps/main/tests/__init__.py b/apps/main/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/main/tests/tests_back.py b/apps/main/tests/tests_back.py new file mode 100644 index 00000000..e09c7b4b --- /dev/null +++ b/apps/main/tests/tests_back.py @@ -0,0 +1,73 @@ +from http.cookies import SimpleCookie + +from django.contrib.contenttypes.models import ContentType +from rest_framework import status +from rest_framework.test import APITestCase + +from account.models import User +from location.models import Country +from main.models import Award, AwardType + + +class AwardTestCase(APITestCase): + + def setUp(self): + self.user = User.objects.create_user( + username='alex', + email='alex@mail.com', + password='alex_password', + is_staff=True, + ) + + # get tokens + tokens = User.create_jwt_tokens(self.user) + self.client.cookies = SimpleCookie( + {'access_token': tokens.get('access_token'), + 'refresh_token': tokens.get('refresh_token')}) + + self.country_ru = Country.objects.create( + name={'en-GB': 'Russian'}, + code='RU', + ) + + self.content_type = ContentType.objects.get(app_label="establishment", model="establishment") + + self.award_type = AwardType.objects.create( + country=self.country_ru, + name="Test award type", + ) + + self.award = Award.objects.create( + award_type=self.award_type, + vintage_year='2017', + state=Award.PUBLISHED, + object_id=1, + content_type_id=1, + ) + + def test_award_CRUD(self): + response = self.client.get('/api/back/main/awards/', format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + + data = { + 'award_type': self.award_type.pk, + 'state': 1, + 'object_id': 1, + 'content_type': 1, + } + + response = self.client.post('/api/back/main/awards/', data=data) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + response = self.client.get(f'/api/back/main/awards/{self.award.id}/', format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + + update_data = { + 'vintage_year': '2019' + } + + response = self.client.patch(f'/api/back/main/awards/{self.award.id}/', data=update_data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + response = self.client.delete(f'/api/back/main/awards/{self.award.id}/') + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) diff --git a/apps/main/urls/back.py b/apps/main/urls/back.py index 93debb5e..d92bddf8 100644 --- a/apps/main/urls/back.py +++ b/apps/main/urls/back.py @@ -7,5 +7,5 @@ app_name = 'main' urlpatterns = [ path('awards/', views.AwardLstView.as_view(), name='awards-list-create'), - path('awards//', views.AwardRUDView.as_view(), name='awards-rud'), + path('awards//', views.AwardRUDView.as_view(), name='awards-rud'), ] From 617552841fa5ff100c4fa3094f8f41da94e3c465 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 21 Nov 2019 09:58:29 +0300 Subject: [PATCH 15/27] refactored product, fix transfer and etc., see changelist --- apps/account/views/common.py | 2 +- apps/account/views/web.py | 2 +- apps/advertisement/views/back.py | 6 +-- apps/authorization/views/common.py | 10 ++-- apps/establishment/admin.py | 10 +++- .../0065_establishment_purchased_products.py | 19 +++++++ apps/establishment/models.py | 13 +++++ apps/establishment/transfer_data.py | 44 +++++++++++++++- apps/establishment/views/back.py | 27 ++++++---- apps/location/views/back.py | 4 +- apps/news/views.py | 8 +-- .../migrations/0018_purchasedproduct.py | 30 +++++++++++ apps/product/models.py | 29 ++++++++++- apps/product/views/back.py | 12 ++--- apps/timetable/serialziers.py | 6 +-- apps/transfer/management/commands/transfer.py | 1 + apps/transfer/models.py | 25 ++++----- apps/transfer/serializers/product.py | 2 +- apps/utils/views.py | 51 ++++++++++--------- 19 files changed, 220 insertions(+), 81 deletions(-) create mode 100644 apps/establishment/migrations/0065_establishment_purchased_products.py create mode 100644 apps/product/migrations/0018_purchasedproduct.py diff --git a/apps/account/views/common.py b/apps/account/views/common.py index d29ce2bb..8b066742 100644 --- a/apps/account/views/common.py +++ b/apps/account/views/common.py @@ -63,7 +63,7 @@ class SendConfirmationEmailView(generics.GenericAPIView): return Response(status=status.HTTP_200_OK) -class ConfirmEmailView(JWTGenericViewMixin): +class ConfirmEmailView(JWTGenericViewMixin, generics.GenericAPIView): """View for confirm changing email""" permission_classes = (permissions.AllowAny,) diff --git a/apps/account/views/web.py b/apps/account/views/web.py index 9f2ebcfd..0fc762f5 100644 --- a/apps/account/views/web.py +++ b/apps/account/views/web.py @@ -33,7 +33,7 @@ class PasswordResetView(generics.GenericAPIView): return Response(status=status.HTTP_200_OK) -class PasswordResetConfirmView(JWTGenericViewMixin): +class PasswordResetConfirmView(JWTGenericViewMixin, generics.GenericAPIView): """View for confirmation new password""" serializer_class = serializers.PasswordResetConfirmSerializer permission_classes = (permissions.AllowAny,) diff --git a/apps/advertisement/views/back.py b/apps/advertisement/views/back.py index d11615ba..a2973589 100644 --- a/apps/advertisement/views/back.py +++ b/apps/advertisement/views/back.py @@ -45,7 +45,7 @@ class AdvertisementPageListCreateView(AdvertisementBackOfficeViewMixin, ad_qs = Advertisement.objects.all() filtered_ad_qs = self.filter_queryset(ad_qs) - ad = get_object_or_404(filtered_ad_qs, pk=self.kwargs['pk']) + ad = get_object_or_404(filtered_ad_qs, pk=self.kwargs.get('pk')) # May raise a permission denied self.check_object_permissions(self.request, ad) @@ -68,8 +68,8 @@ class AdvertisementPageRUDView(AdvertisementBackOfficeViewMixin, ad_qs = Advertisement.objects.all() filtered_ad_qs = self.filter_queryset(ad_qs) - ad = get_object_or_404(filtered_ad_qs, pk=self.kwargs['ad_pk']) - page = get_object_or_404(ad.pages.all(), pk=self.kwargs['page_pk']) + ad = get_object_or_404(filtered_ad_qs, pk=self.kwargs.get('ad_pk')) + page = get_object_or_404(ad.pages.all(), pk=self.kwargs.get('page_pk')) # May raise a permission denied self.check_object_permissions(self.request, page) diff --git a/apps/authorization/views/common.py b/apps/authorization/views/common.py index 9d2069f2..8b466acb 100644 --- a/apps/authorization/views/common.py +++ b/apps/authorization/views/common.py @@ -71,7 +71,7 @@ class OAuth2ViewMixin(CsrfExemptMixin, OAuthLibMixin, BaseOAuth2ViewMixin): # Sign in via Facebook -class OAuth2SignUpView(OAuth2ViewMixin, JWTGenericViewMixin): +class OAuth2SignUpView(OAuth2ViewMixin, JWTGenericViewMixin, generics.GenericAPIView): """ Implements an endpoint to convert a provider token to an access token @@ -142,7 +142,7 @@ class SignUpView(generics.GenericAPIView): return Response(status=status.HTTP_201_CREATED) -class ConfirmationEmailView(JWTGenericViewMixin): +class ConfirmationEmailView(JWTGenericViewMixin, generics.GenericAPIView): """View for confirmation email""" permission_classes = (permissions.AllowAny, ) @@ -174,7 +174,7 @@ class ConfirmationEmailView(JWTGenericViewMixin): # Login by username|email + password -class LoginByUsernameOrEmailView(JWTGenericViewMixin): +class LoginByUsernameOrEmailView(JWTGenericViewMixin, generics.GenericAPIView): """Login by email and password""" permission_classes = (permissions.AllowAny,) serializer_class = serializers.LoginByUsernameOrEmailSerializer @@ -197,7 +197,7 @@ class LoginByUsernameOrEmailView(JWTGenericViewMixin): # Logout -class LogoutView(JWTGenericViewMixin): +class LogoutView(JWTGenericViewMixin, generics.GenericAPIView): """Logout user""" permission_classes = (IsAuthenticatedAndTokenIsValid, ) @@ -215,7 +215,7 @@ class LogoutView(JWTGenericViewMixin): # Refresh token -class RefreshTokenView(JWTGenericViewMixin): +class RefreshTokenView(JWTGenericViewMixin, generics.GenericAPIView): """Refresh access_token""" permission_classes = (permissions.AllowAny, ) serializer_class = serializers.RefreshTokenSerializer diff --git a/apps/establishment/admin.py b/apps/establishment/admin.py index e6b0d991..45716f32 100644 --- a/apps/establishment/admin.py +++ b/apps/establishment/admin.py @@ -7,7 +7,7 @@ from comment.models import Comment from utils.admin import BaseModelAdminMixin from establishment import models from main.models import Award -from product.models import Product +from product.models import Product, PurchasedProduct from review import models as review_models @@ -69,13 +69,19 @@ class EstablishmentNote(admin.TabularInline): extra = 0 +class PurchasedProduct(admin.TabularInline): + model = PurchasedProduct + extra = 0 + + @admin.register(models.Establishment) class EstablishmentAdmin(BaseModelAdminMixin, admin.ModelAdmin): """Establishment admin.""" list_display = ['id', '__str__', 'image_tag', ] search_fields = ['id', 'name', 'index_name', 'slug'] list_filter = ['public_mark', 'toque_number'] - inlines = [GalleryImageInline, CompanyInline, EstablishmentNote] + inlines = [GalleryImageInline, CompanyInline, EstablishmentNote, + PurchasedProduct] # inlines = [ # AwardInline, ContactPhoneInline, ContactEmailInline, diff --git a/apps/establishment/migrations/0065_establishment_purchased_products.py b/apps/establishment/migrations/0065_establishment_purchased_products.py new file mode 100644 index 00000000..25088455 --- /dev/null +++ b/apps/establishment/migrations/0065_establishment_purchased_products.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.7 on 2019-11-20 12:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0018_purchasedproduct'), + ('establishment', '0064_auto_20191119_1546'), + ] + + operations = [ + migrations.AddField( + model_name='establishment', + name='purchased_products', + field=models.ManyToManyField(blank=True, help_text='Attribute from legacy db.\nMust be deleted after the implementation of the market.', related_name='establishments', through='product.PurchasedProduct', to='product.Product', verbose_name='purchased plaques'), + ), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 3aee6c31..26046953 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -397,6 +397,13 @@ class Establishment(GalleryModelMixin, ProjectBaseMixin, URLImageMixin, Translat currency = models.ForeignKey(Currency, blank=True, null=True, default=None, on_delete=models.PROTECT, verbose_name=_('currency')) + purchased_products = models.ManyToManyField('product.Product', blank=True, + through='product.PurchasedProduct', + related_name='establishments', + verbose_name=_('purchased plaques'), + help_text=_('Attribute from legacy db.\n' + 'Must be deleted after the ' + 'implementation of the market.')) objects = EstablishmentQuerySet.as_manager() @@ -409,6 +416,12 @@ class Establishment(GalleryModelMixin, ProjectBaseMixin, URLImageMixin, Translat def __str__(self): return f'id:{self.id}-{self.name}' + def clean_fields(self, exclude=None): + super().clean_fields(exclude) + if self.purchased_products.filter(product_type__index_name='souvenir').exists(): + raise ValidationError( + _('Only souvenirs.')) + def delete(self, using=None, keep_parents=False): """Overridden delete method""" # Delete all related companies diff --git a/apps/establishment/transfer_data.py b/apps/establishment/transfer_data.py index b3dc58f9..741a9989 100644 --- a/apps/establishment/transfer_data.py +++ b/apps/establishment/transfer_data.py @@ -4,7 +4,9 @@ from django.db.models import Q, F from establishment.models import Establishment from location.models import Address -from transfer.models import Establishments, Dishes, EstablishmentNotes +from product.models import PurchasedProduct, Product +from transfer.models import Establishments, Dishes, EstablishmentNotes, \ + EstablishmentMerchandises from transfer.serializers.establishment import EstablishmentSerializer, \ EstablishmentNoteSerializer from transfer.serializers.plate import PlateSerializer @@ -140,6 +142,43 @@ def transfer_establishment_note(): pprint(f"transfer_establishment_note errors: {errors}") +def transfer_purchased_plaques(): + update_products_counter = 0 + already_updated_counter = 0 + not_existed_establishment_counter = 0 + + purchased = EstablishmentMerchandises.objects.values_list( + 'establishment_id', + 'merchandise__vintage', + 'gifted', + 'quantity' + ) + for old_est_id, vintage, gifted, quantity in purchased: + establishment_qs = Establishment.objects.filter(old_id=old_est_id) + product_qs = Product.objects.filter(name='Plaque restaurants', + vintage=vintage) + if establishment_qs.exists() and product_qs.exists(): + product = product_qs.first() + establishment = establishment_qs.first() + + purchases, created = PurchasedProduct.objects.get_or_create( + establishment=establishment, + product=product, + is_gifted=gifted, + quantity=quantity + ) + if created: + update_products_counter += 1 + else: + already_updated_counter += 1 + else: + not_existed_establishment_counter += 1 + + print(f'Updated products: {update_products_counter}\n' + f'Already updated: {already_updated_counter}\n' + f'Not existed establishment: {not_existed_establishment_counter}') + + data_types = { "establishment": [ transfer_establishment, @@ -149,4 +188,7 @@ data_types = { transfer_establishment_addresses ], "menu": [transfer_menu], + "purchased_plaques": [ + transfer_purchased_plaques + ], } diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index ad38e806..d1897397 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -6,6 +6,7 @@ from establishment import filters, models, serializers from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer from utils.permissions import IsCountryAdmin, IsEstablishmentManager from utils.views import CreateDestroyGalleryViewMixin +from timetable.models import Timetable from rest_framework import status from rest_framework.response import Response @@ -36,13 +37,14 @@ class EstablishmentRUDView(generics.RetrieveUpdateDestroyAPIView): class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView): """Establishment schedule RUD view""" serializer_class = ScheduleRUDSerializer + permission_classes = [IsEstablishmentManager] def get_object(self): """ Returns the object the view is displaying. """ - establishment_pk = self.kwargs['pk'] - schedule_id = self.kwargs['schedule_id'] + establishment_pk = self.kwargs.get('pk') + schedule_id = self.kwargs.get('schedule_id') establishment = get_object_or_404(klass=models.Establishment.objects.all(), pk=establishment_pk) @@ -59,6 +61,8 @@ class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView): class EstablishmentScheduleCreateView(generics.CreateAPIView): """Establishment schedule Create view""" serializer_class = ScheduleCreateSerializer + queryset = Timetable.objects.all() + permission_classes = [IsEstablishmentManager] class MenuListCreateView(generics.ListCreateAPIView): @@ -200,8 +204,9 @@ class EstablishmentGalleryCreateDestroyView(EstablishmentMixinViews, """ establishment_qs = self.filter_queryset(self.get_queryset()) - establishment = get_object_or_404(establishment_qs, pk=self.kwargs['pk']) - gallery = get_object_or_404(establishment.establishment_gallery, image_id=self.kwargs['image_id']) + establishment = get_object_or_404(establishment_qs, pk=self.kwargs.get('pk')) + gallery = get_object_or_404(establishment.establishment_gallery, + image_id=self.kwargs.get('image_id')) # May raise a permission denied self.check_object_permissions(self.request, gallery) @@ -217,7 +222,7 @@ class EstablishmentGalleryListView(EstablishmentMixinViews, def get_object(self): """Override get_object method.""" qs = super(EstablishmentGalleryListView, self).get_queryset() - establishment = get_object_or_404(qs, pk=self.kwargs['pk']) + establishment = get_object_or_404(qs, pk=self.kwargs.get('pk')) # May raise a permission denied self.check_object_permissions(self.request, establishment) @@ -240,7 +245,7 @@ class EstablishmentCompanyListCreateView(EstablishmentMixinViews, establishment_qs = models.Establishment.objects.all() filtered_ad_qs = self.filter_queryset(establishment_qs) - establishment = get_object_or_404(filtered_ad_qs, pk=self.kwargs['pk']) + establishment = get_object_or_404(filtered_ad_qs, pk=self.kwargs.get('pk')) # May raise a permission denied self.check_object_permissions(self.request, establishment) @@ -263,8 +268,8 @@ class EstablishmentCompanyRUDView(EstablishmentMixinViews, establishment_qs = models.Establishment.objects.all() filtered_ad_qs = self.filter_queryset(establishment_qs) - establishment = get_object_or_404(filtered_ad_qs, pk=self.kwargs['pk']) - company = get_object_or_404(establishment.companies.all(), pk=self.kwargs['company_pk']) + establishment = get_object_or_404(filtered_ad_qs, pk=self.kwargs.get('pk')) + company = get_object_or_404(establishment.companies.all(), pk=self.kwargs.get('company_pk')) # May raise a permission denied self.check_object_permissions(self.request, company) @@ -273,7 +278,7 @@ class EstablishmentCompanyRUDView(EstablishmentMixinViews, class EstablishmentNoteListCreateView(EstablishmentMixinViews, - generics.ListCreateAPIView): + generics.ListCreateAPIView): """Retrieve|Update|Destroy establishment note view.""" serializer_class = serializers.EstablishmentNoteListCreateSerializer @@ -283,7 +288,7 @@ class EstablishmentNoteListCreateView(EstablishmentMixinViews, establishment_qs = models.Establishment.objects.all() filtered_establishment_qs = self.filter_queryset(establishment_qs) - establishment = get_object_or_404(filtered_establishment_qs, pk=self.kwargs['pk']) + establishment = get_object_or_404(filtered_establishment_qs, pk=self.kwargs.get('pk')) # May raise a permission denied self.check_object_permissions(self.request, establishment) @@ -306,7 +311,7 @@ class EstablishmentNoteRUDView(EstablishmentMixinViews, establishment_qs = models.Establishment.objects.all() filtered_establishment_qs = self.filter_queryset(establishment_qs) - establishment = get_object_or_404(filtered_establishment_qs, pk=self.kwargs['pk']) + establishment = get_object_or_404(filtered_establishment_qs, pk=self.kwargs.get('pk')) note = get_object_or_404(establishment.notes.all(), pk=self.kwargs['note_pk']) # May raise a permission denied diff --git a/apps/location/views/back.py b/apps/location/views/back.py index b6677837..4d420154 100644 --- a/apps/location/views/back.py +++ b/apps/location/views/back.py @@ -51,8 +51,8 @@ class CityGalleryCreateDestroyView(common.CityViewMixin, """ city_qs = self.filter_queryset(self.get_queryset()) - city = get_object_or_404(city_qs, pk=self.kwargs['pk']) - gallery = get_object_or_404(city.city_gallery, image_id=self.kwargs['image_id']) + city = get_object_or_404(city_qs, pk=self.kwargs.get('pk')) + gallery = get_object_or_404(city.city_gallery, image_id=self.kwargs.get('image_id')) # May raise a permission denied self.check_object_permissions(self.request, gallery) diff --git a/apps/news/views.py b/apps/news/views.py index 3e841246..638f208b 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -108,8 +108,8 @@ class NewsBackOfficeGalleryCreateDestroyView(NewsBackOfficeMixinView, """ news_qs = self.filter_queryset(self.get_queryset()) - news = get_object_or_404(news_qs, pk=self.kwargs['pk']) - gallery = get_object_or_404(news.news_gallery, image_id=self.kwargs['image_id']) + news = get_object_or_404(news_qs, pk=self.kwargs.get('pk')) + gallery = get_object_or_404(news.news_gallery, image_id=self.kwargs.get('image_id')) # May raise a permission denied self.check_object_permissions(self.request, gallery) @@ -125,7 +125,7 @@ class NewsBackOfficeGalleryListView(NewsBackOfficeMixinView, def get_object(self): """Override get_object method.""" qs = super(NewsBackOfficeGalleryListView, self).get_queryset() - news = get_object_or_404(qs, pk=self.kwargs['pk']) + news = get_object_or_404(qs, pk=self.kwargs.get('pk')) # May raise a permission denied self.check_object_permissions(self.request, news) @@ -160,7 +160,7 @@ class NewsFavoritesCreateDestroyView(generics.CreateAPIView, generics.DestroyAPI """ Returns the object the view is displaying. """ - news = get_object_or_404(models.News, slug=self.kwargs['slug']) + news = get_object_or_404(models.News, slug=self.kwargs.get('slug')) favorites = get_object_or_404(news.favorites.filter(user=self.request.user)) # May raise a permission denied self.check_object_permissions(self.request, favorites) diff --git a/apps/product/migrations/0018_purchasedproduct.py b/apps/product/migrations/0018_purchasedproduct.py new file mode 100644 index 00000000..b1c23646 --- /dev/null +++ b/apps/product/migrations/0018_purchasedproduct.py @@ -0,0 +1,30 @@ +# Generated by Django 2.2.7 on 2019-11-20 12:49 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0064_auto_20191119_1546'), + ('product', '0017_auto_20191119_1546'), + ] + + operations = [ + migrations.CreateModel( + name='PurchasedProduct', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('is_gifted', models.NullBooleanField(default=None, verbose_name='is gifted')), + ('quantity', models.PositiveSmallIntegerField(verbose_name='quantity')), + ('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='purchased_plaques', to='establishment.Establishment', verbose_name='establishment')), + ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='purchased_by_establishments', to='product.Product', verbose_name='plaque')), + ], + options={ + 'verbose_name': 'purchased plaque', + 'verbose_name_plural': 'purchased plaques', + 'unique_together': {('establishment', 'product')}, + }, + ), + ] diff --git a/apps/product/models.py b/apps/product/models.py index 6baaf43e..e562e4a3 100644 --- a/apps/product/models.py +++ b/apps/product/models.py @@ -263,12 +263,17 @@ class Product(GalleryModelMixin, TranslatedFieldsMixin, BaseAttributes, HasTagsM def grape_variety(self): return self.tags.filter(category__index_name='grape-variety') + @property + def bottle_sizes(self): + return self.tags.filter(category__index_name='bottle_size') + @property def related_tags(self): return super().visible_tags.exclude(category__index_name__in=[ 'sugar-content', 'wine-color', 'bottles-produced', - 'serial-number', 'grape-variety'] - ) + 'serial-number', 'grape-variety', 'serial_number', + 'alcohol_percentage', 'bottle_size', + ]) @property def display_name(self): @@ -315,6 +320,26 @@ class OnlineProduct(Product): verbose_name_plural = _('Online products') +class PurchasedProduct(models.Model): + """Model for storing establishment purchased plaques.""" + + establishment = models.ForeignKey('establishment.Establishment', on_delete=models.CASCADE, + related_name='purchased_plaques', + verbose_name=_('establishment')) + product = models.ForeignKey('product.Product', on_delete=models.CASCADE, + related_name='purchased_by_establishments', + verbose_name=_('plaque')) + is_gifted = models.NullBooleanField(default=None, + verbose_name=_('is gifted')) + quantity = models.PositiveSmallIntegerField(verbose_name=_('quantity')) + + class Meta: + """Meta class.""" + verbose_name = _('purchased plaque') + verbose_name_plural = _('purchased plaques') + unique_together = ('establishment', 'product') + + class Unit(models.Model): """Product unit model.""" name = models.CharField(max_length=255, diff --git a/apps/product/views/back.py b/apps/product/views/back.py index ac780849..fc5e108f 100644 --- a/apps/product/views/back.py +++ b/apps/product/views/back.py @@ -57,8 +57,8 @@ class ProductBackOfficeGalleryCreateDestroyView(ProductBackOfficeMixinView, """ product_qs = self.filter_queryset(self.get_queryset()) - product = get_object_or_404(product_qs, pk=self.kwargs['pk']) - gallery = get_object_or_404(product.product_gallery, image_id=self.kwargs['image_id']) + product = get_object_or_404(product_qs, pk=self.kwargs.get('pk')) + gallery = get_object_or_404(product.product_gallery, image_id=self.kwargs.get('image_id')) # May raise a permission denied self.check_object_permissions(self.request, gallery) @@ -75,7 +75,7 @@ class ProductBackOfficeGalleryListView(ProductBackOfficeMixinView, def get_object(self): """Override get_object method.""" qs = super(ProductBackOfficeGalleryListView, self).get_queryset() - product = get_object_or_404(qs, pk=self.kwargs['pk']) + product = get_object_or_404(qs, pk=self.kwargs.get('pk')) # May raise a permission denied self.check_object_permissions(self.request, product) @@ -149,7 +149,7 @@ class ProductNoteListCreateView(ProductBackOfficeMixinView, product_qs = models.Product.objects.all() filtered_product_qs = self.filter_queryset(product_qs) - product = get_object_or_404(filtered_product_qs, pk=self.kwargs['pk']) + product = get_object_or_404(filtered_product_qs, pk=self.kwargs.get('pk')) # May raise a permission denied self.check_object_permissions(self.request, product) @@ -173,8 +173,8 @@ class ProductNoteRUDView(ProductBackOfficeMixinView, product_qs = models.Product.objects.all() filtered_product_qs = self.filter_queryset(product_qs) - product = get_object_or_404(filtered_product_qs, pk=self.kwargs['pk']) - note = get_object_or_404(product.notes.all(), pk=self.kwargs['note_pk']) + product = get_object_or_404(filtered_product_qs, pk=self.kwargs.get('pk')) + note = get_object_or_404(product.notes.all(), pk=self.kwargs.get('note_pk')) # May raise a permission denied self.check_object_permissions(self.request, note) diff --git a/apps/timetable/serialziers.py b/apps/timetable/serialziers.py index 37725e1d..533bca70 100644 --- a/apps/timetable/serialziers.py +++ b/apps/timetable/serialziers.py @@ -8,6 +8,9 @@ from timetable.models import Timetable class ScheduleRUDSerializer(serializers.ModelSerializer): """Serializer for Establishment model.""" + NULLABLE_FIELDS = ['lunch_start', 'lunch_end', 'dinner_start', + 'dinner_end', 'opening_at', 'closed_at'] + weekday_display = serializers.CharField(source='get_weekday_display', read_only=True) @@ -18,9 +21,6 @@ class ScheduleRUDSerializer(serializers.ModelSerializer): opening_at = serializers.TimeField(required=False) closed_at = serializers.TimeField(required=False) - NULLABLE_FIELDS = ['lunch_start', 'lunch_end', 'dinner_start', - 'dinner_end', 'opening_at', 'closed_at'] - class Meta: """Meta class.""" model = Timetable diff --git a/apps/transfer/management/commands/transfer.py b/apps/transfer/management/commands/transfer.py index d69dd908..9126856f 100644 --- a/apps/transfer/management/commands/transfer.py +++ b/apps/transfer/management/commands/transfer.py @@ -39,6 +39,7 @@ class Command(BaseCommand): 'rating_count', 'product_review', 'newsletter_subscriber', # подписчики на рассылку - переносить после переноса пользователей №1 + 'purchased_plaques', # №6 - перенос купленных тарелок ] def handle(self, *args, **options): diff --git a/apps/transfer/models.py b/apps/transfer/models.py index d8be36ce..f4a1a800 100644 --- a/apps/transfer/models.py +++ b/apps/transfer/models.py @@ -581,22 +581,19 @@ class EstablishmentInfos(MigrateMixin): db_table = 'establishment_infos' -# class EstablishmentMerchandises(MigrateMixin): -# using = 'legacy' -# -# establishment = models.ForeignKey('Establishments', models.DO_NOTHING, blank=True, null=True) +class EstablishmentMerchandises(MigrateMixin): + using = 'legacy' -# TODO: модели Merchandises нету в гугл таблице Check Migrations + establishment = models.ForeignKey('Establishments', models.DO_NOTHING, blank=True, null=True) + merchandise = models.ForeignKey('Merchandise', models.DO_NOTHING, blank=True, null=True) + gifted = models.NullBooleanField(blank=True, null=True) + quantity = models.IntegerField(blank=True, null=True) + created_at = models.DateTimeField() + updated_at = models.DateTimeField() -# merchandise = models.ForeignKey('Merchandises', models.DO_NOTHING, blank=True, null=True) -# gifted = models.IntegerField(blank=True, null=True) -# quantity = models.IntegerField(blank=True, null=True) -# created_at = models.DateTimeField() -# updated_at = models.DateTimeField() -# -# class Meta: -# managed = False -# db_table = 'establishment_merchandises' + class Meta: + managed = False + db_table = 'establishment_merchandises' class Menus(MigrateMixin): diff --git a/apps/transfer/serializers/product.py b/apps/transfer/serializers/product.py index a0f3ef8a..86c6720a 100644 --- a/apps/transfer/serializers/product.py +++ b/apps/transfer/serializers/product.py @@ -265,7 +265,7 @@ class ProductSerializer(TransferSerializerMixin): state = serializers.CharField() bottles_produced = serializers.CharField(allow_null=True, allow_blank=True) unique_key = serializers.CharField(allow_null=True) - price = serializers.DecimalField(max_digits=14, decimal_places=2) + price = serializers.DecimalField(max_digits=14, decimal_places=2, allow_null=True) class Meta: model = models.Product diff --git a/apps/utils/views.py b/apps/utils/views.py index 870e132f..a8580f59 100644 --- a/apps/utils/views.py +++ b/apps/utils/views.py @@ -12,7 +12,7 @@ from gallery.tasks import delete_image # JWT # Login base view mixins -class JWTGenericViewMixin(generics.GenericAPIView): +class JWTGenericViewMixin: """JWT view mixin""" ACCESS_TOKEN_HTTP_ONLY = False @@ -39,30 +39,31 @@ class JWTGenericViewMixin(generics.GenericAPIView): """ COOKIES = [] - if hasattr(self.request, 'locale'): - COOKIES.append(self.COOKIE(key='locale', - value=self.request.locale, - http_only=self.ACCESS_TOKEN_HTTP_ONLY, - secure=self.LOCALE_SECURE, - max_age=settings.COOKIES_MAX_AGE if permanent else None)) - if hasattr(self.request, 'country_code'): - COOKIES.append(self.COOKIE(key='country_code', - value=self.request.country_code, - http_only=self.COUNTRY_CODE_HTTP_ONLY, - secure=self.COUNTRY_CODE_SECURE, - max_age=settings.COOKIES_MAX_AGE if permanent else None)) - if access_token: - COOKIES.append(self.COOKIE(key='access_token', - value=access_token, - http_only=self.ACCESS_TOKEN_HTTP_ONLY, - secure=self.ACCESS_TOKEN_SECURE, - max_age=settings.COOKIES_MAX_AGE if permanent else None)) - if refresh_token: - COOKIES.append(self.COOKIE(key='refresh_token', - value=refresh_token, - http_only=self.REFRESH_TOKEN_HTTP_ONLY, - secure=self.REFRESH_TOKEN_SECURE, - max_age=settings.COOKIES_MAX_AGE if permanent else None)) + if hasattr(self, 'request'): + if hasattr(self.request, 'locale'): + COOKIES.append(self.COOKIE(key='locale', + value=self.request.locale, + http_only=self.ACCESS_TOKEN_HTTP_ONLY, + secure=self.LOCALE_SECURE, + max_age=settings.COOKIES_MAX_AGE if permanent else None)) + if hasattr(self.request, 'country_code'): + COOKIES.append(self.COOKIE(key='country_code', + value=self.request.country_code, + http_only=self.COUNTRY_CODE_HTTP_ONLY, + secure=self.COUNTRY_CODE_SECURE, + max_age=settings.COOKIES_MAX_AGE if permanent else None)) + if access_token: + COOKIES.append(self.COOKIE(key='access_token', + value=access_token, + http_only=self.ACCESS_TOKEN_HTTP_ONLY, + secure=self.ACCESS_TOKEN_SECURE, + max_age=settings.COOKIES_MAX_AGE if permanent else None)) + if refresh_token: + COOKIES.append(self.COOKIE(key='refresh_token', + value=refresh_token, + http_only=self.REFRESH_TOKEN_HTTP_ONLY, + secure=self.REFRESH_TOKEN_SECURE, + max_age=settings.COOKIES_MAX_AGE if permanent else None)) return COOKIES def _put_cookies_in_response(self, cookies: list, response: Response): From e303e15f32313bc24c5d5fece870d389a7730c8f Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 21 Nov 2019 10:28:17 +0300 Subject: [PATCH 16/27] added two characteristics to wine detail serializer --- apps/product/models.py | 6 ++++++ apps/product/serializers/common.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/apps/product/models.py b/apps/product/models.py index e562e4a3..f499afee 100644 --- a/apps/product/models.py +++ b/apps/product/models.py @@ -267,6 +267,12 @@ class Product(GalleryModelMixin, TranslatedFieldsMixin, BaseAttributes, HasTagsM def bottle_sizes(self): return self.tags.filter(category__index_name='bottle_size') + @property + def alcohol_percentage(self): + qs = self.tags.filter(category__index_name='alcohol_percentage') + if qs.exists(): + return qs.first() + @property def related_tags(self): return super().visible_tags.exclude(category__index_name__in=[ diff --git a/apps/product/serializers/common.py b/apps/product/serializers/common.py index a0a56337..14eff642 100644 --- a/apps/product/serializers/common.py +++ b/apps/product/serializers/common.py @@ -128,6 +128,8 @@ class ProductDetailSerializer(ProductBaseSerializer): bottles_produced = TagBaseSerializer(many=True, read_only=True) sugar_contents = TagBaseSerializer(many=True, read_only=True) grape_variety = TagBaseSerializer(many=True, read_only=True) + bottle_sizes = TagBaseSerializer(many=True, read_only=True) + alcohol_percentage = TagBaseSerializer(read_only=True) image_url = serializers.URLField(allow_null=True, read_only=True) new_image = ImageBaseSerializer(source='crop_main_image', allow_null=True, read_only=True) @@ -146,6 +148,8 @@ class ProductDetailSerializer(ProductBaseSerializer): 'new_image', 'grape_variety', 'average_price', + 'bottle_sizes', + 'alcohol_percentage', ] From bebdf1e269ac45a690be942cc832a0bd6541e5d0 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 21 Nov 2019 11:12:52 +0300 Subject: [PATCH 17/27] review test and fixes review crud api --- apps/review/serializers/common.py | 4 ++- apps/review/tests.py | 44 +++++++++++++++++++++++++++++++ apps/review/views/back.py | 2 +- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/apps/review/serializers/common.py b/apps/review/serializers/common.py index 2889db6e..da7b624b 100644 --- a/apps/review/serializers/common.py +++ b/apps/review/serializers/common.py @@ -14,7 +14,9 @@ class ReviewBaseSerializer(serializers.ModelSerializer): 'child', 'published_at', 'vintage', - 'country' + 'country', + 'content_type', + 'object_id', ) diff --git a/apps/review/tests.py b/apps/review/tests.py index e04f5281..eebc1e86 100644 --- a/apps/review/tests.py +++ b/apps/review/tests.py @@ -19,6 +19,7 @@ class BaseTestCase(APITestCase): username=self.username, email=self.email, password=self.password, + is_staff=True, ) tokens = User.create_jwt_tokens(self.user) @@ -61,6 +62,49 @@ class BaseTestCase(APITestCase): ) +class ReviewTestCase(BaseTestCase): + def setUp(self): + super().setUp() + + def test_review_list(self): + response = self.client.get('/api/back/review/') + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_review_post(self): + test_review = { + 'reviewer': self.user.id, + 'status': Review.READY, + 'vintage': 2019, + 'country': self.country_ru.id, + 'object_id': 1, + 'content_type': 1, + } + response = self.client.post('/api/back/review/', data=test_review) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + def test_review_detail(self): + response = self.client.get(f'/api/back/review/{self.test_review.id}/', format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_review_detail_put(self): + data = { + 'id': self.test_review.id, + 'vintage': 2018, + 'reviewer': self.user.id, + 'status': Review.READY, + 'country': self.country_ru.id, + 'object_id': 1, + 'content_type': 1, + } + + response = self.client.put(f'/api/back/review/{self.test_review.id}/', data=data, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_review_delete(self): + response = self.client.delete(f'/api/back/review/{self.test_review.id}/') + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + class InquiriesTestCase(BaseTestCase): def setUp(self): super().setUp() diff --git a/apps/review/views/back.py b/apps/review/views/back.py index c5d89e11..27f9af0d 100644 --- a/apps/review/views/back.py +++ b/apps/review/views/back.py @@ -18,7 +18,7 @@ class ReviewRUDView(generics.RetrieveUpdateDestroyAPIView): """Comment RUD view.""" serializer_class = serializers.ReviewBaseSerializer queryset = models.Review.objects.all() - permission_classes = [IsReviewerManager | IsRestaurantReviewer] + permission_classes = [permissions.IsAdminUser | IsReviewerManager | IsRestaurantReviewer] lookup_field = 'id' From 98468f26041fa4cf2018d51b2d06874f3fa66a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=93=D0=BB=D0=B0?= =?UTF-8?q?=D0=B4=D0=BA=D0=B8=D1=85?= Date: Thu, 21 Nov 2019 11:16:02 +0300 Subject: [PATCH 18/27] Fix ELASTICSEARCH_DSL_AUTOSYNCOC --- project/settings/base.py | 1 - project/settings/development.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/project/settings/base.py b/project/settings/base.py index 0a20bdeb..4299c379 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -503,4 +503,3 @@ ESTABLISHMENT_CHOSEN_TAGS = ['gastronomic', 'en_vogue', 'terrace', 'streetfood', NEWS_CHOSEN_TAGS = ['eat', 'drink', 'cook', 'style', 'international', 'event', 'partnership'] INTERNATIONAL_COUNTRY_CODES = ['www', 'main', 'next'] -# ELASTICSEARCH_DSL_AUTOSYNC = False diff --git a/project/settings/development.py b/project/settings/development.py index 669bc485..06f1199b 100644 --- a/project/settings/development.py +++ b/project/settings/development.py @@ -33,6 +33,7 @@ ELASTICSEARCH_INDEX_NAMES = { 'search_indexes.documents.product': 'development_product', } +# ELASTICSEARCH_DSL_AUTOSYNC = False sentry_sdk.init( dsn="https://35d9bb789677410ab84a822831c6314f@sentry.io/1729093", From e6a143f6ea59225e27734852f5530898f5aaed7a Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 21 Nov 2019 12:29:55 +0300 Subject: [PATCH 19/27] partner model --- apps/partner/admin.py | 1 + .../migrations/0003_auto_20191121_0929.py | 46 +++++++++++++++++++ apps/partner/models.py | 20 ++++++++ 3 files changed, 67 insertions(+) create mode 100644 apps/partner/migrations/0003_auto_20191121_0929.py diff --git a/apps/partner/admin.py b/apps/partner/admin.py index f2973f75..9161837b 100644 --- a/apps/partner/admin.py +++ b/apps/partner/admin.py @@ -6,3 +6,4 @@ from partner import models @admin.register(models.Partner) class PartnerModelAdmin(admin.ModelAdmin): """Model admin for Partner model.""" + raw_id_fields = ('establishment',) diff --git a/apps/partner/migrations/0003_auto_20191121_0929.py b/apps/partner/migrations/0003_auto_20191121_0929.py new file mode 100644 index 00000000..526e14ce --- /dev/null +++ b/apps/partner/migrations/0003_auto_20191121_0929.py @@ -0,0 +1,46 @@ +# Generated by Django 2.2.7 on 2019-11-21 09:29 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0065_establishment_purchased_products'), + ('partner', '0002_auto_20191101_0939'), + ] + + operations = [ + migrations.AddField( + model_name='partner', + name='establishment', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='partners', to='establishment.Establishment', verbose_name='Establishment'), + preserve_default=False, + ), + migrations.AddField( + model_name='partner', + name='expiry_date', + field=models.DateField(blank=True, null=True, verbose_name='expiry date'), + ), + migrations.AddField( + model_name='partner', + name='old_id', + field=models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='old id'), + ), + migrations.AddField( + model_name='partner', + name='price_per_month', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='price per month'), + ), + migrations.AddField( + model_name='partner', + name='starting_date', + field=models.DateField(blank=True, null=True, verbose_name='starting date'), + ), + migrations.AddField( + model_name='partner', + name='type', + field=models.PositiveSmallIntegerField(choices=[(0, 'Partner'), (1, 'Sponsor')], default=0), + ), + ] diff --git a/apps/partner/models.py b/apps/partner/models.py index 1eb70845..61d13023 100644 --- a/apps/partner/models.py +++ b/apps/partner/models.py @@ -1,13 +1,33 @@ from django.db import models from django.utils.translation import gettext_lazy as _ +from establishment.models import Establishment from utils.models import ImageMixin, ProjectBaseMixin class Partner(ProjectBaseMixin): """Partner model.""" + + PARTNER = 0 + SPONSOR = 1 + MODEL_TYPES = ( + (PARTNER, _('Partner')), + (SPONSOR, _('Sponsor')), + ) + + old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None) url = models.URLField(verbose_name=_('Partner URL')) image = models.URLField(verbose_name=_('Partner image URL'), null=True) + establishment = models.ForeignKey( + Establishment, + verbose_name=_('Establishment'), + related_name='partners', + on_delete=models.CASCADE, + ) + type = models.PositiveSmallIntegerField(choices=MODEL_TYPES, default=PARTNER) + starting_date = models.DateField(_('starting date'), blank=True, null=True) + expiry_date = models.DateField(_('expiry date'), blank=True, null=True) + price_per_month = models.DecimalField(_('price per month'), max_digits=10, decimal_places=2, blank=True, null=True) class Meta: verbose_name = _('partner') From 3f164459a7d9ce59032c5ae50c3d33e87f2b1924 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 21 Nov 2019 12:37:05 +0300 Subject: [PATCH 20/27] added transfer for filling city gallery --- apps/location/transfer_data.py | 41 ++++++++++++++++++- apps/transfer/management/commands/transfer.py | 1 + apps/transfer/models.py | 2 +- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/apps/location/transfer_data.py b/apps/location/transfer_data.py index 0d90461a..658d2d20 100644 --- a/apps/location/transfer_data.py +++ b/apps/location/transfer_data.py @@ -1,8 +1,8 @@ from transfer.serializers import location as location_serializers from transfer import models as transfer_models -from location.models import Country +from location.models import Country, CityGallery, City +from gallery.models import Image from pprint import pprint - from requests import get @@ -179,6 +179,42 @@ def update_flags(): query.save() +def transfer_city_gallery(): + created_counter = 0 + cities_not_exists = {} + gallery_obj_exists_counter = 0 + + city_gallery = transfer_models.CityPhotos.objects.exclude(city__isnull=True) \ + .exclude(city__country_code_2__isnull=True) \ + .exclude(city__country_code_2__iexact='') \ + .exclude(city__region_code__isnull=True) \ + .exclude(city__region_code__iexact='') \ + .values_list('city_id', 'attachment_suffix_url') + for old_city_id, image_suffix_url in city_gallery: + city = City.objects.filter(old_id=old_city_id) + if city.exists(): + city = city.first() + image, _ = Image.objects.get_or_create(image=image_suffix_url, + defaults={ + 'image': image_suffix_url, + 'orientation': Image.HORIZONTAL, + 'title': f'{city.name} - {image_suffix_url}', + }) + city_gallery, created = CityGallery.objects.get_or_create(image=image, + city=city, + is_main=True) + if created: + created_counter += 1 + else: + gallery_obj_exists_counter += 1 + else: + cities_not_exists.update({'city_old_id': old_city_id}) + + print(f'Created: {created_counter}\n' + f'City not exists: {cities_not_exists}\n' + f'Already added: {gallery_obj_exists_counter}') + + data_types = { "dictionaries": [ transfer_countries, @@ -192,4 +228,5 @@ data_types = { "update_country_flag": [ update_flags ], + "fill_city_gallery": [transfer_city_gallery] } diff --git a/apps/transfer/management/commands/transfer.py b/apps/transfer/management/commands/transfer.py index 9126856f..2d0ce399 100644 --- a/apps/transfer/management/commands/transfer.py +++ b/apps/transfer/management/commands/transfer.py @@ -40,6 +40,7 @@ class Command(BaseCommand): 'product_review', 'newsletter_subscriber', # подписчики на рассылку - переносить после переноса пользователей №1 'purchased_plaques', # №6 - перенос купленных тарелок + 'fill_city_gallery', # №3 - перенос галереи городов ] def handle(self, *args, **options): diff --git a/apps/transfer/models.py b/apps/transfer/models.py index f4a1a800..a8190879 100644 --- a/apps/transfer/models.py +++ b/apps/transfer/models.py @@ -217,10 +217,10 @@ class CityNames(MigrateMixin): class CityPhotos(MigrateMixin): using = 'legacy' - # city_id = models.IntegerField(blank=True, null=True) city = models.ForeignKey(Cities, models.DO_NOTHING, blank=True, null=True) attachment_file_name = models.CharField(max_length=255, blank=True, null=True) attachment_content_type = models.CharField(max_length=255, blank=True, null=True) + attachment_suffix_url = models.CharField(max_length=255) geometries = models.CharField(max_length=1024, blank=True, null=True) attachment_file_size = models.IntegerField(blank=True, null=True) attachment_updated_at = models.DateTimeField(blank=True, null=True) From d9b88f7ef96ef013611c9d0608f13229ed305dc5 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 21 Nov 2019 13:32:50 +0300 Subject: [PATCH 21/27] partner name field --- apps/partner/migrations/0004_partner_name.py | 18 ++++++++++++++++++ apps/partner/models.py | 1 + 2 files changed, 19 insertions(+) create mode 100644 apps/partner/migrations/0004_partner_name.py diff --git a/apps/partner/migrations/0004_partner_name.py b/apps/partner/migrations/0004_partner_name.py new file mode 100644 index 00000000..ef9ac8e8 --- /dev/null +++ b/apps/partner/migrations/0004_partner_name.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.7 on 2019-11-21 10:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('partner', '0003_auto_20191121_0929'), + ] + + operations = [ + migrations.AddField( + model_name='partner', + name='name', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='name'), + ), + ] diff --git a/apps/partner/models.py b/apps/partner/models.py index 61d13023..c4af1de5 100644 --- a/apps/partner/models.py +++ b/apps/partner/models.py @@ -16,6 +16,7 @@ class Partner(ProjectBaseMixin): ) old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None) + name = models.CharField(_('name'), max_length=255, blank=True, null=True) url = models.URLField(verbose_name=_('Partner URL')) image = models.URLField(verbose_name=_('Partner image URL'), null=True) establishment = models.ForeignKey( From a6bfb3ea6b49ed28c66afae2e5d54ab53e6d226a Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 21 Nov 2019 13:47:37 +0300 Subject: [PATCH 22/27] partner migrations --- ...121_0929.py => 0003_auto_20191121_1045.py} | 10 +++++++--- .../migrations/0004_auto_20191121_1045.py | 19 +++++++++++++++++++ apps/partner/migrations/0004_partner_name.py | 18 ------------------ apps/partner/transfer_data.py | 16 ++++++++++++++-- 4 files changed, 40 insertions(+), 23 deletions(-) rename apps/partner/migrations/{0003_auto_20191121_0929.py => 0003_auto_20191121_1045.py} (76%) create mode 100644 apps/partner/migrations/0004_auto_20191121_1045.py delete mode 100644 apps/partner/migrations/0004_partner_name.py diff --git a/apps/partner/migrations/0003_auto_20191121_0929.py b/apps/partner/migrations/0003_auto_20191121_1045.py similarity index 76% rename from apps/partner/migrations/0003_auto_20191121_0929.py rename to apps/partner/migrations/0003_auto_20191121_1045.py index 526e14ce..6d548091 100644 --- a/apps/partner/migrations/0003_auto_20191121_0929.py +++ b/apps/partner/migrations/0003_auto_20191121_1045.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.7 on 2019-11-21 09:29 +# Generated by Django 2.2.7 on 2019-11-21 10:45 from django.db import migrations, models import django.db.models.deletion @@ -15,14 +15,18 @@ class Migration(migrations.Migration): migrations.AddField( model_name='partner', name='establishment', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='partners', to='establishment.Establishment', verbose_name='Establishment'), - preserve_default=False, + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='partners', to='establishment.Establishment', verbose_name='Establishment'), ), migrations.AddField( model_name='partner', name='expiry_date', field=models.DateField(blank=True, null=True, verbose_name='expiry date'), ), + migrations.AddField( + model_name='partner', + name='name', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='name'), + ), migrations.AddField( model_name='partner', name='old_id', diff --git a/apps/partner/migrations/0004_auto_20191121_1045.py b/apps/partner/migrations/0004_auto_20191121_1045.py new file mode 100644 index 00000000..1906a8c0 --- /dev/null +++ b/apps/partner/migrations/0004_auto_20191121_1045.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.7 on 2019-11-21 10:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('partner', '0003_auto_20191121_1045'), + ] + + operations = [ + migrations.AlterField( + model_name='partner', + name='establishment', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='partners', to='establishment.Establishment', verbose_name='Establishment'), + ), + ] diff --git a/apps/partner/migrations/0004_partner_name.py b/apps/partner/migrations/0004_partner_name.py deleted file mode 100644 index ef9ac8e8..00000000 --- a/apps/partner/migrations/0004_partner_name.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.7 on 2019-11-21 10:32 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('partner', '0003_auto_20191121_0929'), - ] - - operations = [ - migrations.AddField( - model_name='partner', - name='name', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='name'), - ), - ] diff --git a/apps/partner/transfer_data.py b/apps/partner/transfer_data.py index e3219838..f373427a 100644 --- a/apps/partner/transfer_data.py +++ b/apps/partner/transfer_data.py @@ -1,11 +1,23 @@ -from django.db.models import Value, IntegerField, F from pprint import pprint + +from establishment.models import Establishment from transfer.models import EstablishmentBacklinks from transfer.serializers.partner import PartnerSerializer def transfer_partner(): - queryset = EstablishmentBacklinks.objects.filter(type="Partner") + establishments = Establishment.objects.filter(old_id__isnull=False).values_list('old_id', flat=True) + queryset = EstablishmentBacklinks.objects.filter( + establishment_id__in=list(establishments), + ).values( + 'id', + 'establishment_id', + # 'aasm_state', + # 'created_at', + # 'establishment_id', + # 'mark', + # 'vintage', + ) # queryset = EstablishmentBacklinks.objects.all() # Partner and Sponsor From 9156d9cc35e5d9e13bd7c3c8239f412b0c3ae8a5 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 21 Nov 2019 14:00:17 +0300 Subject: [PATCH 23/27] fix partner migrations --- ...121_1045.py => 0003_auto_20191121_1059.py} | 2 +- .../migrations/0004_auto_20191121_1045.py | 19 --------- apps/partner/models.py | 2 + apps/partner/transfer_data.py | 19 ++++----- apps/transfer/serializers/partner.py | 39 ++++++++++--------- 5 files changed, 33 insertions(+), 48 deletions(-) rename apps/partner/migrations/{0003_auto_20191121_1045.py => 0003_auto_20191121_1059.py} (97%) delete mode 100644 apps/partner/migrations/0004_auto_20191121_1045.py diff --git a/apps/partner/migrations/0003_auto_20191121_1045.py b/apps/partner/migrations/0003_auto_20191121_1059.py similarity index 97% rename from apps/partner/migrations/0003_auto_20191121_1045.py rename to apps/partner/migrations/0003_auto_20191121_1059.py index 6d548091..c2e98fbe 100644 --- a/apps/partner/migrations/0003_auto_20191121_1045.py +++ b/apps/partner/migrations/0003_auto_20191121_1059.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.7 on 2019-11-21 10:45 +# Generated by Django 2.2.7 on 2019-11-21 10:59 from django.db import migrations, models import django.db.models.deletion diff --git a/apps/partner/migrations/0004_auto_20191121_1045.py b/apps/partner/migrations/0004_auto_20191121_1045.py deleted file mode 100644 index 1906a8c0..00000000 --- a/apps/partner/migrations/0004_auto_20191121_1045.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.7 on 2019-11-21 10:45 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('partner', '0003_auto_20191121_1045'), - ] - - operations = [ - migrations.AlterField( - model_name='partner', - name='establishment', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='partners', to='establishment.Establishment', verbose_name='Establishment'), - ), - ] diff --git a/apps/partner/models.py b/apps/partner/models.py index c4af1de5..5c5766c8 100644 --- a/apps/partner/models.py +++ b/apps/partner/models.py @@ -24,6 +24,8 @@ class Partner(ProjectBaseMixin): verbose_name=_('Establishment'), related_name='partners', on_delete=models.CASCADE, + blank=True, + null=True, ) type = models.PositiveSmallIntegerField(choices=MODEL_TYPES, default=PARTNER) starting_date = models.DateField(_('starting date'), blank=True, null=True) diff --git a/apps/partner/transfer_data.py b/apps/partner/transfer_data.py index f373427a..868345a8 100644 --- a/apps/partner/transfer_data.py +++ b/apps/partner/transfer_data.py @@ -12,20 +12,21 @@ def transfer_partner(): ).values( 'id', 'establishment_id', - # 'aasm_state', - # 'created_at', - # 'establishment_id', - # 'mark', - # 'vintage', + 'partnership_name', + 'partnership_icon', + 'backlink_url', + 'created_at', + 'type', + 'starting_date', + 'expiry_date', + 'price_per_month', ) - # queryset = EstablishmentBacklinks.objects.all() # Partner and Sponsor - - serialized_data = PartnerSerializer(data=list(queryset.values()), many=True) + serialized_data = PartnerSerializer(data=list(queryset), many=True) if serialized_data.is_valid(): serialized_data.save() else: - pprint(f"News serializer errors: {serialized_data.errors}") + pprint(f"Partner serializer errors: {serialized_data.errors}") data_types = { diff --git a/apps/transfer/serializers/partner.py b/apps/transfer/serializers/partner.py index 7de61486..69cf308e 100644 --- a/apps/transfer/serializers/partner.py +++ b/apps/transfer/serializers/partner.py @@ -2,27 +2,28 @@ from rest_framework import serializers from partner.models import Partner -class PartnerSerializer(serializers.ModelSerializer): - backlink_url = serializers.CharField(source="url") - partnership_icon = serializers.CharField() - partnership_name = serializers.CharField() +class PartnerSerializer(serializers.Serializer): + pass + # 'id', + # 'establishment_id', + # 'partnership_name', + # 'partnership_icon', + # 'backlink_url', + # 'created_at', + # 'type', + # 'starting_date', + # 'expiry_date', + # 'price_per_month', - class Meta: - model = Partner - fields = ( - "backlink_url", - "partnership_icon", - "partnership_name" - ) - def validate(self, data): - data["image"] = partnership_to_image_url.get(data["partnership_name"]).get(data["partnership_icon"]) - data.pop("partnership_name") - data.pop("partnership_icon") - return data - - def create(self, validated_data): - return Partner.objects.create(**validated_data) + # def validate(self, data): + # data["image"] = partnership_to_image_url.get(data["partnership_name"]).get(data["partnership_icon"]) + # data.pop("partnership_name") + # data.pop("partnership_icon") + # return data + # + # def create(self, validated_data): + # return Partner.objects.create(**validated_data) partnership_to_image_url = { From 8e215b2555509ea7556ad6d8995f4bb1b8b4b68b Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 21 Nov 2019 16:40:53 +0300 Subject: [PATCH 24/27] Demonstration carousel item --- apps/main/views/common.py | 2 +- project/settings/base.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/main/views/common.py b/apps/main/views/common.py index 15f89510..18ee0d8d 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -70,7 +70,7 @@ class CarouselListView(generics.ListAPIView): def get_queryset(self): country_code = self.request.country_code - if hasattr(settings, 'CAROUSEL_ITEMS') and country_code in ['www', 'main']: + if hasattr(settings, 'CAROUSEL_ITEMS') and country_code in settings.INTERNATIONAL_COUNTRY_CODES: qs = models.Carousel.objects.filter(id__in=settings.CAROUSEL_ITEMS) return qs qs = models.Carousel.objects.is_parsed().active() diff --git a/project/settings/base.py b/project/settings/base.py index 4299c379..a2aa1af1 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -498,7 +498,7 @@ FALLBACK_LOCALE = 'en-GB' # TMP TODO remove it later # Временный хардкод для демонстрации > 15 ноября, потом удалить! -CAROUSEL_ITEMS = [230, 231, 232] +CAROUSEL_ITEMS = [465] ESTABLISHMENT_CHOSEN_TAGS = ['gastronomic', 'en_vogue', 'terrace', 'streetfood', 'business', 'bar_cocktail', 'brunch', 'pop'] NEWS_CHOSEN_TAGS = ['eat', 'drink', 'cook', 'style', 'international', 'event', 'partnership'] INTERNATIONAL_COUNTRY_CODES = ['www', 'main', 'next'] From 86e29173ba940acb82e9d146bc2a5d595f8d61fe Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 21 Nov 2019 16:45:27 +0300 Subject: [PATCH 25/27] transfer partner --- apps/partner/transfer_data.py | 5 +++ apps/product/transfer_data.py | 50 +++++++++------------- apps/transfer/serializers/partner.py | 62 +++++++++++++++++++--------- 3 files changed, 67 insertions(+), 50 deletions(-) diff --git a/apps/partner/transfer_data.py b/apps/partner/transfer_data.py index 868345a8..acda6c2c 100644 --- a/apps/partner/transfer_data.py +++ b/apps/partner/transfer_data.py @@ -1,11 +1,15 @@ from pprint import pprint from establishment.models import Establishment +from partner.models import Partner from transfer.models import EstablishmentBacklinks from transfer.serializers.partner import PartnerSerializer def transfer_partner(): + """ + Transfer data to Partner model only after transfer Establishment + """ establishments = Establishment.objects.filter(old_id__isnull=False).values_list('old_id', flat=True) queryset = EstablishmentBacklinks.objects.filter( establishment_id__in=list(establishments), @@ -24,6 +28,7 @@ def transfer_partner(): serialized_data = PartnerSerializer(data=list(queryset), many=True) if serialized_data.is_valid(): + Partner.objects.all().delete() # TODO: закоментить, если требуется сохранить старые записи serialized_data.save() else: pprint(f"Partner serializer errors: {serialized_data.errors}") diff --git a/apps/product/transfer_data.py b/apps/product/transfer_data.py index 8e4a26fa..84a8a524 100644 --- a/apps/product/transfer_data.py +++ b/apps/product/transfer_data.py @@ -2,17 +2,6 @@ from pprint import pprint from transfer import models as transfer_models from transfer.serializers import product as product_serializers -from transfer.serializers.partner import PartnerSerializer - - -def transfer_partner(): - queryset = transfer_models.EstablishmentBacklinks.objects.filter(type="Partner") - - serialized_data = PartnerSerializer(data=list(queryset.values()), many=True) - if serialized_data.is_valid(): - serialized_data.save() - else: - pprint(f"News serializer errors: {serialized_data.errors}") def transfer_wine_color(): @@ -50,8 +39,8 @@ def transfer_wine_bottles_produced(): ) queryset = [vars(query) for query in raw_queryset] serialized_data = product_serializers.WineBottlesProducedSerializer( - data=queryset, - many=True) + data=queryset, + many=True) if serialized_data.is_valid(): serialized_data.save() else: @@ -69,8 +58,8 @@ def transfer_wine_classification_type(): ) queryset = [vars(query) for query in raw_queryset] serialized_data = product_serializers.WineClassificationTypeSerializer( - data=queryset, - many=True) + data=queryset, + many=True) if serialized_data.is_valid(): serialized_data.save() else: @@ -79,10 +68,10 @@ def transfer_wine_classification_type(): def transfer_wine_standard(): queryset = transfer_models.ProductClassification.objects.filter(parent_id__isnull=True) \ - .exclude(type='Classification') + .exclude(type='Classification') serialized_data = product_serializers.ProductStandardSerializer( - data=list(queryset.values()), - many=True) + data=list(queryset.values()), + many=True) if serialized_data.is_valid(): serialized_data.save() else: @@ -92,8 +81,8 @@ def transfer_wine_standard(): def transfer_wine_classifications(): queryset = transfer_models.ProductClassification.objects.filter(type='Classification') serialized_data = product_serializers.ProductClassificationSerializer( - data=list(queryset.values()), - many=True) + data=list(queryset.values()), + many=True) if serialized_data.is_valid(): serialized_data.save() else: @@ -104,8 +93,8 @@ def transfer_product(): errors = [] queryset = transfer_models.Products.objects.all() serialized_data = product_serializers.ProductSerializer( - data=list(queryset.values()), - many=True) + data=list(queryset.values()), + many=True) if serialized_data.is_valid(): serialized_data.save() else: @@ -117,8 +106,8 @@ def transfer_product_note(): errors = [] queryset = transfer_models.ProductNotes.objects.exclude(text='') serialized_data = product_serializers.ProductNoteSerializer( - data=list(queryset.values()), - many=True) + data=list(queryset.values()), + many=True) if serialized_data.is_valid(): serialized_data.save() else: @@ -130,8 +119,8 @@ def transfer_plate(): errors = [] queryset = transfer_models.Merchandise.objects.all() serialized_data = product_serializers.PlateSerializer( - data=list(queryset.values()), - many=True) + data=list(queryset.values()), + many=True) if serialized_data.is_valid(): serialized_data.save() else: @@ -143,8 +132,8 @@ def transfer_plate_image(): errors = [] queryset = transfer_models.Merchandise.objects.all() serialized_data = product_serializers.PlateImageSerializer( - data=list(queryset.values()), - many=True) + data=list(queryset.values()), + many=True) if serialized_data.is_valid(): serialized_data.save() else: @@ -153,7 +142,6 @@ def transfer_plate_image(): data_types = { - "partner": [transfer_partner], "wine_characteristics": [ transfer_wine_sugar_content, transfer_wine_color, @@ -161,12 +149,12 @@ data_types = { transfer_wine_classification_type, transfer_wine_standard, transfer_wine_classifications, - ], + ], "product": [ transfer_product, ], "product_note": [ - transfer_product_note, + transfer_product_note, ], "souvenir": [ transfer_plate, diff --git a/apps/transfer/serializers/partner.py b/apps/transfer/serializers/partner.py index 69cf308e..61f56dea 100644 --- a/apps/transfer/serializers/partner.py +++ b/apps/transfer/serializers/partner.py @@ -1,29 +1,53 @@ from rest_framework import serializers + +from establishment.models import Establishment from partner.models import Partner class PartnerSerializer(serializers.Serializer): - pass - # 'id', - # 'establishment_id', - # 'partnership_name', - # 'partnership_icon', - # 'backlink_url', - # 'created_at', - # 'type', - # 'starting_date', - # 'expiry_date', - # 'price_per_month', + id = serializers.IntegerField() + establishment_id = serializers.IntegerField() + partnership_name = serializers.CharField(allow_null=True) + partnership_icon = serializers.CharField(allow_null=True) + backlink_url = serializers.CharField(allow_null=True) + created_at = serializers.DateTimeField(format='%m-%d-%Y %H:%M:%S') + type = serializers.CharField(allow_null=True) + starting_date = serializers.DateField(allow_null=True) + expiry_date = serializers.DateField(allow_null=True) + price_per_month = serializers.DecimalField(max_digits=10, decimal_places=2, allow_null=True) + def validate(self, data): + data.update({ + 'old_id': data.pop('id'), + 'name': data['partnership_name'], + 'url': data.pop('backlink_url'), + 'image': self.get_image(data), + 'establishment': self.get_establishment(data), + 'type': Partner.PARTNER if data['type'] == 'Partner' else Partner.SPONSOR, + 'created': data.pop('created_at'), + }) + data.pop('partnership_icon') + data.pop('partnership_name') + data.pop('establishment_id') + return data - # def validate(self, data): - # data["image"] = partnership_to_image_url.get(data["partnership_name"]).get(data["partnership_icon"]) - # data.pop("partnership_name") - # data.pop("partnership_icon") - # return data - # - # def create(self, validated_data): - # return Partner.objects.create(**validated_data) + @staticmethod + def get_image(data): + return partnership_to_image_url.get(data['partnership_name']).get(data['partnership_icon']) + + @staticmethod + def get_establishment(data): + establishment = Establishment.objects.filter(old_id=data['establishment_id']).first() + if not establishment: + raise ValueError(f"Establishment not found with old_id {data['establishment_id']}: ") + return establishment + + def create(self, validated_data): + obj, _ = Partner.objects.update_or_create( + old_id=validated_data['old_id'], + defaults=validated_data, + ) + return obj partnership_to_image_url = { From 98e140b0c4778841eda1b7bae2a2034d163539fd Mon Sep 17 00:00:00 2001 From: Semyon Yekhmenin Date: Thu, 21 Nov 2019 14:12:22 +0000 Subject: [PATCH 26/27] Feature/news web view update --- apps/news/filters.py | 15 +++++++++++++++ apps/news/serializers.py | 2 ++ 2 files changed, 17 insertions(+) diff --git a/apps/news/filters.py b/apps/news/filters.py index 6ade7eeb..e8e35307 100644 --- a/apps/news/filters.py +++ b/apps/news/filters.py @@ -20,6 +20,16 @@ class NewsListFilterSet(filters.FilterSet): tag_value__in = filters.CharFilter(method='in_tags') type = filters.CharFilter(method='by_type') + state = filters.NumberFilter() + + SORT_BY_CREATED_CHOICE = "created" + SORT_BY_START_CHOICE = "start" + SORT_BY_CHOICES = ( + (SORT_BY_CREATED_CHOICE, "created"), + (SORT_BY_START_CHOICE, "start"), + ) + sort_by = filters.ChoiceFilter(method='sort_by_field', choices=SORT_BY_CHOICES) + class Meta: """Meta class""" model = models.News @@ -29,6 +39,8 @@ class NewsListFilterSet(filters.FilterSet): 'tag_group', 'tag_value__exclude', 'tag_value__in', + 'state', + 'sort_by', ) def in_tags(self, queryset, name, value): @@ -58,3 +70,6 @@ class NewsListFilterSet(filters.FilterSet): return queryset.filter(news_type__name=value) else: return queryset + + def sort_by_field(self, queryset, name, value): + return queryset.order_by(f'-{value}') diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 4eaeaeb4..4a84fa11 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -162,6 +162,7 @@ class NewsDetailWebSerializer(NewsDetailSerializer): class NewsBackOfficeBaseSerializer(NewsBaseSerializer): """News back office base serializer.""" + is_published = serializers.CharField(source='is_publish', read_only=True) class Meta(NewsBaseSerializer.Meta): """Meta class.""" @@ -169,6 +170,7 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): fields = NewsBaseSerializer.Meta.fields + ( 'title', 'subtitle', + 'is_published', ) From 4209b38d93c04e713b342be5ffa9ee88759b502f Mon Sep 17 00:00:00 2001 From: Semyon Yekhmenin Date: Thu, 21 Nov 2019 14:46:56 +0000 Subject: [PATCH 27/27] Fixed is_published filed type in serializer --- apps/news/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 4a84fa11..2264b9ec 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -162,7 +162,7 @@ class NewsDetailWebSerializer(NewsDetailSerializer): class NewsBackOfficeBaseSerializer(NewsBaseSerializer): """News back office base serializer.""" - is_published = serializers.CharField(source='is_publish', read_only=True) + is_published = serializers.BooleanField(source='is_publish', read_only=True) class Meta(NewsBaseSerializer.Meta): """Meta class."""