taiga-back/greenmine/base/notifications/models.py

174 lines
6.5 KiB
Python

# -*- coding: utf-8 -*-
from django.db import models
from django.db.models.fields import FieldDoesNotExist
from django.utils.translation import ugettext_lazy as _
import reversion
class WatcherMixin(models.Model):
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="all_owned_projects",
choices=NOTIFY_LEVEL_CHOICES,
verbose_name=_(u"notify level"))
notify_changes_by_me = models.BooleanField(blank=True, default=False,
verbose_name=_(u"notify changes 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(models.Model):
notifiable_fields = []
class Meta:
abstract = True
@property
def last_version(self):
version_list = reversion.get_for_object(self)
return version_list and version_list[0] or None
def get_changed_fields_list(self, data_dict):
def _key_by_notifiable_field(item):
try:
return self.notifiable_fields.index(item["name"])
except ValueError:
return 100000 # Emulate the maximum value
if self.notifiable_fields:
changed_data = {k:v for k, v in data_dict.items()
if k in self.notifiable_fields}
else:
changed_data = data_dict
field_list = []
for field_name, data_value in changed_data.items():
field_list.append(self._get_changed_field(field_name, data_value))
return sorted(field_list, key=_key_by_notifiable_field)
def get_watchers_to_notify(self, changer):
watchers_to_notify = set()
watchers_by_role = self._get_watchers_by_role()
owner = watchers_by_role.get("owner", None)
if (owner and owner.allow_notify_owned()
and owner.allow_notify_by_me(changer)):
watchers_to_notify.add(owner)
assigned_to = watchers_by_role.get("assigned_to", None)
if (assigned_to and assigned_to.allow_notify_assigned_to()
and assigned_to.allow_notify_by_me(changer)):
watchers_to_notify.add(assigned_to)
suscribed_watchers = watchers_by_role.get("suscribed_watchers", None)
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(changer)):
watchers_to_notify.add(suscribed_watcher)
(project, project_owner) = watchers_by_role.get("project_owner", (None, None))
if (project_owner
and project_owner.allow_notify_project(project)
and project_owner.allow_notify_by_me(changer)):
watchers_to_notify.add(project_owner)
if changer.notify_changes_by_me:
watchers_to_notify.add(changer)
return watchers_to_notify
def _get_changed_field_verbose_name(self, field_name):
try:
return self._meta.get_field(field_name).verbose_name
except FieldDoesNotExist:
return field_name
def _get_changed_field_old_value(self, field_name, data_value):
value = (self.last_version.field_dict.get(field_name, data_value)
if self.last_version else None)
field = self.__class__._meta.get_field_by_name(field_name)[0] or None
if value and field:
# Get the old value from a ForeignKey
if type(field) is models.fields.related.ForeignKey:
try:
value = field.related.parent_model.objects.get(pk=value)
except field.related.parent_model.DoesNotExist:
pass
display_method = getattr(self,"get_notifiable_{field_name}_display".format(
field_name=field_name) ,None)
return display_method(value) if display_method else value
def _get_changed_field_new_value(self, field_name, data_value):
value = getattr(self, field_name, data_value)
display_method = getattr(self,"get_notifiable_{field_name}_display".format(
field_name=field_name) ,None)
return display_method(value) if display_method else value
def _get_changed_field(self, field_name, data_value):
verbose_name = self._get_changed_field_verbose_name(field_name)
old_value = self._get_changed_field_old_value(field_name, None)
new_value = self._get_changed_field_new_value(field_name, data_value)
return {
"name": field_name,
"verbose_name": verbose_name,
"old_value": old_value,
"new_value": new_value,
}
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")