Merge pull request #883 from taigaio/issue/4768/Notify-when-something-is-unassigned
Issue 4768: Notify when something is unassignedremotes/origin/issue/4795/notification_even_they_are_disabled
commit
c0daa9180f
|
@ -72,14 +72,14 @@ def create_notify_policy_if_not_exists(project, user, level=NotifyLevel.involved
|
||||||
model_cls = apps.get_model("notifications", "NotifyPolicy")
|
model_cls = apps.get_model("notifications", "NotifyPolicy")
|
||||||
try:
|
try:
|
||||||
result = model_cls.objects.get_or_create(project=project,
|
result = model_cls.objects.get_or_create(project=project,
|
||||||
user=user,
|
user=user,
|
||||||
defaults={"notify_level": level})
|
defaults={"notify_level": level})
|
||||||
return result[0]
|
return result[0]
|
||||||
except IntegrityError as e:
|
except IntegrityError as e:
|
||||||
raise exc.IntegrityError(_("Notify exists for specified user and project")) from e
|
raise exc.IntegrityError(_("Notify exists for specified user and project")) from e
|
||||||
|
|
||||||
|
|
||||||
def analize_object_for_watchers(obj:object, comment:str, user:object):
|
def analize_object_for_watchers(obj: object, comment: str, user: object):
|
||||||
"""
|
"""
|
||||||
Generic implementation for analize model objects and
|
Generic implementation for analize model objects and
|
||||||
extract mentions from it and add it to watchers.
|
extract mentions from it and add it to watchers.
|
||||||
|
@ -91,7 +91,6 @@ def analize_object_for_watchers(obj:object, comment:str, user:object):
|
||||||
if not hasattr(obj, "add_watcher"):
|
if not hasattr(obj, "add_watcher"):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
texts = (getattr(obj, "description", ""),
|
texts = (getattr(obj, "description", ""),
|
||||||
getattr(obj, "content", ""),
|
getattr(obj, "content", ""),
|
||||||
comment,)
|
comment,)
|
||||||
|
@ -142,7 +141,7 @@ def get_users_to_notify(obj, *, discard_users=None) -> list:
|
||||||
"""
|
"""
|
||||||
project = obj.get_project()
|
project = obj.get_project()
|
||||||
|
|
||||||
def _check_level(project:object, user:object, levels:tuple) -> bool:
|
def _check_level(project: object, user: object, levels: tuple) -> bool:
|
||||||
policy = project.cached_notify_policy_for_user(user)
|
policy = project.cached_notify_policy_for_user(user)
|
||||||
return policy.notify_level in levels
|
return policy.notify_level in levels
|
||||||
|
|
||||||
|
@ -167,7 +166,7 @@ def get_users_to_notify(obj, *, discard_users=None) -> list:
|
||||||
return frozenset(candidates)
|
return frozenset(candidates)
|
||||||
|
|
||||||
|
|
||||||
def _resolve_template_name(model:object, *, change_type:int) -> str:
|
def _resolve_template_name(model: object, *, change_type: int) -> str:
|
||||||
"""
|
"""
|
||||||
Ginven an changed model instance and change type,
|
Ginven an changed model instance and change type,
|
||||||
return the preformated template name for it.
|
return the preformated template name for it.
|
||||||
|
@ -187,7 +186,7 @@ def _resolve_template_name(model:object, *, change_type:int) -> str:
|
||||||
change=change_type)
|
change=change_type)
|
||||||
|
|
||||||
|
|
||||||
def _make_template_mail(name:str):
|
def _make_template_mail(name: str):
|
||||||
"""
|
"""
|
||||||
Helper that creates a adhoc djmail template email
|
Helper that creates a adhoc djmail template email
|
||||||
instance for specified name, and return an instance
|
instance for specified name, and return an instance
|
||||||
|
@ -211,7 +210,7 @@ def send_notifications(obj, *, history):
|
||||||
.get_or_create(key=key,
|
.get_or_create(key=key,
|
||||||
owner=owner,
|
owner=owner,
|
||||||
project=obj.project,
|
project=obj.project,
|
||||||
history_type = history.type))
|
history_type=history.type))
|
||||||
|
|
||||||
notification.updated_datetime = timezone.now()
|
notification.updated_datetime = timezone.now()
|
||||||
notification.save()
|
notification.save()
|
||||||
|
@ -222,6 +221,14 @@ def send_notifications(obj, *, history):
|
||||||
notify_users = get_users_to_notify(obj, discard_users=[notification.owner])
|
notify_users = get_users_to_notify(obj, discard_users=[notification.owner])
|
||||||
notification.notify_users.add(*notify_users)
|
notification.notify_users.add(*notify_users)
|
||||||
|
|
||||||
|
# If the history is an unassignment change we should notify that user too
|
||||||
|
if history.type == HistoryType.change and "assigned_to" in history.diff:
|
||||||
|
if history.diff["assigned_to"][0] is not None:
|
||||||
|
notification.notify_users.add(history.diff["assigned_to"][0])
|
||||||
|
|
||||||
|
if history.diff["assigned_to"][1] is not None:
|
||||||
|
notification.notify_users.add(history.diff["assigned_to"][1])
|
||||||
|
|
||||||
# If we are the min interval is 0 it just work in a synchronous and spamming way
|
# If we are the min interval is 0 it just work in a synchronous and spamming way
|
||||||
if settings.CHANGE_NOTIFICATIONS_MIN_INTERVAL == 0:
|
if settings.CHANGE_NOTIFICATIONS_MIN_INTERVAL == 0:
|
||||||
send_sync_notifications(notification.id)
|
send_sync_notifications(notification.id)
|
||||||
|
@ -392,8 +399,11 @@ def add_watcher(obj, user):
|
||||||
:param user: User adding the watch. :class:`~taiga.users.models.User` instance.
|
:param user: User adding the watch. :class:`~taiga.users.models.User` instance.
|
||||||
"""
|
"""
|
||||||
obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj)
|
obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj)
|
||||||
watched, created = Watched.objects.get_or_create(content_type=obj_type,
|
watched, created = Watched.objects.get_or_create(
|
||||||
object_id=obj.id, user=user, project=obj.project)
|
content_type=obj_type,
|
||||||
|
object_id=obj.id,
|
||||||
|
user=user,
|
||||||
|
project=obj.project)
|
||||||
|
|
||||||
notify_policy, _ = apps.get_model("notifications", "NotifyPolicy").objects.get_or_create(
|
notify_policy, _ = apps.get_model("notifications", "NotifyPolicy").objects.get_or_create(
|
||||||
project=obj.project, user=user, defaults={"notify_level": NotifyLevel.involved})
|
project=obj.project, user=user, defaults={"notify_level": NotifyLevel.involved})
|
||||||
|
@ -422,7 +432,7 @@ def set_notify_policy_level(notify_policy, notify_level):
|
||||||
"""
|
"""
|
||||||
Set notification level for specified policy.
|
Set notification level for specified policy.
|
||||||
"""
|
"""
|
||||||
if not notify_level in [e.value for e in NotifyLevel]:
|
if notify_level not in [e.value for e in NotifyLevel]:
|
||||||
raise exc.IntegrityError(_("Invalid value for notify level"))
|
raise exc.IntegrityError(_("Invalid value for notify level"))
|
||||||
|
|
||||||
notify_policy.notify_level = notify_level
|
notify_policy.notify_level = notify_level
|
||||||
|
|
|
@ -726,6 +726,42 @@ def test_send_notifications_using_services_method_for_wiki_pages(settings, mail)
|
||||||
assert services.make_ms_thread_index(in_reply_to, msg_ts) == headers.get('Thread-Index')
|
assert services.make_ms_thread_index(in_reply_to, msg_ts) == headers.get('Thread-Index')
|
||||||
|
|
||||||
|
|
||||||
|
def test_send_notifications_on_unassigned(client, mail):
|
||||||
|
project = f.ProjectFactory.create()
|
||||||
|
role = f.RoleFactory.create(project=project, permissions=['modify_issue', 'view_issues', 'view_us', 'view_tasks', 'view_wiki_pages'])
|
||||||
|
member1 = f.MembershipFactory.create(project=project, role=role)
|
||||||
|
member2 = f.MembershipFactory.create(project=project, role=role)
|
||||||
|
issue = f.IssueFactory.create(project=project,
|
||||||
|
owner=member1.user,
|
||||||
|
milestone=None,
|
||||||
|
status=project.default_issue_status,
|
||||||
|
severity=project.default_severity,
|
||||||
|
priority=project.default_priority,
|
||||||
|
type=project.default_issue_type)
|
||||||
|
|
||||||
|
take_snapshot(issue, user=issue.owner)
|
||||||
|
|
||||||
|
client.login(member1.user)
|
||||||
|
url = reverse("issues-detail", args=[issue.pk])
|
||||||
|
data = {
|
||||||
|
"assigned_to": member2.user.id,
|
||||||
|
"version": issue.version
|
||||||
|
}
|
||||||
|
response = client.json.patch(url, json.dumps(data))
|
||||||
|
assert len(mail.outbox) == 1
|
||||||
|
assert mail.outbox[0].to == [member2.user.email]
|
||||||
|
|
||||||
|
mail.outbox = []
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"assigned_to": None,
|
||||||
|
"version": issue.version + 1
|
||||||
|
}
|
||||||
|
response = client.json.patch(url, json.dumps(data))
|
||||||
|
assert len(mail.outbox) == 1
|
||||||
|
assert mail.outbox[0].to == [member2.user.email]
|
||||||
|
|
||||||
|
|
||||||
def test_resource_notification_test(client, settings, mail):
|
def test_resource_notification_test(client, settings, mail):
|
||||||
settings.CHANGE_NOTIFICATIONS_MIN_INTERVAL = 1
|
settings.CHANGE_NOTIFICATIONS_MIN_INTERVAL = 1
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue