From 6a1fea76b6f4a49dff27f317ea8d8a3d2978430e Mon Sep 17 00:00:00 2001 From: dormantman Date: Wed, 22 Jan 2020 16:03:23 +0300 Subject: [PATCH 01/95] Added content page method --- apps/main/migrations/0050_contentpage.py | 27 ++++++++++++++++ .../migrations/0051_auto_20200122_1256.py | 27 ++++++++++++++++ apps/main/models.py | 32 +++++++++++++++---- apps/main/serializers/common.py | 18 +++++++++++ apps/main/urls/common.py | 1 + apps/main/views/common.py | 8 +++++ 6 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 apps/main/migrations/0050_contentpage.py create mode 100644 apps/main/migrations/0051_auto_20200122_1256.py diff --git a/apps/main/migrations/0050_contentpage.py b/apps/main/migrations/0050_contentpage.py new file mode 100644 index 00000000..7d369690 --- /dev/null +++ b/apps/main/migrations/0050_contentpage.py @@ -0,0 +1,27 @@ +# Generated by Django 2.2.7 on 2020-01-22 12:41 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0049_remove_navigationbarpermission_section'), + ] + + operations = [ + migrations.CreateModel( + name='ContentPage', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('content', models.TextField(verbose_name='content')), + ('slug', models.SlugField(max_length=255, null=True, unique=True, verbose_name='slug')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/main/migrations/0051_auto_20200122_1256.py b/apps/main/migrations/0051_auto_20200122_1256.py new file mode 100644 index 00000000..bdb85fdb --- /dev/null +++ b/apps/main/migrations/0051_auto_20200122_1256.py @@ -0,0 +1,27 @@ +# Generated by Django 2.2.7 on 2020-01-22 12:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0050_contentpage'), + ] + + operations = [ + migrations.AlterModelOptions( + name='contentpage', + options={'verbose_name': 'content page', 'verbose_name_plural': 'content page'}, + ), + migrations.AddField( + model_name='contentpage', + name='name', + field=models.CharField(default='', max_length=255, verbose_name='name'), + ), + migrations.AlterField( + model_name='contentpage', + name='content', + field=models.TextField(default='', verbose_name='content'), + ), + ] diff --git a/apps/main/models.py b/apps/main/models.py index ea853c04..0cde6ddb 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -20,8 +20,10 @@ from review.models import Review from tag.models import Tag from utils.exceptions import UnprocessableEntityError from utils.methods import dictfetchall -from utils.models import (ProjectBaseMixin, TJSONField, URLImageMixin, - TranslatedFieldsMixin, PlatformMixin) +from utils.models import ( + ProjectBaseMixin, TJSONField, URLImageMixin, + TranslatedFieldsMixin, PlatformMixin, +) class Currency(TranslatedFieldsMixin, models.Model): @@ -156,10 +158,10 @@ class SiteFeature(ProjectBaseMixin): published = models.BooleanField(default=False, verbose_name=_('Published')) main = models.BooleanField(default=False, help_text='shows on main page', - verbose_name=_('Main'),) + verbose_name=_('Main'), ) backoffice = models.BooleanField(default=False, help_text='shows on backoffice page', - verbose_name=_('backoffice'),) + verbose_name=_('backoffice'), ) nested = models.ManyToManyField('self', blank=True, symmetrical=False) old_id = models.IntegerField(null=True, blank=True) @@ -402,6 +404,24 @@ class Footer(ProjectBaseMixin): links = models.ManyToManyField(FooterLink, verbose_name=_('links'), related_name='link_footer') +class ContentPageQuerySet(models.QuerySet): + """QuerySet for model ContentPage.""" + + +class ContentPage(ProjectBaseMixin): + name = models.CharField(_('name'), max_length=255, default='') + content = models.TextField(_('content'), default='') + slug = models.SlugField(max_length=255, unique=True, null=True, + verbose_name=_('slug')) + + objects = ContentPageQuerySet.as_manager() + + class Meta: + """Meta class.""" + verbose_name = _('content page') + verbose_name_plural = _('content page') + + class PanelQuerySet(models.QuerySet): """Panels QuerySet.""" @@ -458,7 +478,7 @@ class Panel(ProjectBaseMixin): } with connections['default'].cursor() as cursor: count = self._raw_count(raw) - start = page*page_size + start = page * page_size cursor.execute(*self.set_limits(start, page_size)) data["count"] = count data["next"] = self.get_next_page(count, page, page_size) @@ -468,7 +488,7 @@ class Panel(ProjectBaseMixin): return data def get_next_page(self, count, page, page_size): - max_page = count/page_size-1 + max_page = count / page_size - 1 if not 0 <= page <= max_page: raise exceptions.NotFound('Invalid page.') if max_page > page: diff --git a/apps/main/serializers/common.py b/apps/main/serializers/common.py index 6a556ee6..bdb3e895 100644 --- a/apps/main/serializers/common.py +++ b/apps/main/serializers/common.py @@ -345,3 +345,21 @@ class NavigationBarPermissionBaseSerializer(serializers.ModelSerializer): 'sections', 'permission_mode_display', ] + + +class ContentPageSerializer(serializers.ModelSerializer): + """Content page serializer""" + + name = serializers.CharField() + content = serializers.CharField() + slug = serializers.CharField() + + class Meta: + """Meta class.""" + model = models.ContentPage + fields = [ + 'id', + 'name', + 'content', + 'slug' + ] diff --git a/apps/main/urls/common.py b/apps/main/urls/common.py index 6b8f26ce..16b4b2af 100644 --- a/apps/main/urls/common.py +++ b/apps/main/urls/common.py @@ -9,4 +9,5 @@ common_urlpatterns = [ path('awards//', AwardRetrieveView.as_view(), name='awards_retrieve'), path('carousel/', CarouselListView.as_view(), name='carousel-list'), path('determine-location/', DetermineLocation.as_view(), name='determine-location'), + path('content-pages/', ContentPage.as_view(), name='content-pages') ] diff --git a/apps/main/views/common.py b/apps/main/views/common.py index c565998d..d5fc413e 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -95,3 +95,11 @@ class DetermineLocation(generics.GenericAPIView): 'country_code': country_code, }) raise Http404 + + +class ContentPage(generics.ListCreateAPIView): + """Method to get content pages""" + + permission_classes = (permissions.AllowAny,) + serializer_class = serializers.ContentPageSerializer + queryset = models.ContentPage.objects.all() From 40bfd479d28ab45323a9a90c8f174dc680f962e3 Mon Sep 17 00:00:00 2001 From: dormantman Date: Wed, 22 Jan 2020 16:14:00 +0300 Subject: [PATCH 02/95] Added access levels --- apps/main/urls/common.py | 6 +++++- apps/main/views/common.py | 28 +++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/apps/main/urls/common.py b/apps/main/urls/common.py index 16b4b2af..d18f95a7 100644 --- a/apps/main/urls/common.py +++ b/apps/main/urls/common.py @@ -9,5 +9,9 @@ common_urlpatterns = [ path('awards//', AwardRetrieveView.as_view(), name='awards_retrieve'), path('carousel/', CarouselListView.as_view(), name='carousel-list'), path('determine-location/', DetermineLocation.as_view(), name='determine-location'), - path('content-pages/', ContentPage.as_view(), name='content-pages') + path('content-pages/', ContentPageView.as_view(), name='content-pages-list'), + path('content-pages/create/', ContentPageAdminView.as_view(), name='content-pages-admin-list'), + path('content-pages/slug//', ContentPageRetrieveView.as_view(), name='content-pages-retrieve'), + path('content-pages/update/slug//', ContentPageRetrieveAdminView.as_view(), + name='content-pages-admin-retrieve') ] diff --git a/apps/main/views/common.py b/apps/main/views/common.py index d5fc413e..bc03c60a 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -97,9 +97,35 @@ class DetermineLocation(generics.GenericAPIView): raise Http404 -class ContentPage(generics.ListCreateAPIView): +class ContentPageView(generics.ListAPIView): """Method to get content pages""" permission_classes = (permissions.AllowAny,) serializer_class = serializers.ContentPageSerializer queryset = models.ContentPage.objects.all() + + +class ContentPageAdminView(generics.ListCreateAPIView): + """Method to get content pages""" + + permission_classes = (permissions.IsAdminUser,) + serializer_class = serializers.ContentPageSerializer + queryset = models.ContentPage.objects.all() + + +class ContentPageRetrieveView(generics.RetrieveAPIView): + """Retrieve method to get content pages""" + + lookup_field = 'slug' + permission_classes = (permissions.AllowAny,) + serializer_class = serializers.ContentPageSerializer + queryset = models.ContentPage.objects.all() + + +class ContentPageRetrieveAdminView(generics.RetrieveUpdateDestroyAPIView): + """Retrieve method to get content pages""" + + lookup_field = 'slug' + permission_classes = (permissions.IsAdminUser,) + serializer_class = serializers.ContentPageSerializer + queryset = models.ContentPage.objects.all() From 3f44a76da97775fdcd872352fa135d982f7b16bf Mon Sep 17 00:00:00 2001 From: dormantman Date: Wed, 22 Jan 2020 16:32:51 +0300 Subject: [PATCH 03/95] Added retrieve id method --- apps/main/urls/common.py | 3 ++- apps/main/views/common.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/main/urls/common.py b/apps/main/urls/common.py index d18f95a7..9741ca60 100644 --- a/apps/main/urls/common.py +++ b/apps/main/urls/common.py @@ -10,8 +10,9 @@ common_urlpatterns = [ path('carousel/', CarouselListView.as_view(), name='carousel-list'), path('determine-location/', DetermineLocation.as_view(), name='determine-location'), path('content-pages/', ContentPageView.as_view(), name='content-pages-list'), + path('content-pages//', ContentPageIdRetrieveView.as_view(), name='content-pages-retrieve-id'), path('content-pages/create/', ContentPageAdminView.as_view(), name='content-pages-admin-list'), - path('content-pages/slug//', ContentPageRetrieveView.as_view(), name='content-pages-retrieve'), + path('content-pages/slug//', ContentPageRetrieveView.as_view(), name='content-pages-retrieve-slug'), path('content-pages/update/slug//', ContentPageRetrieveAdminView.as_view(), name='content-pages-admin-retrieve') ] diff --git a/apps/main/views/common.py b/apps/main/views/common.py index bc03c60a..62b6c1d7 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -122,6 +122,14 @@ class ContentPageRetrieveView(generics.RetrieveAPIView): queryset = models.ContentPage.objects.all() +class ContentPageIdRetrieveView(generics.RetrieveAPIView): + """Retrieve method to get content pages""" + + permission_classes = (permissions.AllowAny,) + serializer_class = serializers.ContentPageSerializer + queryset = models.ContentPage.objects.all() + + class ContentPageRetrieveAdminView(generics.RetrieveUpdateDestroyAPIView): """Retrieve method to get content pages""" From 24c352aeac7c71c3452b19e3bf5d9c78c5cb99df Mon Sep 17 00:00:00 2001 From: "a.gorbunov" Date: Wed, 22 Jan 2020 13:53:28 +0000 Subject: [PATCH 04/95] fix country_id --- apps/establishment/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 7f20c529..e4b59ebe 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -764,7 +764,7 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, """ Return Country id of establishment location """ - return self.address.country_id + return self.address.country_id if hasattr(self.address, 'country_id') else None @property def establishment_id(self): From 749775075c34a5493b71d291ae39c203b96262ac Mon Sep 17 00:00:00 2001 From: "a.gorbunov" Date: Wed, 22 Jan 2020 14:57:37 +0000 Subject: [PATCH 05/95] fix sort --- apps/location/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/location/models.py b/apps/location/models.py index 0fa3cdd1..88dc8472 100644 --- a/apps/location/models.py +++ b/apps/location/models.py @@ -81,21 +81,21 @@ class RegionQuerySet(models.QuerySet): def without_parent_region(self, switcher: bool = True): """Filter regions by parent region.""" return self.filter(parent_region__isnull=switcher)\ - .order_by('name') + .order_by('code') def by_region_id(self, region_id): """Filter regions by region id.""" return self.filter(id=region_id)\ - .order_by('name') + .order_by('code') def by_sub_region_id(self, sub_region_id): """Filter sub regions by sub region id.""" return self.filter(parent_region_id=sub_region_id)\ - .order_by('name') + .order_by('code') def sub_regions_by_region_id(self, region_id): """Filter regions by sub region id.""" - return self.filter(parent_region_id=region_id).order_by('name') + return self.filter(parent_region_id=region_id).order_by('code') class Region(models.Model): From 0552d919f18d6fe579d6f25eb0958096184f798b Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 18:43:51 +0300 Subject: [PATCH 06/95] BO news via ES #1 --- apps/news/filters.py | 2 +- apps/news/models.py | 24 +++++++++++++++++++++++- apps/search_indexes/utils.py | 2 +- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/news/filters.py b/apps/news/filters.py index a56463c5..3385c537 100644 --- a/apps/news/filters.py +++ b/apps/news/filters.py @@ -51,7 +51,7 @@ class NewsListFilterSet(filters.FilterSet): if value not in EMPTY_VALUES: if len(value) < 3: raise ValidationError({'detail': _('Type at least 3 characters to search please.')}) - return queryset.trigram_search(value) + return queryset.es_search(value) return queryset def in_tags(self, queryset, name, value): diff --git a/apps/news/models.py b/apps/news/models.py index 983cc9c2..5eef5a02 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -1,6 +1,7 @@ """News app models.""" import uuid +import elasticsearch_dsl from django.conf import settings from django.contrib.contenttypes import fields as generic from django.contrib.contenttypes.models import ContentType @@ -159,8 +160,29 @@ class NewsQuerySet(TranslationQuerysetMixin): def by_locale(self, locale): return self.filter(title__icontains=locale) + def es_search(self, search_value: str): + from search_indexes.documents import NewsDocument + from search_indexes.utils import OBJECT_FIELD_PROPERTIES + search_value = search_value.lower() + search_fields = ('description', 'title', 'subtitle') + search_keys = [] + for field in search_fields: + for locale in OBJECT_FIELD_PROPERTIES.keys(): + search_keys.append(f'{field}.{locale}') + _query = None + for key in search_keys: + if _query is None: + _query = elasticsearch_dsl.Q('fuzzy', **{key: {'value': f'*{search_value}*', 'fuzziness': 'auto:2,5', + 'prefix_length': 0, 'max_expansions': 100}}) + else: + _query |= elasticsearch_dsl.Q('fuzzy', **{key: {'value': f'*{search_value}*', 'fuzziness': 'auto:2,5', + 'prefix_length': 0, 'max_expansions': 100}}) + search = NewsDocument.search().filter(_query)[0:10000].execute() + ids = [result.meta.id for result in search] + return self.filter(id__in=ids) + def trigram_search(self, search_value: str): - """Search with mistakes by name or last name.""" + """Search with mistakes by description or title or subtitle.""" return self.annotate( description_str=Cast('description', models.TextField()), title_str=Cast('title', models.TextField()), diff --git a/apps/search_indexes/utils.py b/apps/search_indexes/utils.py index d4c4f68a..52e0adb1 100644 --- a/apps/search_indexes/utils.py +++ b/apps/search_indexes/utils.py @@ -2,7 +2,7 @@ from django_elasticsearch_dsl import fields from utils.models import get_current_locale, get_default_locale -FACET_MAX_RESPONSE = 9999999 # Unlimited +FACET_MAX_RESPONSE = 9999999 # Unlimited ALL_LOCALES_LIST = [ 'hr-HR', From 1907494f151fe37cb6ce08e43fae9d08f6ab1c60 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 18:47:08 +0300 Subject: [PATCH 07/95] BO news via ES#2 --- apps/news/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/news/models.py b/apps/news/models.py index 5eef5a02..2b0b6119 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -172,10 +172,10 @@ class NewsQuerySet(TranslationQuerysetMixin): _query = None for key in search_keys: if _query is None: - _query = elasticsearch_dsl.Q('fuzzy', **{key: {'value': f'*{search_value}*', 'fuzziness': 'auto:2,5', + _query = elasticsearch_dsl.Q('fuzzy', **{key: {'value': f'{search_value}', 'fuzziness': 'auto:2,5', 'prefix_length': 0, 'max_expansions': 100}}) else: - _query |= elasticsearch_dsl.Q('fuzzy', **{key: {'value': f'*{search_value}*', 'fuzziness': 'auto:2,5', + _query |= elasticsearch_dsl.Q('fuzzy', **{key: {'value': f'{search_value}', 'fuzziness': 'auto:2,5', 'prefix_length': 0, 'max_expansions': 100}}) search = NewsDocument.search().filter(_query)[0:10000].execute() ids = [result.meta.id for result in search] From 65a6ac0dd08da0a52adfeb85c84e4dfcdf30562e Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 19:06:09 +0300 Subject: [PATCH 08/95] BO new via ES #3 --- apps/news/models.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/apps/news/models.py b/apps/news/models.py index 2b0b6119..337ba0f3 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -165,19 +165,26 @@ class NewsQuerySet(TranslationQuerysetMixin): from search_indexes.utils import OBJECT_FIELD_PROPERTIES search_value = search_value.lower() search_fields = ('description', 'title', 'subtitle') - search_keys = [] + field_to_boost = { + 'title': 3.0, + 'subtitle': 2.0, + 'description': 1.0, + } + search_keys = {} for field in search_fields: for locale in OBJECT_FIELD_PROPERTIES.keys(): - search_keys.append(f'{field}.{locale}') + search_keys.update({f'{field}.{locale}': field_to_boost[field]}) _query = None - for key in search_keys: + for key, boost in search_keys.items(): if _query is None: - _query = elasticsearch_dsl.Q('fuzzy', **{key: {'value': f'{search_value}', 'fuzziness': 'auto:2,5', - 'prefix_length': 0, 'max_expansions': 100}}) + _query = elasticsearch_dsl.Q('match', **{key: {'query': search_value, 'fuzziness': 'auto:2,5', + 'boost': boost}}) else: - _query |= elasticsearch_dsl.Q('fuzzy', **{key: {'value': f'{search_value}', 'fuzziness': 'auto:2,5', - 'prefix_length': 0, 'max_expansions': 100}}) - search = NewsDocument.search().filter(_query)[0:10000].execute() + _query |= elasticsearch_dsl.Q('match', **{key: {'query': search_value, 'fuzziness': 'auto:2,5', + 'boost': boost, + }}) + _query |= elasticsearch_dsl.Q('wildcard', **{key: {'value': f'*{search_value}*', 'boost': boost + 30}}) + search = NewsDocument.search().query('bool', should=_query)[0:10000].execute() ids = [result.meta.id for result in search] return self.filter(id__in=ids) From aab99af87f808114ef306a04626171ecab7a488c Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 19:24:26 +0300 Subject: [PATCH 09/95] BO news via ES #4 --- apps/news/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/news/models.py b/apps/news/models.py index 337ba0f3..c9fa3c8d 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -186,7 +186,8 @@ class NewsQuerySet(TranslationQuerysetMixin): _query |= elasticsearch_dsl.Q('wildcard', **{key: {'value': f'*{search_value}*', 'boost': boost + 30}}) search = NewsDocument.search().query('bool', should=_query)[0:10000].execute() ids = [result.meta.id for result in search] - return self.filter(id__in=ids) + preserved = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(ids)]) + return self.filter(id__in=ids).order_by(preserved) def trigram_search(self, search_value: str): """Search with mistakes by description or title or subtitle.""" From affb985b157e97e6ca7d15d58c494c30c2266c2d Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 19:41:44 +0300 Subject: [PATCH 10/95] BO news via ES #5 (optimize) --- apps/news/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/news/models.py b/apps/news/models.py index c9fa3c8d..fed4da82 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -186,7 +186,8 @@ class NewsQuerySet(TranslationQuerysetMixin): _query |= elasticsearch_dsl.Q('wildcard', **{key: {'value': f'*{search_value}*', 'boost': boost + 30}}) search = NewsDocument.search().query('bool', should=_query)[0:10000].execute() ids = [result.meta.id for result in search] - preserved = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(ids)]) + ids_order = enumerate(ids) + preserved = Case(*[When(pk=pk, then=pos) for pos, pk in ids_order]) return self.filter(id__in=ids).order_by(preserved) def trigram_search(self, search_value: str): From b7fa52a2ab85b3e71e9c1b2ddac635a8551c8a8b Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 20:10:06 +0300 Subject: [PATCH 11/95] increase news shard number --- apps/search_indexes/documents/news.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/search_indexes/documents/news.py b/apps/search_indexes/documents/news.py index 6e287b0a..b24daeb1 100644 --- a/apps/search_indexes/documents/news.py +++ b/apps/search_indexes/documents/news.py @@ -7,7 +7,7 @@ from json import dumps NewsIndex = Index(settings.ELASTICSEARCH_INDEX_NAMES.get(__name__, 'news')) -NewsIndex.settings(number_of_shards=1, number_of_replicas=1) +NewsIndex.settings(number_of_shards=10, number_of_replicas=1) @NewsIndex.doc_type From dd588c039f6b86c43a117ac37b937a9207537e82 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 20:23:46 +0300 Subject: [PATCH 12/95] fix news ES indexing --- apps/search_indexes/documents/news.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/search_indexes/documents/news.py b/apps/search_indexes/documents/news.py index b24daeb1..4f7998d3 100644 --- a/apps/search_indexes/documents/news.py +++ b/apps/search_indexes/documents/news.py @@ -71,5 +71,5 @@ class NewsDocument(Document): The related_models option should be used with caution because it can lead in the index to the updating of a lot of items. """ - if isinstance(related_instance, models.NewsType) and hasattr(related_instance, 'news_set'): - return related_instance.news_set.all() + if isinstance(related_instance, models.NewsType) and hasattr(related_instance, 'news'): + return related_instance.news.all() From 407f9026e046a84ef6d02d4b1da310a0489e43a7 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 20:50:55 +0300 Subject: [PATCH 13/95] crunch for news type --- apps/search_indexes/signals.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/search_indexes/signals.py b/apps/search_indexes/signals.py index 5d9809b9..fd48233b 100644 --- a/apps/search_indexes/signals.py +++ b/apps/search_indexes/signals.py @@ -41,6 +41,9 @@ def update_news(sender, **kwargs): } filter_name = app_label_model_name_to_filter.get((app_label, model_name)) if filter_name: + if filter_name == 'news_type': + registry.update_related(instance) + return qs = News.objects.filter(**{filter_name: instance}) for product in qs: registry.update(product) From e94dd038750ea5bea4226b1fa6dcfd869f5d2d29 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 20:52:15 +0300 Subject: [PATCH 14/95] disable news_type update post save --- apps/search_indexes/signals.py | 5 +--- project/settings/amazon_s3.py | 6 ++--- project/settings/local.py | 42 +++++++++++++++++----------------- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/apps/search_indexes/signals.py b/apps/search_indexes/signals.py index fd48233b..7b78dedd 100644 --- a/apps/search_indexes/signals.py +++ b/apps/search_indexes/signals.py @@ -36,14 +36,11 @@ def update_news(sender, **kwargs): instance = kwargs['instance'] app_label_model_name_to_filter = { ('location', 'country'): 'country', - ('news', 'newstype'): 'news_type', + # ('news', 'newstype'): 'news_type', ('tag', 'tag'): 'tags', } filter_name = app_label_model_name_to_filter.get((app_label, model_name)) if filter_name: - if filter_name == 'news_type': - registry.update_related(instance) - return qs = News.objects.filter(**{filter_name: instance}) for product in qs: registry.update(product) diff --git a/project/settings/amazon_s3.py b/project/settings/amazon_s3.py index b602618d..2b873929 100644 --- a/project/settings/amazon_s3.py +++ b/project/settings/amazon_s3.py @@ -5,9 +5,9 @@ from .base import MEDIA_LOCATION # AMAZON S3 AWS_S3_REGION_NAME = 'eu-central-1' -AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID') -AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY') -AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME') +AWS_ACCESS_KEY_ID = 'AKIA4T5M5TJWDYT3SB6Y' +AWS_SECRET_ACCESS_KEY = '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' +AWS_STORAGE_BUCKET_NAME = 'gm-test.com' AWS_S3_CUSTOM_DOMAIN = f's3.{AWS_S3_REGION_NAME}.amazonaws.com/{AWS_STORAGE_BUCKET_NAME}' AWS_S3_OBJECT_PARAMETERS = {'CacheControl': 'max-age=86400'} AWS_S3_ADDRESSING_STYLE = 'path' diff --git a/project/settings/local.py b/project/settings/local.py index 2ad132f1..a01c566b 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -1,7 +1,7 @@ """Local settings.""" from .base import * import sys -# from .amazon_s3 import * +from .amazon_s3 import * ALLOWED_HOSTS = ['*', ] @@ -28,7 +28,7 @@ MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION) # SORL thumbnails THUMBNAIL_DEBUG = True - +MIDDLEWARE.append('utils.middleware.log_db_queries_per_API_request') # DATABASES DATABASES = { @@ -43,15 +43,15 @@ DATABASES = { 'options': '-c search_path=gm,public' }, }, - 'legacy': { - 'ENGINE': 'django.db.backends.mysql', - # 'HOST': '172.22.0.1', - 'HOST': 'mysql_db', - 'PORT': 3306, - 'NAME': 'dev', - 'USER': 'dev', - 'PASSWORD': 'octosecret123' - }, + # 'legacy': { + # 'ENGINE': 'django.db.backends.mysql', + # # 'HOST': '172.22.0.1', + # 'HOST': 'mysql_db', + # 'PORT': 3306, + # 'NAME': 'dev', + # 'USER': 'dev', + # 'PASSWORD': 'octosecret123' + # }, } @@ -84,26 +84,26 @@ LOGGING = { 'py.warnings': { 'handlers': ['console'], }, - # 'django.db.backends': { - # 'handlers': ['console', ], - # 'level': 'DEBUG', - # 'propagate': False, - # }, + 'django.db.backends': { + 'handlers': ['console', ], + 'level': 'DEBUG', + 'propagate': False, + }, } } # ELASTICSEARCH SETTINGS ELASTICSEARCH_DSL = { 'default': { - 'hosts': 'elasticsearch:9200' + 'hosts': '188.68.209.124:9200' } } ELASTICSEARCH_INDEX_NAMES = { - # 'search_indexes.documents.news': 'local_news', - 'search_indexes.documents.establishment': 'local_establishment', - 'search_indexes.documents.product': 'local_product', - 'search_indexes.documents.tag_category': 'local_tag_category', + 'search_indexes.documents.news': 'development_news', + 'search_indexes.documents.establishment': 'development_establishment', + 'search_indexes.documents.product': 'development_product', + 'search_indexes.documents.tag_category': 'development_tag_category', } ELASTICSEARCH_DSL_AUTOSYNC = False From bdf5c77f0332a92eb5992d750c1b606f4ad703bd Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 20:54:52 +0300 Subject: [PATCH 15/95] post_save async --- apps/search_indexes/signals.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/search_indexes/signals.py b/apps/search_indexes/signals.py index 7b78dedd..0a6fb4f6 100644 --- a/apps/search_indexes/signals.py +++ b/apps/search_indexes/signals.py @@ -1,7 +1,6 @@ """Search indexes app signals.""" from django.db.models.signals import post_save from django.dispatch import receiver -from django_elasticsearch_dsl.registries import registry @receiver(post_save) @@ -31,24 +30,26 @@ def update_document(sender, **kwargs): @receiver(post_save) def update_news(sender, **kwargs): from news.models import News + from search_indexes.tasks import es_update app_label = sender._meta.app_label model_name = sender._meta.model_name instance = kwargs['instance'] app_label_model_name_to_filter = { ('location', 'country'): 'country', - # ('news', 'newstype'): 'news_type', + ('news', 'newstype'): 'news_type', ('tag', 'tag'): 'tags', } filter_name = app_label_model_name_to_filter.get((app_label, model_name)) if filter_name: qs = News.objects.filter(**{filter_name: instance}) - for product in qs: - registry.update(product) + for item in qs: + es_update(item) @receiver(post_save) def update_product(sender, **kwargs): from product.models import Product + from search_indexes.tasks import es_update app_label = sender._meta.app_label model_name = sender._meta.model_name instance = kwargs['instance'] @@ -65,4 +66,4 @@ def update_product(sender, **kwargs): if filter_name: qs = Product.objects.filter(**{filter_name: instance}) for product in qs: - registry.update(product) + es_update(product) From a9acbf395546e107c9d3475092afd81912e9d7e2 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 21:07:01 +0300 Subject: [PATCH 16/95] fix configs --- project/settings/amazon_s3.py | 6 ++--- project/settings/local.py | 42 +++++++++++++++++------------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/project/settings/amazon_s3.py b/project/settings/amazon_s3.py index 2b873929..b602618d 100644 --- a/project/settings/amazon_s3.py +++ b/project/settings/amazon_s3.py @@ -5,9 +5,9 @@ from .base import MEDIA_LOCATION # AMAZON S3 AWS_S3_REGION_NAME = 'eu-central-1' -AWS_ACCESS_KEY_ID = 'AKIA4T5M5TJWDYT3SB6Y' -AWS_SECRET_ACCESS_KEY = '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' -AWS_STORAGE_BUCKET_NAME = 'gm-test.com' +AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID') +AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY') +AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME') AWS_S3_CUSTOM_DOMAIN = f's3.{AWS_S3_REGION_NAME}.amazonaws.com/{AWS_STORAGE_BUCKET_NAME}' AWS_S3_OBJECT_PARAMETERS = {'CacheControl': 'max-age=86400'} AWS_S3_ADDRESSING_STYLE = 'path' diff --git a/project/settings/local.py b/project/settings/local.py index a01c566b..2ad132f1 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -1,7 +1,7 @@ """Local settings.""" from .base import * import sys -from .amazon_s3 import * +# from .amazon_s3 import * ALLOWED_HOSTS = ['*', ] @@ -28,7 +28,7 @@ MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION) # SORL thumbnails THUMBNAIL_DEBUG = True -MIDDLEWARE.append('utils.middleware.log_db_queries_per_API_request') + # DATABASES DATABASES = { @@ -43,15 +43,15 @@ DATABASES = { 'options': '-c search_path=gm,public' }, }, - # 'legacy': { - # 'ENGINE': 'django.db.backends.mysql', - # # 'HOST': '172.22.0.1', - # 'HOST': 'mysql_db', - # 'PORT': 3306, - # 'NAME': 'dev', - # 'USER': 'dev', - # 'PASSWORD': 'octosecret123' - # }, + 'legacy': { + 'ENGINE': 'django.db.backends.mysql', + # 'HOST': '172.22.0.1', + 'HOST': 'mysql_db', + 'PORT': 3306, + 'NAME': 'dev', + 'USER': 'dev', + 'PASSWORD': 'octosecret123' + }, } @@ -84,26 +84,26 @@ LOGGING = { 'py.warnings': { 'handlers': ['console'], }, - 'django.db.backends': { - 'handlers': ['console', ], - 'level': 'DEBUG', - 'propagate': False, - }, + # 'django.db.backends': { + # 'handlers': ['console', ], + # 'level': 'DEBUG', + # 'propagate': False, + # }, } } # ELASTICSEARCH SETTINGS ELASTICSEARCH_DSL = { 'default': { - 'hosts': '188.68.209.124:9200' + 'hosts': 'elasticsearch:9200' } } ELASTICSEARCH_INDEX_NAMES = { - 'search_indexes.documents.news': 'development_news', - 'search_indexes.documents.establishment': 'development_establishment', - 'search_indexes.documents.product': 'development_product', - 'search_indexes.documents.tag_category': 'development_tag_category', + # 'search_indexes.documents.news': 'local_news', + 'search_indexes.documents.establishment': 'local_establishment', + 'search_indexes.documents.product': 'local_product', + 'search_indexes.documents.tag_category': 'local_tag_category', } ELASTICSEARCH_DSL_AUTOSYNC = False From 6ba8fb31cf2c7dacbd3a799cd52c4329e29eb936 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 21:09:25 +0300 Subject: [PATCH 17/95] disable ES sync after news_type updating --- apps/search_indexes/signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/search_indexes/signals.py b/apps/search_indexes/signals.py index 0a6fb4f6..f03fde57 100644 --- a/apps/search_indexes/signals.py +++ b/apps/search_indexes/signals.py @@ -36,7 +36,7 @@ def update_news(sender, **kwargs): instance = kwargs['instance'] app_label_model_name_to_filter = { ('location', 'country'): 'country', - ('news', 'newstype'): 'news_type', + # ('news', 'newstype'): 'news_type', ('tag', 'tag'): 'tags', } filter_name = app_label_model_name_to_filter.get((app_label, model_name)) From 8aefe9b0e90cdc453574a31098cf7c93ae514303 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 21:12:30 +0300 Subject: [PATCH 18/95] temp disable tags updating --- apps/search_indexes/signals.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/search_indexes/signals.py b/apps/search_indexes/signals.py index f03fde57..b2a8cbde 100644 --- a/apps/search_indexes/signals.py +++ b/apps/search_indexes/signals.py @@ -18,7 +18,7 @@ def update_document(sender, **kwargs): # todo: remove after migration ('establishment', 'establishmenttype'): 'establishment_type', ('establishment', 'establishmentsubtype'): 'establishment_subtypes', - ('tag', 'tag'): 'tags', + # ('tag', 'tag'): 'tags', } filter_name = app_label_model_name_to_filter.get((app_label, model_name)) if filter_name: @@ -37,7 +37,7 @@ def update_news(sender, **kwargs): app_label_model_name_to_filter = { ('location', 'country'): 'country', # ('news', 'newstype'): 'news_type', - ('tag', 'tag'): 'tags', + # ('tag', 'tag'): 'tags', } filter_name = app_label_model_name_to_filter.get((app_label, model_name)) if filter_name: @@ -56,7 +56,7 @@ def update_product(sender, **kwargs): app_label_model_name_to_filter = { ('product', 'productstandard'): 'standards', ('product', 'producttype'): 'product_type', - ('tag', 'tag'): 'tags', + # ('tag', 'tag'): 'tags', # ('location', 'wineregion'): 'wine_region', # ('location', 'winesubregion'): 'wine_sub_region', # ('location', 'winevillage'): 'wine_village', From cc9c641df3e48d503bf24e6b6c6f12994fb51e60 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 21:25:09 +0300 Subject: [PATCH 19/95] disable ES --- apps/search_indexes/signals.py | 138 ++++++++++++++++----------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/apps/search_indexes/signals.py b/apps/search_indexes/signals.py index b2a8cbde..ef23d4e1 100644 --- a/apps/search_indexes/signals.py +++ b/apps/search_indexes/signals.py @@ -1,69 +1,69 @@ -"""Search indexes app signals.""" -from django.db.models.signals import post_save -from django.dispatch import receiver - - -@receiver(post_save) -def update_document(sender, **kwargs): - from establishment.models import Establishment - from search_indexes.tasks import es_update - app_label = sender._meta.app_label - model_name = sender._meta.model_name - instance = kwargs['instance'] - - app_label_model_name_to_filter = { - ('location', 'country'): 'address__city__country', - ('location', 'city'): 'address__city', - ('location', 'address'): 'address', - # todo: remove after migration - ('establishment', 'establishmenttype'): 'establishment_type', - ('establishment', 'establishmentsubtype'): 'establishment_subtypes', - # ('tag', 'tag'): 'tags', - } - filter_name = app_label_model_name_to_filter.get((app_label, model_name)) - if filter_name: - qs = Establishment.objects.filter(**{filter_name: instance}) - for item in qs: - es_update(item) - - -@receiver(post_save) -def update_news(sender, **kwargs): - from news.models import News - from search_indexes.tasks import es_update - app_label = sender._meta.app_label - model_name = sender._meta.model_name - instance = kwargs['instance'] - app_label_model_name_to_filter = { - ('location', 'country'): 'country', - # ('news', 'newstype'): 'news_type', - # ('tag', 'tag'): 'tags', - } - filter_name = app_label_model_name_to_filter.get((app_label, model_name)) - if filter_name: - qs = News.objects.filter(**{filter_name: instance}) - for item in qs: - es_update(item) - - -@receiver(post_save) -def update_product(sender, **kwargs): - from product.models import Product - from search_indexes.tasks import es_update - app_label = sender._meta.app_label - model_name = sender._meta.model_name - instance = kwargs['instance'] - app_label_model_name_to_filter = { - ('product', 'productstandard'): 'standards', - ('product', 'producttype'): 'product_type', - # ('tag', 'tag'): 'tags', - # ('location', 'wineregion'): 'wine_region', - # ('location', 'winesubregion'): 'wine_sub_region', - # ('location', 'winevillage'): 'wine_village', - ('establishment', 'establishment'): 'establishment', - } - filter_name = app_label_model_name_to_filter.get((app_label, model_name)) - if filter_name: - qs = Product.objects.filter(**{filter_name: instance}) - for product in qs: - es_update(product) +# """Search indexes app signals.""" +# from django.db.models.signals import post_save +# from django.dispatch import receiver +# +# +# @receiver(post_save) +# def update_document(sender, **kwargs): +# from establishment.models import Establishment +# from search_indexes.tasks import es_update +# app_label = sender._meta.app_label +# model_name = sender._meta.model_name +# instance = kwargs['instance'] +# +# app_label_model_name_to_filter = { +# ('location', 'country'): 'address__city__country', +# ('location', 'city'): 'address__city', +# ('location', 'address'): 'address', +# # todo: remove after migration +# ('establishment', 'establishmenttype'): 'establishment_type', +# ('establishment', 'establishmentsubtype'): 'establishment_subtypes', +# # ('tag', 'tag'): 'tags', +# } +# filter_name = app_label_model_name_to_filter.get((app_label, model_name)) +# if filter_name: +# qs = Establishment.objects.filter(**{filter_name: instance}) +# for item in qs: +# es_update(item) +# +# +# @receiver(post_save) +# def update_news(sender, **kwargs): +# from news.models import News +# from search_indexes.tasks import es_update +# app_label = sender._meta.app_label +# model_name = sender._meta.model_name +# instance = kwargs['instance'] +# app_label_model_name_to_filter = { +# ('location', 'country'): 'country', +# # ('news', 'newstype'): 'news_type', +# # ('tag', 'tag'): 'tags', +# } +# filter_name = app_label_model_name_to_filter.get((app_label, model_name)) +# if filter_name: +# qs = News.objects.filter(**{filter_name: instance}) +# for item in qs: +# es_update(item) +# +# +# @receiver(post_save) +# def update_product(sender, **kwargs): +# from product.models import Product +# from search_indexes.tasks import es_update +# app_label = sender._meta.app_label +# model_name = sender._meta.model_name +# instance = kwargs['instance'] +# app_label_model_name_to_filter = { +# ('product', 'productstandard'): 'standards', +# ('product', 'producttype'): 'product_type', +# # ('tag', 'tag'): 'tags', +# # ('location', 'wineregion'): 'wine_region', +# # ('location', 'winesubregion'): 'wine_sub_region', +# # ('location', 'winevillage'): 'wine_village', +# ('establishment', 'establishment'): 'establishment', +# } +# filter_name = app_label_model_name_to_filter.get((app_label, model_name)) +# if filter_name: +# qs = Product.objects.filter(**{filter_name: instance}) +# for product in qs: +# es_update(product) From 503ce303509c5ac602e90f819f5fdf5eee097afe Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 21:26:13 +0300 Subject: [PATCH 20/95] disable es --- apps/search_indexes/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/search_indexes/__init__.py b/apps/search_indexes/__init__.py index 6c812219..c45cbc38 100644 --- a/apps/search_indexes/__init__.py +++ b/apps/search_indexes/__init__.py @@ -1 +1 @@ -from search_indexes.signals import update_document +# from search_indexes.signals import update_document From 4a1676abcd23286abfdb40d755ef6478bdf3f545 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 21:29:28 +0300 Subject: [PATCH 21/95] Revert "disable es" This reverts commit 503ce30 --- apps/search_indexes/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/search_indexes/__init__.py b/apps/search_indexes/__init__.py index c45cbc38..6c812219 100644 --- a/apps/search_indexes/__init__.py +++ b/apps/search_indexes/__init__.py @@ -1 +1 @@ -# from search_indexes.signals import update_document +from search_indexes.signals import update_document From 8cc5355200b30a28f13759a243844f21912e6b45 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 21:29:32 +0300 Subject: [PATCH 22/95] Revert "disable ES" This reverts commit cc9c641 --- apps/search_indexes/signals.py | 138 ++++++++++++++++----------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/apps/search_indexes/signals.py b/apps/search_indexes/signals.py index ef23d4e1..b2a8cbde 100644 --- a/apps/search_indexes/signals.py +++ b/apps/search_indexes/signals.py @@ -1,69 +1,69 @@ -# """Search indexes app signals.""" -# from django.db.models.signals import post_save -# from django.dispatch import receiver -# -# -# @receiver(post_save) -# def update_document(sender, **kwargs): -# from establishment.models import Establishment -# from search_indexes.tasks import es_update -# app_label = sender._meta.app_label -# model_name = sender._meta.model_name -# instance = kwargs['instance'] -# -# app_label_model_name_to_filter = { -# ('location', 'country'): 'address__city__country', -# ('location', 'city'): 'address__city', -# ('location', 'address'): 'address', -# # todo: remove after migration -# ('establishment', 'establishmenttype'): 'establishment_type', -# ('establishment', 'establishmentsubtype'): 'establishment_subtypes', -# # ('tag', 'tag'): 'tags', -# } -# filter_name = app_label_model_name_to_filter.get((app_label, model_name)) -# if filter_name: -# qs = Establishment.objects.filter(**{filter_name: instance}) -# for item in qs: -# es_update(item) -# -# -# @receiver(post_save) -# def update_news(sender, **kwargs): -# from news.models import News -# from search_indexes.tasks import es_update -# app_label = sender._meta.app_label -# model_name = sender._meta.model_name -# instance = kwargs['instance'] -# app_label_model_name_to_filter = { -# ('location', 'country'): 'country', -# # ('news', 'newstype'): 'news_type', -# # ('tag', 'tag'): 'tags', -# } -# filter_name = app_label_model_name_to_filter.get((app_label, model_name)) -# if filter_name: -# qs = News.objects.filter(**{filter_name: instance}) -# for item in qs: -# es_update(item) -# -# -# @receiver(post_save) -# def update_product(sender, **kwargs): -# from product.models import Product -# from search_indexes.tasks import es_update -# app_label = sender._meta.app_label -# model_name = sender._meta.model_name -# instance = kwargs['instance'] -# app_label_model_name_to_filter = { -# ('product', 'productstandard'): 'standards', -# ('product', 'producttype'): 'product_type', -# # ('tag', 'tag'): 'tags', -# # ('location', 'wineregion'): 'wine_region', -# # ('location', 'winesubregion'): 'wine_sub_region', -# # ('location', 'winevillage'): 'wine_village', -# ('establishment', 'establishment'): 'establishment', -# } -# filter_name = app_label_model_name_to_filter.get((app_label, model_name)) -# if filter_name: -# qs = Product.objects.filter(**{filter_name: instance}) -# for product in qs: -# es_update(product) +"""Search indexes app signals.""" +from django.db.models.signals import post_save +from django.dispatch import receiver + + +@receiver(post_save) +def update_document(sender, **kwargs): + from establishment.models import Establishment + from search_indexes.tasks import es_update + app_label = sender._meta.app_label + model_name = sender._meta.model_name + instance = kwargs['instance'] + + app_label_model_name_to_filter = { + ('location', 'country'): 'address__city__country', + ('location', 'city'): 'address__city', + ('location', 'address'): 'address', + # todo: remove after migration + ('establishment', 'establishmenttype'): 'establishment_type', + ('establishment', 'establishmentsubtype'): 'establishment_subtypes', + # ('tag', 'tag'): 'tags', + } + filter_name = app_label_model_name_to_filter.get((app_label, model_name)) + if filter_name: + qs = Establishment.objects.filter(**{filter_name: instance}) + for item in qs: + es_update(item) + + +@receiver(post_save) +def update_news(sender, **kwargs): + from news.models import News + from search_indexes.tasks import es_update + app_label = sender._meta.app_label + model_name = sender._meta.model_name + instance = kwargs['instance'] + app_label_model_name_to_filter = { + ('location', 'country'): 'country', + # ('news', 'newstype'): 'news_type', + # ('tag', 'tag'): 'tags', + } + filter_name = app_label_model_name_to_filter.get((app_label, model_name)) + if filter_name: + qs = News.objects.filter(**{filter_name: instance}) + for item in qs: + es_update(item) + + +@receiver(post_save) +def update_product(sender, **kwargs): + from product.models import Product + from search_indexes.tasks import es_update + app_label = sender._meta.app_label + model_name = sender._meta.model_name + instance = kwargs['instance'] + app_label_model_name_to_filter = { + ('product', 'productstandard'): 'standards', + ('product', 'producttype'): 'product_type', + # ('tag', 'tag'): 'tags', + # ('location', 'wineregion'): 'wine_region', + # ('location', 'winesubregion'): 'wine_sub_region', + # ('location', 'winevillage'): 'wine_village', + ('establishment', 'establishment'): 'establishment', + } + filter_name = app_label_model_name_to_filter.get((app_label, model_name)) + if filter_name: + qs = Product.objects.filter(**{filter_name: instance}) + for product in qs: + es_update(product) From 1cd0458acc934b493df915c5a869b9cc6b356c93 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 21:29:34 +0300 Subject: [PATCH 23/95] Revert "temp disable tags updating" This reverts commit 8aefe9b --- apps/search_indexes/signals.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/search_indexes/signals.py b/apps/search_indexes/signals.py index b2a8cbde..f03fde57 100644 --- a/apps/search_indexes/signals.py +++ b/apps/search_indexes/signals.py @@ -18,7 +18,7 @@ def update_document(sender, **kwargs): # todo: remove after migration ('establishment', 'establishmenttype'): 'establishment_type', ('establishment', 'establishmentsubtype'): 'establishment_subtypes', - # ('tag', 'tag'): 'tags', + ('tag', 'tag'): 'tags', } filter_name = app_label_model_name_to_filter.get((app_label, model_name)) if filter_name: @@ -37,7 +37,7 @@ def update_news(sender, **kwargs): app_label_model_name_to_filter = { ('location', 'country'): 'country', # ('news', 'newstype'): 'news_type', - # ('tag', 'tag'): 'tags', + ('tag', 'tag'): 'tags', } filter_name = app_label_model_name_to_filter.get((app_label, model_name)) if filter_name: @@ -56,7 +56,7 @@ def update_product(sender, **kwargs): app_label_model_name_to_filter = { ('product', 'productstandard'): 'standards', ('product', 'producttype'): 'product_type', - # ('tag', 'tag'): 'tags', + ('tag', 'tag'): 'tags', # ('location', 'wineregion'): 'wine_region', # ('location', 'winesubregion'): 'wine_sub_region', # ('location', 'winevillage'): 'wine_village', From 1c01604f434680295c7d58fc4f0183226e7655ef Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 21:29:37 +0300 Subject: [PATCH 24/95] Revert "disable ES sync after news_type updating" This reverts commit 6ba8fb3 --- apps/search_indexes/signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/search_indexes/signals.py b/apps/search_indexes/signals.py index f03fde57..0a6fb4f6 100644 --- a/apps/search_indexes/signals.py +++ b/apps/search_indexes/signals.py @@ -36,7 +36,7 @@ def update_news(sender, **kwargs): instance = kwargs['instance'] app_label_model_name_to_filter = { ('location', 'country'): 'country', - # ('news', 'newstype'): 'news_type', + ('news', 'newstype'): 'news_type', ('tag', 'tag'): 'tags', } filter_name = app_label_model_name_to_filter.get((app_label, model_name)) From d81d2445f5fefa37dfffc66a45331f15ff153387 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Wed, 22 Jan 2020 21:47:21 +0300 Subject: [PATCH 25/95] tune news search for BO --- apps/news/filters.py | 2 +- apps/news/models.py | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/news/filters.py b/apps/news/filters.py index 3385c537..064f7175 100644 --- a/apps/news/filters.py +++ b/apps/news/filters.py @@ -51,7 +51,7 @@ class NewsListFilterSet(filters.FilterSet): if value not in EMPTY_VALUES: if len(value) < 3: raise ValidationError({'detail': _('Type at least 3 characters to search please.')}) - return queryset.es_search(value) + return queryset.es_search(value, relevance_order='ordering' not in self.request.query_params) return queryset def in_tags(self, queryset, name, value): diff --git a/apps/news/models.py b/apps/news/models.py index fed4da82..89dacf5a 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -160,7 +160,7 @@ class NewsQuerySet(TranslationQuerysetMixin): def by_locale(self, locale): return self.filter(title__icontains=locale) - def es_search(self, search_value: str): + def es_search(self, search_value: str, relevance_order=True): from search_indexes.documents import NewsDocument from search_indexes.utils import OBJECT_FIELD_PROPERTIES search_value = search_value.lower() @@ -186,9 +186,12 @@ class NewsQuerySet(TranslationQuerysetMixin): _query |= elasticsearch_dsl.Q('wildcard', **{key: {'value': f'*{search_value}*', 'boost': boost + 30}}) search = NewsDocument.search().query('bool', should=_query)[0:10000].execute() ids = [result.meta.id for result in search] - ids_order = enumerate(ids) - preserved = Case(*[When(pk=pk, then=pos) for pos, pk in ids_order]) - return self.filter(id__in=ids).order_by(preserved) + qs = self.filter(id__in=ids) + if relevance_order: + ids_order = enumerate(ids) + preserved = Case(*[When(pk=pk, then=pos) for pos, pk in ids_order]) + qs = qs.order_by(preserved) + return qs def trigram_search(self, search_value: str): """Search with mistakes by description or title or subtitle.""" From 0216cadd852c8545e9c4e4dbb6fcf1393061f0d0 Mon Sep 17 00:00:00 2001 From: evgeniy-st Date: Thu, 23 Jan 2020 10:02:19 +0300 Subject: [PATCH 26/95] remove sentry --- project/settings/development.py | 7 ------- requirements/base.txt | 2 -- 2 files changed, 9 deletions(-) diff --git a/project/settings/development.py b/project/settings/development.py index 3048541b..d5a31178 100644 --- a/project/settings/development.py +++ b/project/settings/development.py @@ -1,8 +1,6 @@ """Development settings.""" from .base import * from .amazon_s3 import * -import sentry_sdk -from sentry_sdk.integrations.django import DjangoIntegration ALLOWED_HOSTS = ['gm.id-east.ru', '95.213.204.126', '0.0.0.0'] @@ -47,11 +45,6 @@ ELASTICSEARCH_INDEX_NAMES = { # ELASTICSEARCH_DSL_AUTOSYNC = False -sentry_sdk.init( - dsn="https://35d9bb789677410ab84a822831c6314f@sentry.io/1729093", - integrations=[DjangoIntegration()] -) - # DATABASE DATABASES.update({ diff --git a/requirements/base.txt b/requirements/base.txt index 25a0256c..395d7959 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -41,8 +41,6 @@ django-elasticsearch-dsl-drf==0.20.2 elasticsearch==7.1.0 elasticsearch-dsl==7.1.0 -sentry-sdk==0.11.2 - # AMAZON S3 boto3==1.9.238 django-storages==1.7.2 From 5560a0149d1b8653a245cf5abc1b490b88e73bdd Mon Sep 17 00:00:00 2001 From: Ruslan Stepanov Date: Thu, 23 Jan 2020 07:40:56 +0000 Subject: [PATCH 27/95] Revert (nickname field) common.py --- apps/comment/serializers/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/comment/serializers/common.py b/apps/comment/serializers/common.py index 6f0d049d..c928aca4 100644 --- a/apps/comment/serializers/common.py +++ b/apps/comment/serializers/common.py @@ -8,7 +8,7 @@ from establishment.models import EstablishmentType class CommentBaseSerializer(serializers.ModelSerializer): """Comment serializer""" - user_name = serializers.CharField(read_only=True, + nickname = serializers.CharField(read_only=True, source='user.username') is_mine = serializers.BooleanField(read_only=True) profile_pic = serializers.URLField(read_only=True, @@ -32,7 +32,7 @@ class CommentBaseSerializer(serializers.ModelSerializer): 'created', 'text', 'mark', - 'user_name', + 'nickname', 'user_email', 'profile_pic', 'status', From a6f3384126f79d65c02501e3e6b0822a28cc2561 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 23 Jan 2020 11:26:00 +0300 Subject: [PATCH 28/95] change employees search precision const --- apps/establishment/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index e4b59ebe..f2c7e2ee 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -1020,7 +1020,7 @@ class EmployeeQuerySet(models.QuerySet): ), relevance=(F('search_name_similarity') + F('search_exact_match') + F('search_contains_match') + F('search_last_name_similarity')) - ).filter(relevance__gte=0.3).order_by('-relevance') + ).filter(relevance__gte=0.1).order_by('-relevance') def search_by_name_or_last_name(self, value): """Search by name or last_name.""" From 587f6c0bad0e5c5dec901c096b7e427a26e359af Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 23 Jan 2020 13:28:57 +0300 Subject: [PATCH 29/95] fix role counter, added additional field role choice id --- apps/account/models.py | 63 ++++++++++++++++++++++++-------- apps/account/serializers/back.py | 6 --- apps/account/urls/back.py | 3 +- apps/account/views/back.py | 38 +++++-------------- 4 files changed, 59 insertions(+), 51 deletions(-) diff --git a/apps/account/models.py b/apps/account/models.py index acb56089..0175ccf0 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -12,6 +12,7 @@ from django.utils.html import mark_safe from django.utils.http import urlsafe_base64_encode from django.utils.translation import ugettext_lazy as _ from rest_framework.authtoken.models import Token +from collections import Counter from authorization.models import Application from establishment.models import Establishment, EstablishmentSubType @@ -84,26 +85,13 @@ class Role(ProjectBaseMixin): objects = RoleQuerySet.as_manager() - @classmethod - def role_names(cls): - return [role_name for role_name in dict(cls.ROLE_CHOICES).values()] - @classmethod def role_types(cls): roles = [] - for role_id, role in dict(cls.ROLE_CHOICES).items(): - roles.append({'id': role_id, 'name': role}) + for role, display_name in dict(cls.ROLE_CHOICES).items(): + roles.append({'role_name': role, 'role_counter': display_name, 'role': 0}) return roles - @classmethod - def role_condition_expressions(cls) -> list: - role_choices = {role_id: role_name._proxy____args[0] - for role_id, role_name in dict(cls.ROLE_CHOICES).items()} - - whens = [models.When(role=role_id, then=models.Value(role_name)) - for role_id, role_name in role_choices.items()] - return whens - class UserManager(BaseUserManager): """Extended manager for User model.""" @@ -124,6 +112,14 @@ class UserManager(BaseUserManager): class UserQuerySet(models.QuerySet): """Extended queryset for User model.""" + def with_base_related(self): + """Return QuerySet with base related.""" + return self.select_related('last_country', 'last_country__country') + + def with_extend_related(self): + """Return QuerySet with extend related.""" + return self.with_base_related().prefetch_related('roles', 'subscriber') + def active(self, switcher=True): """Filter only active users.""" return self.filter(is_active=switcher) @@ -403,10 +399,47 @@ class User(AbstractUser): class UserRoleQueryset(models.QuerySet): """QuerySet for model UserRole.""" + def _role_counter(self, country_code: str = None) -> dict: + additional_filters = { + 'state': self.model.VALIDATED, + } + + if country_code: + additional_filters.update({'role__site__country__code': country_code}) + + user_roles = ( + self.filter(**additional_filters) + .distinct('user_id', 'role__role') + .values('user_id', 'role__role') + ) + return dict(Counter([i['role__role'] for i in user_roles])) + def country_admin_role(self): return self.filter(role__role=self.model.role.field.target_field.model.COUNTRY_ADMIN, state=self.model.VALIDATED) + def aggregate_role_counter(self, country_code: str = None) -> list: + _role_choices = dict(Role.ROLE_CHOICES) + role_counter = [] + + # fill existed roles + for role, count in self._role_counter(country_code=country_code).items(): + role_counter.append({ + 'role': role, + 'role_name': _role_choices[role], + 'count': count, + }) + + # check by roles + for role, role_name in _role_choices.items(): + if role not in [i['role'] for i in role_counter]: + role_counter.append({ + 'role': role, + 'role_name': _role_choices[role], + 'count': 0, + }) + return role_counter + class UserRole(ProjectBaseMixin): """UserRole model.""" diff --git a/apps/account/serializers/back.py b/apps/account/serializers/back.py index b38b9042..2baaf656 100644 --- a/apps/account/serializers/back.py +++ b/apps/account/serializers/back.py @@ -143,9 +143,3 @@ class UserRoleSerializer(serializers.ModelSerializer): 'user', 'establishment' ] - - -class RoleTabRetrieveSerializer(serializers.Serializer): - """Serializer for BackOffice role tab.""" - role_name = serializers.CharField() - role_counter = serializers.IntegerField() diff --git a/apps/account/urls/back.py b/apps/account/urls/back.py index db7cb772..a24a33e6 100644 --- a/apps/account/urls/back.py +++ b/apps/account/urls/back.py @@ -7,8 +7,7 @@ app_name = 'account' urlpatterns = [ path('role/', views.RoleListView.as_view(), name='role-list-create'), - path('role/types/', views.RoleChoiceListView.as_view(), name='role-type-list'), - path('role/tab/', views.RoleTabRetrieveView.as_view(), name='role-tab'), + path('role/types/', views.RoleTypeRetrieveView.as_view(), name='role-types'), path('user-role/', views.UserRoleListView.as_view(), name='user-role-list-create'), path('user/', views.UserListView.as_view(), name='user-create-list'), path('user//', views.UserRUDView.as_view(), name='user-rud'), diff --git a/apps/account/views/back.py b/apps/account/views/back.py index f339ffb7..0fd9bdde 100644 --- a/apps/account/views/back.py +++ b/apps/account/views/back.py @@ -18,39 +18,18 @@ class RoleListView(generics.ListCreateAPIView): filter_class = filters.RoleListFilter -class RoleChoiceListView(generics.GenericAPIView): - """Return role choices.""" - def get(self, request, *args, **kwargs): - """Implement GET-method""" - return Response(models.Role.role_types(), status=status.HTTP_200_OK) - - -class RoleTabRetrieveView(generics.GenericAPIView): +class RoleTypeRetrieveView(generics.GenericAPIView): permission_classes = [permissions.IsAdminUser] - def get_queryset(self): - """Overridden get_queryset method.""" - additional_filters = {} + def get(self, request, *args, **kwargs): + """Implement GET-method""" + country_code = None if (self.request.user.userrole_set.country_admin_role().exists() and hasattr(self.request, 'country_code')): - additional_filters.update({'country__code': self.request.country_code}) - - return models.Role.objects.filter(**additional_filters)\ - .annotate_role_name()\ - .values('role_name')\ - .annotate_role_counter()\ - .values('role_name', 'role_counter') - - def get(self, request, *args, **kwargs): - """Implement GET-method""" - data = list(self.get_queryset()) - - # todo: Need refactoring. Extend data list with non-existed role. - for role in models.Role.role_names(): - if role not in [role.get('role_name') for role in data]: - data.append({'role_name': role, 'role_counter': 0}) + country_code = self.request.country_code + data = models.UserRole.objects.aggregate_role_counter(country_code) return Response(data, status=status.HTTP_200_OK) @@ -61,7 +40,6 @@ class UserRoleListView(generics.ListCreateAPIView): class UserListView(generics.ListCreateAPIView): """User list create view.""" - queryset = User.objects.prefetch_related('roles', 'subscriber') serializer_class = serializers.BackUserSerializer permission_classes = (permissions.IsAdminUser,) filter_class = filters.AccountBackOfficeFilter @@ -76,6 +54,10 @@ class UserListView(generics.ListCreateAPIView): 'date_joined', ) + def get_queryset(self): + """Overridden get_queryset method.""" + return User.objects.with_extend_related() + class UserRUDView(generics.RetrieveUpdateDestroyAPIView): """User RUD view.""" From e5faa90c76c1a28657487cdacc8d3c781775586d Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 23 Jan 2020 13:54:36 +0300 Subject: [PATCH 30/95] multiply filters for employee --- apps/establishment/filters.py | 24 +++++++++++++++++------- apps/establishment/models.py | 18 +++++++++--------- apps/establishment/views/back.py | 2 +- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/apps/establishment/filters.py b/apps/establishment/filters.py index 3a7d3398..7ae5d13c 100644 --- a/apps/establishment/filters.py +++ b/apps/establishment/filters.py @@ -1,7 +1,8 @@ """Establishment app filters.""" from django.core.validators import EMPTY_VALUES from django.utils.translation import ugettext_lazy as _ -from django_filters import rest_framework as filters +from django_filters import rest_framework as filters, Filter +from django_filters.fields import Lookup from rest_framework.serializers import ValidationError from establishment import models @@ -63,13 +64,19 @@ class EstablishmentTypeTagFilter(filters.FilterSet): ) +class ListFilter(Filter): + def filter(self, qs, value): + value_list = value.split(u',') + return super().filter(qs, Lookup(value_list, 'in')) + + class EmployeeBackFilter(filters.FilterSet): """Employee filter set.""" search = filters.CharFilter(method='search_by_name_or_last_name') - position_id = filters.NumberFilter(method='search_by_actual_position_id') - public_mark = filters.NumberFilter(method='search_by_public_mark') - toque_number = filters.NumberFilter(method='search_by_toque_number') + position_id = filters.CharFilter(method='search_by_actual_position_id') + public_mark = filters.CharFilter(method='search_by_public_mark') + toque_number = filters.CharFilter(method='search_by_toque_number') username = filters.CharFilter(method='search_by_username_or_name') class Meta: @@ -93,19 +100,22 @@ class EmployeeBackFilter(filters.FilterSet): def search_by_actual_position_id(self, queryset, name, value): """Search by actual position_id.""" if value not in EMPTY_VALUES: - return queryset.search_by_position_id(value) + value_list = [int(val) for val in value.split(',')] + return queryset.search_by_position_id(value_list) return queryset def search_by_public_mark(self, queryset, name, value): """Search by establishment public_mark.""" if value not in EMPTY_VALUES: - return queryset.search_by_public_mark(value) + value_list = [int(val) for val in value.split(',')] + return queryset.search_by_public_mark(value_list) return queryset def search_by_toque_number(self, queryset, name, value): """Search by establishment toque_number.""" if value not in EMPTY_VALUES: - return queryset.search_by_toque_number(value) + value_list = [int(val) for val in value.split(',')] + return queryset.search_by_toque_number(value_list) return queryset def search_by_username_or_name(self, queryset, name, value): diff --git a/apps/establishment/models.py b/apps/establishment/models.py index f2c7e2ee..edbafdc7 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -1034,22 +1034,22 @@ class EmployeeQuerySet(models.QuerySet): Q(establishmentemployee__to_date__isnull=True) ) - def search_by_position_id(self, value): + def search_by_position_id(self, value_list): """Search by position_id.""" - return self.filter( - Q(establishmentemployee__position_id=value), + return self.search_by_actual_employee().filter( + Q(establishmentemployee__position_id__in=value_list), ) - def search_by_public_mark(self, value): + def search_by_public_mark(self, value_list): """Search by establishment public_mark.""" - return self.filter( - Q(establishmentemployee__establishment__public_mark=value), + return self.search_by_actual_employee().filter( + Q(establishmentemployee__establishment__public_mark__in=value_list), ) - def search_by_toque_number(self, value): + def search_by_toque_number(self, value_list): """Search by establishment toque_number.""" - return self.filter( - Q(establishmentemployee__establishment__toque_number=value), + return self.search_by_actual_employee().filter( + Q(establishmentemployee__establishment__toque_number__in=value_list), ) def search_by_username_or_name(self, value): diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 4bbf9533..04ba6581 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -187,7 +187,7 @@ class EmployeeListCreateView(generics.ListCreateAPIView): permission_classes = (permissions.AllowAny,) filter_class = filters.EmployeeBackFilter serializer_class = serializers.EmployeeBackSerializers - queryset = models.Employee.objects.all().with_back_office_related() + queryset = models.Employee.objects.all().distinct().with_back_office_related() class EmployeesListSearchViews(generics.ListAPIView): From 18978355bc9f4a9a616b4f8e286f26fe743b9f1a Mon Sep 17 00:00:00 2001 From: dormantman Date: Thu, 23 Jan 2020 13:56:17 +0300 Subject: [PATCH 31/95] Change method model --- apps/main/migrations/0050_contentpage.py | 27 ----------- .../migrations/0051_auto_20200122_1256.py | 27 ----------- apps/main/models.py | 18 -------- apps/main/serializers/common.py | 20 +------- apps/main/views/common.py | 46 ++++++++++++++----- 5 files changed, 35 insertions(+), 103 deletions(-) delete mode 100644 apps/main/migrations/0050_contentpage.py delete mode 100644 apps/main/migrations/0051_auto_20200122_1256.py diff --git a/apps/main/migrations/0050_contentpage.py b/apps/main/migrations/0050_contentpage.py deleted file mode 100644 index 7d369690..00000000 --- a/apps/main/migrations/0050_contentpage.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 2.2.7 on 2020-01-22 12:41 - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('main', '0049_remove_navigationbarpermission_section'), - ] - - operations = [ - migrations.CreateModel( - name='ContentPage', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')), - ('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')), - ('content', models.TextField(verbose_name='content')), - ('slug', models.SlugField(max_length=255, null=True, unique=True, verbose_name='slug')), - ], - options={ - 'abstract': False, - }, - ), - ] diff --git a/apps/main/migrations/0051_auto_20200122_1256.py b/apps/main/migrations/0051_auto_20200122_1256.py deleted file mode 100644 index bdb85fdb..00000000 --- a/apps/main/migrations/0051_auto_20200122_1256.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 2.2.7 on 2020-01-22 12:56 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('main', '0050_contentpage'), - ] - - operations = [ - migrations.AlterModelOptions( - name='contentpage', - options={'verbose_name': 'content page', 'verbose_name_plural': 'content page'}, - ), - migrations.AddField( - model_name='contentpage', - name='name', - field=models.CharField(default='', max_length=255, verbose_name='name'), - ), - migrations.AlterField( - model_name='contentpage', - name='content', - field=models.TextField(default='', verbose_name='content'), - ), - ] diff --git a/apps/main/models.py b/apps/main/models.py index 0cde6ddb..8856bec5 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -404,24 +404,6 @@ class Footer(ProjectBaseMixin): links = models.ManyToManyField(FooterLink, verbose_name=_('links'), related_name='link_footer') -class ContentPageQuerySet(models.QuerySet): - """QuerySet for model ContentPage.""" - - -class ContentPage(ProjectBaseMixin): - name = models.CharField(_('name'), max_length=255, default='') - content = models.TextField(_('content'), default='') - slug = models.SlugField(max_length=255, unique=True, null=True, - verbose_name=_('slug')) - - objects = ContentPageQuerySet.as_manager() - - class Meta: - """Meta class.""" - verbose_name = _('content page') - verbose_name_plural = _('content page') - - class PanelQuerySet(models.QuerySet): """Panels QuerySet.""" diff --git a/apps/main/serializers/common.py b/apps/main/serializers/common.py index bdb3e895..9eccd718 100644 --- a/apps/main/serializers/common.py +++ b/apps/main/serializers/common.py @@ -5,7 +5,7 @@ from rest_framework import serializers from location.serializers import CountrySerializer from main import models from tag.serializers import TagBackOfficeSerializer -from utils.serializers import ProjectModelSerializer, TranslatedField, RecursiveFieldSerializer +from utils.serializers import ProjectModelSerializer, RecursiveFieldSerializer, TranslatedField class FeatureSerializer(serializers.ModelSerializer): @@ -345,21 +345,3 @@ class NavigationBarPermissionBaseSerializer(serializers.ModelSerializer): 'sections', 'permission_mode_display', ] - - -class ContentPageSerializer(serializers.ModelSerializer): - """Content page serializer""" - - name = serializers.CharField() - content = serializers.CharField() - slug = serializers.CharField() - - class Meta: - """Meta class.""" - model = models.ContentPage - fields = [ - 'id', - 'name', - 'content', - 'slug' - ] diff --git a/apps/main/views/common.py b/apps/main/views/common.py index 62b6c1d7..c6b77821 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -1,10 +1,13 @@ """Main app views.""" from django.http import Http404 -from django.conf import settings from rest_framework import generics, permissions from rest_framework.response import Response from main import methods, models, serializers +from news.models import News, NewsType +from news.serializers import NewsDetailSerializer, NewsListSerializer +from utils.serializers import EmptySerializer + # # class FeatureViewMixin: @@ -42,7 +45,6 @@ from main import methods, models, serializers # class SiteFeaturesRUDView(SiteFeaturesViewMixin, # generics.RetrieveUpdateDestroyAPIView): # """Site features RUD.""" -from utils.serializers import EmptySerializer class AwardView(generics.ListAPIView): @@ -101,16 +103,24 @@ class ContentPageView(generics.ListAPIView): """Method to get content pages""" permission_classes = (permissions.AllowAny,) - serializer_class = serializers.ContentPageSerializer - queryset = models.ContentPage.objects.all() + serializer_class = NewsListSerializer + queryset = News.objects.all() + + def get_queryset(self): + static_page_category = NewsType.objects.get(name='static-page') + return super().get_queryset().filter(news_type=static_page_category) class ContentPageAdminView(generics.ListCreateAPIView): """Method to get content pages""" permission_classes = (permissions.IsAdminUser,) - serializer_class = serializers.ContentPageSerializer - queryset = models.ContentPage.objects.all() + serializer_class = NewsListSerializer + queryset = News.objects.all() + + def get_queryset(self): + static_page_category = NewsType.objects.get(name='static-page') + return super().get_queryset().filter(news_type=static_page_category) class ContentPageRetrieveView(generics.RetrieveAPIView): @@ -118,16 +128,24 @@ class ContentPageRetrieveView(generics.RetrieveAPIView): lookup_field = 'slug' permission_classes = (permissions.AllowAny,) - serializer_class = serializers.ContentPageSerializer - queryset = models.ContentPage.objects.all() + serializer_class = NewsDetailSerializer + queryset = News.objects.all() + + def get_queryset(self): + static_page_category = NewsType.objects.get(name='static-page') + return super().get_queryset().filter(news_type=static_page_category) class ContentPageIdRetrieveView(generics.RetrieveAPIView): """Retrieve method to get content pages""" permission_classes = (permissions.AllowAny,) - serializer_class = serializers.ContentPageSerializer - queryset = models.ContentPage.objects.all() + serializer_class = NewsDetailSerializer + queryset = News.objects.all() + + def get_queryset(self): + static_page_category = NewsType.objects.get(name='static-page') + return super().get_queryset().filter(news_type=static_page_category) class ContentPageRetrieveAdminView(generics.RetrieveUpdateDestroyAPIView): @@ -135,5 +153,9 @@ class ContentPageRetrieveAdminView(generics.RetrieveUpdateDestroyAPIView): lookup_field = 'slug' permission_classes = (permissions.IsAdminUser,) - serializer_class = serializers.ContentPageSerializer - queryset = models.ContentPage.objects.all() + serializer_class = NewsDetailSerializer + queryset = News.objects.all() + + def get_queryset(self): + static_page_category = NewsType.objects.get(name='static-page') + return super().get_queryset().filter(news_type=static_page_category) From 874de420f6cff60f5def32d4579ccfc0a5730ee8 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 23 Jan 2020 14:17:29 +0300 Subject: [PATCH 32/95] drinks and schedule for menu --- .../migrations/0076_auto_20200123_1115.py | 24 +++++++++++++++++++ apps/establishment/models.py | 7 ++++++ 2 files changed, 31 insertions(+) create mode 100644 apps/establishment/migrations/0076_auto_20200123_1115.py diff --git a/apps/establishment/migrations/0076_auto_20200123_1115.py b/apps/establishment/migrations/0076_auto_20200123_1115.py new file mode 100644 index 00000000..c18a645e --- /dev/null +++ b/apps/establishment/migrations/0076_auto_20200123_1115.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2.7 on 2020-01-23 11:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('timetable', '0003_auto_20191003_0943'), + ('establishment', '0075_employee_photo'), + ] + + operations = [ + migrations.AddField( + model_name='menu', + name='is_drinks_included', + field=models.BooleanField(default=False, verbose_name='is drinks included'), + ), + migrations.AddField( + model_name='menu', + name='schedule', + field=models.ManyToManyField(blank=True, related_name='menus', to='timetable.Timetable', verbose_name='Establishment schedule'), + ), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index edbafdc7..b3612301 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -1230,6 +1230,13 @@ class Menu(TranslatedFieldsMixin, BaseAttributes): establishment = models.ForeignKey( 'establishment.Establishment', verbose_name=_('establishment'), on_delete=models.CASCADE) + is_drinks_included = models.BooleanField(_('is drinks included'), default=False) + schedule = models.ManyToManyField( + to='timetable.Timetable', + blank=True, + verbose_name=_('Establishment schedule'), + related_name='menus', + ) class Meta: verbose_name = _('menu') From 7391463f5ad0a01c655c461d80247ecd8c07cb5b Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 23 Jan 2020 14:47:55 +0300 Subject: [PATCH 33/95] uploads model for menu --- apps/establishment/filters.py | 6 ---- .../migrations/0077_menuuploads.py | 34 +++++++++++++++++++ apps/establishment/models.py | 16 ++++++++- 3 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 apps/establishment/migrations/0077_menuuploads.py diff --git a/apps/establishment/filters.py b/apps/establishment/filters.py index 7ae5d13c..37d15dfc 100644 --- a/apps/establishment/filters.py +++ b/apps/establishment/filters.py @@ -64,12 +64,6 @@ class EstablishmentTypeTagFilter(filters.FilterSet): ) -class ListFilter(Filter): - def filter(self, qs, value): - value_list = value.split(u',') - return super().filter(qs, Lookup(value_list, 'in')) - - class EmployeeBackFilter(filters.FilterSet): """Employee filter set.""" diff --git a/apps/establishment/migrations/0077_menuuploads.py b/apps/establishment/migrations/0077_menuuploads.py new file mode 100644 index 00000000..5abdc13b --- /dev/null +++ b/apps/establishment/migrations/0077_menuuploads.py @@ -0,0 +1,34 @@ +# Generated by Django 2.2.7 on 2020-01-23 11:47 + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('establishment', '0076_auto_20200123_1115'), + ] + + operations = [ + migrations.CreateModel( + name='MenuUploads', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('file', models.FileField(upload_to='', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=('jpg', 'jpeg', 'png', 'doc', 'docx', 'pdf'))], verbose_name='File')), + ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='menuuploads_records_created', to=settings.AUTH_USER_MODEL, verbose_name='created by')), + ('menu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='uploads', to='establishment.Menu', verbose_name='Menu')), + ('modified_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='menuuploads_records_modified', to=settings.AUTH_USER_MODEL, verbose_name='modified by')), + ], + options={ + 'verbose_name': 'menu upload', + 'verbose_name_plural': 'menu uploads', + }, + ), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index b3612301..6332d97e 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -14,7 +14,7 @@ from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.search import TrigramDistance, TrigramSimilarity from django.contrib.postgres.indexes import GinIndex from django.core.exceptions import ValidationError -from django.core.validators import MinValueValidator, MaxValueValidator +from django.core.validators import MinValueValidator, MaxValueValidator, FileExtensionValidator from django.db import models from django.db.models import When, Case, F, ExpressionWrapper, Subquery, Q, Prefetch, Sum from django.utils import timezone @@ -1243,6 +1243,20 @@ class Menu(TranslatedFieldsMixin, BaseAttributes): verbose_name_plural = _('menu') +class MenuUploads(BaseAttributes): + """Menu files""" + + menu = models.ForeignKey(Menu, verbose_name=_('Menu'), on_delete=models.CASCADE, related_name='uploads') + file = models.FileField( + _('File'), + validators=[FileExtensionValidator(allowed_extensions=('jpg', 'jpeg', 'png', 'doc', 'docx', 'pdf')), ], + ) + + class Meta: + verbose_name = _('menu upload') + verbose_name_plural = _('menu uploads') + + class SocialChoice(models.Model): title = models.CharField(_('title'), max_length=255, unique=True) From 6b819e2ed4dd52e9e6078a1e600d273b84bdf615 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 23 Jan 2020 17:31:37 +0300 Subject: [PATCH 34/95] modified search filter for backoffice account view --- apps/account/filters.py | 8 +----- apps/account/models.py | 62 +++++++++++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/apps/account/filters.py b/apps/account/filters.py index 88c9e9f0..c9f0bb0f 100644 --- a/apps/account/filters.py +++ b/apps/account/filters.py @@ -33,14 +33,8 @@ class AccountBackOfficeFilter(filters.FilterSet): return queryset def search_text(self, queryset, name, value): - queryset = queryset.annotate_vector() if value not in EMPTY_VALUES: - # search by exact value - filtered_qs = queryset.filter(vector=value) - if not filtered_qs.exists(): - # if filtered qs is None find something - filtered_qs = queryset.filter(vector__icontains=value) - return filtered_qs + return queryset.full_text_search(value) return queryset def by_role_country_code(self, queryset, name, value): diff --git a/apps/account/models.py b/apps/account/models.py index 0175ccf0..08944f0c 100644 --- a/apps/account/models.py +++ b/apps/account/models.py @@ -1,6 +1,6 @@ """Account models""" from datetime import datetime - +from django.contrib.postgres.search import TrigramSimilarity from django.conf import settings from django.contrib.auth.models import AbstractUser, UserManager as BaseUserManager from django.core.mail import send_mail @@ -21,7 +21,6 @@ from main.models import SiteSettings from utils.models import GMTokenGenerator from utils.models import ImageMixin, ProjectBaseMixin, PlatformMixin from utils.tokens import GMRefreshToken -from django.contrib.postgres.search import SearchVector from phonenumber_field.modelfields import PhoneNumberField @@ -146,15 +145,56 @@ class UserQuerySet(models.QuerySet): role = Role.objects.filter(role=Role.ESTABLISHMENT_MANAGER).first() return self.by_role(role).filter(userrole__establishment=establishment) - def annotate_vector(self): - """Full-text search""" - return self.annotate(vector=SearchVector( - 'username', - 'first_name', - 'last_name', - 'email', - 'phone', - )) + def full_text_search(self, search_value: str): + return self.annotate( + username_similarity=models.Case( + models.When( + models.Q(username__isnull=False), + then=TrigramSimilarity('username', search_value.lower()) + ), + default=0, + output_field=models.FloatField() + ), + first_name_similarity=models.Case( + models.When( + models.Q(first_name__isnull=False), + then=TrigramSimilarity('first_name', search_value.lower()) + ), + default=0, + output_field=models.FloatField() + ), + last_name_similarity=models.Case( + models.When( + models.Q(last_name__isnull=False), + then=TrigramSimilarity('last_name', search_value.lower()) + ), + default=0, + output_field=models.FloatField() + ), + email_similarity=models.Case( + models.When( + models.Q(email__isnull=False), + then=TrigramSimilarity('email', search_value.lower()) + ), + default=0, + output_field=models.FloatField() + ), + phone_similarity=models.Case( + models.When( + models.Q(phone__isnull=False), + then=TrigramSimilarity('phone', search_value.lower()) + ), + default=0, + output_field=models.FloatField() + ), + relevance=( + models.F('username_similarity') + + models.F('first_name_similarity') + + models.F('last_name_similarity') + + models.F('email_similarity') + + models.F('phone_similarity') + ), + ).filter(relevance__gte=0.1).order_by('-relevance') def by_role_country_code(self, country_code: str): """Filter by role country code.""" From 0c40ed06e1fc1e8c61fd5ed5b37e12fbd325ec92 Mon Sep 17 00:00:00 2001 From: dormantman Date: Thu, 23 Jan 2020 17:38:55 +0300 Subject: [PATCH 35/95] Changed to text/html response --- apps/main/views/common.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/apps/main/views/common.py b/apps/main/views/common.py index c6b77821..c82af99e 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -1,11 +1,13 @@ """Main app views.""" from django.http import Http404 from rest_framework import generics, permissions +from rest_framework.renderers import StaticHTMLRenderer from rest_framework.response import Response from main import methods, models, serializers from news.models import News, NewsType from news.serializers import NewsDetailSerializer, NewsListSerializer +from news.views import NewsMixinView from utils.serializers import EmptySerializer @@ -107,7 +109,7 @@ class ContentPageView(generics.ListAPIView): queryset = News.objects.all() def get_queryset(self): - static_page_category = NewsType.objects.get(name='static-page') + static_page_category = NewsType.objects.get(name='static') return super().get_queryset().filter(news_type=static_page_category) @@ -119,21 +121,26 @@ class ContentPageAdminView(generics.ListCreateAPIView): queryset = News.objects.all() def get_queryset(self): - static_page_category = NewsType.objects.get(name='static-page') + static_page_category = NewsType.objects.get(name='static') return super().get_queryset().filter(news_type=static_page_category) -class ContentPageRetrieveView(generics.RetrieveAPIView): +class ContentPageRetrieveView(NewsMixinView, generics.RetrieveAPIView): """Retrieve method to get content pages""" - lookup_field = 'slug' + lookup_field = None permission_classes = (permissions.AllowAny,) serializer_class = NewsDetailSerializer queryset = News.objects.all() + renderer_classes = [StaticHTMLRenderer] def get_queryset(self): - static_page_category = NewsType.objects.get(name='static-page') - return super().get_queryset().filter(news_type=static_page_category) + static_page_category = NewsType.objects.get(name='static') + return self.queryset.filter(news_type__id=static_page_category.id) + + def get(self, request, *args, **kwargs): + instance = self.get_object() + return Response(instance.description.get(request.locale)) class ContentPageIdRetrieveView(generics.RetrieveAPIView): @@ -142,20 +149,25 @@ class ContentPageIdRetrieveView(generics.RetrieveAPIView): permission_classes = (permissions.AllowAny,) serializer_class = NewsDetailSerializer queryset = News.objects.all() + renderer_classes = [StaticHTMLRenderer] def get_queryset(self): - static_page_category = NewsType.objects.get(name='static-page') - return super().get_queryset().filter(news_type=static_page_category) + static_page_category = NewsType.objects.get(name='static') + return self.queryset.filter(news_type__id=static_page_category.id) + + def get(self, request, *args, **kwargs): + instance = self.get_object() + return Response(instance.description.get(request.locale)) -class ContentPageRetrieveAdminView(generics.RetrieveUpdateDestroyAPIView): +class ContentPageRetrieveAdminView(NewsMixinView, generics.RetrieveUpdateDestroyAPIView): """Retrieve method to get content pages""" - lookup_field = 'slug' + lookup_field = None permission_classes = (permissions.IsAdminUser,) serializer_class = NewsDetailSerializer queryset = News.objects.all() def get_queryset(self): - static_page_category = NewsType.objects.get(name='static-page') + static_page_category = NewsType.objects.get(name='static') return super().get_queryset().filter(news_type=static_page_category) From d98a6aeb001c5d38da8743c098e2ae0f93ba5f84 Mon Sep 17 00:00:00 2001 From: dormantman Date: Thu, 23 Jan 2020 17:45:59 +0300 Subject: [PATCH 36/95] Change structure --- apps/main/views/common.py | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/apps/main/views/common.py b/apps/main/views/common.py index c82af99e..b93096b4 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -101,17 +101,30 @@ class DetermineLocation(generics.GenericAPIView): raise Http404 -class ContentPageView(generics.ListAPIView): +class ContentPageBaseView(generics.GenericAPIView): + + @property + def static_page_category(self): + name = 'static' + + static_page_category = NewsType.objects.filter(name=name).first() + + if static_page_category is None: + static_page_category = NewsType.objects.create(name=name) + + return static_page_category + + def get_queryset(self): + return super().get_queryset().filter(news_type=self.static_page_category) + + +class ContentPageView(ContentPageBaseView, generics.ListAPIView): """Method to get content pages""" permission_classes = (permissions.AllowAny,) serializer_class = NewsListSerializer queryset = News.objects.all() - def get_queryset(self): - static_page_category = NewsType.objects.get(name='static') - return super().get_queryset().filter(news_type=static_page_category) - class ContentPageAdminView(generics.ListCreateAPIView): """Method to get content pages""" @@ -120,10 +133,6 @@ class ContentPageAdminView(generics.ListCreateAPIView): serializer_class = NewsListSerializer queryset = News.objects.all() - def get_queryset(self): - static_page_category = NewsType.objects.get(name='static') - return super().get_queryset().filter(news_type=static_page_category) - class ContentPageRetrieveView(NewsMixinView, generics.RetrieveAPIView): """Retrieve method to get content pages""" @@ -134,10 +143,6 @@ class ContentPageRetrieveView(NewsMixinView, generics.RetrieveAPIView): queryset = News.objects.all() renderer_classes = [StaticHTMLRenderer] - def get_queryset(self): - static_page_category = NewsType.objects.get(name='static') - return self.queryset.filter(news_type__id=static_page_category.id) - def get(self, request, *args, **kwargs): instance = self.get_object() return Response(instance.description.get(request.locale)) @@ -151,10 +156,6 @@ class ContentPageIdRetrieveView(generics.RetrieveAPIView): queryset = News.objects.all() renderer_classes = [StaticHTMLRenderer] - def get_queryset(self): - static_page_category = NewsType.objects.get(name='static') - return self.queryset.filter(news_type__id=static_page_category.id) - def get(self, request, *args, **kwargs): instance = self.get_object() return Response(instance.description.get(request.locale)) @@ -167,7 +168,3 @@ class ContentPageRetrieveAdminView(NewsMixinView, generics.RetrieveUpdateDestroy permission_classes = (permissions.IsAdminUser,) serializer_class = NewsDetailSerializer queryset = News.objects.all() - - def get_queryset(self): - static_page_category = NewsType.objects.get(name='static') - return super().get_queryset().filter(news_type=static_page_category) From 79001be037defa3954d73044fdbeb0c8a39a2143 Mon Sep 17 00:00:00 2001 From: dormantman Date: Thu, 23 Jan 2020 17:53:13 +0300 Subject: [PATCH 37/95] Added 404 pages --- apps/main/views/common.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/main/views/common.py b/apps/main/views/common.py index b93096b4..aa4c2db0 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -145,6 +145,10 @@ class ContentPageRetrieveView(NewsMixinView, generics.RetrieveAPIView): def get(self, request, *args, **kwargs): instance = self.get_object() + + if instance is None: + raise Http404 + return Response(instance.description.get(request.locale)) @@ -158,6 +162,10 @@ class ContentPageIdRetrieveView(generics.RetrieveAPIView): def get(self, request, *args, **kwargs): instance = self.get_object() + + if instance is None: + raise Http404 + return Response(instance.description.get(request.locale)) From f899de0369ec1de68b21dec5f53bec777a6a1068 Mon Sep 17 00:00:00 2001 From: dormantman Date: Thu, 23 Jan 2020 17:59:18 +0300 Subject: [PATCH 38/95] Added check to news mixin --- apps/main/views/common.py | 8 -------- apps/news/views.py | 11 +++++++++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/apps/main/views/common.py b/apps/main/views/common.py index aa4c2db0..b93096b4 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -145,10 +145,6 @@ class ContentPageRetrieveView(NewsMixinView, generics.RetrieveAPIView): def get(self, request, *args, **kwargs): instance = self.get_object() - - if instance is None: - raise Http404 - return Response(instance.description.get(request.locale)) @@ -162,10 +158,6 @@ class ContentPageIdRetrieveView(generics.RetrieveAPIView): def get(self, request, *args, **kwargs): instance = self.get_object() - - if instance is None: - raise Http404 - return Response(instance.description.get(request.locale)) diff --git a/apps/news/views.py b/apps/news/views.py index 5429b4a7..4f0adc1e 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -1,5 +1,6 @@ """News app views.""" from django.conf import settings +from django.http import Http404 from django.shortcuts import get_object_or_404 from django.utils import translation from rest_framework import generics, permissions, response @@ -40,8 +41,14 @@ class NewsMixinView: return qs def get_object(self): - return self.get_queryset() \ - .filter(slugs__values__contains=[self.kwargs['slug']]).first() + instance = self.get_queryset().filter( + slugs__values__contains=[self.kwargs['slug']] + ).first() + + if instance is None: + raise Http404 + + return instance class NewsListView(NewsMixinView, generics.ListAPIView): From f9c5bdceee46e801fd4162ebebfe8d1e56929963 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 23 Jan 2020 11:29:16 +0300 Subject: [PATCH 39/95] Revert "Revert (nickname field) common.py" This reverts commit 5560a01 --- apps/comment/serializers/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/comment/serializers/common.py b/apps/comment/serializers/common.py index c928aca4..6f0d049d 100644 --- a/apps/comment/serializers/common.py +++ b/apps/comment/serializers/common.py @@ -8,7 +8,7 @@ from establishment.models import EstablishmentType class CommentBaseSerializer(serializers.ModelSerializer): """Comment serializer""" - nickname = serializers.CharField(read_only=True, + user_name = serializers.CharField(read_only=True, source='user.username') is_mine = serializers.BooleanField(read_only=True) profile_pic = serializers.URLField(read_only=True, @@ -32,7 +32,7 @@ class CommentBaseSerializer(serializers.ModelSerializer): 'created', 'text', 'mark', - 'nickname', + 'user_name', 'user_email', 'profile_pic', 'status', From 6d2aee572a9bcbee04759f0095cc1daf191e81ee Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 23 Jan 2020 18:02:12 +0300 Subject: [PATCH 40/95] fix configs --- apps/location/models.py | 11 ++++------- apps/location/views/common.py | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/apps/location/models.py b/apps/location/models.py index 88dc8472..97c02dd7 100644 --- a/apps/location/models.py +++ b/apps/location/models.py @@ -80,22 +80,19 @@ class RegionQuerySet(models.QuerySet): def without_parent_region(self, switcher: bool = True): """Filter regions by parent region.""" - return self.filter(parent_region__isnull=switcher)\ - .order_by('code') + return self.filter(parent_region__isnull=switcher) def by_region_id(self, region_id): """Filter regions by region id.""" - return self.filter(id=region_id)\ - .order_by('code') + return self.filter(id=region_id) def by_sub_region_id(self, sub_region_id): """Filter sub regions by sub region id.""" - return self.filter(parent_region_id=sub_region_id)\ - .order_by('code') + return self.filter(parent_region_id=sub_region_id) def sub_regions_by_region_id(self, region_id): """Filter regions by sub region id.""" - return self.filter(parent_region_id=region_id).order_by('code') + return self.filter(parent_region_id=region_id) class Region(models.Model): diff --git a/apps/location/views/common.py b/apps/location/views/common.py index d9d5520b..963c0cae 100644 --- a/apps/location/views/common.py +++ b/apps/location/views/common.py @@ -18,7 +18,7 @@ class CountryViewMixin(generics.GenericAPIView): class RegionViewMixin(generics.GenericAPIView): """View Mixin for model Region""" model = models.Region - queryset = models.Region.objects.all() + queryset = models.Region.objects.all().order_by('name') class CityViewMixin(generics.GenericAPIView): From d27a47f905857ccf722a46defd130c8e7c04d5cd Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 23 Jan 2020 18:03:06 +0300 Subject: [PATCH 41/95] Revert "Revert "Revert (nickname field) common.py"" This reverts commit f9c5bdc --- apps/comment/serializers/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/comment/serializers/common.py b/apps/comment/serializers/common.py index 6f0d049d..c928aca4 100644 --- a/apps/comment/serializers/common.py +++ b/apps/comment/serializers/common.py @@ -8,7 +8,7 @@ from establishment.models import EstablishmentType class CommentBaseSerializer(serializers.ModelSerializer): """Comment serializer""" - user_name = serializers.CharField(read_only=True, + nickname = serializers.CharField(read_only=True, source='user.username') is_mine = serializers.BooleanField(read_only=True) profile_pic = serializers.URLField(read_only=True, @@ -32,7 +32,7 @@ class CommentBaseSerializer(serializers.ModelSerializer): 'created', 'text', 'mark', - 'user_name', + 'nickname', 'user_email', 'profile_pic', 'status', From 591fdc9d6a1ae6d8798f49695ff7fc152796c3fc Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 23 Jan 2020 18:10:17 +0300 Subject: [PATCH 42/95] fix content-pages --- apps/main/views/common.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/main/views/common.py b/apps/main/views/common.py index b93096b4..801c623c 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -1,5 +1,6 @@ """Main app views.""" from django.http import Http404 +from django.conf import settings from rest_framework import generics, permissions from rest_framework.renderers import StaticHTMLRenderer from rest_framework.response import Response @@ -145,7 +146,9 @@ class ContentPageRetrieveView(NewsMixinView, generics.RetrieveAPIView): def get(self, request, *args, **kwargs): instance = self.get_object() - return Response(instance.description.get(request.locale)) + return Response(instance.description.get(request.locale, + instance.description.get(settings.FALLBACK_LOCALE, + next(iter(instance.description))))) class ContentPageIdRetrieveView(generics.RetrieveAPIView): @@ -158,7 +161,9 @@ class ContentPageIdRetrieveView(generics.RetrieveAPIView): def get(self, request, *args, **kwargs): instance = self.get_object() - return Response(instance.description.get(request.locale)) + return Response(instance.description.get(request.locale, + instance.description.get(settings.FALLBACK_LOCALE, + next(iter(instance.description))))) class ContentPageRetrieveAdminView(NewsMixinView, generics.RetrieveUpdateDestroyAPIView): From 3ebd8596864b028aa46340a5c83b3facab743210 Mon Sep 17 00:00:00 2001 From: dormantman Date: Thu, 23 Jan 2020 18:14:26 +0300 Subject: [PATCH 43/95] Fix content-pages --- apps/main/views/common.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/main/views/common.py b/apps/main/views/common.py index 801c623c..922261cb 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -127,7 +127,7 @@ class ContentPageView(ContentPageBaseView, generics.ListAPIView): queryset = News.objects.all() -class ContentPageAdminView(generics.ListCreateAPIView): +class ContentPageAdminView(ContentPageBaseView, generics.ListCreateAPIView): """Method to get content pages""" permission_classes = (permissions.IsAdminUser,) @@ -135,7 +135,7 @@ class ContentPageAdminView(generics.ListCreateAPIView): queryset = News.objects.all() -class ContentPageRetrieveView(NewsMixinView, generics.RetrieveAPIView): +class ContentPageRetrieveView(NewsMixinView, ContentPageBaseView, generics.RetrieveAPIView): """Retrieve method to get content pages""" lookup_field = None @@ -151,7 +151,7 @@ class ContentPageRetrieveView(NewsMixinView, generics.RetrieveAPIView): next(iter(instance.description))))) -class ContentPageIdRetrieveView(generics.RetrieveAPIView): +class ContentPageIdRetrieveView(ContentPageBaseView, generics.RetrieveAPIView): """Retrieve method to get content pages""" permission_classes = (permissions.AllowAny,) @@ -166,10 +166,11 @@ class ContentPageIdRetrieveView(generics.RetrieveAPIView): next(iter(instance.description))))) -class ContentPageRetrieveAdminView(NewsMixinView, generics.RetrieveUpdateDestroyAPIView): +class ContentPageRetrieveAdminView(NewsMixinView, ContentPageBaseView, generics.RetrieveUpdateDestroyAPIView): """Retrieve method to get content pages""" lookup_field = None permission_classes = (permissions.IsAdminUser,) serializer_class = NewsDetailSerializer queryset = News.objects.all() + From dd1b241c41b89a4f58e753402b667432340825e5 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 23 Jan 2020 18:26:02 +0300 Subject: [PATCH 44/95] verbose award_type for API --- apps/main/models.py | 8 ++++++++ apps/main/serializers/common.py | 13 +++++++++++++ apps/main/views/back.py | 4 ++-- apps/main/views/common.py | 4 ++-- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/apps/main/models.py b/apps/main/models.py index 8856bec5..605cb4e3 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -175,6 +175,12 @@ class SiteFeature(ProjectBaseMixin): unique_together = ('site_settings', 'feature') +class AwardQuerySet(models.QuerySet): + + def with_base_related(self): + return self.prefetch_related('award_type') + + class Award(TranslatedFieldsMixin, URLImageMixin, models.Model): """Award model.""" WAITING = 0 @@ -200,6 +206,8 @@ class Award(TranslatedFieldsMixin, URLImageMixin, models.Model): old_id = models.IntegerField(null=True, blank=True) + objects = AwardQuerySet.as_manager() + def __str__(self): title = 'None' lang = TranslationSettings.get_solo().default_language diff --git a/apps/main/serializers/common.py b/apps/main/serializers/common.py index 9eccd718..17477cb0 100644 --- a/apps/main/serializers/common.py +++ b/apps/main/serializers/common.py @@ -192,6 +192,15 @@ class SiteShortSerializer(serializers.ModelSerializer): ] +class AwardTypeBaseSerializer(serializers.ModelSerializer): + class Meta: + model = models.AwardType + fields = ( + 'id', + 'name', + ) + + class AwardBaseSerializer(serializers.ModelSerializer): """Award base serializer.""" @@ -210,6 +219,8 @@ class AwardBaseSerializer(serializers.ModelSerializer): class AwardSerializer(AwardBaseSerializer): """Award serializer.""" + award_type = AwardTypeBaseSerializer(read_only=True) + class Meta: model = models.Award fields = AwardBaseSerializer.Meta.fields + ['award_type', ] @@ -218,6 +229,8 @@ class AwardSerializer(AwardBaseSerializer): class BackAwardSerializer(AwardBaseSerializer): """Award serializer.""" + award_type = AwardTypeBaseSerializer(read_only=True) + class Meta: model = models.Award fields = AwardBaseSerializer.Meta.fields + [ diff --git a/apps/main/views/back.py b/apps/main/views/back.py index a8974721..92acd83e 100644 --- a/apps/main/views/back.py +++ b/apps/main/views/back.py @@ -15,7 +15,7 @@ from main.views import SiteSettingsView, SiteListView class AwardLstView(generics.ListCreateAPIView): """Award list create view.""" - queryset = Award.objects.all() + queryset = Award.objects.all().with_base_related() serializer_class = serializers.BackAwardSerializer permission_classes = (permissions.IsAdminUser,) filterset_class = AwardFilter @@ -23,7 +23,7 @@ class AwardLstView(generics.ListCreateAPIView): class AwardRUDView(generics.RetrieveUpdateDestroyAPIView): """Award RUD view.""" - queryset = Award.objects.all() + queryset = Award.objects.all().with_base_related() serializer_class = serializers.BackAwardSerializer permission_classes = (permissions.IsAdminUser,) lookup_field = 'id' diff --git a/apps/main/views/common.py b/apps/main/views/common.py index 922261cb..ef779a37 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -53,14 +53,14 @@ from utils.serializers import EmptySerializer class AwardView(generics.ListAPIView): """Awards list view.""" serializer_class = serializers.AwardSerializer - queryset = models.Award.objects.all() + queryset = models.Award.objects.all().with_base_related() permission_classes = (permissions.AllowAny,) class AwardRetrieveView(generics.RetrieveAPIView): """Award retrieve view.""" serializer_class = serializers.AwardSerializer - queryset = models.Award.objects.all() + queryset = models.Award.objects.all().with_base_related() permission_classes = (permissions.IsAuthenticatedOrReadOnly,) From 4002e684d59652a3cd258150deffe61432d957e7 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 23 Jan 2020 18:34:52 +0300 Subject: [PATCH 45/95] fix god damned content-pages --- apps/main/views/common.py | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/apps/main/views/common.py b/apps/main/views/common.py index ef779a37..03a0ad72 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -1,12 +1,10 @@ """Main app views.""" from django.http import Http404 -from django.conf import settings from rest_framework import generics, permissions -from rest_framework.renderers import StaticHTMLRenderer from rest_framework.response import Response from main import methods, models, serializers -from news.models import News, NewsType +from news.models import News from news.serializers import NewsDetailSerializer, NewsListSerializer from news.views import NewsMixinView from utils.serializers import EmptySerializer @@ -106,17 +104,10 @@ class ContentPageBaseView(generics.GenericAPIView): @property def static_page_category(self): - name = 'static' - - static_page_category = NewsType.objects.filter(name=name).first() - - if static_page_category is None: - static_page_category = NewsType.objects.create(name=name) - - return static_page_category + return 'static' def get_queryset(self): - return super().get_queryset().filter(news_type=self.static_page_category) + return super().get_queryset().filter(news_type__name=self.static_page_category) class ContentPageView(ContentPageBaseView, generics.ListAPIView): @@ -142,13 +133,6 @@ class ContentPageRetrieveView(NewsMixinView, ContentPageBaseView, generics.Retri permission_classes = (permissions.AllowAny,) serializer_class = NewsDetailSerializer queryset = News.objects.all() - renderer_classes = [StaticHTMLRenderer] - - def get(self, request, *args, **kwargs): - instance = self.get_object() - return Response(instance.description.get(request.locale, - instance.description.get(settings.FALLBACK_LOCALE, - next(iter(instance.description))))) class ContentPageIdRetrieveView(ContentPageBaseView, generics.RetrieveAPIView): @@ -157,13 +141,6 @@ class ContentPageIdRetrieveView(ContentPageBaseView, generics.RetrieveAPIView): permission_classes = (permissions.AllowAny,) serializer_class = NewsDetailSerializer queryset = News.objects.all() - renderer_classes = [StaticHTMLRenderer] - - def get(self, request, *args, **kwargs): - instance = self.get_object() - return Response(instance.description.get(request.locale, - instance.description.get(settings.FALLBACK_LOCALE, - next(iter(instance.description))))) class ContentPageRetrieveAdminView(NewsMixinView, ContentPageBaseView, generics.RetrieveUpdateDestroyAPIView): @@ -173,4 +150,3 @@ class ContentPageRetrieveAdminView(NewsMixinView, ContentPageBaseView, generics. permission_classes = (permissions.IsAdminUser,) serializer_class = NewsDetailSerializer queryset = News.objects.all() - From e0c2138ba709377c8b9f4a294d5744d3806b2c85 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 23 Jan 2020 19:33:02 +0300 Subject: [PATCH 46/95] fix crops --- apps/gallery/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gallery/serializers.py b/apps/gallery/serializers.py index 41e75cd7..fb5bfa26 100644 --- a/apps/gallery/serializers.py +++ b/apps/gallery/serializers.py @@ -98,7 +98,7 @@ class CropImageSerializer(ImageSerializer): x1, y1 = int(crop.split(' ')[0][:-2]), int(crop.split(' ')[1][:-2]) x2, y2 = x1 + width, y1 + height crop_params = { - 'geometry': f'{self._image.image.width}x{self._image.image.width}', + 'geometry': f'{round(x2 - x1)}x{round(y2 - y1)}', 'quality': 100, 'cropbox': f'{x1},{y1},{x2},{y2}' } From f70a3abb8cb7ff6e0e3fea3bdf52959596201b65 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 23 Jan 2020 20:26:22 +0300 Subject: [PATCH 47/95] award types list view --- apps/main/urls/back.py | 1 + apps/main/views/back.py | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/main/urls/back.py b/apps/main/urls/back.py index 3d39f008..c600a45f 100644 --- a/apps/main/urls/back.py +++ b/apps/main/urls/back.py @@ -8,6 +8,7 @@ app_name = 'main' urlpatterns = [ path('awards/', views.AwardLstView.as_view(), name='awards-list-create'), path('awards//', views.AwardRUDView.as_view(), name='awards-rud'), + path('award-types/', views.AwardTypesListView.as_view(), name='awards-types-list'), path('content_type/', views.ContentTypeView.as_view(), name='content_type-list'), path('sites/', views.SiteListBackOfficeView.as_view(), name='site-list-create'), path('site-settings//', views.SiteSettingsBackOfficeView.as_view(), diff --git a/apps/main/views/back.py b/apps/main/views/back.py index 92acd83e..ece392aa 100644 --- a/apps/main/views/back.py +++ b/apps/main/views/back.py @@ -9,7 +9,7 @@ from main import serializers from main.serializers.back import PanelSerializer from main import tasks from main.filters import AwardFilter -from main.models import Award, Footer, PageType, Panel, SiteFeature, Feature +from main.models import Award, Footer, PageType, Panel, SiteFeature, Feature, AwardType from main.views import SiteSettingsView, SiteListView @@ -29,6 +29,14 @@ class AwardRUDView(generics.RetrieveUpdateDestroyAPIView): lookup_field = 'id' +class AwardTypesListView(generics.ListAPIView): + """AwardType List view.""" + pagination_class = None + queryset = AwardType.objects.all() + serializer_class = serializers.AwardTypeBaseSerializer + permission_classes = (permissions.AllowAny, ) + + class ContentTypeView(generics.ListAPIView): """ContentType list view""" queryset = ContentType.objects.all() From 1d8d18d4de8fe97816d8db03c286ba38c0f29807 Mon Sep 17 00:00:00 2001 From: "a.gorbunov" Date: Thu, 23 Jan 2020 17:54:18 +0000 Subject: [PATCH 48/95] added order field --- apps/location/views/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/location/views/common.py b/apps/location/views/common.py index 963c0cae..d9968755 100644 --- a/apps/location/views/common.py +++ b/apps/location/views/common.py @@ -18,7 +18,7 @@ class CountryViewMixin(generics.GenericAPIView): class RegionViewMixin(generics.GenericAPIView): """View Mixin for model Region""" model = models.Region - queryset = models.Region.objects.all().order_by('name') + queryset = models.Region.objects.all().order_by('name', 'code') class CityViewMixin(generics.GenericAPIView): From d8d9a97746385b4f0732c3078616c626c87bbd6d Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 23 Jan 2020 21:02:32 +0300 Subject: [PATCH 49/95] remove award from employee --- apps/establishment/models.py | 6 ++++++ apps/establishment/urls/back.py | 1 + apps/establishment/views/back.py | 12 ++++++++++++ 3 files changed, 19 insertions(+) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 6332d97e..56537ebb 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -6,6 +6,7 @@ from typing import List import elasticsearch_dsl from django.conf import settings +from django.shortcuts import get_object_or_404 from django.contrib.contenttypes import fields as generic from django.contrib.gis.db.models.functions import Distance from django.contrib.gis.geos import Point @@ -1155,6 +1156,11 @@ class Employee(BaseAttributes): ) return image_property + def remove_award(self, award_id: int): + from main.models import Award + award = get_object_or_404(Award, pk=award_id) + self.awards.remove(award) + class EstablishmentScheduleQuerySet(models.QuerySet): """QuerySet for model EstablishmentSchedule""" diff --git a/apps/establishment/urls/back.py b/apps/establishment/urls/back.py index 00d44fc2..2de741c1 100644 --- a/apps/establishment/urls/back.py +++ b/apps/establishment/urls/back.py @@ -47,6 +47,7 @@ urlpatterns = [ path('employees/', views.EmployeeListCreateView.as_view(), name='employees'), path('employees/search/', views.EmployeesListSearchViews.as_view(), name='employees-search'), path('employees//', views.EmployeeRUDView.as_view(), name='employees-rud'), + path('employees//', views.RemoveAwardView.as_view(), name='employees-award-delete'), path('/employee//position/', views.EstablishmentEmployeeCreateView.as_view(), name='employees-establishment-create'), diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 04ba6581..6a7ccde8 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -218,6 +218,18 @@ class EmployeeRUDView(generics.RetrieveUpdateDestroyAPIView): queryset = models.Employee.objects.all().with_back_office_related() +class RemoveAwardView(generics.DestroyAPIView): + lookup_field = 'pk' + serializer_class = serializers.EmployeeBackSerializers + queryset = models.Employee.objects.all().with_back_office_related() + permission_classes = (permissions.AllowAny, ) + + def get_object(self): + employee = super().get_object() + employee.remove_award(self.kwargs['award_id']) + return employee + + class EstablishmentTypeListCreateView(generics.ListCreateAPIView): """Establishment type list/create view.""" serializer_class = serializers.EstablishmentTypeBaseSerializer From 34977db8053134d87880c40498dd86d680923872 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 23 Jan 2020 21:03:24 +0300 Subject: [PATCH 50/95] fix permission --- apps/establishment/views/back.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 6a7ccde8..d39ce6b2 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -222,7 +222,6 @@ class RemoveAwardView(generics.DestroyAPIView): lookup_field = 'pk' serializer_class = serializers.EmployeeBackSerializers queryset = models.Employee.objects.all().with_back_office_related() - permission_classes = (permissions.AllowAny, ) def get_object(self): employee = super().get_object() From 7c404df718d462eac942824877ad78d99ba9f2c7 Mon Sep 17 00:00:00 2001 From: dormantman Date: Thu, 23 Jan 2020 21:08:09 +0300 Subject: [PATCH 51/95] Added mobile method --- apps/advertisement/models.py | 6 ++---- apps/advertisement/urls/mobile.py | 1 + apps/advertisement/views/common.py | 4 ++++ apps/advertisement/views/mobile.py | 1 - 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/advertisement/models.py b/apps/advertisement/models.py index 5d335176..81b5dbb2 100644 --- a/apps/advertisement/models.py +++ b/apps/advertisement/models.py @@ -45,10 +45,8 @@ class Advertisement(ProjectBaseMixin): url = models.URLField(verbose_name=_('Ad URL')) block_level = models.CharField(verbose_name=_('Block level'), max_length=10, blank=True, null=True) target_languages = models.ManyToManyField(Language) - start = models.DateTimeField(null=True, - verbose_name=_('start')) - end = models.DateTimeField(blank=True, null=True, default=None, - verbose_name=_('end')) + start = models.DateTimeField(null=True, verbose_name=_('start')) + end = models.DateTimeField(blank=True, null=True, default=None, verbose_name=_('end')) sites = models.ManyToManyField('main.SiteSettings', related_name='advertisements', verbose_name=_('site')) diff --git a/apps/advertisement/urls/mobile.py b/apps/advertisement/urls/mobile.py index f61003da..350aecd3 100644 --- a/apps/advertisement/urls/mobile.py +++ b/apps/advertisement/urls/mobile.py @@ -8,6 +8,7 @@ from .common import common_urlpatterns app_name = 'advertisements' urlpatterns = [ + path('', views.AdvertisementPageTypeMobileListView.as_view(), name='list'), path('/', views.AdvertisementPageTypeMobileListView.as_view(), name='list'), ] diff --git a/apps/advertisement/views/common.py b/apps/advertisement/views/common.py index 12702d4b..08f739ef 100644 --- a/apps/advertisement/views/common.py +++ b/apps/advertisement/views/common.py @@ -24,6 +24,10 @@ class AdvertisementPageTypeListView(AdvertisementBaseView, generics.ListAPIView) def get_queryset(self): """Overridden get queryset method.""" product_type = self.kwargs.get('page_type') + + if product_type is None: + product_type = 'mobile' + qs = super(AdvertisementPageTypeListView, self).get_queryset() if product_type: return qs.by_page_type(product_type) \ diff --git a/apps/advertisement/views/mobile.py b/apps/advertisement/views/mobile.py index 7ae2f688..6fdb95bd 100644 --- a/apps/advertisement/views/mobile.py +++ b/apps/advertisement/views/mobile.py @@ -16,4 +16,3 @@ class AdvertisementPageTypeMobileListView(AdvertisementPageTypeListView): qs = super().get_queryset().exclude(frequency_percentage__lte=percentage) qs.update(views_count=F('views_count') + 1) return qs - From f52016bfee1eb23dc9ee41a8f4310dd5cde8ed95 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 23 Jan 2020 22:56:01 +0300 Subject: [PATCH 52/95] create award for employee and bind --- apps/main/serializers/common.py | 26 ++++++++++++++++++++++++++ apps/main/urls/back.py | 1 + apps/main/views/back.py | 7 +++++++ 3 files changed, 34 insertions(+) diff --git a/apps/main/serializers/common.py b/apps/main/serializers/common.py index 17477cb0..3976c92d 100644 --- a/apps/main/serializers/common.py +++ b/apps/main/serializers/common.py @@ -4,6 +4,7 @@ from rest_framework import serializers from location.serializers import CountrySerializer from main import models +from establishment.models import Employee from tag.serializers import TagBackOfficeSerializer from utils.serializers import ProjectModelSerializer, RecursiveFieldSerializer, TranslatedField @@ -241,6 +242,31 @@ class BackAwardSerializer(AwardBaseSerializer): ] +class BackAwardEmployeeCreateSerializer(serializers.ModelSerializer): + """Award, The Creator.""" + + award_type = serializers.PrimaryKeyRelatedField(required=True, queryset=models.AwardType.objects.all()) + title = serializers.CharField(write_only=True) + + def get_title(self, obj): + pass + + class Meta: + model = models.Award + fields = ( + 'id', + 'award_type', + 'title', + 'vintage_year', + ) + + def validate(self, attrs): + attrs['object_id'] = self.context.get('request').parser_context.get('kwargs')['employee_id'] + attrs['content_type'] = ContentType.objects.get_for_model(Employee) + attrs['title'] = {self.context.get('request').locale: attrs['title']} + return attrs + + class CarouselListSerializer(serializers.ModelSerializer): """Serializer for retrieving list of carousel items.""" diff --git a/apps/main/urls/back.py b/apps/main/urls/back.py index c600a45f..8b8f5102 100644 --- a/apps/main/urls/back.py +++ b/apps/main/urls/back.py @@ -8,6 +8,7 @@ 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/create-and-bind//', views.AwardCreateAndBind.as_view(), name='award-employee-create'), path('award-types/', views.AwardTypesListView.as_view(), name='awards-types-list'), path('content_type/', views.ContentTypeView.as_view(), name='content_type-list'), path('sites/', views.SiteListBackOfficeView.as_view(), name='site-list-create'), diff --git a/apps/main/views/back.py b/apps/main/views/back.py index ece392aa..28295c0e 100644 --- a/apps/main/views/back.py +++ b/apps/main/views/back.py @@ -21,6 +21,13 @@ class AwardLstView(generics.ListCreateAPIView): filterset_class = AwardFilter +class AwardCreateAndBind(generics.CreateAPIView): + """Award create and bind to employee by id""" + queryset = Award.objects.all().with_base_related() + serializer_class = serializers.BackAwardEmployeeCreateSerializer + permission_classes = (permissions.IsAdminUser, ) + + class AwardRUDView(generics.RetrieveUpdateDestroyAPIView): """Award RUD view.""" queryset = Award.objects.all().with_base_related() From 0f053dff32d95218acaa71b97eef12060c9bb024 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 23 Jan 2020 23:24:03 +0300 Subject: [PATCH 53/95] return employee after binding --- apps/main/views/back.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/main/views/back.py b/apps/main/views/back.py index 28295c0e..23fdc48a 100644 --- a/apps/main/views/back.py +++ b/apps/main/views/back.py @@ -7,6 +7,8 @@ from rest_framework.response import Response from main import serializers from main.serializers.back import PanelSerializer +from establishment.serializers.back import EmployeeBackSerializers +from establishment.models import Employee from main import tasks from main.filters import AwardFilter from main.models import Award, Footer, PageType, Panel, SiteFeature, Feature, AwardType @@ -27,6 +29,15 @@ class AwardCreateAndBind(generics.CreateAPIView): serializer_class = serializers.BackAwardEmployeeCreateSerializer permission_classes = (permissions.IsAdminUser, ) + def create(self, request, *args, **kwargs): + """!!!Overriden!!!""" + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + self.perform_create(serializer) + response_serializer = EmployeeBackSerializers(Employee.objects.get(pk=kwargs['employee_id'])) + headers = self.get_success_headers(response_serializer.data) + return Response(response_serializer.data, status=status.HTTP_201_CREATED, headers=headers) + class AwardRUDView(generics.RetrieveUpdateDestroyAPIView): """Award RUD view.""" From 9b6918b3b051d8176789f43dfd4b27e52225a1f2 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 24 Jan 2020 08:59:11 +0300 Subject: [PATCH 54/95] menu old_id for transfer --- .../migrations/0078_menu_old_id.py | 18 ++++++++++++++++++ apps/establishment/models.py | 1 + 2 files changed, 19 insertions(+) create mode 100644 apps/establishment/migrations/0078_menu_old_id.py diff --git a/apps/establishment/migrations/0078_menu_old_id.py b/apps/establishment/migrations/0078_menu_old_id.py new file mode 100644 index 00000000..9401741e --- /dev/null +++ b/apps/establishment/migrations/0078_menu_old_id.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.7 on 2020-01-24 05:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0077_menuuploads'), + ] + + operations = [ + migrations.AddField( + model_name='menu', + name='old_id', + field=models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='old id'), + ), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 56537ebb..77f90c23 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -1243,6 +1243,7 @@ class Menu(TranslatedFieldsMixin, BaseAttributes): verbose_name=_('Establishment schedule'), related_name='menus', ) + old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None) class Meta: verbose_name = _('menu') From 792c11e1286adfefdab58905f893dd88283f524d Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 24 Jan 2020 09:20:22 +0300 Subject: [PATCH 55/95] transfer formulas menu --- apps/establishment/management/commands/add_menus.py | 8 ++++++-- make_data_migration.sh | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/establishment/management/commands/add_menus.py b/apps/establishment/management/commands/add_menus.py index 3875b8ca..8692094b 100644 --- a/apps/establishment/management/commands/add_menus.py +++ b/apps/establishment/management/commands/add_menus.py @@ -1,4 +1,5 @@ from django.core.management.base import BaseCommand +from tqdm import tqdm from establishment.models import Establishment, Menu, Plate from transfer.models import Menus @@ -10,14 +11,17 @@ class Command(BaseCommand): def handle(self, *args, **kwargs): count = 0 menus = Menus.objects.filter(name__isnull=False).exclude(name='') - for old_menu in menus: + for old_menu in tqdm(menus, desc='Add formulas menu'): est = Establishment.objects.filter( old_id=old_menu.establishment_id).first() if est: menu, _ = Menu.objects.get_or_create( category={'en-GB': 'formulas'}, - establishment=est + establishment=est, + old_id=old_menu.id, + is_drinks_included=True if old_menu.drinks == 'included' else False, + created=old_menu.created_at, ) plate, created = Plate.objects.get_or_create( name={"en-GB": old_menu.name}, diff --git a/make_data_migration.sh b/make_data_migration.sh index d5edc793..6de3a713 100755 --- a/make_data_migration.sh +++ b/make_data_migration.sh @@ -37,4 +37,8 @@ ./manage.py add_employee ./manage.py add_position ./manage.py add_empl_position -./manage.py update_employee \ No newline at end of file +./manage.py update_employee + +# меню из Dishes(dessert, main_course, starter) и Menus(formulas) +./manage.py transfer --menu +./manage.py add_menus \ No newline at end of file From 29a7fb82ce6cf5d9165b8c5588b1db231ae35ef3 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 24 Jan 2020 10:21:35 +0300 Subject: [PATCH 56/95] dishes menu list-create --- apps/establishment/filters.py | 24 ++++++++++++++ .../migrations/0079_auto_20200124_0720.py | 23 +++++++++++++ apps/establishment/models.py | 30 ++++++++++++++++- apps/establishment/serializers/back.py | 33 ++++++++++++++++++- apps/establishment/serializers/common.py | 1 + apps/establishment/urls/back.py | 1 + apps/establishment/views/back.py | 10 ++++-- 7 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 apps/establishment/migrations/0079_auto_20200124_0720.py diff --git a/apps/establishment/filters.py b/apps/establishment/filters.py index 37d15dfc..b52c909f 100644 --- a/apps/establishment/filters.py +++ b/apps/establishment/filters.py @@ -126,3 +126,27 @@ class EmployeeBackSearchFilter(EmployeeBackFilter): raise ValidationError({'detail': _('Type at least 3 characters to search please.')}) return queryset.trigram_search(value) return queryset + + +class MenuDishesBackFilter(filters.FilterSet): + """Menu filter set.""" + + category = filters.CharFilter(method='search_by_category') + is_drinks_included = filters.BooleanFilter(field_name='is_drinks_included') + establishment_id = filters.NumberFilter(field_name='establishment_id') + + class Meta: + """Meta class.""" + + model = models.Menu + fields = ( + 'category', + 'is_drinks_included', + 'establishment_id', + ) + + def search_by_category(self, queryset, name, value): + """Search by category.""" + if value not in EMPTY_VALUES: + return queryset.search_by_category(value) + return queryset diff --git a/apps/establishment/migrations/0079_auto_20200124_0720.py b/apps/establishment/migrations/0079_auto_20200124_0720.py new file mode 100644 index 00000000..01c4e509 --- /dev/null +++ b/apps/establishment/migrations/0079_auto_20200124_0720.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.7 on 2020-01-24 07:20 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('establishment', '0078_menu_old_id'), + ] + + operations = [ + migrations.AlterModelOptions( + name='menu', + options={'ordering': ('-created',), 'verbose_name': 'menu', 'verbose_name_plural': 'menu'}, + ), + migrations.AlterField( + model_name='plate', + name='menu', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='plates', to='establishment.Menu', verbose_name='menu'), + ), + ] diff --git a/apps/establishment/models.py b/apps/establishment/models.py index 77f90c23..bae62a1a 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -1214,7 +1214,11 @@ class Plate(TranslatedFieldsMixin, models.Model): _('currency code'), max_length=250, blank=True, null=True, default=None) menu = models.ForeignKey( - 'establishment.Menu', verbose_name=_('menu'), on_delete=models.CASCADE) + 'establishment.Menu', + verbose_name=_('menu'), + related_name='plates', + on_delete=models.CASCADE, + ) @property def establishment_id(self): @@ -1225,6 +1229,27 @@ class Plate(TranslatedFieldsMixin, models.Model): verbose_name_plural = _('plates') +class MenuQuerySet(models.QuerySet): + def with_schedule_plates_establishment(self): + return self.select_related( + 'establishment', + ).prefetch_related( + 'schedule', + 'plates', + ) + + def dishes(self): + return self.filter( + Q(category__icontains='starter') | + Q(category__icontains='dessert') | + Q(category__icontains='main_course') + ) + + def search_by_category(self, value): + """Search by category.""" + return self.filter(category__icontains=value) + + class Menu(TranslatedFieldsMixin, BaseAttributes): """Menu model.""" @@ -1245,9 +1270,12 @@ class Menu(TranslatedFieldsMixin, BaseAttributes): ) old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None) + objects = MenuQuerySet.as_manager() + class Meta: verbose_name = _('menu') verbose_name_plural = _('menu') + ordering = ('-created',) class MenuUploads(BaseAttributes): diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 4000bef9..147dd1c5 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -12,8 +12,9 @@ from location.models import Address from location.serializers import AddressDetailSerializer, TranslatedField from main.models import Currency from main.serializers import AwardSerializer +from timetable.serialziers import ScheduleRUDSerializer from utils.decorators import with_base_attributes -from utils.serializers import ImageBaseSerializer, TimeZoneChoiceField +from utils.serializers import ImageBaseSerializer, TimeZoneChoiceField, ProjectModelSerializer def phones_handler(phones_list, establishment): @@ -513,3 +514,33 @@ class EstablishmentAdminListSerializer(UserShortSerializer): 'username', 'email' ] + + +class _PlateSerializer(ProjectModelSerializer): + name_translated = TranslatedField() + + class Meta: + model = models.Plate + fields = [ + 'name_translated', + 'price', + ] + + +class MenuDishesSerializer(ProjectModelSerializer): + """for dessert, main_course and starter category""" + + schedule = ScheduleRUDSerializer(many=True, allow_null=True) + plates = _PlateSerializer(read_only=True, many=True, source='plate_set') + category_translated = serializers.CharField(read_only=True) + + class Meta: + model = models.Menu + fields = [ + 'id', + 'category', + 'category_translated', + 'establishment', + 'schedule', + 'plates', + ] diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py index 066452a7..cd60e9f1 100644 --- a/apps/establishment/serializers/common.py +++ b/apps/establishment/serializers/common.py @@ -24,6 +24,7 @@ from utils.serializers import (ProjectModelSerializer, TranslatedField, logger = logging.getLogger(__name__) + class ContactPhonesSerializer(serializers.ModelSerializer): """Contact phone serializer""" diff --git a/apps/establishment/urls/back.py b/apps/establishment/urls/back.py index 2de741c1..6899d1f0 100644 --- a/apps/establishment/urls/back.py +++ b/apps/establishment/urls/back.py @@ -30,6 +30,7 @@ urlpatterns = [ name='note-rud'), path('slug//admin/', views.EstablishmentAdminView.as_view(), name='establishment-admin-list'), + path('menus/dishes/', views.MenuDishesListCreateView.as_view(), name='menu-dishes-list'), path('menus/', views.MenuListCreateView.as_view(), name='menu-list'), path('menus//', views.MenuRUDView.as_view(), name='menu-rud'), path('plates/', views.PlateListCreateView.as_view(), name='plates'), diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index d39ce6b2..2610d967 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -1,6 +1,4 @@ """Establishment app views.""" -from django.core.exceptions import ObjectDoesNotExist -from django.http import Http404 from django.shortcuts import get_object_or_404 from django_filters.rest_framework import DjangoFilterBackend from rest_framework import generics, permissions @@ -417,3 +415,11 @@ class EstablishmentAdminView(generics.ListAPIView): establishment = get_object_or_404( models.Establishment, slug=self.kwargs['slug']) return User.objects.establishment_admin(establishment).distinct() + + +class MenuDishesListCreateView(generics.ListCreateAPIView): + """Menu (dessert, main_course, starter) list create view.""" + serializer_class = serializers.MenuDishesSerializer + queryset = models.Menu.objects.with_schedule_plates_establishment().dishes().distinct() + permission_classes = [IsWineryReviewer | IsEstablishmentManager] + filter_class = filters.MenuDishesBackFilter From 22b1b3064d938ef9608eb1cbcf28ed409dbc71e0 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 24 Jan 2020 10:31:07 +0300 Subject: [PATCH 57/95] is_drinks_included and last_update in serializer --- apps/establishment/serializers/back.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 147dd1c5..509d2557 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -533,6 +533,7 @@ class MenuDishesSerializer(ProjectModelSerializer): schedule = ScheduleRUDSerializer(many=True, allow_null=True) plates = _PlateSerializer(read_only=True, many=True, source='plate_set') category_translated = serializers.CharField(read_only=True) + last_update = serializers.DateTimeField(source='created') class Meta: model = models.Menu @@ -541,6 +542,8 @@ class MenuDishesSerializer(ProjectModelSerializer): 'category', 'category_translated', 'establishment', + 'is_drinks_included', 'schedule', 'plates', + 'last_update', ] From a195ed3a5374c60b5c6a626d831c6bcbec2164d4 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 24 Jan 2020 10:35:55 +0300 Subject: [PATCH 58/95] menu dishes rud --- apps/establishment/serializers/back.py | 16 ++++++++++++++++ apps/establishment/urls/back.py | 1 + apps/establishment/views/back.py | 7 +++++++ 3 files changed, 24 insertions(+) diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 509d2557..7cbc1279 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -547,3 +547,19 @@ class MenuDishesSerializer(ProjectModelSerializer): 'plates', 'last_update', ] + + +class MenuDishesRUDSerializers(ProjectModelSerializer): + """for dessert, main_course and starter category""" + + plates = _PlateSerializer(read_only=True, many=True, source='plate_set') + + class Meta: + model = models.Menu + fields = [ + 'id', + 'category', + 'plates', + 'establishment', + 'is_drinks_included', + ] diff --git a/apps/establishment/urls/back.py b/apps/establishment/urls/back.py index 6899d1f0..3129a060 100644 --- a/apps/establishment/urls/back.py +++ b/apps/establishment/urls/back.py @@ -31,6 +31,7 @@ urlpatterns = [ path('slug//admin/', views.EstablishmentAdminView.as_view(), name='establishment-admin-list'), path('menus/dishes/', views.MenuDishesListCreateView.as_view(), name='menu-dishes-list'), + path('menus/dishes//', views.MenuDishesRUDView.as_view(), name='menu-dishes-rud'), path('menus/', views.MenuListCreateView.as_view(), name='menu-list'), path('menus//', views.MenuRUDView.as_view(), name='menu-rud'), path('plates/', views.PlateListCreateView.as_view(), name='plates'), diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 2610d967..65aacbb2 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -423,3 +423,10 @@ class MenuDishesListCreateView(generics.ListCreateAPIView): queryset = models.Menu.objects.with_schedule_plates_establishment().dishes().distinct() permission_classes = [IsWineryReviewer | IsEstablishmentManager] filter_class = filters.MenuDishesBackFilter + + +class MenuDishesRUDView(generics.RetrieveUpdateDestroyAPIView): + """Menu (dessert, main_course, starter) RUD view.""" + serializer_class = serializers.MenuDishesRUDSerializers + queryset = models.Menu.objects.dishes().distinct() + permission_classes = [IsWineryReviewer | IsEstablishmentManager] From 28718d17d109ef45baa7f6831928f62975bdf8de Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 24 Jan 2020 13:02:22 +0300 Subject: [PATCH 59/95] aspect for crop --- apps/gallery/serializers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/gallery/serializers.py b/apps/gallery/serializers.py index fb5bfa26..c777689c 100644 --- a/apps/gallery/serializers.py +++ b/apps/gallery/serializers.py @@ -98,7 +98,8 @@ class CropImageSerializer(ImageSerializer): x1, y1 = int(crop.split(' ')[0][:-2]), int(crop.split(' ')[1][:-2]) x2, y2 = x1 + width, y1 + height crop_params = { - 'geometry': f'{round(x2 - x1)}x{round(y2 - y1)}', + 'geometry': f'{round(x2 - x1)}x{round(y2 - y1)}' if 'certain_aspect' not in validated_data else + validated_data['certain_aspect'], 'quality': 100, 'cropbox': f'{x1},{y1},{x2},{y2}' } From 3f3848e317a9447f9423cbd1fb196621a6bf090b Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 24 Jan 2020 13:23:54 +0300 Subject: [PATCH 60/95] fix crop --- apps/gallery/serializers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/gallery/serializers.py b/apps/gallery/serializers.py index c777689c..f7d2d2a3 100644 --- a/apps/gallery/serializers.py +++ b/apps/gallery/serializers.py @@ -59,6 +59,7 @@ class CropImageSerializer(ImageSerializer): MinValueValidator(1), MaxValueValidator(100)]) cropped_image = serializers.DictField(read_only=True, allow_null=True) + certain_aspect = serializers.CharField(allow_blank=True, allow_null=True, required=False) class Meta(ImageSerializer.Meta): """Meta class.""" @@ -71,6 +72,7 @@ class CropImageSerializer(ImageSerializer): 'crop', 'quality', 'cropped_image', + 'certain_aspect', ] def validate(self, attrs): From 34055d36695e56febe31f22ebf703f4390bb5d3e Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 24 Jan 2020 13:31:00 +0300 Subject: [PATCH 61/95] fix plate_set --- apps/establishment/models.py | 4 ++-- apps/establishment/serializers/back.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index bae62a1a..fb56b8b2 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -142,8 +142,8 @@ class EstablishmentQuerySet(models.QuerySet): def with_extended_related(self): return self.with_extended_address_related().select_related('establishment_type'). \ prefetch_related('establishment_subtypes', 'awards', 'schedule', - 'phones', 'gallery', 'menu_set', 'menu_set__plate_set', - 'menu_set__plate_set__currency', 'currency'). \ + 'phones', 'gallery', 'menu_set', 'menu_set__plates', + 'menu_set__plates__currency', 'currency'). \ prefetch_actual_employees() def with_type_related(self): diff --git a/apps/establishment/serializers/back.py b/apps/establishment/serializers/back.py index 7cbc1279..5991a1fd 100644 --- a/apps/establishment/serializers/back.py +++ b/apps/establishment/serializers/back.py @@ -531,7 +531,7 @@ class MenuDishesSerializer(ProjectModelSerializer): """for dessert, main_course and starter category""" schedule = ScheduleRUDSerializer(many=True, allow_null=True) - plates = _PlateSerializer(read_only=True, many=True, source='plate_set') + plates = _PlateSerializer(read_only=True, many=True, source='plate_set', allow_null=True) category_translated = serializers.CharField(read_only=True) last_update = serializers.DateTimeField(source='created') @@ -552,7 +552,8 @@ class MenuDishesSerializer(ProjectModelSerializer): class MenuDishesRUDSerializers(ProjectModelSerializer): """for dessert, main_course and starter category""" - plates = _PlateSerializer(read_only=True, many=True, source='plate_set') + plates = _PlateSerializer(read_only=True, many=True, source='plate_set', allow_null=True) + schedule = ScheduleRUDSerializer(read_only=True, many=True, allow_null=True) class Meta: model = models.Menu @@ -562,4 +563,5 @@ class MenuDishesRUDSerializers(ProjectModelSerializer): 'plates', 'establishment', 'is_drinks_included', + 'schedule', ] From a2f537a5accb30a0ce518facfb689b56b90719be Mon Sep 17 00:00:00 2001 From: Ruslan Stepanov Date: Fri, 24 Jan 2020 11:59:13 +0000 Subject: [PATCH 62/95] Update content-pages --- apps/main/views/common.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/main/views/common.py b/apps/main/views/common.py index 03a0ad72..3a77055a 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -107,7 +107,10 @@ class ContentPageBaseView(generics.GenericAPIView): return 'static' def get_queryset(self): - return super().get_queryset().filter(news_type__name=self.static_page_category) + return News.objects.with_base_related() \ + .order_by('-is_highlighted', '-publication_date', '-publication_time') \ + .filter(news_type__name=self.static_page_category) + class ContentPageView(ContentPageBaseView, generics.ListAPIView): From a82d6685a97d33bbe93889614d5976c83518bbc7 Mon Sep 17 00:00:00 2001 From: dormantman Date: Fri, 24 Jan 2020 15:08:42 +0300 Subject: [PATCH 63/95] Fix news content-pages mixin --- apps/main/views/common.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/main/views/common.py b/apps/main/views/common.py index 3a77055a..2670dd50 100644 --- a/apps/main/views/common.py +++ b/apps/main/views/common.py @@ -112,7 +112,6 @@ class ContentPageBaseView(generics.GenericAPIView): .filter(news_type__name=self.static_page_category) - class ContentPageView(ContentPageBaseView, generics.ListAPIView): """Method to get content pages""" @@ -129,7 +128,7 @@ class ContentPageAdminView(ContentPageBaseView, generics.ListCreateAPIView): queryset = News.objects.all() -class ContentPageRetrieveView(NewsMixinView, ContentPageBaseView, generics.RetrieveAPIView): +class ContentPageRetrieveView(ContentPageBaseView, NewsMixinView, generics.RetrieveAPIView): """Retrieve method to get content pages""" lookup_field = None @@ -146,7 +145,7 @@ class ContentPageIdRetrieveView(ContentPageBaseView, generics.RetrieveAPIView): queryset = News.objects.all() -class ContentPageRetrieveAdminView(NewsMixinView, ContentPageBaseView, generics.RetrieveUpdateDestroyAPIView): +class ContentPageRetrieveAdminView(ContentPageBaseView, NewsMixinView, generics.RetrieveUpdateDestroyAPIView): """Retrieve method to get content pages""" lookup_field = None From 25488ca2fb1397d19bb133e67a0854b96b041e9b Mon Sep 17 00:00:00 2001 From: dormantman Date: Fri, 24 Jan 2020 15:38:34 +0300 Subject: [PATCH 64/95] Added new mobile route --- apps/advertisement/urls/web.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/advertisement/urls/web.py b/apps/advertisement/urls/web.py index 4d17c831..5cc1f2a5 100644 --- a/apps/advertisement/urls/web.py +++ b/apps/advertisement/urls/web.py @@ -7,6 +7,7 @@ from .common import common_urlpatterns app_name = 'advertisements' urlpatterns = [ + path('', views.AdvertisementPageTypeWebListView.as_view(), name='list'), path('/', views.AdvertisementPageTypeWebListView.as_view(), name='list'), ] From 5353c5a67c20af11daca28bbf850e5131da62192 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 24 Jan 2020 15:44:05 +0300 Subject: [PATCH 65/95] fix award unbind --- apps/establishment/views/back.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 65aacbb2..723d3844 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -1,7 +1,9 @@ """Establishment app views.""" + from django.shortcuts import get_object_or_404 from django_filters.rest_framework import DjangoFilterBackend -from rest_framework import generics, permissions +from rest_framework import generics, permissions, status +from rest_framework.response import Response from account.models import User from establishment import filters, models, serializers @@ -226,6 +228,10 @@ class RemoveAwardView(generics.DestroyAPIView): employee.remove_award(self.kwargs['award_id']) return employee + def delete(self, request, *args, **kwargs): + instance = self.get_object() + return Response(status=status.HTTP_204_NO_CONTENT) + class EstablishmentTypeListCreateView(generics.ListCreateAPIView): """Establishment type list/create view.""" From cd0317d94dfa8b5d73dcd8943207f5f9e59c2357 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 24 Jan 2020 15:48:02 +0300 Subject: [PATCH 66/95] image for city is not longer required field --- apps/location/serializers/common.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/location/serializers/common.py b/apps/location/serializers/common.py index ad60a7a2..1da144f8 100644 --- a/apps/location/serializers/common.py +++ b/apps/location/serializers/common.py @@ -87,7 +87,8 @@ class CityBaseSerializer(serializers.ModelSerializer): image_id = serializers.PrimaryKeyRelatedField( source='image', queryset=gallery_models.Image.objects.all(), - write_only=True + write_only=True, + required=False, ) country = CountrySerializer(read_only=True) From ec351bf12aedb0acf4364b0ceaaa63e15fd6baa7 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 24 Jan 2020 16:44:16 +0300 Subject: [PATCH 67/95] fix plate --- apps/establishment/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/establishment/models.py b/apps/establishment/models.py index fb56b8b2..5f575c78 100644 --- a/apps/establishment/models.py +++ b/apps/establishment/models.py @@ -690,8 +690,8 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, plates = self.menu_set.filter( models.Q(category={'en-GB': 'formulas'}) ).aggregate( - max=models.Max('plate__price', output_field=models.FloatField()), - min=models.Min('plate__price', output_field=models.FloatField())) + max=models.Max('plates__price', output_field=models.FloatField()), + min=models.Min('plates__price', output_field=models.FloatField())) return plates @property @@ -701,8 +701,8 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin, models.Q(category={'en-GB': 'main_course'}) | models.Q(category={'en-GB': 'dessert'}) ).aggregate( - max=models.Max('plate__price', output_field=models.FloatField()), - min=models.Min('plate__price', output_field=models.FloatField()), + max=models.Max('plates__price', output_field=models.FloatField()), + min=models.Min('plates__price', output_field=models.FloatField()), ) return plates From 3254573897f660393d3b85d6cb492803b0bc2951 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 24 Jan 2020 16:50:49 +0300 Subject: [PATCH 68/95] country name translated for roles --- apps/account/serializers/common.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/account/serializers/common.py b/apps/account/serializers/common.py index 1673bef9..9ca3caf0 100644 --- a/apps/account/serializers/common.py +++ b/apps/account/serializers/common.py @@ -45,6 +45,7 @@ class RoleBaseSerializer(serializers.ModelSerializer): role_display = serializers.CharField(source='get_role_display', read_only=True) navigation_bar_permission = NavigationBarPermissionBaseSerializer(read_only=True) country_code = serializers.CharField(source='country.code', read_only=True, allow_null=True) + country_name_translated = serializers.CharField(source='country.name_translated', read_only=True, allow_null=True) class Meta: """Meta class.""" @@ -54,6 +55,7 @@ class RoleBaseSerializer(serializers.ModelSerializer): 'role_display', 'navigation_bar_permission', 'country_code', + 'country_name_translated', ] From 62ab6248ba9544b3c67c8c70e7ae737d9e988ffa Mon Sep 17 00:00:00 2001 From: dormantman Date: Fri, 24 Jan 2020 17:41:43 +0300 Subject: [PATCH 69/95] Fix subscribes --- .../migrations/0011_auto_20200124_1351.py | 39 +++++++++++++++++++ apps/notification/models.py | 31 ++++++++++++--- apps/notification/serializers/common.py | 11 ++++++ apps/notification/tasks.py | 1 - apps/notification/views/common.py | 2 +- 5 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 apps/notification/migrations/0011_auto_20200124_1351.py diff --git a/apps/notification/migrations/0011_auto_20200124_1351.py b/apps/notification/migrations/0011_auto_20200124_1351.py new file mode 100644 index 00000000..5b86c7ec --- /dev/null +++ b/apps/notification/migrations/0011_auto_20200124_1351.py @@ -0,0 +1,39 @@ +# Generated by Django 2.2.7 on 2020-01-24 13:51 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('notification', '0010_auto_20191231_0135'), + ] + + operations = [ + migrations.AddField( + model_name='subscribe', + name='old_subscriber_id', + field=models.PositiveIntegerField(null=True, verbose_name='Old subscriber id'), + ), + migrations.AddField( + model_name='subscribe', + name='old_subscription_type_id', + field=models.PositiveIntegerField(null=True, verbose_name='Old subscription type id'), + ), + migrations.AlterField( + model_name='subscribe', + name='subscribe_date', + field=models.DateTimeField(blank=True, default=None, null=True, verbose_name='Last subscribe date'), + ), + migrations.AlterField( + model_name='subscribe', + name='subscriber', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='notification.Subscriber'), + ), + migrations.AlterField( + model_name='subscribe', + name='subscription_type', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='notification.SubscriptionType'), + ), + ] diff --git a/apps/notification/models.py b/apps/notification/models.py index e0855c62..6f1ab33c 100644 --- a/apps/notification/models.py +++ b/apps/notification/models.py @@ -2,6 +2,7 @@ from django.conf import settings from django.db import models +from django.db.models import F from django.utils.timezone import now from django.utils.translation import ugettext_lazy as _ @@ -10,6 +11,7 @@ from location.models import Country from notification.tasks import send_unsubscribe_email from utils.methods import generate_string_code from utils.models import ProjectBaseMixin, TJSONField, TranslatedFieldsMixin +from notification.tasks import send_unsubscribe_email class SubscriptionType(ProjectBaseMixin, TranslatedFieldsMixin): @@ -133,7 +135,12 @@ class Subscriber(ProjectBaseMixin): def unsubscribe(self, query: dict): """Unsubscribe user.""" - self.subscribe_set.update(unsubscribe_date=now()) + + self.subscribe_set.update( + unsunbscribe_date=now(), + old_subscriber_id=F('subscriber_id'), + old_subscription_type_id=F('subscription_type_id') + ).update(subscriber_id=None, subscription_type_id=None) if settings.USE_CELERY: send_unsubscribe_email.delay(self.email) @@ -166,15 +173,20 @@ class SubscribeQuerySet(models.QuerySet): """Fetches active subscriptions.""" return self.exclude(unsubscribe_date__isnull=not switcher) + def subscription_history(self, subscriber_id: int): + return self.filter(old_subscriber_id=subscriber_id) + class Subscribe(ProjectBaseMixin): """Subscribe model.""" - - subscribe_date = models.DateTimeField(_('Last subscribe date'), blank=True, null=True, default=now) + subscribe_date = models.DateTimeField(_('Last subscribe date'), default=now(), blank=True, null=True) unsubscribe_date = models.DateTimeField(_('Last unsubscribe date'), blank=True, null=True, default=None) - subscriber = models.ForeignKey(Subscriber, on_delete=models.CASCADE) - subscription_type = models.ForeignKey(SubscriptionType, on_delete=models.CASCADE) + subscriber = models.ForeignKey(Subscriber, on_delete=models.CASCADE, null=True) + subscription_type = models.ForeignKey(SubscriptionType, on_delete=models.CASCADE, null=True) + + old_subscriber_id = models.PositiveIntegerField(_("Old subscriber id"), null=True) + old_subscription_type_id = models.PositiveIntegerField(_("Old subscription type id"), null=True) objects = SubscribeQuerySet.as_manager() @@ -183,3 +195,12 @@ class Subscribe(ProjectBaseMixin): verbose_name = _('Subscribe') verbose_name_plural = _('Subscribes') + + def save(self, *args, **kwargs): + if self.subscriber is not None: + self.old_subscriber_id = self.subscriber.pk + + if self.subscription_type is not None: + self.old_subscription_type_id = self.subscription_type.pk + + return super().save(*args, **kwargs) diff --git a/apps/notification/serializers/common.py b/apps/notification/serializers/common.py index 869879cb..48caee48 100644 --- a/apps/notification/serializers/common.py +++ b/apps/notification/serializers/common.py @@ -166,3 +166,14 @@ class SubscribeSerializer(serializers.ModelSerializer): 'subscription_types', 'link_to_unsubscribe', ) + + +class UnsubscribeSerializer(serializers.ModelSerializer): + email = serializers.EmailField(read_only=True, required=False, source='send_to') + subscription_types = SubscriptionTypeSerializer(source='active_subscriptions', read_only=True, many=True) + + class Meta: + """Meta class.""" + + model = models.Subscriber + fields = SubscribeSerializer.Meta.fields diff --git a/apps/notification/tasks.py b/apps/notification/tasks.py index 790c9650..3f9f7249 100644 --- a/apps/notification/tasks.py +++ b/apps/notification/tasks.py @@ -3,7 +3,6 @@ from datetime import datetime from celery import shared_task from django.conf import settings from django.core.mail import send_mail -from django.utils.translation import gettext_lazy as _ from django.template.loader import get_template, render_to_string from main.models import SiteSettings diff --git a/apps/notification/views/common.py b/apps/notification/views/common.py index b6584394..bb6c9d6b 100644 --- a/apps/notification/views/common.py +++ b/apps/notification/views/common.py @@ -70,7 +70,7 @@ class UnsubscribeView(generics.UpdateAPIView): queryset = models.Subscriber.objects.all() serializer_class = serializers.SubscribeSerializer - def patch(self, request, *args, **kw): + def put(self, request, *args, **kw): obj = self.get_object() obj.unsubscribe(request.query_params) serializer = self.get_serializer(instance=obj) From 36c4641ed0db224c33c6cc8d2b0e96f9dfb77732 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 24 Jan 2020 17:49:34 +0300 Subject: [PATCH 70/95] fix plates transfer --- apps/transfer/serializers/product.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/transfer/serializers/product.py b/apps/transfer/serializers/product.py index dddc1bcc..a28217d0 100644 --- a/apps/transfer/serializers/product.py +++ b/apps/transfer/serializers/product.py @@ -12,6 +12,7 @@ from django.conf import settings from functools import reduce from gallery.models import Image from translation.models import SiteInterfaceDictionary +from main.models import SiteSettings class WineColorSerializer(TransferSerializerMixin): @@ -496,12 +497,14 @@ class PlateSerializer(TransferSerializerMixin): id = serializers.IntegerField() name = serializers.CharField() vintage = serializers.CharField() + site_id = serializers.IntegerField() class Meta(ProductSerializer.Meta): fields = ( 'id', 'name', 'vintage', + 'site_id', ) def validate(self, attrs): @@ -513,9 +516,9 @@ class PlateSerializer(TransferSerializerMixin): attrs['vintage'] = self.get_vintage_year(attrs.pop('vintage')) attrs['product_type'] = product_type attrs['state'] = self.Meta.model.PUBLISHED - attrs['subtype'] = self.get_product_sub_type(product_type, - self.PRODUCT_SUB_TYPE_INDEX_NAME) + attrs['subtype'] = self.get_product_sub_type(product_type, self.PRODUCT_SUB_TYPE_INDEX_NAME) attrs['slug'] = self.get_slug(name, old_id) + attrs['site'] = self.get_site(attrs.pop('site_id', None)) return attrs def create(self, validated_data): @@ -532,6 +535,12 @@ class PlateSerializer(TransferSerializerMixin): obj.subtypes.add(*[i for i in subtypes if i]) return obj + def get_site(self, old_id: int): + if old_id: + site_qs = SiteSettings.objects.filter(old_id=old_id) + if site_qs.exists(): + return site_qs.first() + class PlateImageSerializer(TransferSerializerMixin): From 5a1d33c981d700fe3c281d791e8d0dcfd24d8a66 Mon Sep 17 00:00:00 2001 From: dormantman Date: Fri, 24 Jan 2020 17:52:47 +0300 Subject: [PATCH 71/95] Remove save --- apps/notification/models.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/apps/notification/models.py b/apps/notification/models.py index 6f1ab33c..adcb4fab 100644 --- a/apps/notification/models.py +++ b/apps/notification/models.py @@ -195,12 +195,3 @@ class Subscribe(ProjectBaseMixin): verbose_name = _('Subscribe') verbose_name_plural = _('Subscribes') - - def save(self, *args, **kwargs): - if self.subscriber is not None: - self.old_subscriber_id = self.subscriber.pk - - if self.subscription_type is not None: - self.old_subscription_type_id = self.subscription_type.pk - - return super().save(*args, **kwargs) From c82362b268fde5b7f28203e5932599ed61867e02 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 24 Jan 2020 17:54:30 +0300 Subject: [PATCH 72/95] should_read same country --- apps/news/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/news/models.py b/apps/news/models.py index 89dacf5a..2316fc27 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -136,6 +136,7 @@ class NewsQuerySet(TranslationQuerysetMixin): def should_read(self, news, user): return self.model.objects.exclude(pk=news.pk).published(). \ annotate_in_favorites(user). \ + filter(country=news.country). \ with_base_related().by_type(news.news_type).distinct().order_by('?') def same_theme(self, news, user): From 0f73cf94779d414858fd545c2258d5b66f1fa624 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 24 Jan 2020 18:08:51 +0300 Subject: [PATCH 73/95] fix news agenda --- 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 564cd2c9..6e9781db 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -207,7 +207,7 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): """News back office base serializer.""" is_published = serializers.BooleanField(source='is_publish', read_only=True) descriptions = serializers.ListField(required=False) - agenda = AgendaSerializer() + agenda = AgendaSerializer(required=False, allow_null=True) class Meta(NewsBaseSerializer.Meta): """Meta class.""" From 0ad1408f18029e1bb4b613abf6d80912366f63e1 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Fri, 24 Jan 2020 18:12:53 +0300 Subject: [PATCH 74/95] added account removal action --- apps/account/views/common.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/account/views/common.py b/apps/account/views/common.py index 8b066742..e242c393 100644 --- a/apps/account/views/common.py +++ b/apps/account/views/common.py @@ -17,7 +17,7 @@ from utils.views import JWTGenericViewMixin # User views -class UserRetrieveUpdateView(generics.RetrieveUpdateAPIView): +class UserRetrieveUpdateView(generics.RetrieveUpdateDestroyAPIView): """User update view.""" serializer_class = serializers.UserSerializer queryset = models.User.objects.active() @@ -25,6 +25,10 @@ class UserRetrieveUpdateView(generics.RetrieveUpdateAPIView): def get_object(self): return self.request.user + def delete(self, request, *args, **kwargs): + """Overridden behavior of DELETE method.""" + return Response(status=status.HTTP_204_NO_CONTENT) + class ChangePasswordView(generics.GenericAPIView): """Change password view""" From 8acb86a5ab30f67fafb635c1e5f14e97d50706bb Mon Sep 17 00:00:00 2001 From: dormantman Date: Fri, 24 Jan 2020 18:25:17 +0300 Subject: [PATCH 75/95] Added history to subscriptions --- apps/notification/models.py | 12 ++++++++++-- apps/notification/serializers/common.py | 20 ++++++++++++++++---- apps/notification/tasks.py | 1 + 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/apps/notification/models.py b/apps/notification/models.py index adcb4fab..094fcad4 100644 --- a/apps/notification/models.py +++ b/apps/notification/models.py @@ -137,10 +137,14 @@ class Subscriber(ProjectBaseMixin): """Unsubscribe user.""" self.subscribe_set.update( - unsunbscribe_date=now(), + unsubscribe_date=now(), old_subscriber_id=F('subscriber_id'), old_subscription_type_id=F('subscription_type_id') - ).update(subscriber_id=None, subscription_type_id=None) + ) + self.subscribe_set.update( + subscriber_id=None, + subscription_type_id=None + ) if settings.USE_CELERY: send_unsubscribe_email.delay(self.email) @@ -166,6 +170,10 @@ class Subscriber(ProjectBaseMixin): def active_subscriptions(self): return self.subscription_types.exclude(subscriber__subscribe__unsubscribe_date__isnull=False) + @property + def subscription_history(self): + return Subscribe.objects.subscription_history(self.pk) + class SubscribeQuerySet(models.QuerySet): diff --git a/apps/notification/serializers/common.py b/apps/notification/serializers/common.py index 48caee48..9eda31b3 100644 --- a/apps/notification/serializers/common.py +++ b/apps/notification/serializers/common.py @@ -141,14 +141,24 @@ class UpdateSubscribeSerializer(serializers.ModelSerializer): class SubscribeObjectSerializer(serializers.ModelSerializer): - """Subscribe serializer.""" + """Subscription type serializer.""" + + subscription_type = serializers.SerializerMethodField() class Meta: """Meta class.""" - model = models.Subscriber - fields = ('subscriber',) - read_only_fields = ('subscribe_date', 'unsubscribe_date',) + model = models.Subscribe + fields = ( + 'subscribe_date', + 'unsubscribe_date', + 'subscription_type' + ) + + def get_subscription_type(self, instance): + return SubscriptionTypeSerializer( + models.SubscriptionType.objects.get(pk=instance.old_subscription_type_id) + ).data class SubscribeSerializer(serializers.ModelSerializer): @@ -156,6 +166,7 @@ class SubscribeSerializer(serializers.ModelSerializer): email = serializers.EmailField(required=False, source='send_to') subscription_types = SubscriptionTypeSerializer(source='active_subscriptions', read_only=True, many=True) + history = SubscribeObjectSerializer(source='subscription_history', many=True) class Meta: """Meta class.""" @@ -165,6 +176,7 @@ class SubscribeSerializer(serializers.ModelSerializer): 'email', 'subscription_types', 'link_to_unsubscribe', + 'history', ) diff --git a/apps/notification/tasks.py b/apps/notification/tasks.py index 3f9f7249..cea15287 100644 --- a/apps/notification/tasks.py +++ b/apps/notification/tasks.py @@ -7,6 +7,7 @@ from django.template.loader import get_template, render_to_string from main.models import SiteSettings from notification import models +from django.utils.translation import gettext_lazy as _ @shared_task From e998865de58bb06f23142ec0e8c559d6c887e0e0 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 24 Jan 2020 18:49:01 +0300 Subject: [PATCH 76/95] try to fix subscriptions --- apps/notification/serializers/common.py | 8 ++++++++ apps/notification/views/common.py | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/apps/notification/serializers/common.py b/apps/notification/serializers/common.py index 869879cb..67787b2e 100644 --- a/apps/notification/serializers/common.py +++ b/apps/notification/serializers/common.py @@ -95,6 +95,14 @@ class CreateAndUpdateSubscribeSerializer(serializers.ModelSerializer): return subscriber + def update(self, instance, validated_data): + if settings.USE_CELERY: + send_subscribes_update_email.delay(instance.pk) + else: + send_subscribes_update_email(instance.pk) + + return super().update(instance, validated_data) + class UpdateSubscribeSerializer(serializers.ModelSerializer): """Update with code Subscribe serializer.""" diff --git a/apps/notification/views/common.py b/apps/notification/views/common.py index a0534d13..d41ef22c 100644 --- a/apps/notification/views/common.py +++ b/apps/notification/views/common.py @@ -1,6 +1,6 @@ """Notification app common views.""" from django.shortcuts import get_object_or_404 -from rest_framework import generics, permissions +from rest_framework import generics, permissions, status from rest_framework.response import Response from notification import models @@ -15,6 +15,19 @@ class CreateSubscribeView(generics.CreateAPIView): permission_classes = (permissions.AllowAny,) serializer_class = serializers.CreateAndUpdateSubscribeSerializer + def create(self, request, *args, **kwargs): + data = request.data + if 'email' in request.data: + # we shouldn't create new subscriber if we have one + subscriber = models.Subscriber.objects.filter(email=request.data['email']).first() + if subscriber: + data = subscriber # new serializer will simply update instance instead of creating another one + serializer = self.get_serializer(data=data) + serializer.is_valid(raise_exception=True) + self.perform_create(serializer) + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + class UpdateSubscribeView(generics.UpdateAPIView): """Subscribe info view.""" From 03946d0626e870f75b9c1291691995dd4dcf54c4 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 24 Jan 2020 18:52:36 +0300 Subject: [PATCH 77/95] try to fix subscriptions #2 --- apps/notification/views/common.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/notification/views/common.py b/apps/notification/views/common.py index d41ef22c..375826e1 100644 --- a/apps/notification/views/common.py +++ b/apps/notification/views/common.py @@ -17,12 +17,11 @@ class CreateSubscribeView(generics.CreateAPIView): def create(self, request, *args, **kwargs): data = request.data + instance = None if 'email' in request.data: # we shouldn't create new subscriber if we have one - subscriber = models.Subscriber.objects.filter(email=request.data['email']).first() - if subscriber: - data = subscriber # new serializer will simply update instance instead of creating another one - serializer = self.get_serializer(data=data) + instance = models.Subscriber.objects.filter(email=request.data['email']).first() + serializer = self.get_serializer(data=data) if instance is None else self.get_serializer(instance, data=data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) From 2e4daaf9a1c3919ad6275261518c6715ad244ac6 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Fri, 24 Jan 2020 19:02:22 +0300 Subject: [PATCH 78/95] try to fix subscriptions #3 --- apps/notification/serializers/common.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/notification/serializers/common.py b/apps/notification/serializers/common.py index 67787b2e..7c22f713 100644 --- a/apps/notification/serializers/common.py +++ b/apps/notification/serializers/common.py @@ -40,6 +40,7 @@ class CreateAndUpdateSubscribeSerializer(serializers.ModelSerializer): model = models.Subscriber fields = ( + 'id', 'email', 'subscription_types', 'link_to_unsubscribe', @@ -54,7 +55,7 @@ class CreateAndUpdateSubscribeSerializer(serializers.ModelSerializer): user = request.user # validate email - email = attrs.get('send_to') + email = attrs.pop('send_to') if attrs.get('email'): email = attrs.get('email') From 1d3cf9dac0ef1698b15ee76dfaf0d115a6b14181 Mon Sep 17 00:00:00 2001 From: "a.gorbunov" Date: Mon, 27 Jan 2020 06:41:52 +0000 Subject: [PATCH 79/95] select related before load --- apps/collection/models.py | 4 ++++ apps/collection/views/back.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/collection/models.py b/apps/collection/models.py index 0457f38d..8024effb 100644 --- a/apps/collection/models.py +++ b/apps/collection/models.py @@ -57,6 +57,10 @@ class CollectionQuerySet(RelatedObjectsCountMixin): """Returned only published collection""" return self.filter(is_publish=True) + def with_base_related(self): + """Select relate objects""" + return self.select_related('country') + class Collection(ProjectBaseMixin, CollectionDateMixin, TranslatedFieldsMixin, URLImageMixin): diff --git a/apps/collection/views/back.py b/apps/collection/views/back.py index 481f70da..032bafb3 100644 --- a/apps/collection/views/back.py +++ b/apps/collection/views/back.py @@ -75,7 +75,7 @@ class CollectionBackOfficeViewSet(mixins.CreateModelMixin, """ViewSet for Collection model for BackOffice users.""" permission_classes = (permissions.IsAuthenticated,) - queryset = models.Collection.objects.all() + queryset = models.Collection.objects.with_base_related() filter_backends = [DjangoFilterBackend, OrderingFilter] serializer_class = serializers.CollectionBackOfficeSerializer bind_object_serializer_class = serializers.CollectionBindObjectSerializer From bcecb39ee29c2151d34a36381994d86f9424f03b Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 27 Jan 2020 11:11:24 +0300 Subject: [PATCH 80/95] disable checking on email field in UserDetailSerializer --- apps/account/serializers/common.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/apps/account/serializers/common.py b/apps/account/serializers/common.py index 9ca3caf0..6722e539 100644 --- a/apps/account/serializers/common.py +++ b/apps/account/serializers/common.py @@ -122,12 +122,6 @@ class UserSerializer(serializers.ModelSerializer): subscriptions_handler(subscriptions_list, user) return user - def validate_email(self, value): - """Validate email value""" - if hasattr(self.instance, 'email') and self.instance.email and value == self.instance.email: - raise serializers.ValidationError(detail='Equal email address.') - return value - def validate_username(self, value): """Custom username validation""" valid = utils_methods.username_validator(username=value) @@ -141,12 +135,13 @@ class UserSerializer(serializers.ModelSerializer): if 'subscription_types' in validated_data: subscriptions_list = validated_data.pop('subscription_types') + new_email = validated_data.get('email') old_email = instance.email instance = super().update(instance, validated_data) - if 'email' in validated_data: + if new_email and new_email != old_email: instance.email_confirmed = False instance.email = old_email - instance.unconfirmed_email = validated_data['email'] + instance.unconfirmed_email = new_email instance.save() # Send verification link on user email for change email address if settings.USE_CELERY: From 7386887f491c30b9d27fb98148e7951aeeebba79 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 27 Jan 2020 11:24:59 +0300 Subject: [PATCH 81/95] state fields for BO --- apps/news/serializers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/news/serializers.py b/apps/news/serializers.py index 6e9781db..76b55ae6 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -208,6 +208,7 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): is_published = serializers.BooleanField(source='is_publish', read_only=True) descriptions = serializers.ListField(required=False) agenda = AgendaSerializer(required=False, allow_null=True) + state_display = serializers.CharField(source='get_state_display', read_only=True) class Meta(NewsBaseSerializer.Meta): """Meta class.""" @@ -226,7 +227,9 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): 'created', 'modified', 'descriptions', - 'agenda' + 'agenda', + 'state', + 'state_display', ) extra_kwargs = { 'created': {'read_only': True}, @@ -234,6 +237,8 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): 'duplication_date': {'read_only': True}, 'locale_to_description_is_active': {'allow_null': False}, 'must_of_the_week': {'read_only': True}, + 'state': {'read_only': True}, + 'state_display': {'read_only': True}, } def validate(self, attrs): From b1817061dbd000ae788d1a0a06e09dd2523665c7 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 27 Jan 2020 12:53:37 +0300 Subject: [PATCH 82/95] try to fix subscriptions #4 --- apps/notification/views/common.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/apps/notification/views/common.py b/apps/notification/views/common.py index 375826e1..9562b922 100644 --- a/apps/notification/views/common.py +++ b/apps/notification/views/common.py @@ -56,20 +56,7 @@ class SubscribeInfoAuthUserView(generics.RetrieveAPIView): lookup_field = None def get_object(self): - user = self.request.user - - subscriber = models.Subscriber.objects.filter(user=user).first() - - if subscriber is None: - subscriber = models.Subscriber.objects.make_subscriber( - email=user.email, user=user, ip_address=get_user_ip(self.request), - country_code=self.request.country_code, locale=self.request.locale - ) - - else: - return get_object_or_404(models.Subscriber, user=user) - - return subscriber + return get_object_or_404(models.Subscriber, user=self.request.user) class UnsubscribeView(generics.UpdateAPIView): From e1c16628197c9bae27b812a838d77a7259947ff7 Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 27 Jan 2020 13:19:36 +0300 Subject: [PATCH 83/95] refactored filters for partner view --- apps/partner/filters.py | 23 +++++++++++++++++++++++ apps/partner/views/back.py | 12 +++++------- 2 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 apps/partner/filters.py diff --git a/apps/partner/filters.py b/apps/partner/filters.py new file mode 100644 index 00000000..e9b32f5f --- /dev/null +++ b/apps/partner/filters.py @@ -0,0 +1,23 @@ +"""Partner app filters.""" +from django_filters import rest_framework as filters + +from partner.models import Partner + + +class PartnerFilterSet(filters.FilterSet): + """Establishment filter set.""" + + establishment = filters.NumberFilter( + help_text='Allows to get partner list by establishment ID.') + type = filters.ChoiceFilter( + choices=Partner.MODEL_TYPES, + help_text=f'Allows to filter partner list by partner type. ' + f'Enum: {dict(Partner.MODEL_TYPES)}') + + class Meta: + """Meta class.""" + model = Partner + fields = ( + 'establishment', + 'type', + ) diff --git a/apps/partner/views/back.py b/apps/partner/views/back.py index 1033d0ee..7ea440f0 100644 --- a/apps/partner/views/back.py +++ b/apps/partner/views/back.py @@ -1,22 +1,20 @@ -from django_filters.rest_framework import DjangoFilterBackend, filters from rest_framework import generics, permissions +from partner import filters from partner.models import Partner from partner.serializers import back as serializers from utils.permissions import IsEstablishmentManager class PartnerLstView(generics.ListCreateAPIView): - """Partner list create view.""" + """Partner list/create view. + Allows to get partners for current country, or create a new one. + """ queryset = Partner.objects.all() serializer_class = serializers.BackPartnerSerializer pagination_class = None permission_classes = [permissions.IsAdminUser | IsEstablishmentManager] - filter_backends = (DjangoFilterBackend,) - filterset_fields = ( - 'establishment', - 'type', - ) + filter_class = filters.PartnerFilterSet class PartnerRUDView(generics.RetrieveUpdateDestroyAPIView): From 8be85707c7c2c166de88228e96e1ed0916b6e3be Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 27 Jan 2020 14:15:01 +0300 Subject: [PATCH 84/95] fix published news --- apps/news/models.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/news/models.py b/apps/news/models.py index 2316fc27..3a529cc4 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -128,8 +128,14 @@ class NewsQuerySet(TranslationQuerysetMixin): return self.exclude(models.Q(publication_date__isnull=True) | models.Q(publication_time__isnull=True)). \ filter(models.Q(models.Q(end__gte=now) | models.Q(end__isnull=True)), - state__in=self.model.PUBLISHED_STATES, publication_date__lte=date_now, - publication_time__lte=time_now) + state__in=self.model.PUBLISHED_STATES)\ + .annotate(visible_now=Case( + When(publication_date__gt=date_now, then=False), + When(Q(publication_date=date_now) & Q(publication_time__gt=time_now), then=False), + default=True, + output_field=models.BooleanField() + ))\ + .exclude(visible_now=False) # todo: filter by best score # todo: filter by country? From 21006e77cb753fd19cb0cbc605d6ebde7854d687 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 27 Jan 2020 15:16:29 +0300 Subject: [PATCH 85/95] user id in self info --- apps/account/serializers/common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/account/serializers/common.py b/apps/account/serializers/common.py index 6722e539..c44170d4 100644 --- a/apps/account/serializers/common.py +++ b/apps/account/serializers/common.py @@ -87,6 +87,7 @@ class UserSerializer(serializers.ModelSerializer): class Meta: model = models.User fields = [ + 'id', 'username', 'first_name', 'last_name', From bf60479c66fcabbbf2e560cad00928cf8c1cd0de Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 27 Jan 2020 15:38:06 +0300 Subject: [PATCH 86/95] user short name serializer added username --- apps/account/serializers/common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/account/serializers/common.py b/apps/account/serializers/common.py index c44170d4..a537c1bf 100644 --- a/apps/account/serializers/common.py +++ b/apps/account/serializers/common.py @@ -191,6 +191,7 @@ class UserShortSerializer(UserSerializer): 'id', 'fullname', 'email', + 'username', ] From a8630661d89b0a6a2120c91717137d47cf4c002d Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 27 Jan 2020 17:30:09 +0300 Subject: [PATCH 87/95] fix state --- 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 76b55ae6..f820fc29 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -237,7 +237,7 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): 'duplication_date': {'read_only': True}, 'locale_to_description_is_active': {'allow_null': False}, 'must_of_the_week': {'read_only': True}, - 'state': {'read_only': True}, + # 'state': {'read_only': True}, 'state_display': {'read_only': True}, } From b6bfc51aadf48e72ed1371f7f2c2bb6ee30267fe Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 27 Jan 2020 18:30:25 +0300 Subject: [PATCH 88/95] fix issue w/ news update --- 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 f820fc29..f88c79f0 100644 --- a/apps/news/serializers.py +++ b/apps/news/serializers.py @@ -304,7 +304,7 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer): slugs_set = set(slugs_list) if models.News.objects.filter( slugs__values__contains=list(slugs.values()) - ).exists() or len(slugs_list) != len(slugs_set): + ).exclude(pk=instance.pk).exists() or len(slugs_list) != len(slugs_set): raise serializers.ValidationError({'slugs': _('Slug should be unique')}) agenda_data = validated_data.get('agenda') From f5da5f71a310f1006879f179560a343f4cbac77d Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 27 Jan 2020 18:45:23 +0300 Subject: [PATCH 89/95] base related qs for cities --- apps/establishment/views/back.py | 2 +- apps/location/models.py | 3 +++ apps/location/views/back.py | 2 +- apps/location/views/common.py | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 723d3844..62163ba3 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -353,7 +353,7 @@ class EstablishmentNoteListCreateView(EstablishmentMixinViews, lookup_field = 'slug' serializer_class = serializers.EstablishmentNoteListCreateSerializer - + permission_classes = (permissions.AllowAny, ) def get_object(self): """Returns the object the view is displaying.""" establishment_qs = models.Establishment.objects.all() diff --git a/apps/location/models.py b/apps/location/models.py index 97c02dd7..a8f4c43a 100644 --- a/apps/location/models.py +++ b/apps/location/models.py @@ -139,6 +139,9 @@ class CityQuerySet(models.QuerySet): """Return establishments by country code""" return self.filter(country__code=code) + def with_base_related(self): + return self.prefetch_related('country', 'region', 'region__country') + class City(models.Model): """Region model.""" diff --git a/apps/location/views/back.py b/apps/location/views/back.py index a0d1bd36..1e1957fe 100644 --- a/apps/location/views/back.py +++ b/apps/location/views/back.py @@ -44,7 +44,7 @@ class CityListCreateView(common.CityViewMixin, generics.ListCreateAPIView): def get_queryset(self): """Overridden method 'get_queryset'.""" qs = models.City.objects.all().annotate(locale_name=KeyTextTransform(get_current_locale(), 'name_translated'))\ - .order_by('locale_name') + .order_by('locale_name').with_base_related() if self.request.country_code: qs = qs.by_country_code(self.request.country_code) return qs diff --git a/apps/location/views/common.py b/apps/location/views/common.py index d9968755..475288fe 100644 --- a/apps/location/views/common.py +++ b/apps/location/views/common.py @@ -24,7 +24,7 @@ class RegionViewMixin(generics.GenericAPIView): class CityViewMixin(generics.GenericAPIView): """View Mixin for model City""" model = models.City - queryset = models.City.objects.all() + queryset = models.City.objects.with_base_related() class AddressViewMixin(generics.GenericAPIView): @@ -101,7 +101,7 @@ class CityListView(CityViewMixin, generics.ListAPIView): def get_queryset(self): qs = super().get_queryset() if self.request.country_code: - qs = qs.by_country_code(self.request.country_code) + qs = qs.by_country_code(self.request.country_code).with_base_related() return qs From 8a1651bf1feb2c2e55b618e987372881ceed60e3 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 27 Jan 2020 18:45:50 +0300 Subject: [PATCH 90/95] fix permission --- apps/establishment/views/back.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py index 62163ba3..723d3844 100644 --- a/apps/establishment/views/back.py +++ b/apps/establishment/views/back.py @@ -353,7 +353,7 @@ class EstablishmentNoteListCreateView(EstablishmentMixinViews, lookup_field = 'slug' serializer_class = serializers.EstablishmentNoteListCreateSerializer - permission_classes = (permissions.AllowAny, ) + def get_object(self): """Returns the object the view is displaying.""" establishment_qs = models.Establishment.objects.all() From 205e2fa996414f17d641ca2a016c442b85f9c256 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 27 Jan 2020 19:19:54 +0300 Subject: [PATCH 91/95] optimize collections --- apps/collection/models.py | 2 +- apps/collection/views/back.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/collection/models.py b/apps/collection/models.py index 8024effb..91173ed0 100644 --- a/apps/collection/models.py +++ b/apps/collection/models.py @@ -110,7 +110,7 @@ class Collection(ProjectBaseMixin, CollectionDateMixin, """Return list of related objects.""" related_objects = [] # get related objects - for related_object in self._meta.related_objects: + for related_object in self._meta.related_objects.with_base_related(): related_objects.append(related_object) return related_objects diff --git a/apps/collection/views/back.py b/apps/collection/views/back.py index 032bafb3..73fb7b18 100644 --- a/apps/collection/views/back.py +++ b/apps/collection/views/back.py @@ -21,7 +21,7 @@ class CollectionViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): def get_queryset(self): """Overridden method 'get_queryset'.""" - qs = models.Collection.objects.all().order_by('-created') + qs = models.Collection.objects.all().order_by('-created').with_base_related() if self.request.country_code: qs = qs.by_country_code(self.request.country_code) return qs From 7a6655930750180dac3b261f9f8ff81521f4886a Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 27 Jan 2020 19:38:47 +0300 Subject: [PATCH 92/95] remove obviously unnecessary field --- .../0012_remove_subscribe_subscribe_date.py | 17 +++++++++++++++++ apps/notification/models.py | 5 ++++- apps/notification/serializers/common.py | 3 +++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 apps/notification/migrations/0012_remove_subscribe_subscribe_date.py diff --git a/apps/notification/migrations/0012_remove_subscribe_subscribe_date.py b/apps/notification/migrations/0012_remove_subscribe_subscribe_date.py new file mode 100644 index 00000000..cc805e4b --- /dev/null +++ b/apps/notification/migrations/0012_remove_subscribe_subscribe_date.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.7 on 2020-01-27 16:37 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('notification', '0011_auto_20200124_1351'), + ] + + operations = [ + migrations.RemoveField( + model_name='subscribe', + name='subscribe_date', + ), + ] diff --git a/apps/notification/models.py b/apps/notification/models.py index 094fcad4..a176034e 100644 --- a/apps/notification/models.py +++ b/apps/notification/models.py @@ -187,7 +187,6 @@ class SubscribeQuerySet(models.QuerySet): class Subscribe(ProjectBaseMixin): """Subscribe model.""" - subscribe_date = models.DateTimeField(_('Last subscribe date'), default=now(), blank=True, null=True) unsubscribe_date = models.DateTimeField(_('Last unsubscribe date'), blank=True, null=True, default=None) subscriber = models.ForeignKey(Subscriber, on_delete=models.CASCADE, null=True) @@ -198,6 +197,10 @@ class Subscribe(ProjectBaseMixin): objects = SubscribeQuerySet.as_manager() + @property + def subscribe_date(self): + return self.created + class Meta: """Meta class.""" diff --git a/apps/notification/serializers/common.py b/apps/notification/serializers/common.py index 6d5e9beb..282a763c 100644 --- a/apps/notification/serializers/common.py +++ b/apps/notification/serializers/common.py @@ -163,6 +163,9 @@ class SubscribeObjectSerializer(serializers.ModelSerializer): 'unsubscribe_date', 'subscription_type' ) + extra_kwargs = { + 'subscribe_date': {'read_only': True}, + } def get_subscription_type(self, instance): return SubscriptionTypeSerializer( From b9ee22c4a57bdcf9677083627797e6ffe767ae44 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 27 Jan 2020 19:44:07 +0300 Subject: [PATCH 93/95] filter by states news --- apps/news/filters.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/news/filters.py b/apps/news/filters.py index 064f7175..7e2068a4 100644 --- a/apps/news/filters.py +++ b/apps/news/filters.py @@ -24,6 +24,8 @@ class NewsListFilterSet(filters.FilterSet): state = filters.NumberFilter() + state__in = filters.CharFilter(method='by_states_list') + SORT_BY_CREATED_CHOICE = "created" SORT_BY_START_CHOICE = "start" SORT_BY_CHOICES = ( @@ -54,6 +56,10 @@ class NewsListFilterSet(filters.FilterSet): return queryset.es_search(value, relevance_order='ordering' not in self.request.query_params) return queryset + def by_states_list(self, queryset, name, value): + states = value.splite('__') + return self.filters(state__in=states) + def in_tags(self, queryset, name, value): tags = value.split('__') return queryset.filter(tags__value__in=tags) From 9b6c70cf746cd6af2f227a1cd2aa5df06d45a201 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 27 Jan 2020 19:45:04 +0300 Subject: [PATCH 94/95] fix typo --- apps/news/filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/news/filters.py b/apps/news/filters.py index 7e2068a4..6ead63f7 100644 --- a/apps/news/filters.py +++ b/apps/news/filters.py @@ -57,7 +57,7 @@ class NewsListFilterSet(filters.FilterSet): return queryset def by_states_list(self, queryset, name, value): - states = value.splite('__') + states = value.split('__') return self.filters(state__in=states) def in_tags(self, queryset, name, value): From 42d21a79a40db9cc312772db2ab87eac94eb6c63 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Mon, 27 Jan 2020 19:46:17 +0300 Subject: [PATCH 95/95] fix typo again =\ --- apps/news/filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/news/filters.py b/apps/news/filters.py index 6ead63f7..7a2e4382 100644 --- a/apps/news/filters.py +++ b/apps/news/filters.py @@ -58,7 +58,7 @@ class NewsListFilterSet(filters.FilterSet): def by_states_list(self, queryset, name, value): states = value.split('__') - return self.filters(state__in=states) + return queryset.filter(state__in=states) def in_tags(self, queryset, name, value): tags = value.split('__')