Add notifications module (not finished)
parent
28a59bbc88
commit
6e71ebde5e
|
@ -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
|
||||
|
||||
|
@ -19,7 +20,6 @@ import uuid
|
|||
@receiver(signals.pre_save)
|
||||
def attach_uuid(sender, instance, **kwargs):
|
||||
fields = sender._meta.init_name_map()
|
||||
#fields = sender._meta.get_all_field_names()
|
||||
|
||||
if 'modified_date' in fields:
|
||||
instance.modified_date = now()
|
||||
|
@ -30,9 +30,8 @@ def attach_uuid(sender, instance, **kwargs):
|
|||
instance.uuid = unicode(uuid.uuid1())
|
||||
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
color = models.CharField(max_length=9, null=False, blank=False, default="#669933",
|
||||
class User(AbstractUser, WatcherMixin):
|
||||
color = models.CharField(max_length=9, null=False, blank=False,
|
||||
verbose_name=_('color'))
|
||||
description = models.TextField(null=False, blank=True,
|
||||
verbose_name=_('description'))
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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')
|
||||
|
|
@ -12,6 +12,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,
|
||||
|
@ -173,7 +174,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,
|
||||
|
@ -230,15 +231,13 @@ class Project(models.Model):
|
|||
|
||||
@property
|
||||
def list_of_milestones(self):
|
||||
return [
|
||||
{
|
||||
'name': milestone.name,
|
||||
'finish_date': milestone.estimated_finish,
|
||||
'closed_points': milestone.closed_points,
|
||||
'client_increment_points': milestone.client_increment_points,
|
||||
'team_increment_points': milestone.team_increment_points
|
||||
} for milestone in self.milestones.all().order_by('estimated_start')
|
||||
]
|
||||
return [{
|
||||
'name': milestone.name,
|
||||
'finish_date': milestone.estimated_finish,
|
||||
'closed_points': milestone.closed_points,
|
||||
'client_increment_points': milestone.client_increment_points,
|
||||
'team_increment_points': milestone.team_increment_points
|
||||
} for milestone in self.milestones.all().order_by('estimated_start')]
|
||||
|
||||
@property
|
||||
def list_roles(self):
|
||||
|
@ -268,9 +267,14 @@ class Project(models.Model):
|
|||
.exclude(role__id__in=role_ids)\
|
||||
.delete()
|
||||
|
||||
class Milestone(models.Model):
|
||||
uuid = models.CharField(
|
||||
max_length=40, unique=True, null=False, blank=True,
|
||||
def _get_watchers_by_role(self):
|
||||
return {
|
||||
'owner': self.owner,
|
||||
}
|
||||
|
||||
|
||||
class Milestone(models.Model, WatchedMixin):
|
||||
uuid = models.CharField(max_length=40, unique=True, null=False, blank=True,
|
||||
verbose_name=_('uuid'))
|
||||
|
||||
name = models.CharField(
|
||||
|
@ -373,6 +377,12 @@ class Milestone(models.Model):
|
|||
#return sum(points)
|
||||
return 0
|
||||
|
||||
def _get_watchers_by_role(self):
|
||||
return {
|
||||
'owner': self.owner,
|
||||
'project_owner': (self.project, self.project.owner),
|
||||
}
|
||||
|
||||
|
||||
class RolePoints(models.Model):
|
||||
user_story = models.ForeignKey('UserStory', null=False, blank=False,
|
||||
|
@ -389,7 +399,7 @@ class RolePoints(models.Model):
|
|||
unique_together = ('user_story', 'role')
|
||||
|
||||
|
||||
class UserStory(models.Model):
|
||||
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,
|
||||
|
@ -455,6 +465,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,
|
||||
|
@ -486,7 +519,7 @@ class Attachment(models.Model):
|
|||
self.content_type, self.object_id, self.id)
|
||||
|
||||
|
||||
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=True, blank=False,
|
||||
|
@ -552,8 +585,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,
|
||||
|
@ -626,6 +667,14 @@ class Issue(models.Model):
|
|||
def is_closed(self):
|
||||
return self.status.is_closed
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -201,6 +201,8 @@ INSTALLED_APPS = [
|
|||
|
||||
'greenmine.base',
|
||||
'greenmine.base.mail',
|
||||
'greenmine.base.notifications',
|
||||
'greenmine.base.notifications.email',
|
||||
'greenmine.scrum',
|
||||
'greenmine.wiki',
|
||||
'greenmine.documents',
|
||||
|
|
Loading…
Reference in New Issue