Commit 798a73b8 authored by Jeremy Kerr's avatar Jeremy Kerr
Browse files

models: Add PatchChangeNotification and record patch state changes



Add a PatchChangeNotification model to keep track of changes to a
patch's state. Hook this up to Patch's pre_save signal.

Requires a DB schema upgrade.
Signed-off-by: default avatarJeremy Kerr <jk@ozlabs.org>
parent 41f19b66
......@@ -64,6 +64,7 @@ class Project(models.Model):
name = models.CharField(max_length=255, unique=True)
listid = models.CharField(max_length=255, unique=True)
listemail = models.CharField(max_length=200)
send_notifications = models.BooleanField()
def __unicode__(self):
return self.name
......@@ -406,3 +407,46 @@ class EmailOptout(models.Model):
def __unicode__(self):
return self.email
class PatchChangeNotification(models.Model):
patch = models.ForeignKey(Patch, primary_key = True)
last_modified = models.DateTimeField(default = datetime.datetime.now)
orig_state = models.ForeignKey(State)
def _patch_change_callback(sender, instance, **kwargs):
# we only want notification of modified patches
if instance.pk is None:
return
if instance.project is None or not instance.project.send_notifications:
return
try:
orig_patch = Patch.objects.get(pk = instance.pk)
except Patch.DoesNotExist:
return
# If there's no interesting changes, abort without creating the
# notification
if orig_patch.state == instance.state:
return
notification = None
try:
notification = PatchChangeNotification.objects.get(patch = instance)
except PatchChangeNotification.DoesNotExist:
pass
if notification is None:
notification = PatchChangeNotification(patch = instance,
orig_state = orig_patch.state)
elif notification.orig_state == instance.state:
# If we're back at the original state, there is no need to notify
notification.delete()
return
notification.last_modified = datetime.datetime.now()
notification.save()
models.signals.pre_save.connect(_patch_change_callback, sender = Patch)
......@@ -27,3 +27,4 @@ from patchwork.tests.confirm import *
from patchwork.tests.registration import *
from patchwork.tests.user import *
from patchwork.tests.mail_settings import *
from patchwork.tests.notifications import *
# Patchwork - automated patch tracking system
# Copyright (C) 2011 Jeremy Kerr <jk@ozlabs.org>
#
# 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 django.test import TestCase
from django.core.urlresolvers import reverse
from django.db.utils import IntegrityError
from patchwork.models import Patch, State, PatchChangeNotification
from patchwork.tests.utils import defaults, create_maintainer
class PatchNotificationModelTest(TestCase):
"""Tests for the creation & update of the PatchChangeNotification model"""
def setUp(self):
self.project = defaults.project
self.project.send_notifications = True
self.project.save()
self.submitter = defaults.patch_author_person
self.submitter.save()
self.patch = Patch(project = self.project, msgid = 'testpatch',
name = 'testpatch', content = '',
submitter = self.submitter)
def tearDown(self):
self.patch.delete()
self.submitter.delete()
self.project.delete()
def testPatchCreation(self):
"""Ensure we don't get a notification on create"""
self.patch.save()
self.assertEqual(PatchChangeNotification.objects.count(), 0)
def testPatchUninterestingChange(self):
"""Ensure we don't get a notification for "uninteresting" changes"""
self.patch.save()
self.patch.archived = True
self.patch.save()
self.assertEqual(PatchChangeNotification.objects.count(), 0)
def testPatchChange(self):
"""Ensure we get a notification for interesting patch changes"""
self.patch.save()
oldstate = self.patch.state
state = State.objects.exclude(pk = oldstate.pk)[0]
self.patch.state = state
self.patch.save()
self.assertEqual(PatchChangeNotification.objects.count(), 1)
notification = PatchChangeNotification.objects.all()[0]
self.assertEqual(notification.patch, self.patch)
self.assertEqual(notification.orig_state, oldstate)
def testNotificationCancelled(self):
"""Ensure we cancel notifications that are no longer valid"""
self.patch.save()
oldstate = self.patch.state
state = State.objects.exclude(pk = oldstate.pk)[0]
self.patch.state = state
self.patch.save()
self.assertEqual(PatchChangeNotification.objects.count(), 1)
self.patch.state = oldstate
self.patch.save()
self.assertEqual(PatchChangeNotification.objects.count(), 0)
def testNotificationUpdated(self):
"""Ensure we update notifications when the patch has a second change,
but keep the original patch details"""
self.patch.save()
oldstate = self.patch.state
newstates = State.objects.exclude(pk = oldstate.pk)[:2]
self.patch.state = newstates[0]
self.patch.save()
self.assertEqual(PatchChangeNotification.objects.count(), 1)
notification = PatchChangeNotification.objects.all()[0]
self.assertEqual(notification.orig_state, oldstate)
orig_timestamp = notification.last_modified
self.patch.state = newstates[1]
self.patch.save()
self.assertEqual(PatchChangeNotification.objects.count(), 1)
notification = PatchChangeNotification.objects.all()[0]
self.assertEqual(notification.orig_state, oldstate)
self.assertTrue(notification.last_modified > orig_timestamp)
def testProjectNotificationsDisabled(self):
"""Ensure we don't see notifications created when a project is
configured not to send them"""
self.project.send_notifications = False
self.project.save()
self.patch.save()
oldstate = self.patch.state
state = State.objects.exclude(pk = oldstate.pk)[0]
self.patch.state = state
self.patch.save()
self.assertEqual(PatchChangeNotification.objects.count(), 0)
......@@ -23,6 +23,7 @@ GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_bundle TO 'www-data'@localhost
GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_bundle_patches TO 'www-data'@localhost;
GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_patch TO 'www-data'@localhost;
GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_emailoptout TO 'www-data'@localhost;
GRANT SELECT, UPDATE, INSERT, DELETE ON patchwork_patchchangenotification TO 'www-data'@localhost;
-- allow the mail user (in this case, 'nobody') to add patches
GRANT INSERT, SELECT ON patchwork_patch TO 'nobody'@localhost;
......
......@@ -23,7 +23,8 @@ GRANT SELECT, UPDATE, INSERT, DELETE ON
patchwork_bundle,
patchwork_bundlepatch,
patchwork_patch,
patchwork_emailoptout
patchwork_emailoptout,
patchwork_patchchangenotification
TO "www-data";
GRANT SELECT, UPDATE ON
auth_group_id_seq,
......@@ -45,7 +46,8 @@ GRANT SELECT, UPDATE ON
patchwork_state_id_seq,
patchwork_emailconfirmation_id_seq,
patchwork_userprofile_id_seq,
patchwork_userprofile_maintainer_projects_id_seq
patchwork_userprofile_maintainer_projects_id_seq,
patchwork_patchchangenotification_id_seq
TO "www-data";
-- allow the mail user (in this case, 'nobody') to add patches
......
BEGIN;
CREATE TABLE "patchwork_patchchangenotification" (
"patch_id" integer NOT NULL PRIMARY KEY REFERENCES "patchwork_patch" ("id") DEFERRABLE INITIALLY DEFERRED,
"last_modified" timestamp with time zone NOT NULL,
"orig_state_id" integer NOT NULL REFERENCES "patchwork_state" ("id") DEFERRABLE INITIALLY DEFERRED
)
;
ALTER TABLE "patchwork_project" ADD COLUMN
"send_notifications" boolean NOT NULL DEFAULT False;
ALTER TABLE "patchwork_project" ALTER COLUMN
"send_notifications" DROP DEFAULT;
COMMIT;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment