Merge branch 'develop' into feature/guides

This commit is contained in:
Anatoly 2020-01-09 15:00:58 +03:00
commit a59016944c
18 changed files with 403 additions and 120 deletions

View File

@ -8,6 +8,7 @@ from rest_framework import serializers
from rest_framework import validators as rest_validators from rest_framework import validators as rest_validators
from account import models, tasks from account import models, tasks
from notification.models import Subscriber
from utils import exceptions as utils_exceptions from utils import exceptions as utils_exceptions
from utils import methods as utils_methods from utils import methods as utils_methods
@ -46,6 +47,12 @@ class UserSerializer(serializers.ModelSerializer):
'newsletter', 'newsletter',
] ]
def create(self, validated_data):
user = super(UserSerializer, self).create(validated_data)
validated_data['user'] = user
Subscriber.objects.make_subscriber(**validated_data)
return user
def validate_email(self, value): def validate_email(self, value):
"""Validate email value""" """Validate email value"""
if value == self.instance.email: if value == self.instance.email:

View File

@ -1,43 +1,60 @@
from datetime import datetime from datetime import datetime
from smtplib import SMTPException
from celery import shared_task from celery import shared_task
from django.core.mail import send_mail
from notification.models import Subscriber
from news import models
from django.template.loader import render_to_string, get_template
from django.conf import settings from django.conf import settings
from smtplib import SMTPException from django.core.mail import send_mail
from django.core.validators import EMPTY_VALUES from django.core.validators import EMPTY_VALUES
from django.template.loader import get_template, render_to_string
from main.models import SiteSettings from main.models import SiteSettings
from news import models
from notification.models import Subscribe
@shared_task @shared_task
def send_email_with_news(news_ids): def send_email_with_news(news_ids):
subscribers = Subscriber.objects.filter(state=Subscriber.USABLE) subscribes = Subscribe.objects.all() \
.prefetch_related('subscriber') \
.prefetch_related('subscription_type')
sent_news = models.News.objects.filter(id__in=news_ids) sent_news = models.News.objects.filter(id__in=news_ids)
htmly = get_template(settings.NEWS_EMAIL_TEMPLATE) htmly = get_template(settings.NEWS_EMAIL_TEMPLATE)
year = datetime.now().year year = datetime.now().year
socials = list(SiteSettings.objects.with_country())
socials = dict(zip(map(lambda s: s.country.code, socials), socials)) socials = list(SiteSettings.objects.with_country().select_related('country'))
for s in subscribers: socials = dict(zip(map(lambda social: social.country.code, socials), socials))
socials_for_subscriber = socials.get(s.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: try:
for n in sent_news: for new in sent_news:
context = {"title": n.title.get(s.locale), context = {
"subtitle": n.subtitle.get(s.locale), "title": new.title.get(subscriber.locale),
"description": n.description.get(s.locale), "subtitle": new.subtitle.get(subscriber.locale),
"code": s.update_code, "description": new.description.get(subscriber.locale),
"image_url": n.image_url if n.image_url not in EMPTY_VALUES else None, "code": subscriber.update_code,
"image_url": new.image_url if new.image_url not in EMPTY_VALUES else None,
"domain_uri": settings.DOMAIN_URI, "domain_uri": settings.DOMAIN_URI,
"slug": n.slug, "slug": new.slug,
"country_code": s.country_code, "country_code": subscriber.country_code,
"twitter_page_url": socials_for_subscriber.twitter_page_url if socials_for_subscriber else '#', "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 '#', "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 '#', "facebook_page_url": socials_for_subscriber.facebook_page_url if socials_for_subscriber else '#',
"send_to": s.send_to, "send_to": subscriber.send_to,
"year": year} "year": year
send_mail("G&M News", render_to_string(settings.NEWS_EMAIL_TEMPLATE, context), }
settings.EMAIL_HOST_USER, [s.send_to], fail_silently=False, send_mail(
html_message=htmly.render(context)) "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)
)
except SMTPException: except SMTPException:
continue continue

View File

@ -145,7 +145,7 @@ def add_tags():
) )
if created: if created:
text_value = ' '.join(new_tag.value.split('_')) 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() translation.save()
new_tag.translation = translation new_tag.translation = translation
new_tag.save() new_tag.save()

View File

@ -2,17 +2,17 @@
from django.urls import path from django.urls import path
from news import views from news import views
from search_indexes.views import NewsDocumentViewSet
app_name = 'news' app_name = 'news'
urlpatterns = [ urlpatterns = [
path('', views.NewsBackOfficeLCView.as_view(), name='list-create'), path('', views.NewsBackOfficeLCView.as_view(), name='list-create'),
path('<int:pk>/', views.NewsBackOfficeRUDView.as_view(), path('<int:pk>/', views.NewsBackOfficeRUDView.as_view(), name='retrieve-update-destroy'),
name='retrieve-update-destroy'), path('<int:pk>/gallery/', views.NewsBackOfficeGalleryListView.as_view(), name='gallery-list'),
path('<int:pk>/gallery/', views.NewsBackOfficeGalleryListView.as_view(),
name='gallery-list'),
path('<int:pk>/gallery/<int:image_id>/', views.NewsBackOfficeGalleryCreateDestroyView.as_view(), path('<int:pk>/gallery/<int:image_id>/', views.NewsBackOfficeGalleryCreateDestroyView.as_view(),
name='gallery-create-destroy'), name='gallery-create-destroy'),
path('<int:pk>/carousels/', views.NewsCarouselCreateDestroyView.as_view(), name='create-destroy-carousels'), path('<int:pk>/carousels/', views.NewsCarouselCreateDestroyView.as_view(), name='create-destroy-carousels'),
path('<int:pk>/clone/<str:country_code>', views.NewsCloneView.as_view(), name='clone-news-item'), path('<int:pk>/clone/<str:country_code>', views.NewsCloneView.as_view(), name='clone-news-item'),
path('search/', NewsDocumentViewSet.as_view({'get': 'list'}), name='search-news'),
] ]

View File

@ -0,0 +1,38 @@
# Generated by Django 2.2.7 on 2019-12-27 12:12
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('notification', '0004_auto_20191118_1307'),
]
operations = [
migrations.RemoveField(
model_name='subscriber',
name='subscription_type',
),
migrations.CreateModel(
name='Subscribe',
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')),
('subscriber', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='notification.Subscriber')),
('subscription_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='notification.SubscriptionType')),
],
options={
'verbose_name': 'Subscribe',
'verbose_name_plural': 'Subscribes',
},
),
migrations.AddField(
model_name='subscriber',
name='subscription_types',
field=models.ManyToManyField(through='notification.Subscribe', to='notification.SubscriptionType'),
),
]

View File

@ -0,0 +1,24 @@
# Generated by Django 2.2.7 on 2019-12-27 12:16
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('notification', '0005_auto_20191227_1212'),
]
operations = [
migrations.AddField(
model_name='subscribe',
name='subscribe_date',
field=models.DateTimeField(blank=True, default=django.utils.timezone.now, null=True, verbose_name='Last subscribe date'),
),
migrations.AddField(
model_name='subscribe',
name='unsubscribe_date',
field=models.DateTimeField(blank=True, default=None, null=True, verbose_name='Last unsubscribe date'),
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 2.2.7 on 2019-12-27 14:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('notification', '0006_auto_20191227_1216'),
]
operations = [
migrations.RemoveField(
model_name='subscriber',
name='state',
),
migrations.AddField(
model_name='subscribe',
name='state',
field=models.PositiveIntegerField(choices=[(0, 'Unusable'), (1, 'Usable')], default=1, verbose_name='State'),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 2.2.7 on 2019-12-30 15:04
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('notification', '0007_auto_20191227_1426'),
]
operations = [
migrations.RemoveField(
model_name='subscribe',
name='state',
),
]

View File

@ -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'),
),
]

View File

@ -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'),
),
]

View File

@ -1,10 +1,14 @@
"""Notification app models.""" """Notification app models."""
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from account.models import User from account.models import User
from location.models import Country
from utils.methods import generate_string_code from utils.methods import generate_string_code
from utils.models import ProjectBaseMixin, TranslatedFieldsMixin, TJSONField from utils.models import ProjectBaseMixin, TJSONField, TranslatedFieldsMixin
class SubscriptionType(ProjectBaseMixin, TranslatedFieldsMixin): class SubscriptionType(ProjectBaseMixin, TranslatedFieldsMixin):
@ -12,6 +16,9 @@ class SubscriptionType(ProjectBaseMixin, TranslatedFieldsMixin):
name = TJSONField(blank=True, null=True, default=None, name = TJSONField(blank=True, null=True, default=None,
verbose_name=_('name'), verbose_name=_('name'),
help_text='{"en-GB":"some text"}') help_text='{"en-GB":"some text"}')
country = models.ForeignKey(Country, on_delete=models.PROTECT,
blank=True, null=True, default=None,
verbose_name=_('country'))
# todo: associate user & subscriber after users registration # todo: associate user & subscriber after users registration
@ -19,7 +26,7 @@ class SubscriberManager(models.Manager):
"""Extended manager for Subscriber model.""" """Extended manager for Subscriber model."""
def make_subscriber(self, email=None, user=None, ip_address=None, country_code=None, def make_subscriber(self, email=None, user=None, ip_address=None, country_code=None,
locale=None, subscription_type=None, *args, **kwargs): locale=None, subscription_types=None, *args, **kwargs):
"""Make subscriber and update info.""" """Make subscriber and update info."""
# search existing object # search existing object
if not user: if not user:
@ -40,14 +47,15 @@ class SubscriberManager(models.Manager):
obj.ip_address = ip_address obj.ip_address = ip_address
obj.country_code = country_code obj.country_code = country_code
obj.locale = locale obj.locale = locale
obj.state = self.model.USABLE
obj.update_code = generate_string_code() obj.update_code = generate_string_code()
obj.subscription_type = subscription_type
obj.save() obj.save()
obj.subscription_types.set(subscription_types)
obj.subscribe_set.update(unsubscribe_date=None)
else: else:
obj = self.model.objects.create(user=user, email=email, ip_address=ip_address, obj = self.model.objects.create(user=user, email=email, ip_address=ip_address,
country_code=country_code, locale=locale, country_code=country_code, locale=locale)
subscription_type=subscription_type) obj.subscription_types.set(subscription_types)
obj.subscribe_set.update(unsubscribe_date=None)
return obj return obj
def associate_user(self, user): def associate_user(self, user):
@ -62,27 +70,9 @@ class SubscriberManager(models.Manager):
return obj return obj
class SubscriberQuerySet(models.QuerySet):
"""Extended queryset for Subscriber model."""
def by_usable(self, switcher=True):
if switcher:
return self.filter(state=self.model.USABLE)
else:
return self.filter(state=self.model.UNUSABLE)
class Subscriber(ProjectBaseMixin): class Subscriber(ProjectBaseMixin):
"""Subscriber model.""" """Subscriber model."""
UNUSABLE = 0
USABLE = 1
STATE_CHOICES = (
(UNUSABLE, _('Unusable')),
(USABLE, _('Usable')),
)
user = models.ForeignKey( user = models.ForeignKey(
User, User,
blank=True, blank=True,
@ -96,7 +86,6 @@ class Subscriber(ProjectBaseMixin):
ip_address = models.GenericIPAddressField(blank=True, null=True, default=None, verbose_name=_('IP address')) ip_address = models.GenericIPAddressField(blank=True, null=True, default=None, verbose_name=_('IP address'))
country_code = models.CharField(max_length=10, blank=True, null=True, default=None, verbose_name=_('Country code')) country_code = models.CharField(max_length=10, blank=True, null=True, default=None, verbose_name=_('Country code'))
locale = models.CharField(blank=True, null=True, default=None, max_length=10, verbose_name=_('Locale identifier')) locale = models.CharField(blank=True, null=True, default=None, max_length=10, verbose_name=_('Locale identifier'))
state = models.PositiveIntegerField(choices=STATE_CHOICES, default=USABLE, verbose_name=_('State'))
update_code = models.CharField( update_code = models.CharField(
max_length=254, max_length=254,
blank=True, blank=True,
@ -107,9 +96,9 @@ class Subscriber(ProjectBaseMixin):
) )
old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None) old_id = models.PositiveIntegerField(_('old id'), blank=True, null=True, default=None)
subscription_type = models.ForeignKey(SubscriptionType, on_delete=models.CASCADE, null=True, default=None) subscription_types = models.ManyToManyField(SubscriptionType, through='Subscribe')
objects = SubscriberManager.from_queryset(SubscriberQuerySet)() objects = SubscriberManager()
class Meta: class Meta:
"""Meta class.""" """Meta class."""
@ -123,10 +112,9 @@ class Subscriber(ProjectBaseMixin):
self.update_code = generate_string_code() self.update_code = generate_string_code()
return super(Subscriber, self).save(*args, **kwargs) return super(Subscriber, self).save(*args, **kwargs)
def unsubscribe(self): def unsubscribe(self, query: dict):
"""Unsubscribe user.""" """Unsubscribe user."""
self.state = self.UNUSABLE self.subscribe_set.update(unsubscribe_date=now())
self.save()
@property @property
def send_to(self): def send_to(self):
@ -141,3 +129,19 @@ class Subscriber(ProjectBaseMixin):
url = settings.SITE_REDIRECT_URL_UNSUBSCRIBE url = settings.SITE_REDIRECT_URL_UNSUBSCRIBE
query = f'?code={self.update_code}' query = f'?code={self.update_code}'
return f'{schema}://{site_domain}{url}{query}' return f'{schema}://{site_domain}{url}{query}'
class Subscribe(ProjectBaseMixin):
"""Subscribe model."""
subscribe_date = models.DateTimeField(_('Last subscribe date'), blank=True, null=True, default=now)
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)
class Meta:
"""Meta class."""
verbose_name = _('Subscribe')
verbose_name_plural = _('Subscribes')

View File

@ -1,6 +1,8 @@
"""Notification app serializers.""" """Notification app serializers."""
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from location.serializers import CountrySimpleSerializer
from notification import models from notification import models
from utils.methods import get_user_ip from utils.methods import get_user_ip
from utils.serializers import TranslatedField from utils.serializers import TranslatedField
@ -10,6 +12,8 @@ class SubscriptionTypeSerializer(serializers.ModelSerializer):
"""Subscription type serializer.""" """Subscription type serializer."""
name_translated = TranslatedField() name_translated = TranslatedField()
country = CountrySimpleSerializer()
class Meta: class Meta:
"""Meta class.""" """Meta class."""
@ -18,14 +22,15 @@ class SubscriptionTypeSerializer(serializers.ModelSerializer):
'id', 'id',
'index_name', 'index_name',
'name_translated', 'name_translated',
'country',
) )
class SubscribeSerializer(serializers.ModelSerializer): class CreateSubscribeSerializer(serializers.ModelSerializer):
"""Subscribe serializer.""" """Create Subscribe serializer."""
email = serializers.EmailField(required=False, source='send_to') email = serializers.EmailField(required=False, source='send_to')
subscription_type = SubscriptionTypeSerializer(read_only=True) subscription_types = serializers.PrimaryKeyRelatedField(many=True, queryset=models.SubscriptionType.objects.all())
class Meta: class Meta:
"""Meta class.""" """Meta class."""
@ -33,10 +38,9 @@ class SubscribeSerializer(serializers.ModelSerializer):
model = models.Subscriber model = models.Subscriber
fields = ( fields = (
'email', 'email',
'subscription_type', 'subscription_types',
'state', 'link_to_unsubscribe',
) )
read_only_fields = ('state',)
def validate(self, attrs): def validate(self, attrs):
"""Validate attrs.""" """Validate attrs."""
@ -45,6 +49,10 @@ class SubscribeSerializer(serializers.ModelSerializer):
# validate email # validate email
email = attrs.get('send_to') email = attrs.get('send_to')
if attrs.get('email'):
email = attrs.get('email')
if user.is_authenticated: if user.is_authenticated:
if email is not None and email != user.email: if email is not None and email != user.email:
raise serializers.ValidationError(_('Does not match user email')) raise serializers.ValidationError(_('Does not match user email'))
@ -57,18 +65,40 @@ class SubscribeSerializer(serializers.ModelSerializer):
attrs['country_code'] = request.country_code attrs['country_code'] = request.country_code
attrs['locale'] = request.locale attrs['locale'] = request.locale
attrs['ip_address'] = get_user_ip(request) attrs['ip_address'] = get_user_ip(request)
if user.is_authenticated: if user.is_authenticated:
attrs['user'] = user attrs['user'] = user
subscription_type_id = self.context.get('request').parser_context.get('kwargs').get("subscription_type_pk")
subscription_type_qs = models.SubscriptionType.objects.filter(id=subscription_type_id)
if not subscription_type_qs.exists():
raise serializers.ValidationError({'detail': _(f'SubscriptionType not found.')})
attrs["subscription_type"] = subscription_type_qs.first()
return attrs return attrs
def create(self, validated_data): def create(self, validated_data):
"""Create obj.""" """Create obj."""
subscriber = models.Subscriber.objects.make_subscriber(**validated_data) return models.Subscriber.objects.make_subscriber(**validated_data)
return subscriber
class SubscribeObjectSerializer(serializers.ModelSerializer):
"""Subscribe serializer."""
class Meta:
"""Meta class."""
model = models.Subscriber
fields = ('subscriber', )
read_only_fields = ('subscribe_date', 'unsubscribe_date',)
class SubscribeSerializer(serializers.ModelSerializer):
"""Subscribe serializer."""
email = serializers.EmailField(required=False, source='send_to')
subscription_types = SubscriptionTypeSerializer(many=True, read_only=True)
class Meta:
"""Meta class."""
model = models.Subscriber
fields = (
'email',
'subscription_types',
'link_to_unsubscribe',
)

View File

@ -1,11 +1,15 @@
from datetime import datetime, timedelta
from http.cookies import SimpleCookie from http.cookies import SimpleCookie
from django.test import TestCase
from rest_framework.test import APITestCase
from rest_framework import status from rest_framework import status
from rest_framework.test import APITestCase
from account.models import User from account.models import Role, User, UserRole
from notification.models import Subscriber from location.models import Country
from main.models import SiteSettings
from news.models import News, NewsType
from notification.models import Subscriber, SubscriptionType
from translation.models import Language
class BaseTestCase(APITestCase): class BaseTestCase(APITestCase):
@ -17,23 +21,22 @@ class BaseTestCase(APITestCase):
self.user = User.objects.create_user(username=self.username, email=self.email, password=self.password) self.user = User.objects.create_user(username=self.username, email=self.email, password=self.password)
# get tokens # get tokens
tokens = User.create_jwt_tokens(self.user) tokens = User.create_jwt_tokens(self.user)
self.client.cookies = SimpleCookie({'access_token': tokens.get('access_token'), self.client.cookies = SimpleCookie({
'refresh_token': tokens.get('refresh_token')}) 'access_token': tokens.get('access_token'),
'refresh_token': tokens.get('refresh_token')
})
class NotificationAnonSubscribeTestCase(APITestCase): class NotificationAnonSubscribeTestCase(APITestCase):
def test_subscribe(self): def test_subscribe(self):
test_data = { test_data = {
"email": "test@email.com", "email": "test@email.com"
"state": 1
} }
response = self.client.post("/api/web/notifications/subscribe/", data=test_data, format="json") response = self.client.post("/api/web/notifications/subscribe/", data=test_data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.json()["email"], test_data["email"]) self.assertEqual(response.json()["email"], test_data["email"])
self.assertEqual(response.json()["state"], test_data["state"])
class NotificationSubscribeTestCase(BaseTestCase): class NotificationSubscribeTestCase(BaseTestCase):
@ -42,21 +45,17 @@ class NotificationSubscribeTestCase(BaseTestCase):
super().setUp() super().setUp()
self.test_data = { self.test_data = {
"email": self.email, "email": self.email
"state": 1
} }
def test_subscribe(self): def test_subscribe(self):
response = self.client.post("/api/web/notifications/subscribe/", data=self.test_data, format="json") response = self.client.post("/api/web/notifications/subscribe/", data=self.test_data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.json()["email"], self.email) self.assertEqual(response.json()["email"], self.email)
self.assertEqual(response.json()["state"], self.test_data["state"])
def test_subscribe_info_auth_user(self): def test_subscribe_info_auth_user(self):
Subscriber.objects.create(user=self.user, email=self.email)
Subscriber.objects.create(user=self.user, email=self.email, state=1)
response = self.client.get("/api/web/notifications/subscribe-info/", data=self.test_data, format="json") response = self.client.get("/api/web/notifications/subscribe-info/", data=self.test_data, format="json")
@ -66,13 +65,12 @@ class NotificationSubscribeTestCase(BaseTestCase):
class NotificationSubscribeInfoTestCase(APITestCase): class NotificationSubscribeInfoTestCase(APITestCase):
def test_subscribe_info(self): def test_subscribe_info(self):
self.username = 'sedragurda' self.username = 'sedragurda'
self.password = 'sedragurdaredips19' self.password = 'sedragurdaredips19'
self.email = 'sedragurda@desoz.com' self.email = 'sedragurda@desoz.com'
self.user = User.objects.create_user(username=self.username, email=self.email, password=self.password) self.user = User.objects.create_user(username=self.username, email=self.email, password=self.password)
test_subscriber = Subscriber.objects.create(user=self.user, email=self.email, state=1) test_subscriber = Subscriber.objects.create(user=self.user, email=self.email)
response = self.client.get(f"/api/web/notifications/subscribe-info/{test_subscriber.update_code}/") response = self.client.get(f"/api/web/notifications/subscribe-info/{test_subscriber.update_code}/")
@ -82,12 +80,10 @@ class NotificationSubscribeInfoTestCase(APITestCase):
class NotificationUnsubscribeAuthUserTestCase(BaseTestCase): class NotificationUnsubscribeAuthUserTestCase(BaseTestCase):
def test_unsubscribe_auth_user(self): def test_unsubscribe_auth_user(self):
Subscriber.objects.create(user=self.user, email=self.email)
Subscriber.objects.create(user=self.user, email=self.email, state=1)
self.test_data = { self.test_data = {
"email": self.email, "email": self.email
"state": 1
} }
response = self.client.patch("/api/web/notifications/unsubscribe/", data=self.test_data, format="json") response = self.client.patch("/api/web/notifications/unsubscribe/", data=self.test_data, format="json")
@ -104,13 +100,102 @@ class NotificationUnsubscribeTestCase(APITestCase):
self.user = User.objects.create_user(username=self.username, email=self.email, password=self.password) self.user = User.objects.create_user(username=self.username, email=self.email, password=self.password)
self.test_data = { self.test_data = {
"email": self.email, "email": self.email
"state": 1
} }
test_subscriber = Subscriber.objects.create(user=self.user, email=self.email, state=1) test_subscriber = Subscriber.objects.create(user=self.user, email=self.email)
response = self.client.patch(f"/api/web/notifications/unsubscribe/{test_subscriber.update_code}/", response = self.client.patch(f"/api/web/notifications/unsubscribe/{test_subscriber.update_code}/",
data=self.test_data, format="json") data=self.test_data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
class NotificationManySubscribeTestCase(APITestCase):
def test_subscribe(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)
# get tokens
tokens = User.create_jwt_tokens(self.user)
self.client.cookies = SimpleCookie(
{
'access_token': tokens.get('access_token'),
'refresh_token': tokens.get('refresh_token')
})
self.test_news_type = NewsType.objects.create(name="Test news type")
self.lang, created = Language.objects.get_or_create(
title='Russia',
locale='ru-RU'
)
self.country_ru, created = Country.objects.get_or_create(
name={"en-GB": "Russian"}
)
self.site_ru, created = SiteSettings.objects.get_or_create(
subdomain='ru'
)
role = Role.objects.create(
role=Role.CONTENT_PAGE_MANAGER,
site_id=self.site_ru.id
)
role.save()
user_role = UserRole.objects.create(
user=self.user,
role=role
)
user_role.save()
self.test_news = News.objects.create(
created_by=self.user, modified_by=self.user,
title={"ru-RU": "Test news"},
news_type=self.test_news_type,
description={"ru-RU": "Description test news"},
end=datetime.now() + timedelta(hours=2),
state=News.PUBLISHED,
slugs={'en-GB': 'test-news-slug'},
country=self.country_ru,
site=self.site_ru
)
self.test_subscribe_type = SubscriptionType.objects.create(
index_name='test_index_name',
name={"ru-RU": "Test subscription type"}
)
test_data = {
'email': self.email,
'subscription_types_pk': [
self.test_subscribe_type.id
]
}
response = self.client.post("/api/web/notifications/subscribe/", data=test_data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
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
}
test_subscriber = Subscriber.objects.create(user=self.user, email=self.email)
response = self.client.patch(f"/api/web/notifications/unsubscribe/{test_subscriber.update_code}/",
data=test_data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)

View File

@ -0,0 +1 @@
urlpatterns = []

View File

@ -5,7 +5,7 @@ from notification.views import common
app_name = "notification" app_name = "notification"
urlpatterns = [ urlpatterns = [
path('subscribe/<int:subscription_type_pk>', common.SubscribeView.as_view(), name='subscribe'), path('subscribe/', common.CreateSubscribeView.as_view(), name='create-subscribe'),
path('subscribe-info/', common.SubscribeInfoAuthUserView.as_view(), name='check-code-auth'), path('subscribe-info/', common.SubscribeInfoAuthUserView.as_view(), name='check-code-auth'),
path('subscribe-info/<code>/', common.SubscribeInfoView.as_view(), name='check-code'), path('subscribe-info/<code>/', common.SubscribeInfoView.as_view(), name='check-code'),
path('unsubscribe/', common.UnsubscribeAuthUserView.as_view(), name='unsubscribe-auth'), path('unsubscribe/', common.UnsubscribeAuthUserView.as_view(), name='unsubscribe-auth'),

View File

@ -2,22 +2,17 @@
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from rest_framework import generics, permissions from rest_framework import generics, permissions
from rest_framework.response import Response from rest_framework.response import Response
from notification import models from notification import models
from notification.serializers import common as serializers from notification.serializers import common as serializers
class SubscribeView(generics.GenericAPIView): class CreateSubscribeView(generics.CreateAPIView):
"""Subscribe View.""" """Create subscribe View."""
queryset = models.Subscriber.objects.all() queryset = models.Subscriber.objects.all()
permission_classes = (permissions.AllowAny, ) permission_classes = (permissions.AllowAny,)
serializer_class = serializers.SubscribeSerializer serializer_class = serializers.CreateSubscribeSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(data=serializer.data)
class SubscribeInfoView(generics.RetrieveAPIView): class SubscribeInfoView(generics.RetrieveAPIView):
@ -25,7 +20,7 @@ class SubscribeInfoView(generics.RetrieveAPIView):
lookup_field = 'update_code' lookup_field = 'update_code'
lookup_url_kwarg = 'code' lookup_url_kwarg = 'code'
permission_classes = (permissions.AllowAny, ) permission_classes = (permissions.AllowAny,)
queryset = models.Subscriber.objects.all() queryset = models.Subscriber.objects.all()
serializer_class = serializers.SubscribeSerializer serializer_class = serializers.SubscribeSerializer
@ -33,7 +28,7 @@ class SubscribeInfoView(generics.RetrieveAPIView):
class SubscribeInfoAuthUserView(generics.ListAPIView): class SubscribeInfoAuthUserView(generics.ListAPIView):
"""Subscribe info auth user view.""" """Subscribe info auth user view."""
permission_classes = (permissions.IsAuthenticated, ) permission_classes = (permissions.IsAuthenticated,)
serializer_class = serializers.SubscribeSerializer serializer_class = serializers.SubscribeSerializer
def get_queryset(self): def get_queryset(self):
@ -42,18 +37,18 @@ class SubscribeInfoAuthUserView(generics.ListAPIView):
return queryset.filter(user=user) return queryset.filter(user=user)
class UnsubscribeView(generics.GenericAPIView): class UnsubscribeView(generics.UpdateAPIView):
"""Unsubscribe view.""" """Unsubscribe view."""
lookup_field = 'update_code' lookup_field = 'update_code'
lookup_url_kwarg = 'code' lookup_url_kwarg = 'code'
permission_classes = (permissions.AllowAny, ) permission_classes = (permissions.AllowAny,)
queryset = models.Subscriber.objects.all() queryset = models.Subscriber.objects.all()
serializer_class = serializers.SubscribeSerializer serializer_class = serializers.SubscribeSerializer
def patch(self, request, *args, **kw): def patch(self, request, *args, **kw):
obj = self.get_object() obj = self.get_object()
obj.unsubscribe() obj.unsubscribe(request.query_params)
serializer = self.get_serializer(instance=obj) serializer = self.get_serializer(instance=obj)
return Response(data=serializer.data) return Response(data=serializer.data)
@ -61,14 +56,14 @@ class UnsubscribeView(generics.GenericAPIView):
class UnsubscribeAuthUserView(generics.GenericAPIView): class UnsubscribeAuthUserView(generics.GenericAPIView):
"""Unsubscribe auth user view.""" """Unsubscribe auth user view."""
permission_classes = (permissions.IsAuthenticated, ) permission_classes = (permissions.IsAuthenticated,)
queryset = models.Subscriber.objects.all() queryset = models.Subscriber.objects.all()
serializer_class = serializers.SubscribeSerializer serializer_class = serializers.SubscribeSerializer
def patch(self, request, *args, **kw): def patch(self, request, *args, **kw):
user = request.user user = request.user
obj = get_object_or_404(models.Subscriber, user=user) obj = get_object_or_404(models.Subscriber, user=user)
obj.unsubscribe() obj.unsubscribe(request.query_params)
serializer = self.get_serializer(instance=obj) serializer = self.get_serializer(instance=obj)
return Response(data=serializer.data) return Response(data=serializer.data)
@ -78,4 +73,3 @@ class SubscriptionTypesView(generics.ListAPIView):
permission_classes = (permissions.AllowAny,) permission_classes = (permissions.AllowAny,)
queryset = models.SubscriptionType.objects.all() queryset = models.SubscriptionType.objects.all()
serializer_class = serializers.SubscriptionTypeSerializer serializer_class = serializers.SubscriptionTypeSerializer

View File

@ -10,10 +10,12 @@ class TagsBaseFilterSet(filters.FilterSet):
# Object type choices # Object type choices
NEWS = 'news' NEWS = 'news'
ESTABLISHMENT = 'establishment' ESTABLISHMENT = 'establishment'
RECIPES = 'recipe'
TYPE_CHOICES = ( TYPE_CHOICES = (
(NEWS, 'News'), (NEWS, 'News'),
(ESTABLISHMENT, 'Establishment'), (ESTABLISHMENT, 'Establishment'),
(RECIPES, 'Recipe'),
) )
type = filters.MultipleChoiceFilter(choices=TYPE_CHOICES, type = filters.MultipleChoiceFilter(choices=TYPE_CHOICES,
@ -91,5 +93,7 @@ class TagsFilterSet(TagsBaseFilterSet):
if self.ESTABLISHMENT in value: if self.ESTABLISHMENT in value:
queryset = queryset.for_establishments().filter(category__value_type='list').filter(value__in=settings.ESTABLISHMENT_CHOSEN_TAGS).distinct( queryset = queryset.for_establishments().filter(category__value_type='list').filter(value__in=settings.ESTABLISHMENT_CHOSEN_TAGS).distinct(
'value') 'value')
if self.RECIPES in value:
queryset = queryset.for_news().filter(value__in=settings.RECIPES_CHOSEN_TAGS).distinct('value')
return queryset return queryset

View File

@ -10,6 +10,7 @@ urlpatterns = [
path('gallery/', include(('gallery.urls', 'gallery'), namespace='gallery')), path('gallery/', include(('gallery.urls', 'gallery'), namespace='gallery')),
path('location/', include('location.urls.back')), path('location/', include('location.urls.back')),
path('news/', include('news.urls.back')), path('news/', include('news.urls.back')),
path('notifications/', include(('notification.urls.back', 'notification'), namespace='notification')),
path('review/', include('review.urls.back')), path('review/', include('review.urls.back')),
path('tags/', include(('tag.urls.back', 'tag'), namespace='tag')), path('tags/', include(('tag.urls.back', 'tag'), namespace='tag')),
path('products/', include(('product.urls.back', 'product'), namespace='product')), path('products/', include(('product.urls.back', 'product'), namespace='product')),