Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Email users #119

Merged
merged 2 commits into from Sep 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion boxes/views.py
Expand Up @@ -7,7 +7,7 @@
from humans.views import LoginRequiredMixin
from django.conf import settings
from .forms import CreateBoxForm, SubmitBoxForm
from .models import Box, Membership, Message
from .models import Box, Membership
from .tasks import process_email


Expand Down
42 changes: 41 additions & 1 deletion humans/admin.py
@@ -1,6 +1,9 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as DefaultUserAdmin
from .models import User
from django.contrib import messages
from django.utils import timezone
from .models import User, Notification
from .tasks import enqueue_email_notifications
from boxes.admin import BoxInline


Expand All @@ -24,6 +27,43 @@ def __init__(self, *args, **kwargs):
}),)


@admin.register(Notification)
class NotificationAdmin(admin.ModelAdmin):
list_display = ('subject',
'sent_at',
'send_to')
list_filter = ('send_to', 'sent_at')

fields = ["subject", "body", "send_to"]
search_fields = ['subject', 'body']

actions = ["send_notification", "delete_selected"]

def delete_model(self, request, obj):
if obj.sent_at:
msg = 'Cannot delete "{}", the notification was already sent'
messages.error(request, msg.format(obj.subject))
else:
obj.delete()

def delete_selected(self, request, queryset):
queryset.filter(sent_at=None).delete()
msg = "Removed all unsent notifications in selection"
messages.success(request, msg)

def send_notification(self, request, queryset):
queryset = queryset.filter(sent_at=None)
for notification in queryset:
send_to = notification.send_to.id if notification.send_to else None
enqueue_email_notifications.delay(notification.id,
send_to)
queryset.update(sent_at=timezone.now())
messages.success(request, "All notifications enqueued for sending")

send_notification.short_description = "Send selected notifications"
delete_selected.short_description = "Delete selected notifications"


admin.site.site_header = "HawkPost Administration"
admin.site.site_title = "HawkPost Admin"
admin.site.index_title = "Project Models"
33 changes: 33 additions & 0 deletions humans/migrations/0007_notification.py
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-08-27 15:52
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('auth', '0007_alter_validators_add_error_messages'),
('humans', '0006_user_server_signed'),
]

operations = [
migrations.CreateModel(
name='Notification',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('subject', models.CharField(max_length=150)),
('body', models.TextField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('sent_at', models.DateTimeField(null=True)),
('send_to', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
],
options={
'verbose_name': 'Notification',
'verbose_name_plural': 'Notifications',
},
),
]
28 changes: 27 additions & 1 deletion humans/models.py
@@ -1,5 +1,5 @@
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.models import AbstractUser, Group
from timezone_field import TimeZoneField


Expand Down Expand Up @@ -27,3 +27,29 @@ def has_public_key(self):
@property
def has_keyserver_url(self):
return True if self.keyserver_url else False


class Notification(models.Model):
"""
These notifications are emails sent to all users (or some subset)
by an Administrator. Just once.
"""

subject = models.CharField(null=False, blank=False, max_length=150)
body = models.TextField(null=False, blank=False)

created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

sent_at = models.DateTimeField(null=True)
send_to = models.ForeignKey(Group, null=True, blank=True)

class Meta:
verbose_name = "Notification"
verbose_name_plural = "Notifications"

def __str__(self):
return self.subject

def delete(self):
return super().delete() if not self.sent_at else False
25 changes: 24 additions & 1 deletion humans/tasks.py
@@ -1,11 +1,12 @@
from celery.task.schedules import crontab
from celery.decorators import periodic_task
from celery import shared_task
from celery.utils.log import get_task_logger
from django.core.mail import EmailMultiAlternatives
from django.conf import settings
from django.db.models import Q
from django.template.loader import render_to_string
from .models import User
from .models import User, Notification
from .utils import key_state
import requests

Expand Down Expand Up @@ -75,3 +76,25 @@ def update_public_keys():
user.save()

logger.info("Finished Updating user keys")


@shared_task
def send_email_notification(subject, body, email):
email = EmailMultiAlternatives(subject, body,
settings.DEFAULT_FROM_EMAIL,
[email])
email.send()


@shared_task
def enqueue_email_notifications(notification_id, group_id):
notification = Notification.objects.get(id=notification_id)
if group_id:
users = User.objects.filter(groups__id=group_id)
else:
users = User.objects.all()

for user in users:
send_email_notification.delay(notification.subject,
notification.body,
user.email)
50 changes: 50 additions & 0 deletions humans/tests.py
@@ -1,6 +1,12 @@
from django.contrib.auth.models import Group
from django.utils import timezone
from django.test import TestCase
from django.core import mail
from django.conf import settings
from boxes.tests import create_and_login_user
from .models import Notification, User
from .forms import UpdateUserInfoForm
from .tasks import enqueue_email_notifications
from .utils import key_state

VALID_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK-----
Expand Down Expand Up @@ -174,6 +180,14 @@
REVOKED_KEY_FINGERPRINT = "C341AE04573A4D7CCBDE594C83AFB921E7A24CCA"


def create_notification(sent=False, group=None):
sent_at = timezone.now() if sent else None
return Notification.objects.create(subject="Test subject",
body="Test Body",
sent_at=sent_at,
send_to=group)


class UpdateUserFormTests(TestCase):

def test_empty_fingerprint(self):
Expand Down Expand Up @@ -256,3 +270,39 @@ def test_setup_complete(self):
user.fingerprint = VALID_KEY_FINGERPRINT
user.save()
self.assertEqual(user.has_setup_complete(), True)


class NotificationsTests(TestCase):

def test_delete_sent_notifications(self):
notification = create_notification(sent=True)
notification_id = notification.id
self.assertEqual(notification.delete(), False)
queryset = Notification.objects.filter(id=notification_id)
self.assertEqual(len(queryset), 1)

def test_delete_unsent_notification(self):
notification = create_notification(sent=False)
notification_id = notification.id
self.assertNotEqual(notification.delete(), False)
queryset = Notification.objects.filter(id=notification_id)
self.assertEqual(len(queryset), 0)

def test_send_when_group_is_defined(self):
settings.CELERY_ALWAYS_EAGER = True
for i in range(4):
create_and_login_user(self.client)
last_user = create_and_login_user(self.client)
group = Group.objects.create(name="Test Group")
group.user_set.add(last_user)
notification = create_notification(sent=False, group=group)
enqueue_email_notifications(notification.id, notification.send_to.id)
self.assertEqual(len(mail.outbox), 1)

def test_send_when_group_is_not_defined(self):
settings.CELERY_ALWAYS_EAGER = True
for i in range(4):
create_and_login_user(self.client)
notification = create_notification(sent=False)
enqueue_email_notifications(notification.id, None)
self.assertEqual(len(mail.outbox), User.objects.count())