Skip to content

Commit

Permalink
communications: Send emails to admins when prescriber left the org
Browse files Browse the repository at this point in the history
  • Loading branch information
tonial committed Jan 6, 2025
1 parent 256b032 commit 4adac28
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 18 deletions.
9 changes: 7 additions & 2 deletions itou/communications/dispatch/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ class BaseNotification:

can_be_disabled = True

def __init__(self, user, structure=None, /, **kwargs):
def __init__(self, user, structure=None, forward_from_user=None, /, **kwargs):
self.user = user
self.structure = structure
self.forward_from_user = forward_from_user
self.context = kwargs

def __repr__(self):
Expand All @@ -29,7 +30,11 @@ def should_send(self):
return True

def get_context(self):
return self.validate_context()
return self.validate_context() | {
"user": self.user,
"structure": self.structure,
"forward_from_user": self.forward_from_user,
}

def validate_context(self):
return self.context
29 changes: 19 additions & 10 deletions itou/communications/dispatch/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,6 @@ class EmailNotification(BaseNotification):
REQUIRED = BaseNotification.REQUIRED + ["subject_template", "body_template"]

def build(self):
# TODO: Temporary log for analysis : remove by the end of November 2024
if self.user.is_prescriber and self.structure:
memberships = (
PrescriberMembership.objects.active().filter(organization=self.structure).select_related("user")
)
members = [m.user for m in memberships]
if self.user not in members:
admin_count = len([m for m in memberships if m.is_admin])
logger.info("Estimate new email sent to admin_count=%d", admin_count)
return get_email_message(
[self.user.email],
self.get_context(),
Expand All @@ -30,5 +21,23 @@ def build(self):
)

def send(self):
if (
# If it is already a forwarded notification, do not check if the user is still a member of the organization
not self.forward_from_user
# Don't use should_send() if the user left the org because we don't want to use his settings
and self.is_applicable()
and self.user.is_prescriber
and self.structure
):
memberships = (
PrescriberMembership.objects.active().filter(organization=self.structure).select_related("user")
)
members = [m.user for m in memberships]
if self.user not in members:
admins = [m.user for m in memberships if m.is_admin]
logger.info("Send email copy to admin, admin_count=%d", len(admins))
for admin in admins:
self.__class__(admin, self.structure, self.user, **self.context).send()
return
if self.should_send():
return self.build().send()
self.build().send()
6 changes: 6 additions & 0 deletions itou/templates/layout/base_email_text_body.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
{% autoescape off %}

{% if forward_from_user|default:False %}
Vous recevez cet e-mail parce que l'utilisateur {{ forward_from_user.get_full_name }} ({{ forward_from_user.email}}) ne fait plus partie de votre organisation.

-----
{% endif %}

{% block body %}{% endblock %}

{% include "layout/base_email_signature.txt" %}
Expand Down
5 changes: 0 additions & 5 deletions itou/users/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ class OrganizationActiveMembersReminderNotification(
body_template = "users/emails/check_authorized_members_email_body.txt"
can_be_disabled = False

def get_context(self):
context = super().get_context()
context["structure"] = self.structure
return context


@notifications_registry.register
class JobSeekerCreatedByProxyNotification(EmailNotification):
Expand Down
46 changes: 45 additions & 1 deletion tests/communications/test_dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
WithStructureMixin,
)
from itou.communications.models import NotificationRecord, NotificationSettings
from tests.prescribers.factories import PrescriberMembershipFactory
from tests.users.factories import EmployerFactory, JobSeekerFactory, PrescriberFactory


Expand Down Expand Up @@ -166,8 +167,15 @@ def test_method_should_send(
assert not manageable_non_applicable_notification(self.user, self.organization).should_send()

def test_method_get_context(self):
assert BaseNotification(self.user, self.organization).get_context() == {}
assert BaseNotification(self.user, self.organization).get_context() == {
"user": self.user,
"structure": self.organization,
"forward_from_user": None,
}
assert BaseNotification(self.user, self.organization, kw1=1, kw2=2).get_context() == {
"user": self.user,
"structure": self.organization,
"forward_from_user": None,
"kw1": 1,
"kw2": 2,
}
Expand Down Expand Up @@ -209,6 +217,42 @@ def test_method_send(self, email_notification, django_capture_on_commit_callback
assert mailoutbox[0].to == [self.user.email]
assert "Cet email est envoyé depuis un environnement de démonstration" in mailoutbox[0].body

def test_method_send_for_prescriber_that_left_his_org(
self, email_notification, django_capture_on_commit_callbacks, mailoutbox, caplog
):
self.user.prescribermembership_set.update(is_active=False)

admin_1 = PrescriberMembershipFactory(
user=PrescriberFactory(),
organization=self.organization,
is_admin=True,
).user
admin_2 = PrescriberMembershipFactory(
user=PrescriberFactory(),
organization=self.organization,
is_admin=True,
).user
PrescriberMembershipFactory(
user=PrescriberFactory(),
organization=self.organization,
is_admin=False,
)

with django_capture_on_commit_callbacks(execute=True):
email_notification(self.user, self.organization).send()

assert caplog.messages == ["Send email copy to admin, admin_count=2"]
assert len(mailoutbox) == 2
assert set(mailoutbox[0].to + mailoutbox[1].to) == {admin_1.email, admin_2.email}
assert (
f"Vous recevez cet e-mail parce que l'utilisateur {self.user.get_full_name()} ({self.user.email})"
" ne fait plus partie de votre organisation" in mailoutbox[0].body
)
assert (
f"Vous recevez cet e-mail parce que l'utilisateur {self.user.get_full_name()} ({self.user.email})"
" ne fait plus partie de votre organisation" in mailoutbox[1].body
)


class TestProfiledNotification:
def setup_method(self):
Expand Down
2 changes: 2 additions & 0 deletions tests/www/apply/__snapshots__/test_process.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -1800,6 +1800,7 @@
'origin': list([
'job_application_sender_left_org[www/apply/views/process_views.py]',
'details_for_company[www/apply/views/process_views.py]',
'_check_user_view_wrapper[utils/auth.py]',
]),
'sql': '''
SELECT %s AS "a"
Expand Down Expand Up @@ -2950,6 +2951,7 @@
'origin': list([
'job_application_sender_left_org[www/apply/views/process_views.py]',
'details_for_company[www/apply/views/process_views.py]',
'_check_user_view_wrapper[utils/auth.py]',
]),
'sql': '''
SELECT %s AS "a"
Expand Down

0 comments on commit 4adac28

Please sign in to comment.