Add notifications module (not finished)

remotes/origin/enhancement/email-actions
Andrés Moya 2013-07-15 11:00:13 +02:00
parent fdc72d06ff
commit 48b5dcb2a3
6 changed files with 242 additions and 6 deletions

View File

@ -11,6 +11,7 @@ from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import UserManager, AbstractUser, Group
from greenmine.scrum.models import Project, UserStory, Task
from greenmine.base.notifications.models import WatcherMixin
import uuid
@ -45,7 +46,7 @@ def attach_unique_reference(sender, instance, **kwargs):
project.save()
class User(AbstractUser):
class User(AbstractUser, WatcherMixin):
color = models.CharField(max_length=9, null=False, blank=False,
verbose_name=_('color'))
description = models.TextField(null=False, blank=True,

View File

View File

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
from greenmine.base.notifications.models import watched_changed
@receiver(watched_changed)
def send_mail_when_watched_changed(sender, **kwargs):
changed_attributes = kwargs.get('changed_attributes')
watchers_to_notify = sender.get_watchers_to_notify()
print 'Cambiado', sender
print 'Atributos', changed_attributes
print 'Notificar a', watchers_to_notify

View File

@ -0,0 +1,145 @@
# -*- coding: utf-8 -*-
from django.db import models
from django.dispatch import Signal
from django.utils.translation import ugettext_lazy as _
watched_changed = Signal(providing_args = ['changed_attributes'])
class WatcherMixin(object):
NOTIFY_LEVEL_CHOICES = (
('all_owned_projects', _(u'All events on my projects')),
('only_watching', _(u'Only events for objects i watch')),
('only_assigned', _(u'Only events for objects assigned to me')),
('only_owner', _(u'Only events for objects owned by me')),
('no_events', _(u'No events')),
)
notify_level = models.CharField(max_length=32, null=False, blank=False, default='only_watching',
choices=NOTIFY_LEVEL_CHOICES, verbose_name=_(u'notify level'))
notify_changes_by_me = models.BooleanField(null=False, blank=True,
verbose_name=_(u'notify changes made by me'))
class Meta:
abstract = True
def allow_notify_owned(self):
return (self.notify_level in [
'only_owner',
'only_assigned',
'only_watching',
'all_owned_projects',
])
def allow_notify_assigned_to(self):
return (self.notify_level in [
'only_assigned',
'only_watching',
'all_owned_projects',
])
def allow_notify_suscribed(self):
return (self.notify_level in [
'only_watching',
'all_owned_projects',
])
def allow_notify_project(self, project):
return self.notify_level == 'all_owned_projects' \
and project.owner.pk == self.pk
def allow_notify_by_me(self, changer):
return (changer.pk != self.pk) \
or self.notify_changes_by_me
class WatchedMixin(object):
class Meta:
abstract = True
def start_change(self, changer):
self._changer = changer
self._saved_attributes = self._get_attributes_to_notify()
def cancel_change(self):
del self._changer
del self._saved_attributes
def complete_change(self):
changed_attributes = self._get_changed_attributes()
del self._changer
del self._saved_attributes
watched_changed.send(sender = self, changed_attributes = changed_attributes)
def get_watchers_to_notify(self):
watchers_to_notify = set()
watchers_by_role = self._get_watchers_by_role()
owner = watchers_by_role.get('owner')
if owner \
and owner.allow_notify_owned() \
and owner.allow_notify_by_me(self._changer):
watchers_to_notify.add(owner)
assigned_to = watchers_by_role.get('assigned_to')
if (assigned_to
and assigned_to.allow_notify_assigned_to()
and assigned_to.allow_notify_by_me(self._changer)):
watchers_to_notify.add(assigned_to)
suscribed_watchers = watchers_by_role.get('suscribed_watchers')
if suscribed_watchers:
for suscribed_watcher in suscribed_watchers:
if suscribed_watcher \
and suscribed_watcher.allow_notify_suscribed() \
and suscribed_watcher.allow_notify_by_me(self._changer):
watchers_to_notify.add(suscribed_watcher)
#(project, project_owner) = watchers_by_role.get('project_owner')
#if project_owner \
# and project_owner.allow_notify_project(project) \
# and project_owner.allow_notify_by_me(self._changer):
# watchers_to_notify.add(project_owner)
return watchers_to_notify
def _get_changed_attributes(self):
changed_attributes = {}
current_attributes = self._get_attributes_to_notify()
for name, saved_value in self._saved_attributes.items():
current_value = current_attributes.get(name)
if saved_value != current_value:
changed_attributes[name] = (saved_value, current_value)
return changed_attributes
def _get_watchers_by_role(self):
'''
Return the actual instances of watchers of this object, classified by role.
For example:
return {
'owner': self.owner,
'assigned_to': self.assigned_to,
'suscribed_watchers': self.watchers.all(),
'project_owner': (self.project, self.project.owner),
}
'''
raise NotImplementedError('You must subclass WatchedMixin and provide _get_watchers_by_role method')
def _get_attributes_to_notify(self):
'''
Return the names and values of the attributes of this object that will be checked for change in
change notifications. Example:
return {
'name': self.name,
'description': self.description,
'status': self.status.name,
...
}
'''
raise NotImplementedError('You must subclass WatchedMixin and provide _get_attributes_to_notify method')

View File

@ -11,6 +11,7 @@ from picklefield.fields import PickledObjectField
from greenmine.base.utils.slug import slugify_uniquely, ref_uniquely
from greenmine.base.utils import iter_points
from greenmine.base.notifications.models import WatchedMixin
from greenmine.scrum.choices import (ISSUESTATUSES, TASKSTATUSES, USSTATUSES,
POINTS_CHOICES, SEVERITY_CHOICES,
ISSUETYPES, TASK_CHANGE_CHOICES,
@ -167,7 +168,7 @@ class Membership(models.Model):
unique_together = ('user', 'project')
class Project(models.Model):
class Project(models.Model, WatchedMixin):
uuid = models.CharField(max_length=40, unique=True, null=False, blank=True,
verbose_name=_('uuid'))
name = models.CharField(max_length=250, unique=True, null=False, blank=False,
@ -222,8 +223,25 @@ class Project(models.Model):
super(Project, self).save(*args, **kwargs)
def _get_watchers_by_role(self):
return {
'owner': self.owner,
}
class Milestone(models.Model):
def _get_attributes_to_notify(self):
return {
'name': self.name,
'slug': self.slug,
'description': self.description,
'modified_date': self.modified_date,
'owner': self.owner.get_full_name(),
'members': ', '.join([member.get_full_name() for member in self.members.all()]),
'public': self.public,
'tags': self.tags,
}
class Milestone(models.Model, WatchedMixin):
uuid = models.CharField(max_length=40, unique=True, null=False, blank=True,
verbose_name=_('uuid'))
name = models.CharField(max_length=200, db_index=True, null=False, blank=False,
@ -272,8 +290,22 @@ class Milestone(models.Model):
super(Milestone, self).save(*args, **kwargs)
def _get_watchers_by_role(self):
return {
'owner': self.owner,
'project_owner': (self.project, self.project.owner),
}
class UserStory(models.Model):
def _get_attributes_to_notify(self):
return {
'name': self.name,
'slug': self.slug,
'owner': self.owner.get_full_name(),
'modified_date': self.modified_date,
}
class UserStory(WatchedMixin, models.Model):
uuid = models.CharField(max_length=40, unique=True, null=False, blank=True,
verbose_name=_('uuid'))
ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None,
@ -338,6 +370,29 @@ class UserStory(models.Model):
def is_closed(self):
return self.status.is_closed
def _get_watchers_by_role(self):
return {
'owner': self.owner,
'suscribed_watchers': self.watchers.all(),
'project_owner': (self.project, self.project.owner),
}
def _get_attributes_to_notify(self):
return {
'milestone': self.milestone.name,
'owner': self.owner.get_full_name(),
'status': self.status.name,
'points': self.points.name,
'order': self.order,
'modified_date': self.modified_date,
'finish_date': self.finish_date,
'subject': self.subject,
'description': self.description,
'client_requirement': self.client_requirement,
'team_requirement': self.team_requirement,
'tags': self.tags,
}
class Attachment(models.Model):
owner = models.ForeignKey('base.User', null=False, blank=False,
@ -370,7 +425,7 @@ class Attachment(models.Model):
)
class Task(models.Model):
class Task(models.Model, WatchedMixin):
uuid = models.CharField(max_length=40, unique=True, null=False, blank=True,
verbose_name=_('uuid'))
user_story = models.ForeignKey('UserStory', null=False, blank=False,
@ -443,8 +498,16 @@ class Task(models.Model):
super(Task, self).save(*args, **kwargs)
def _get_watchers_by_role(self):
return {
'owner': self.owner,
'assigned_to': self.assigned_to,
'suscribed_watchers': self.watchers.all(),
'project_owner': (self.project, self.project.owner),
}
class Issue(models.Model):
class Issue(models.Model, WatchedMixin):
uuid = models.CharField(max_length=40, unique=True, null=False, blank=True,
verbose_name=_('uuid'))
ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None,
@ -516,6 +579,14 @@ class Issue(models.Model):
super(Issue, self).save(*args, **kwargs)
def _get_watchers_by_role(self):
return {
'owner': self.owner,
'assigned_to': self.assigned_to,
'suscribed_watchers': self.watchers.all(),
'project_owner': (self.project, self.project.owner),
}
# Model related signals handlers

View File

@ -204,6 +204,8 @@ INSTALLED_APPS = [
'greenmine.base',
'greenmine.base.mail',
'greenmine.base.notifications',
'greenmine.base.notifications.email',
'greenmine.scrum',
'greenmine.wiki',
'greenmine.documents',