diff --git a/apps/establishment/models.py b/apps/establishment/models.py
index f15a8b1c..f5c2d616 100644
--- a/apps/establishment/models.py
+++ b/apps/establishment/models.py
@@ -856,6 +856,11 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin,
metadata.append(category_tags)
return metadata
+ @property
+ def distillery_types(self):
+ """Tags from tag category - distillery_type."""
+ return self.tags.filter(category__index_name='distillery_type')
+
class EstablishmentNoteQuerySet(models.QuerySet):
"""QuerySet for model EstablishmentNote."""
diff --git a/apps/establishment/serializers/common.py b/apps/establishment/serializers/common.py
index deabd100..ada87016 100644
--- a/apps/establishment/serializers/common.py
+++ b/apps/establishment/serializers/common.py
@@ -54,7 +54,6 @@ class SocialNetworkRelatedSerializers(serializers.ModelSerializer):
class PlateSerializer(ProjectModelSerializer):
name_translated = TranslatedField()
- currency = CurrencySerializer(read_only=True)
class Meta:
model = models.Plate
@@ -329,6 +328,7 @@ class EstablishmentBaseSerializer(ProjectModelSerializer):
read_only=True)
tz = serializers.CharField(read_only=True, source='timezone_as_str')
new_image = ImageBaseSerializer(source='crop_main_image', allow_null=True, read_only=True)
+ distillery_types = TagBaseSerializer(read_only=True, many=True, allow_null=True)
class Meta:
"""Meta class."""
@@ -354,6 +354,7 @@ class EstablishmentBaseSerializer(ProjectModelSerializer):
'new_image',
'tz',
'wine_regions',
+ 'distillery_types',
]
diff --git a/apps/establishment/views/back.py b/apps/establishment/views/back.py
index a195e31a..19a75fd5 100644
--- a/apps/establishment/views/back.py
+++ b/apps/establishment/views/back.py
@@ -19,7 +19,7 @@ class EstablishmentMixinViews:
def get_queryset(self):
"""Overrided method 'get_queryset'."""
- return models.Establishment.objects.published().with_base_related()
+ return models.Establishment.objects.with_base_related()
class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAPIView):
diff --git a/apps/news/serializers.py b/apps/news/serializers.py
index 2c7983d1..c0df73a4 100644
--- a/apps/news/serializers.py
+++ b/apps/news/serializers.py
@@ -232,9 +232,9 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer):
def create(self, validated_data):
slugs = validated_data.get('slugs')
- slugs_list = list(map(lambda x: x.lower(), slugs.values()))
- slugs_set = set(slugs_list)
if slugs:
+ slugs_list = list(map(lambda x: x.lower(), slugs.values()))
+ slugs_set = set(slugs_list)
if models.News.objects.filter(
slugs__values__contains=list(slugs.values())
).exists() or len(slugs_list) != len(slugs_set):
@@ -249,9 +249,9 @@ class NewsBackOfficeBaseSerializer(NewsBaseSerializer):
def update(self, instance, validated_data):
slugs = validated_data.get('slugs')
- slugs_list = list(map(lambda x: x.lower(), slugs.values()))
- slugs_set = set(slugs_list)
if slugs:
+ slugs_list = list(map(lambda x: x.lower(), slugs.values()))
+ slugs_set = set(slugs_list)
if models.News.objects.filter(
slugs__values__contains=list(slugs.values())
).exists() or len(slugs_list) != len(slugs_set):
diff --git a/apps/news/tasks.py b/apps/news/tasks.py
index 0267fcf4..21afdafb 100644
--- a/apps/news/tasks.py
+++ b/apps/news/tasks.py
@@ -15,27 +15,35 @@ from notification.models import Subscribe
@shared_task
def send_email_with_news(news_ids):
subscribes = Subscribe.objects.all() \
- .prefetch_related('subscriber') \
- .prefetch_related('subscription_type')
+ .prefetch_related('subscriber', 'subscription_type') \
+ .active()
+
sent_news = models.News.objects.filter(id__in=news_ids)
- htmly = get_template(settings.NEWS_EMAIL_TEMPLATE)
+ html_template = get_template(settings.NEWS_EMAIL_TEMPLATE)
year = datetime.now().year
socials = list(SiteSettings.objects.with_country().select_related('country'))
socials = dict(zip(map(lambda social: social.country.code, socials), socials))
- for subscribe in subscribes.filter(unsubscribe_date=None):
+ for subscribe in subscribes:
+ subscriber = subscribe.subscriber
+
country = subscribe.subscription_type.country
if country is None:
- continue
+ continue # subscription_type has no country
- socials_for_subscriber = socials.get(country.code)
- subscriber = subscribe.subscriber
+ else:
+ country_code = country.code
+
+ socials_for_subscriber = socials.get(country_code)
try:
for new in sent_news:
+ if new.country.code != country_code:
+ continue
+
context = {
"title": new.title.get(subscriber.locale),
"subtitle": new.subtitle.get(subscriber.locale),
@@ -43,7 +51,7 @@ def send_email_with_news(news_ids):
"code": subscriber.update_code,
"image_url": new.image_url if new.image_url not in EMPTY_VALUES else None,
"domain_uri": settings.DOMAIN_URI,
- "slug": new.slug,
+ "slug": new.slugs.get(subscriber.locale),
"country_code": subscriber.country_code,
"twitter_page_url": socials_for_subscriber.twitter_page_url if socials_for_subscriber else '#',
"instagram_page_url": socials_for_subscriber.instagram_page_url if socials_for_subscriber else '#',
@@ -52,9 +60,12 @@ def send_email_with_news(news_ids):
"year": year
}
send_mail(
- "G&M News", render_to_string(settings.NEWS_EMAIL_TEMPLATE, context),
- settings.EMAIL_HOST_USER, [subscriber.send_to], fail_silently=False,
- html_message=htmly.render(context)
+ subject="G&M News",
+ message=render_to_string(settings.NEWS_EMAIL_TEMPLATE, context),
+ from_email=settings.EMAIL_HOST_USER,
+ recipient_list=[subscriber.send_to],
+ fail_silently=False,
+ html_message=html_template.render(context)
)
except SMTPException:
continue
diff --git a/apps/notification/models.py b/apps/notification/models.py
index 88c87ce1..c7d277d8 100644
--- a/apps/notification/models.py
+++ b/apps/notification/models.py
@@ -9,6 +9,7 @@ from account.models import User
from location.models import Country
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):
@@ -116,6 +117,11 @@ class Subscriber(ProjectBaseMixin):
"""Unsubscribe user."""
self.subscribe_set.update(unsubscribe_date=now())
+ if settings.USE_CELERY:
+ send_unsubscribe_email.delay(self.pk)
+ else:
+ send_unsubscribe_email(self.pk)
+
@property
def send_to(self):
"""Actual email."""
@@ -135,6 +141,13 @@ class Subscriber(ProjectBaseMixin):
return self.subscription_types.exclude(subscriber__subscribe__unsubscribe_date__isnull=False)
+class SubscribeQuerySet(models.QuerySet):
+
+ def active(self, switcher=True):
+ """Fetches active subscriptions."""
+ return self.exclude(unsubscribe_date__isnull=not switcher)
+
+
class Subscribe(ProjectBaseMixin):
"""Subscribe model."""
@@ -144,6 +157,8 @@ class Subscribe(ProjectBaseMixin):
subscriber = models.ForeignKey(Subscriber, on_delete=models.CASCADE)
subscription_type = models.ForeignKey(SubscriptionType, on_delete=models.CASCADE)
+ objects = SubscribeQuerySet.as_manager()
+
class Meta:
"""Meta class."""
diff --git a/apps/notification/serializers/common.py b/apps/notification/serializers/common.py
index e1e69991..88585da7 100644
--- a/apps/notification/serializers/common.py
+++ b/apps/notification/serializers/common.py
@@ -1,10 +1,11 @@
"""Notification app serializers."""
-
+from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from location.serializers import CountrySimpleSerializer
from notification import models
+from notification.tasks import send_subscribes_update_email
from utils.methods import get_user_ip
from utils.serializers import TranslatedField
@@ -74,7 +75,23 @@ class CreateSubscribeSerializer(serializers.ModelSerializer):
def create(self, validated_data):
"""Create obj."""
- return models.Subscriber.objects.make_subscriber(**validated_data)
+
+ subscriber = models.Subscriber.objects.make_subscriber(**validated_data)
+
+ if settings.USE_CELERY:
+ send_subscribes_update_email.delay(subscriber.pk)
+ else:
+ send_subscribes_update_email(subscriber.pk)
+
+ 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 SubscribeObjectSerializer(serializers.ModelSerializer):
diff --git a/apps/notification/tasks.py b/apps/notification/tasks.py
new file mode 100644
index 00000000..38b11cd5
--- /dev/null
+++ b/apps/notification/tasks.py
@@ -0,0 +1,84 @@
+from datetime import datetime
+
+from celery import shared_task
+from django.conf import settings
+from django.core.mail import send_mail
+from django.template.loader import get_template, render_to_string
+
+from main.models import SiteSettings
+from notification import models
+
+
+@shared_task
+def send_subscribes_update_email(subscriber_id):
+ subscriber = models.Subscriber.objects.get(id=subscriber_id)
+
+ country_code = subscriber.country_code
+
+ html_template = get_template(settings.NOTIFICATION_SUBSCRIBE_TEMPLATE)
+ year = datetime.now().year
+
+ socials = list(SiteSettings.objects.with_country().select_related('country'))
+ socials = dict(zip(map(lambda social: social.country.code, socials), socials))
+
+ socials_for_subscriber = socials.get(country_code)
+
+ context = {
+ "title": "You have subscribed on news G&M",
+ "description": "
".join([
+ name.get(subscriber.locale)
+ for name in subscriber.subscription_types.values_list('name', flat=True)
+ ]),
+ "code": subscriber.update_code,
+ "link_to_unsubscribe": subscriber.link_to_unsubscribe,
+ "twitter_page_url": socials_for_subscriber.twitter_page_url if socials_for_subscriber else '#',
+ "instagram_page_url": socials_for_subscriber.instagram_page_url if socials_for_subscriber else '#',
+ "facebook_page_url": socials_for_subscriber.facebook_page_url if socials_for_subscriber else '#',
+ "send_to": subscriber.send_to,
+ "year": year
+ }
+
+ send_mail(
+ subject="G&M Subscriptions",
+ message=render_to_string(settings.NOTIFICATION_SUBSCRIBE_TEMPLATE, context),
+ from_email=settings.EMAIL_HOST_USER,
+ recipient_list=[subscriber.send_to],
+ fail_silently=False,
+ html_message=html_template.render(context)
+ )
+
+
+@shared_task
+def send_unsubscribe_email(subscriber_id):
+ subscriber = models.Subscriber.objects.get(id=subscriber_id)
+
+ country_code = subscriber.country_code
+
+ html_template = get_template(settings.NOTIFICATION_SUBSCRIBE_TEMPLATE)
+ year = datetime.now().year
+
+ socials = list(SiteSettings.objects.with_country().select_related('country'))
+ socials = dict(zip(map(lambda social: social.country.code, socials), socials))
+
+ socials_for_subscriber = socials.get(country_code)
+
+ context = {
+ "title": "You have successfully unsubscribed from G&M news",
+ "description": "",
+ "code": subscriber.update_code,
+ "link_to_unsubscribe": subscriber.link_to_unsubscribe,
+ "twitter_page_url": socials_for_subscriber.twitter_page_url if socials_for_subscriber else '#',
+ "instagram_page_url": socials_for_subscriber.instagram_page_url if socials_for_subscriber else '#',
+ "facebook_page_url": socials_for_subscriber.facebook_page_url if socials_for_subscriber else '#',
+ "send_to": subscriber.send_to,
+ "year": year
+ }
+
+ send_mail(
+ subject="G&M Subscriptions",
+ message=render_to_string(settings.NOTIFICATION_SUBSCRIBE_TEMPLATE, context),
+ from_email=settings.EMAIL_HOST_USER,
+ recipient_list=[subscriber.send_to],
+ fail_silently=False,
+ html_message=html_template.render(context)
+ )
diff --git a/apps/search_indexes/documents/establishment.py b/apps/search_indexes/documents/establishment.py
index 4a975615..20185b9c 100644
--- a/apps/search_indexes/documents/establishment.py
+++ b/apps/search_indexes/documents/establishment.py
@@ -85,6 +85,14 @@ class EstablishmentDocument(Document):
'value': fields.KeywordField(),
},
multi=True)
+ distillery_types = fields.ObjectField(
+ properties={
+ 'id': fields.IntegerField(attr='id'),
+ 'label': fields.ObjectField(attr='label_indexing',
+ properties=OBJECT_FIELD_PROPERTIES),
+ 'value': fields.KeywordField(),
+ },
+ multi=True)
products = fields.ObjectField(
properties={
'wine_origins': fields.ListField(
diff --git a/apps/search_indexes/serializers.py b/apps/search_indexes/serializers.py
index be0d517d..b3c55ab1 100644
--- a/apps/search_indexes/serializers.py
+++ b/apps/search_indexes/serializers.py
@@ -277,6 +277,7 @@ class EstablishmentDocumentSerializer(InFavoritesMixin, DocumentSerializer):
tags = TagsDocumentSerializer(many=True, source='visible_tags')
restaurant_category = TagsDocumentSerializer(many=True, allow_null=True)
restaurant_cuisine = TagsDocumentSerializer(many=True, allow_null=True)
+ distillery_types = TagsDocumentSerializer(many=True, allow_null=True)
artisan_category = TagsDocumentSerializer(many=True, allow_null=True)
schedule = ScheduleDocumentSerializer(many=True, allow_null=True)
wine_origins = WineOriginSerializer(many=True)
@@ -310,6 +311,16 @@ class EstablishmentDocumentSerializer(InFavoritesMixin, DocumentSerializer):
# 'collections',
'type',
'subtypes',
+ 'distillery_types',
+ )
+
+
+class EstablishmentBackOfficeDocumentSerializer(EstablishmentDocumentSerializer):
+
+ class Meta(EstablishmentDocumentSerializer.Meta):
+ document = EstablishmentDocumentSerializer.Meta.document
+ fields = EstablishmentDocumentSerializer.Meta.fields + (
+ 'created',
)
diff --git a/apps/search_indexes/views.py b/apps/search_indexes/views.py
index fd896738..a3399fc1 100644
--- a/apps/search_indexes/views.py
+++ b/apps/search_indexes/views.py
@@ -1,5 +1,5 @@
"""Search indexes app views."""
-from django_elasticsearch_dsl_drf import constants
+from django_elasticsearch_dsl_drf import constants, pagination
from django_elasticsearch_dsl_drf.filter_backends import (
FilteringFilterBackend,
GeoSpatialOrderingFilterBackend,
@@ -321,14 +321,26 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet):
class EstablishmentBackOfficeViewSet(EstablishmentDocumentViewSet):
- filter_backends = EstablishmentDocumentViewSet.filter_backends + [
- OrderingFilterBackend
- ]
+ def get_queryset(self):
+ if not self.request.query_params.get('search'):
+ self.request.GET._mutable = True
+ self.request.query_params.update({
+ 'ordering': '-created',
+ })
+ self.request.GET._mutable = False
+ return super(EstablishmentBackOfficeViewSet, self).get_queryset()
+ serializer_class = serializers.EstablishmentBackOfficeDocumentSerializer
+ pagination_class = pagination.PageNumberPagination
+ filter_backends = [
+ FilteringFilterBackend,
+ filters.CustomSearchFilterBackend,
+ filters.CustomGeoSpatialFilteringFilterBackend,
+ GeoSpatialOrderingFilterBackend,
+ OrderingFilterBackend,
+ ]
ordering_fields = {
- 'created': {
- 'field': 'created'
- }
+ 'created': 'created',
}
diff --git a/apps/tag/migrations/0018_auto_20200113_1357.py b/apps/tag/migrations/0018_auto_20200113_1357.py
new file mode 100644
index 00000000..35f4379b
--- /dev/null
+++ b/apps/tag/migrations/0018_auto_20200113_1357.py
@@ -0,0 +1,19 @@
+# Generated by Django 2.2.7 on 2020-01-13 13:57
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('tag', '0017_auto_20191220_1623'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='tagcategory',
+ name='country',
+ field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='location.Country'),
+ ),
+ ]
diff --git a/apps/tag/models.py b/apps/tag/models.py
index 006802f9..2a57a1c4 100644
--- a/apps/tag/models.py
+++ b/apps/tag/models.py
@@ -146,8 +146,8 @@ class TagCategory(models.Model):
(BOOLEAN, _('boolean')),
)
country = models.ForeignKey('location.Country',
- on_delete=models.SET_NULL, null=True,
- default=None)
+ on_delete=models.SET_NULL,
+ blank=True, null=True, default=None)
public = models.BooleanField(default=False)
index_name = models.CharField(max_length=255, blank=True, null=True,
verbose_name=_('indexing name'), unique=True)
diff --git a/project/settings/base.py b/project/settings/base.py
index f34638aa..08a36609 100644
--- a/project/settings/base.py
+++ b/project/settings/base.py
@@ -454,6 +454,7 @@ CHANGE_EMAIL_TEMPLATE = 'account/change_email.html'
CONFIRM_EMAIL_TEMPLATE = 'authorization/confirm_email.html'
NEWS_EMAIL_TEMPLATE = 'news/news_email.html'
NOTIFICATION_PASSWORD_TEMPLATE = 'account/password_change_email.html'
+NOTIFICATION_SUBSCRIBE_TEMPLATE = 'notification/update_email.html'
# COOKIES
diff --git a/project/settings/development.py b/project/settings/development.py
index 88b88789..24a1a795 100644
--- a/project/settings/development.py
+++ b/project/settings/development.py
@@ -74,3 +74,10 @@ CELERY_RESULT_BACKEND = BROKER_URL
CELERY_BROKER_URL = BROKER_URL
COOKIE_DOMAIN = '.id-east.ru'
+
+# Email settings
+EMAIL_USE_TLS = True
+EMAIL_HOST = 'smtp.gmail.com'
+EMAIL_HOST_USER = 'anatolyfeteleu@gmail.com'
+EMAIL_HOST_PASSWORD = 'nggrlnbehzksgmbt'
+EMAIL_PORT = 587
\ No newline at end of file
diff --git a/project/templates/notification/update_email.html b/project/templates/notification/update_email.html
new file mode 100644
index 00000000..0b4444c2
--- /dev/null
+++ b/project/templates/notification/update_email.html
@@ -0,0 +1,75 @@
+
+
+
+ Follow us
+