-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
39 changed files
with
961 additions
and
160 deletions.
There are no files selected for viewing
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.contrib import admin | ||
|
||
# Register your models here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Generated by Django 2.2 on 2019-05-10 10:13 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='UserNotification', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('email_notifications_subscription', models.BooleanField(default=True)), | ||
('in_app_notifications_subscription', models.BooleanField(default=True)), | ||
], | ||
), | ||
] |
23 changes: 23 additions & 0 deletions
23
authors/apps/appnotifications/migrations/0002_usernotification_user.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Generated by Django 2.2 on 2019-05-10 10:13 | ||
|
||
from django.conf import settings | ||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
('appnotifications', '0001_initial'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='usernotification', | ||
name='user', | ||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='notification_preferences', to=settings.AUTH_USER_MODEL), | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from django.conf import settings | ||
from django.db import models | ||
from django.db.models.signals import post_save | ||
from django.dispatch import receiver | ||
|
||
from authors.apps.authentication.models import User | ||
|
||
from ...utils import notification_handlers | ||
|
||
|
||
class UserNotification(models.Model): | ||
""" | ||
User Notification model stores user's preferences for notifications. | ||
By default all users are "opted in" to notifications | ||
""" | ||
|
||
user = models.OneToOneField(User, on_delete=models.CASCADE, unique=True, | ||
related_name='notification_preferences') | ||
email_notifications_subscription = models.BooleanField(default=True) | ||
in_app_notifications_subscription = models.BooleanField(default=True) | ||
|
||
|
||
@receiver(post_save, sender=User) | ||
def setup_notification_permissions(sender, **kwargs): | ||
instance = kwargs.get('instance') | ||
created = kwargs.get('created', False) | ||
|
||
if created: | ||
data = { | ||
'user': instance, | ||
'email_notifications_subscription': True, | ||
'in_app_notifications_subscription': True | ||
} | ||
UserNotification.objects.create(**data) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from rest_framework import serializers | ||
from notifications.models import Notification | ||
from authors.apps.articles.models import Article | ||
from authors.apps.authentication.models import User | ||
from .models import UserNotification | ||
|
||
|
||
class Subscription(serializers.ModelSerializer): | ||
""" | ||
serializer class for unsubscribing from either email or in-app | ||
notifications | ||
""" | ||
class Meta: | ||
model = UserNotification | ||
fields = ('email_notifications_subscription', | ||
'in_app_notifications_subscription') | ||
|
||
|
||
class NotificationSerializer(serializers.ModelSerializer): | ||
""" | ||
serializer class for notification objects | ||
""" | ||
timestamp = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S") | ||
|
||
class Meta: | ||
model = Notification | ||
fields = ( | ||
'id', | ||
'unread', | ||
'verb', | ||
'timestamp', | ||
'description' | ||
) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from ...articles.tests.basetests import BaseTest | ||
from rest_framework.reverse import reverse | ||
|
||
|
||
class NotificationBaseTest(BaseTest): | ||
""" | ||
Base test case for testing notifications | ||
""" | ||
|
||
follow_url = reverse("profiles:follow", args=["adam"]) | ||
notification_url = reverse("notifications:all-notifications") | ||
unread_notification_url = reverse("notifications:unread-notifications") | ||
subscribe_unsubscribe_url = reverse("notifications:subscription") | ||
|
||
def follow_user(self): | ||
self.is_authenticated("jim@gmail.com", "@Us3r.com") | ||
self.client.post(self.follow_url) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
from rest_framework import status | ||
from .basetest import NotificationBaseTest | ||
from rest_framework.reverse import reverse | ||
|
||
|
||
class TestNotifications(NotificationBaseTest): | ||
""" | ||
Class to test notifications | ||
""" | ||
|
||
def test_successful_notification_article(self): | ||
""" | ||
Test for successful notification of a user upon article creation | ||
""" | ||
self.follow_user() | ||
self.create_article() | ||
self.is_authenticated("jim@gmail.com", "@Us3r.com") | ||
response = self.client.get(self.notification_url) | ||
self.assertEqual( | ||
response.data['message'], 'You have 1 notification(s)') | ||
self.assertIn('notifications', str(response.data)) | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
|
||
def test_no_notifications(self): | ||
""" | ||
Test user having no notifications | ||
""" | ||
self.is_authenticated("jim@gmail.com", "@Us3r.com") | ||
response = self.client.get(self.notification_url) | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
|
||
def test_get_unread_notifications(self): | ||
""" | ||
Test retrieval of unread notifications | ||
""" | ||
self.follow_user() | ||
self.create_article() | ||
self.is_authenticated("jim@gmail.com", "@Us3r.com") | ||
response = self.client.get(self.unread_notification_url) | ||
self.assertIn('notifications', str(response.data)) | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
|
||
def test_subscribe_notififications(self): | ||
""" | ||
Test subscribing and unsubscribing of notifications | ||
""" | ||
self.is_authenticated("jim@gmail.com", "@Us3r.com") | ||
response = self.client.patch(self.subscribe_unsubscribe_url, { | ||
'email_notifications_subscription': 'false', | ||
'in_app_notifications_subscription': 'false'}) | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
|
||
def test_unsubscribe_email_notification(self): | ||
""" | ||
Test unsubscribing from email notifications | ||
""" | ||
response = self.client.get(reverse("notifications:opt_out_link", | ||
args=[self.get_token()])) | ||
self.assertEqual(response.data['message'], | ||
'You have unsubscribed from email notifications') | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
|
||
def test_notification_from_comments(self): | ||
""" | ||
Test notifications from comments | ||
""" | ||
self.is_authenticated("jim@gmail.com", "@Us3r.com") | ||
self.post_favorite() | ||
self.is_authenticated("adam@gmail.com", "@Us3r.com") | ||
self.post_comment() | ||
self.is_authenticated("jim@gmail.com", "@Us3r.com") | ||
response = self.client.get(self.notification_url) | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
from django.urls import path | ||
from . import views | ||
|
||
app_name = 'notifications' | ||
|
||
urlpatterns = [ | ||
path( | ||
'notifications/', | ||
views.AllNotificationsAPIview.as_view(), | ||
name="all-notifications" | ||
), | ||
path( | ||
'notifications/unread/', | ||
views.UnreadNotificationsAPIview.as_view(), | ||
name="unread-notifications" | ||
), | ||
path( | ||
'notifications/subscription/', | ||
views.SubscribeUnsubscribeAPIView.as_view(), | ||
name="subscription" | ||
), | ||
path( | ||
'notifications/unsubscribe_email/<str:token>/', | ||
views.UnsubscribeEmailAPIView.as_view(), | ||
name="opt_out_link" | ||
) | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
from django.shortcuts import get_object_or_404 | ||
from django.utils import timezone | ||
from notifications.models import Notification | ||
from rest_framework import status | ||
from rest_framework.authtoken.models import Token | ||
from rest_framework.generics import (GenericAPIView, ListAPIView, | ||
RetrieveUpdateAPIView) | ||
from rest_framework.permissions import IsAuthenticated | ||
from rest_framework.response import Response | ||
|
||
from authors.apps.appnotifications.models import UserNotification | ||
from authors.apps.authentication.models import User | ||
|
||
from .serializers import NotificationSerializer, Subscription | ||
|
||
|
||
class SubscribeUnsubscribeAPIView(RetrieveUpdateAPIView): | ||
""" | ||
lets users to subscribe and unsubscribe to notifications. | ||
""" | ||
permission_classes = [IsAuthenticated] | ||
serializer_class = Subscription | ||
|
||
def update(self, request, *args, **kwargs): | ||
user = UserNotification.objects.get(user=request.user) | ||
serializer_data = request.data | ||
serializer = self.serializer_class( | ||
instance=user, data=serializer_data, partial=True) | ||
serializer.is_valid(raise_exception=True) | ||
serializer.save() | ||
return Response(serializer.data, status=status.HTTP_200_OK) | ||
|
||
|
||
class UnsubscribeEmailAPIView(GenericAPIView): | ||
""" | ||
lets users to unsubscribe from email notifications. | ||
""" | ||
serializer_class = Subscription | ||
|
||
def get(self, request, *args, **kwargs): | ||
token = kwargs['token'] | ||
token_object = Token.objects.get(key=token) | ||
user = token_object.user | ||
user = UserNotification.objects.get(user=user) | ||
user.email_notifications_subscription = False | ||
user.save() | ||
resp = { | ||
"message": "You have unsubscribed from email notifications" | ||
} | ||
return Response(data=resp, status=status.HTTP_200_OK) | ||
|
||
|
||
class NotificationApiView(ListAPIView): | ||
serializer_class = NotificationSerializer | ||
permission_classes = (IsAuthenticated,) | ||
|
||
def get(self, request, *args, **kwargs): | ||
notifications = self.notifications(request) | ||
serializer = self.serializer_class( | ||
notifications, many=True | ||
) | ||
if notifications.count() == 0: | ||
resp = { | ||
"message": "You have no notifications" | ||
} | ||
else: | ||
resp = { | ||
"message": f"You have {notifications.count()} notification(s)", | ||
"notifications": serializer.data | ||
} | ||
return Response(resp) | ||
|
||
|
||
class AllNotificationsAPIview(NotificationApiView): | ||
""" | ||
list all user's notifications | ||
""" | ||
def notifications(self, request): | ||
request.user.notifications.mark_all_as_read() | ||
request.user.notifications.mark_as_sent() | ||
return request.user.notifications | ||
|
||
|
||
class UnreadNotificationsAPIview(NotificationApiView): | ||
""" | ||
list all user's unread notifications | ||
""" | ||
def notifications(self, request): | ||
request.user.notifications.mark_as_sent() | ||
return request.user.notifications.unread() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class ArticlesConfig(AppConfig): | ||
name = 'articles' | ||
|
||
def ready(self): | ||
from authors.utils import notification_handlers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.