From 7edd836a75f3609f857f5a9a64f55ada60bb4433 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Tue, 31 Dec 2019 01:22:45 +0300 Subject: [PATCH 1/7] helping hand representation --- apps/news/tasks.py | 2 ++ apps/notification/models.py | 1 + apps/notification/urls/back.py | 1 + apps/notification/views/common.py | 1 + project/urls/back.py | 1 + 5 files changed, 6 insertions(+) create mode 100644 apps/notification/urls/back.py diff --git a/apps/news/tasks.py b/apps/news/tasks.py index 80a2a626..c08ee8c7 100644 --- a/apps/news/tasks.py +++ b/apps/news/tasks.py @@ -26,6 +26,8 @@ def send_email_with_news(news_ids): for subscriber in subscribers: socials_for_subscriber = socials.get(subscriber.country_code) try: + # TODO: вот тут надо учесть, подписки на какие страны есть у юзера активные (нулл время отписки) и не посылать лишнего + # TODO: обрати внимание на кол-во запросов в БД плс. они пишутся в консоль for new in sent_news: context = { "title": new.title.get(subscriber.locale), diff --git a/apps/notification/models.py b/apps/notification/models.py index d1ebf055..3a9dbbf2 100644 --- a/apps/notification/models.py +++ b/apps/notification/models.py @@ -15,6 +15,7 @@ class SubscriptionType(ProjectBaseMixin, TranslatedFieldsMixin): name = TJSONField(blank=True, null=True, default=None, verbose_name=_('name'), help_text='{"en-GB":"some text"}') + # TODO: не хватает связи со страной. ForeignKey # todo: associate user & subscriber after users registration diff --git a/apps/notification/urls/back.py b/apps/notification/urls/back.py new file mode 100644 index 00000000..2012ebe1 --- /dev/null +++ b/apps/notification/urls/back.py @@ -0,0 +1 @@ +urlpatterns = [] \ No newline at end of file diff --git a/apps/notification/views/common.py b/apps/notification/views/common.py index f7fbf9fe..257c3286 100644 --- a/apps/notification/views/common.py +++ b/apps/notification/views/common.py @@ -28,6 +28,7 @@ class SubscribeInfoView(generics.RetrieveAPIView): class SubscribeInfoAuthUserView(generics.ListAPIView): """Subscribe info auth user view.""" + # TODO: тут пользователь должен видеть свои подписки. проверь плс, что работает permission_classes = (permissions.IsAuthenticated,) serializer_class = serializers.SubscribeSerializer diff --git a/project/urls/back.py b/project/urls/back.py index e7e2b43b..8245e492 100644 --- a/project/urls/back.py +++ b/project/urls/back.py @@ -10,6 +10,7 @@ urlpatterns = [ path('gallery/', include(('gallery.urls', 'gallery'), namespace='gallery')), path('location/', include('location.urls.back')), path('news/', include('news.urls.back')), + path('notifications/', include(('notification.urls.back', 'notification'), namespace='notification')), path('review/', include('review.urls.back')), path('tags/', include(('tag.urls.back', 'tag'), namespace='tag')), path('products/', include(('product.urls.back', 'product'), namespace='product')), From d4e993abee95434d08986898cef4046c7d82cc45 Mon Sep 17 00:00:00 2001 From: dormantman Date: Tue, 31 Dec 2019 05:13:36 +0300 Subject: [PATCH 2/7] Added country field --- .../0009_subscriptiontype_country.py | 20 +++++++++++++++++++ .../migrations/0010_auto_20191231_0135.py | 19 ++++++++++++++++++ apps/notification/models.py | 9 ++++----- apps/notification/serializers/common.py | 9 +++++---- 4 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 apps/notification/migrations/0009_subscriptiontype_country.py create mode 100644 apps/notification/migrations/0010_auto_20191231_0135.py diff --git a/apps/notification/migrations/0009_subscriptiontype_country.py b/apps/notification/migrations/0009_subscriptiontype_country.py new file mode 100644 index 00000000..9af323dc --- /dev/null +++ b/apps/notification/migrations/0009_subscriptiontype_country.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.7 on 2019-12-31 01:31 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('location', '0033_merge_20191224_0920'), + ('notification', '0008_remove_subscribe_state'), + ] + + operations = [ + migrations.AddField( + model_name='subscriptiontype', + name='country', + field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='location.Country', verbose_name='Country'), + ), + ] diff --git a/apps/notification/migrations/0010_auto_20191231_0135.py b/apps/notification/migrations/0010_auto_20191231_0135.py new file mode 100644 index 00000000..7b83a738 --- /dev/null +++ b/apps/notification/migrations/0010_auto_20191231_0135.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.7 on 2019-12-31 01:35 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('notification', '0009_subscriptiontype_country'), + ] + + operations = [ + migrations.AlterField( + model_name='subscriptiontype', + name='country', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='location.Country', verbose_name='country'), + ), + ] diff --git a/apps/notification/models.py b/apps/notification/models.py index 3a9dbbf2..2f0ba500 100644 --- a/apps/notification/models.py +++ b/apps/notification/models.py @@ -6,6 +6,7 @@ from django.utils.timezone import now from django.utils.translation import ugettext_lazy as _ from account.models import User +from location.models import Country from utils.methods import generate_string_code from utils.models import ProjectBaseMixin, TJSONField, TranslatedFieldsMixin @@ -15,7 +16,9 @@ class SubscriptionType(ProjectBaseMixin, TranslatedFieldsMixin): name = TJSONField(blank=True, null=True, default=None, verbose_name=_('name'), help_text='{"en-GB":"some text"}') - # TODO: не хватает связи со страной. ForeignKey + country = models.ForeignKey(Country, on_delete=models.PROTECT, + blank=True, null=True, default=None, + verbose_name=_('country')) # todo: associate user & subscriber after users registration @@ -127,10 +130,6 @@ class Subscriber(ProjectBaseMixin): query = f'?code={self.update_code}' return f'{schema}://{site_domain}{url}{query}' - @property - def subscribe_objects(self): - return Subscribe.objects.filter(subscriber=self) - class Subscribe(ProjectBaseMixin): """Subscribe model.""" diff --git a/apps/notification/serializers/common.py b/apps/notification/serializers/common.py index 036d8929..4a9e64f5 100644 --- a/apps/notification/serializers/common.py +++ b/apps/notification/serializers/common.py @@ -2,6 +2,7 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers +from location.serializers import CountrySimpleSerializer from notification import models from utils.methods import get_user_ip from utils.serializers import TranslatedField @@ -11,6 +12,7 @@ class SubscriptionTypeSerializer(serializers.ModelSerializer): """Subscription type serializer.""" name_translated = TranslatedField() + country = CountrySimpleSerializer() class Meta: """Meta class.""" @@ -20,6 +22,7 @@ class SubscriptionTypeSerializer(serializers.ModelSerializer): 'id', 'index_name', 'name_translated', + 'country', ) @@ -80,7 +83,7 @@ class SubscribeObjectSerializer(serializers.ModelSerializer): """Meta class.""" model = models.Subscriber - fields = () + fields = ('subscriber', ) read_only_fields = ('subscribe_date', 'unsubscribe_date',) @@ -88,8 +91,7 @@ class SubscribeSerializer(serializers.ModelSerializer): """Subscribe serializer.""" email = serializers.EmailField(required=False, source='send_to') - subscription_types = SubscriptionTypeSerializer(read_only=True, source='subscription_types_set') - subscribe_objects = SubscribeObjectSerializer(read_only=True) + subscription_types = SubscriptionTypeSerializer(many=True, read_only=True) class Meta: """Meta class.""" @@ -99,5 +101,4 @@ class SubscribeSerializer(serializers.ModelSerializer): 'email', 'subscription_types', 'link_to_unsubscribe', - 'subscribe_objects', ) From 241012da424217f4dff895e352e8312ea1a0a58f Mon Sep 17 00:00:00 2001 From: dormantman Date: Tue, 31 Dec 2019 05:29:48 +0300 Subject: [PATCH 3/7] Added prefetch optimize --- apps/news/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/news/tasks.py b/apps/news/tasks.py index c08ee8c7..4a980144 100644 --- a/apps/news/tasks.py +++ b/apps/news/tasks.py @@ -20,7 +20,7 @@ def send_email_with_news(news_ids): htmly = get_template(settings.NEWS_EMAIL_TEMPLATE) year = datetime.now().year - socials = list(SiteSettings.objects.with_country()) + socials = list(SiteSettings.objects.with_country().select_related('country')) socials = dict(zip(map(lambda social: social.country.code, socials), socials)) for subscriber in subscribers: From 140cc6cfa5f63e56aae42bcf592cddf63d33a8ad Mon Sep 17 00:00:00 2001 From: dormantman Date: Tue, 31 Dec 2019 16:09:04 +0300 Subject: [PATCH 4/7] Reformat sending mail structure --- apps/news/tasks.py | 19 +++++++++++++------ apps/notification/tests.py | 10 ++++++++-- apps/notification/views/common.py | 1 - 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/apps/news/tasks.py b/apps/news/tasks.py index 4a980144..0267fcf4 100644 --- a/apps/news/tasks.py +++ b/apps/news/tasks.py @@ -9,12 +9,14 @@ from django.template.loader import get_template, render_to_string from main.models import SiteSettings from news import models -from notification.models import Subscriber +from notification.models import Subscribe @shared_task def send_email_with_news(news_ids): - subscribers = Subscriber.objects.all() + subscribes = Subscribe.objects.all() \ + .prefetch_related('subscriber') \ + .prefetch_related('subscription_type') sent_news = models.News.objects.filter(id__in=news_ids) htmly = get_template(settings.NEWS_EMAIL_TEMPLATE) @@ -23,11 +25,16 @@ def send_email_with_news(news_ids): socials = list(SiteSettings.objects.with_country().select_related('country')) socials = dict(zip(map(lambda social: social.country.code, socials), socials)) - for subscriber in subscribers: - socials_for_subscriber = socials.get(subscriber.country_code) + for subscribe in subscribes.filter(unsubscribe_date=None): + country = subscribe.subscription_type.country + + if country is None: + continue + + socials_for_subscriber = socials.get(country.code) + subscriber = subscribe.subscriber + try: - # TODO: вот тут надо учесть, подписки на какие страны есть у юзера активные (нулл время отписки) и не посылать лишнего - # TODO: обрати внимание на кол-во запросов в БД плс. они пишутся в консоль for new in sent_news: context = { "title": new.title.get(subscriber.locale), diff --git a/apps/notification/tests.py b/apps/notification/tests.py index 410365ff..cf380410 100644 --- a/apps/notification/tests.py +++ b/apps/notification/tests.py @@ -80,7 +80,7 @@ class NotificationSubscribeInfoTestCase(APITestCase): class NotificationUnsubscribeAuthUserTestCase(BaseTestCase): def test_unsubscribe_auth_user(self): - Subscriber.objects.create(user=self.user, email=self.email, state=1) + Subscriber.objects.create(user=self.user, email=self.email) self.test_data = { "email": self.email @@ -174,7 +174,7 @@ class NotificationManySubscribeTestCase(APITestCase): test_data = { 'email': self.email, 'subscription_types_pk': [ - self.test_subscribe_type + self.test_subscribe_type.id ] } @@ -183,6 +183,12 @@ class NotificationManySubscribeTestCase(APITestCase): self.assertEqual(response.json()["email"], test_data["email"]) def test_unsubscribe(self): + self.username = 'sedragurda' + self.password = 'sedragurdaredips19' + self.email = 'sedragurda@desoz.com' + self.user = User.objects.create_user( + username=self.username, email=self.email, password=self.password) + test_data = { "email": self.email } diff --git a/apps/notification/views/common.py b/apps/notification/views/common.py index 257c3286..f7fbf9fe 100644 --- a/apps/notification/views/common.py +++ b/apps/notification/views/common.py @@ -28,7 +28,6 @@ class SubscribeInfoView(generics.RetrieveAPIView): class SubscribeInfoAuthUserView(generics.ListAPIView): """Subscribe info auth user view.""" - # TODO: тут пользователь должен видеть свои подписки. проверь плс, что работает permission_classes = (permissions.IsAuthenticated,) serializer_class = serializers.SubscribeSerializer From 33a616ab96a9a099177597c4b504bf303ef953ab Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 9 Jan 2020 13:08:59 +0300 Subject: [PATCH 5/7] fix transfer translation for news --- apps/news/transfer_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/news/transfer_data.py b/apps/news/transfer_data.py index c057da84..f4e16739 100644 --- a/apps/news/transfer_data.py +++ b/apps/news/transfer_data.py @@ -145,7 +145,7 @@ def add_tags(): ) if created: text_value = ' '.join(new_tag.value.split('_')) - translation = SiteInterfaceDictionary(page={'en-GB': text_value}, keywords=f'tag.{new_tag.category}.{new_tag.value}') + translation = SiteInterfaceDictionary(text={'en-GB': text_value}, keywords=f'tag.{new_tag.category}.{new_tag.value}') translation.save() new_tag.translation = translation new_tag.save() From fa8b4d6d979d613d67423eee3b7c42ada1398567 Mon Sep 17 00:00:00 2001 From: Kuroshini Date: Thu, 9 Jan 2020 13:48:38 +0300 Subject: [PATCH 6/7] chosen tags for recipes --- apps/tag/filters.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/tag/filters.py b/apps/tag/filters.py index a731272e..28c3db33 100644 --- a/apps/tag/filters.py +++ b/apps/tag/filters.py @@ -10,10 +10,12 @@ class TagsBaseFilterSet(filters.FilterSet): # Object type choices NEWS = 'news' ESTABLISHMENT = 'establishment' + RECIPES = 'recipe' TYPE_CHOICES = ( (NEWS, 'News'), (ESTABLISHMENT, 'Establishment'), + (RECIPES, 'Recipe'), ) type = filters.MultipleChoiceFilter(choices=TYPE_CHOICES, @@ -91,5 +93,7 @@ class TagsFilterSet(TagsBaseFilterSet): if self.ESTABLISHMENT in value: queryset = queryset.for_establishments().filter(category__value_type='list').filter(value__in=settings.ESTABLISHMENT_CHOSEN_TAGS).distinct( 'value') + if self.RECIPES in value: + queryset = queryset.for_news().filter(value__in=settings.RECIPES_CHOSEN_TAGS).distinct('value') return queryset From b457ffe9758aeadb58f494666b4e5287dfe0208b Mon Sep 17 00:00:00 2001 From: Anatoly Date: Thu, 9 Jan 2020 14:59:29 +0300 Subject: [PATCH 7/7] small changes --- apps/collection/tasks.py | 9 +++++---- apps/utils/export.py | 31 +++++++++++++++---------------- project/settings/local.py | 4 +++- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/apps/collection/tasks.py b/apps/collection/tasks.py index 9535d1f8..b08a61d0 100644 --- a/apps/collection/tasks.py +++ b/apps/collection/tasks.py @@ -148,10 +148,11 @@ def export_guide(guide_id, user_id, file_type='csv'): guide = Guide.objects.get(id=guide_id) root = GuideElement.objects.get_root_node(guide) if root: - nodes = root.get_descendants().select_related('review', 'establishment', 'wine_region', - 'product', 'city', 'wine_color_section', - 'section', 'label_photo', 'guide', - 'city__country', 'establishment__establishment_type')[:100] + nodes = root.get_descendants().select_related( + 'review', 'establishment', 'wine_region', + 'product', 'city', 'wine_color_section', + 'section', 'label_photo', 'guide', + 'city__country', 'establishment__establishment_type') serializer = GuideElementExportSerializer(nodes, many=True) data = serializer.data SendGuideExport( diff --git a/apps/utils/export.py b/apps/utils/export.py index 9a4630ff..a4dda8c6 100644 --- a/apps/utils/export.py +++ b/apps/utils/export.py @@ -81,13 +81,12 @@ class DocTemplate: def template(self, data: list): - for instance in data: - instance = dict(instance) - element_id = instance.get('id') - index_name = section_name_into_index_name(instance.get('section_name')) + for obj in data: + obj = dict(obj) + index_name = section_name_into_index_name(obj.get('section_name')) # ESTABLISHMENT HEADING (LEVEL 1) - self.add_heading(name=instance['name'], + self.add_heading(name=obj['name'], font_style={'size': Pt(18), 'name': 'Palatino', 'bold': False}, level=1) # ESTABLISHMENT TYPE PARAGRAPH @@ -100,13 +99,13 @@ class DocTemplate: font_style={'size': Pt(13), 'name': 'Arial', 'bold': True}, level=2) # CITY NAME HEADING (LEVEL 3) - self.add_heading(name=instance['city_name'], + self.add_heading(name=obj['city_name'], font_style={'size': Pt(12), 'name': 'Arial', 'bold': True, 'italic': True}, color_rgb=(102, 102, 102)) self.add_empty_line() # REVIEW HEADING (LEVEL 2) - review = instance.get('review') + review = obj.get('review') if review: self.add_heading(name='Review', font_style={'size': Pt(13), 'name': 'Arial', 'bold': True}, @@ -123,7 +122,7 @@ class DocTemplate: self.add_empty_line() # PHONE HEADING (LEVEL 2) - phones = instance.get('phones') + phones = obj.get('phones') if phones: self.add_heading(name='Phones', font_style={'size': Pt(13), 'name': 'Arial', 'bold': True}, @@ -133,18 +132,18 @@ class DocTemplate: self.add_empty_line() # ADDRESS HEADING (LEVEL 2) - address = instance.get('address') + address = obj.get('address') if address: self.add_heading(name='Address', font_style={'size': Pt(13), 'name': 'Arial', 'bold': True}, level=2) # ADDRESS DATA PARAGRAPH - self.add_paragraph(name=instance.get('address'), + self.add_paragraph(name=obj.get('address'), font_style={'size': Pt(10), 'name': 'Arial'}) self.add_empty_line() # TIMETABLE HEADING (LEVEL 2) - schedule = instance.get('schedule') + schedule = obj.get('schedule') if schedule: self.add_heading(name='Schedule', font_style={'size': Pt(13), 'name': 'Arial', 'bold': True}, @@ -158,7 +157,7 @@ class DocTemplate: self.add_empty_line() # PUBLIC MARK HEADING (LEVEL 2) - public_mark = instance.get('public_mark') + public_mark = obj.get('public_mark') if public_mark: self.add_heading(name='Mark', font_style={'size': Pt(13), 'name': 'Arial', 'bold': True}, @@ -169,7 +168,7 @@ class DocTemplate: self.add_empty_line() # TOQUE HEADING (LEVEL 2) - toque = instance.get('toque_number') + toque = obj.get('toque_number') if toque: self.add_heading(name='Toque', font_style={'size': Pt(13), 'name': 'Arial', 'bold': True}, @@ -180,7 +179,7 @@ class DocTemplate: self.add_empty_line() # TOQUE HEADING (LEVEL 2) - price_level = instance.get('price_level') + price_level = obj.get('price_level') if price_level: self.add_heading(name='Price level', font_style={'size': Pt(13), 'name': 'Arial', 'bold': True}, @@ -191,7 +190,7 @@ class DocTemplate: self.add_empty_line() # SERVICES HEADING (LEVEL 2) - services = instance.get('services') + services = obj.get('services') if services: self.add_heading(name='Services', font_style={'size': Pt(13), 'name': 'Arial', 'bold': True}, @@ -201,7 +200,7 @@ class DocTemplate: self.add_empty_line() # METADATA HEADING (LEVEL 2) - metadata = instance.get('metadata') + metadata = obj.get('metadata') if metadata: for obj in metadata: for section, tags in obj.items(): diff --git a/project/settings/local.py b/project/settings/local.py index d5f5c404..a5c0ec8a 100644 --- a/project/settings/local.py +++ b/project/settings/local.py @@ -30,7 +30,6 @@ MEDIA_ROOT = os.path.join(PUBLIC_ROOT, MEDIA_LOCATION) THUMBNAIL_DEBUG = True - # DATABASES DATABASES = { 'default': { @@ -119,3 +118,6 @@ EMAIL_HOST = 'smtp.gmail.com' EMAIL_HOST_USER = 'anatolyfeteleu@gmail.com' EMAIL_HOST_PASSWORD = 'nggrlnbehzksgmbt' EMAIL_PORT = 587 + +# ADD TRANSFER TO INSTALLED APPS +INSTALLED_APPS.append('transfer.apps.TransferConfig')