Every function in this file is related to notifications. Rename the file and functions therein to something more meaningful.
Signed-off-by: Stephen Finucane <[email protected]> --- patchwork/management/commands/cron.py | 5 +- patchwork/notifications.py | 116 ++++++++++++++++++++++++++++++++++ patchwork/tests/test_expiry.py | 10 +-- patchwork/tests/test_notifications.py | 2 +- patchwork/utils.py | 113 --------------------------------- 5 files changed, 125 insertions(+), 121 deletions(-) create mode 100644 patchwork/notifications.py delete mode 100644 patchwork/utils.py diff --git a/patchwork/management/commands/cron.py b/patchwork/management/commands/cron.py index 4272177..e3e906a 100644 --- a/patchwork/management/commands/cron.py +++ b/patchwork/management/commands/cron.py @@ -19,7 +19,8 @@ from django.core.management.base import BaseCommand -from patchwork.utils import send_notifications, do_expiry +from patchwork.notifications import expire_notifications +from patchwork.notifications import send_notifications class Command(BaseCommand): @@ -32,4 +33,4 @@ class Command(BaseCommand): self.stderr.write("Failed sending to %s: %s" % (recipient.email, error)) - do_expiry() + expire_notifications() diff --git a/patchwork/notifications.py b/patchwork/notifications.py new file mode 100644 index 0000000..5420401 --- /dev/null +++ b/patchwork/notifications.py @@ -0,0 +1,116 @@ +# Patchwork - automated patch tracking system +# Copyright (C) 2008 Jeremy Kerr <[email protected]> +# +# This file is part of the Patchwork package. +# +# Patchwork is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Patchwork is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Patchwork; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import datetime +import itertools + +from django.conf import settings +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.core.mail import EmailMessage +from django.db.models import Count, Q, F + +from patchwork.compat import render_to_string +from patchwork.models import EmailConfirmation +from patchwork.models import EmailOptout +from patchwork.models import PatchChangeNotification + + +def send_notifications(): + date_limit = datetime.datetime.now() - datetime.timedelta( + minutes=settings.NOTIFICATION_DELAY_MINUTES) + + # We delay sending notifications to a user if they have other + # notifications that are still in the "pending" state. To do this, + # we compare the total number of patch change notifications queued + # for each user against the number of "ready" notifications. + qs = PatchChangeNotification.objects.all() + qs2 = PatchChangeNotification.objects\ + .filter(last_modified__lt=date_limit)\ + .values('patch__submitter')\ + .annotate(count=Count('patch__submitter')) + qs2 = {elem['patch__submitter']: elem['count'] for elem in qs2} + + groups = itertools.groupby(qs.order_by('patch__submitter'), + lambda n: n.patch.submitter) + + errors = [] + + for (recipient, notifications) in groups: + notifications = list(notifications) + + if recipient.id not in qs2 or qs2[recipient.id] < len(notifications): + continue + + projects = set([n.patch.project.linkname for n in notifications]) + + def delete_notifications(): + pks = [n.pk for n in notifications] + PatchChangeNotification.objects.filter(pk__in=pks).delete() + + if EmailOptout.is_optout(recipient.email): + delete_notifications() + continue + + context = { + 'site': Site.objects.get_current(), + 'notifications': notifications, + 'projects': projects, + } + + subject = render_to_string( + 'patchwork/patch-change-notification-subject.text', + context).strip() + content = render_to_string('patchwork/patch-change-notification.mail', + context) + + message = EmailMessage(subject=subject, body=content, + from_email=settings.NOTIFICATION_FROM_EMAIL, + to=[recipient.email], + headers={'Precedence': 'bulk'}) + + try: + message.send() + except Exception as ex: + errors.append((recipient, ex)) + continue + + delete_notifications() + + return errors + + +def expire_notifications(): + """Expire any pending confirmations. + + Users whose registration confirmation has expired are removed. + """ + # expire any invalid confirmations + q = (Q(date__lt=datetime.datetime.now() - EmailConfirmation.validity) | + Q(active=False)) + EmailConfirmation.objects.filter(q).delete() + + # remove inactive users with no pending confirmation + pending_confs = EmailConfirmation.objects.values('user') + users = User.objects.filter(is_active=False, + last_login=F('date_joined')).exclude( + id__in=pending_confs) + + # delete users + users.delete() diff --git a/patchwork/tests/test_expiry.py b/patchwork/tests/test_expiry.py index 7622464..054d156 100644 --- a/patchwork/tests/test_expiry.py +++ b/patchwork/tests/test_expiry.py @@ -25,9 +25,9 @@ from django.test import TestCase from patchwork.models import EmailConfirmation from patchwork.models import Patch from patchwork.models import Person +from patchwork.notifications import expire_notifications from patchwork.tests.utils import create_patch from patchwork.tests.utils import create_user -from patchwork.utils import do_expiry class TestRegistrationExpiry(TestCase): @@ -50,7 +50,7 @@ class TestRegistrationExpiry(TestCase): datetime.timedelta(hours=1)) user, conf = self.register(date) - do_expiry() + expire_notifications() self.assertFalse(User.objects.filter(pk=user.pk).exists()) self.assertFalse( @@ -61,7 +61,7 @@ class TestRegistrationExpiry(TestCase): datetime.timedelta(hours=1)) user, conf = self.register(date) - do_expiry() + expire_notifications() self.assertTrue(User.objects.filter(pk=user.pk).exists()) self.assertTrue( @@ -75,7 +75,7 @@ class TestRegistrationExpiry(TestCase): conf.user.save() conf.deactivate() - do_expiry() + expire_notifications() self.assertTrue(User.objects.filter(pk=user.pk).exists()) self.assertFalse( @@ -100,7 +100,7 @@ class TestRegistrationExpiry(TestCase): conf.save() # ... which expires - do_expiry() + expire_notifications() # we should see no matching user self.assertFalse(User.objects.filter(email=patch.submitter.email) diff --git a/patchwork/tests/test_notifications.py b/patchwork/tests/test_notifications.py index 5c426fc..6cd3200 100644 --- a/patchwork/tests/test_notifications.py +++ b/patchwork/tests/test_notifications.py @@ -25,11 +25,11 @@ from django.test import TestCase from patchwork.models import EmailOptout from patchwork.models import PatchChangeNotification +from patchwork.notifications import send_notifications from patchwork.tests.utils import create_patch from patchwork.tests.utils import create_patches from patchwork.tests.utils import create_project from patchwork.tests.utils import create_state -from patchwork.utils import send_notifications class PatchNotificationModelTest(TestCase): diff --git a/patchwork/utils.py b/patchwork/utils.py deleted file mode 100644 index b496af4..0000000 --- a/patchwork/utils.py +++ /dev/null @@ -1,113 +0,0 @@ -# Patchwork - automated patch tracking system -# Copyright (C) 2008 Jeremy Kerr <[email protected]> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -from __future__ import absolute_import - -import datetime -import itertools - -from django.conf import settings -from django.contrib.auth.models import User -from django.contrib.sites.models import Site -from django.core.mail import EmailMessage -from django.db.models import Count, Q, F - -from patchwork.compat import render_to_string -from patchwork.models import (PatchChangeNotification, EmailOptout, - EmailConfirmation) - - -def send_notifications(): - date_limit = datetime.datetime.now() - datetime.timedelta( - minutes=settings.NOTIFICATION_DELAY_MINUTES) - - # We delay sending notifications to a user if they have other - # notifications that are still in the "pending" state. To do this, - # we compare the total number of patch change notifications queued - # for each user against the number of "ready" notifications. - qs = PatchChangeNotification.objects.all() - qs2 = PatchChangeNotification.objects\ - .filter(last_modified__lt=date_limit)\ - .values('patch__submitter')\ - .annotate(count=Count('patch__submitter')) - qs2 = {elem['patch__submitter']: elem['count'] for elem in qs2} - - groups = itertools.groupby(qs.order_by('patch__submitter'), - lambda n: n.patch.submitter) - - errors = [] - - for (recipient, notifications) in groups: - notifications = list(notifications) - - if recipient.id not in qs2 or qs2[recipient.id] < len(notifications): - continue - - projects = set([n.patch.project.linkname for n in notifications]) - - def delete_notifications(): - pks = [n.pk for n in notifications] - PatchChangeNotification.objects.filter(pk__in=pks).delete() - - if EmailOptout.is_optout(recipient.email): - delete_notifications() - continue - - context = { - 'site': Site.objects.get_current(), - 'notifications': notifications, - 'projects': projects, - } - - subject = render_to_string( - 'patchwork/patch-change-notification-subject.text', - context).strip() - content = render_to_string('patchwork/patch-change-notification.mail', - context) - - message = EmailMessage(subject=subject, body=content, - from_email=settings.NOTIFICATION_FROM_EMAIL, - to=[recipient.email], - headers={'Precedence': 'bulk'}) - - try: - message.send() - except Exception as ex: - errors.append((recipient, ex)) - continue - - delete_notifications() - - return errors - - -def do_expiry(): - # expire any pending confirmations - q = (Q(date__lt=datetime.datetime.now() - EmailConfirmation.validity) | - Q(active=False)) - EmailConfirmation.objects.filter(q).delete() - - # expire inactive users with no pending confirmation - pending_confs = EmailConfirmation.objects.values('user') - users = User.objects.filter(is_active=False, - last_login=F('date_joined')).exclude( - id__in=pending_confs) - - # delete users - users.delete() -- 2.7.4 _______________________________________________ Patchwork mailing list [email protected] https://lists.ozlabs.org/listinfo/patchwork
