-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(create notifications): create notifications feature
- create notitifcations app - setup folder structure - setup models feat(create notifications): write tests for endpoints - write tests for retrieving all notififcations - write tests for opting in or out of notifications feat(create notifications): create comment notifications - setup comment notification model - setup comment notification serializer - setup comment notification views - implement tests for the views
- Loading branch information
zaabu
committed
Oct 21, 2018
1 parent
3204619
commit 8b1a373
Showing
30 changed files
with
2,279 additions
and
52 deletions.
There are no files selected for viewing
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 |
---|---|---|
@@ -1,4 +1,5 @@ | ||
[run] | ||
omit = | ||
*/settings/* | ||
*/wsgi.py | ||
*/wsgi.py | ||
authors/apps/notifications/cron_job.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
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
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,9 @@ | ||
from django.contrib import admin | ||
from .models import Notification, CommentNotification | ||
|
||
# Register your models here. | ||
|
||
admin.site.register(Notification) | ||
admin.site.register(CommentNotification) | ||
|
||
|
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,5 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class NotificationsConfig(AppConfig): | ||
name = 'notifications' |
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,52 @@ | ||
from django_cron import CronJobBase, Schedule | ||
from django.conf import settings | ||
from django.template.loader import render_to_string | ||
from django.core.mail import EmailMessage | ||
|
||
from .models import Notification, CommentNotification | ||
|
||
|
||
class EmailNotificationCron(CronJobBase): | ||
"""Create the cron job for email sending.""" | ||
|
||
RUN_EVERY_MINS = settings.RUN_EVERY_MINS | ||
schedule = Schedule(run_every_mins=RUN_EVERY_MINS) | ||
code = 'authors.apps.notifications.cron_job.EmailNotificationCron' | ||
|
||
def do(self): | ||
"""Send emails to all the persons to be notified.""" | ||
subject = 'Authors Haven Notification' | ||
|
||
article_recipients = [] | ||
notifications = Notification.objects.all() | ||
message_template = "article_notification.html" | ||
self.send_notification( | ||
notifications, article_recipients, message_template, subject) | ||
|
||
comment_recipients = [] | ||
notifications = CommentNotification.objects.all() | ||
message_template = "comment_notification.html" | ||
self.send_notification( | ||
notifications, comment_recipients, message_template, subject) | ||
|
||
def send_notification(self, notifications, recipients, message_template, subject): | ||
for notification in notifications: | ||
if not notification.email_sent: | ||
self.get_recipients(notification, recipients) | ||
content = {'notification': notification} | ||
message = render_to_string(message_template, content) | ||
mail = EmailMessage( | ||
subject=subject, | ||
body=message, | ||
to=recipients, | ||
from_email=settings.EMAIL_HOST_USER) | ||
mail.content_subtype = "html" | ||
mail.send(fail_silently=False) | ||
notification.email_sent = True | ||
notification.save() | ||
|
||
def get_recipients(self, notification, recipients): | ||
for user in notification.notified.all(): | ||
if (user not in notification.read.all() | ||
and user.profile.email_notification_enabled): | ||
recipients.append(user.email) |
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,51 @@ | ||
# Generated by Django 2.1.1 on 2018-10-19 09:45 | ||
|
||
from django.conf import settings | ||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
('comments', '0007_merge_20181017_1744'), | ||
('articles', '0012_merge_20181019_1051'), | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='CommentNotification', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('notification', models.TextField()), | ||
('created_at', models.DateTimeField(auto_now_add=True)), | ||
('classification', models.TextField(default='comment')), | ||
('email_sent', models.BooleanField(default=False)), | ||
('comment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='comments.Comment')), | ||
('notified', models.ManyToManyField(blank=True, related_name='comment_notified', to=settings.AUTH_USER_MODEL)), | ||
('read', models.ManyToManyField(blank=True, related_name='comment_read', to=settings.AUTH_USER_MODEL)), | ||
], | ||
options={ | ||
'ordering': ['-created_at'], | ||
}, | ||
), | ||
migrations.CreateModel( | ||
name='Notification', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('notification', models.TextField()), | ||
('created_at', models.DateTimeField(auto_now_add=True)), | ||
('classification', models.TextField(default='article')), | ||
('email_sent', models.BooleanField(default=False)), | ||
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='articles.Article')), | ||
('notified', models.ManyToManyField(blank=True, related_name='notified', to=settings.AUTH_USER_MODEL)), | ||
('read', models.ManyToManyField(blank=True, related_name='read', to=settings.AUTH_USER_MODEL)), | ||
], | ||
options={ | ||
'ordering': ['-created_at'], | ||
}, | ||
), | ||
] |
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,100 @@ | ||
from django.db import models | ||
from authors.apps.authentication.models import User | ||
from authors.apps.profiles.models import Profile | ||
from authors.apps.articles.models import Article | ||
from authors.apps.comments.models import Comment | ||
from authors.apps.profiles.models import FollowingUser | ||
|
||
|
||
class Notification(models.Model): | ||
""" | ||
Defines fields for notifications. | ||
""" | ||
|
||
class Meta: | ||
# Order notification by time notified | ||
ordering = ['-created_at'] | ||
|
||
# article to send | ||
article = models.ForeignKey(Article, on_delete=models.CASCADE) | ||
# notification message | ||
notification = models.TextField() | ||
created_at = models.DateTimeField(auto_now_add=True) | ||
# users to be notified | ||
notified = models.ManyToManyField( | ||
User, related_name='notified', blank=True) | ||
# users that have read | ||
read = models.ManyToManyField(User, related_name='read', blank=True) | ||
classification = models.TextField(default="article") | ||
# check whether email has been sent | ||
email_sent = models.BooleanField(default=False) | ||
|
||
def __str__(self): | ||
"Returns a string representation of notification." | ||
return self.notification | ||
|
||
|
||
def notify_follower(author, notification, article): | ||
""" | ||
Function that adds a notification to the Notification model. | ||
in order to add them to the notified column of the notification. | ||
""" | ||
created_notification = Notification.objects.create( | ||
notification=notification, classification="article", article=article) | ||
|
||
userlist = FollowingUser.objects.filter( | ||
followed_user=author).values_list( | ||
'following_user', flat=True) | ||
followers = User.objects.filter(id__in=userlist) | ||
|
||
for follower in followers: | ||
# checks if notification is set to True | ||
if follower.profile.app_notification_enabled is True: | ||
created_notification.notified.add(follower.id) | ||
created_notification.save() | ||
|
||
|
||
class CommentNotification(models.Model): | ||
""" | ||
Defines fields for notifications. | ||
""" | ||
|
||
class Meta: | ||
# Order notification by time notified | ||
ordering = ['-created_at'] | ||
|
||
# article to send | ||
comment = models.ForeignKey(Comment, on_delete=models.CASCADE) | ||
# notification message | ||
notification = models.TextField() | ||
created_at = models.DateTimeField(auto_now_add=True) | ||
# users to be notified | ||
notified = models.ManyToManyField( | ||
User, related_name='comment_notified', blank=True) | ||
# users that have read | ||
read = models.ManyToManyField( | ||
User, related_name='comment_read', blank=True) | ||
classification = models.TextField(default="comment") | ||
# check whether email has been sent | ||
email_sent = models.BooleanField(default=False) | ||
|
||
def __str__(self): | ||
"Returns a string representation of notification." | ||
return self.notification | ||
|
||
|
||
def notify_comment_follower(article_id, notification, comment): | ||
""" | ||
Function that adds a notification to the Notification model. | ||
in order to add them to the notified column of the notification. | ||
""" | ||
created_notification = CommentNotification.objects.create( | ||
notification=notification, classification="comment", comment=comment) | ||
|
||
followers = Profile.objects.filter(favorites=article_id) | ||
|
||
for follower in followers: | ||
# checks if notification is set to True | ||
if follower.app_notification_enabled is True: | ||
created_notification.notified.add(follower.id) | ||
created_notification.save() |
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,25 @@ | ||
import json | ||
|
||
from rest_framework.renderers import JSONRenderer | ||
|
||
|
||
class NotificationJSONRenderer(JSONRenderer): | ||
charset = 'utf-8' | ||
|
||
def render(self, data, media_type=None, renderer_context=None): | ||
""" | ||
Check for errors key in data | ||
""" | ||
errors = data.get('errors', None) | ||
|
||
if errors: | ||
""" | ||
We will let the default JSONRenderer handle | ||
rendering errors. | ||
""" | ||
return super(NotificationJSONRenderer, self).render(data) | ||
|
||
# Finally, we can render our data under the "profile" namespace. | ||
return json.dumps({ | ||
'notification': data | ||
}) |
Oops, something went wrong.