From 05742f42f938bf6b9a2630caf36560c728d1d0d9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C4=93teris=20Caune?= <cuu508@gmail.com>
Date: Fri, 14 Jul 2023 09:51:36 +0300
Subject: [PATCH] Update the senddeletionscheduled command to notify team
 members too

---
 .../commands/senddeletionscheduled.py         | 33 ++++++++++-----
 .../tests/test_senddeletionscheduled.py       | 41 +++++++++++++++----
 .../emails/deletion-scheduled-body-html.html  | 21 +++++-----
 .../emails/deletion-scheduled-body-text.html  | 10 +++--
 4 files changed, 73 insertions(+), 32 deletions(-)

diff --git a/hc/accounts/management/commands/senddeletionscheduled.py b/hc/accounts/management/commands/senddeletionscheduled.py
index 75c0a9b7..e53cab82 100644
--- a/hc/accounts/management/commands/senddeletionscheduled.py
+++ b/hc/accounts/management/commands/senddeletionscheduled.py
@@ -3,6 +3,7 @@ from __future__ import annotations
 import time
 
 from django.conf import settings
+from django.contrib.auth.models import User
 from django.core.management.base import BaseCommand
 from django.utils.timezone import now
 
@@ -16,23 +17,35 @@ class Command(BaseCommand):
     def pause(self):
         time.sleep(1)
 
+    def members(self, user):
+        q = User.objects.filter(memberships__project__owner=user)
+        q = q.exclude(last_login=None)
+        return q.order_by("email")
+
     def handle(self, *args, **options):
         q = Profile.objects.order_by("id")
         q = q.filter(deletion_scheduled_date__gt=now())
 
         sent = 0
         for profile in q:
-            self.stdout.write(f"Sending notice to {profile.user.email}")
+            recipients = [profile.user.email]
+            # Include team members in the recipient list too:
+            for u in self.members(profile.user):
+                recipients.append(u.email)
 
-            ctx = {
-                "email": profile.user.email,
-                "support_email": settings.SUPPORT_EMAIL,
-                "deletion_scheduled_date": profile.deletion_scheduled_date,
-            }
-            emails.deletion_scheduled(profile.user.email, ctx)
-            sent += 1
+            for recipient in recipients:
+                role = "owner" if recipient == profile.user.email else "member"
+                self.stdout.write(f"Sending notice to {recipient} ({role})")
+                ctx = {
+                    "owner_email": profile.user.email,
+                    "num_checks": profile.num_checks_used(),
+                    "support_email": settings.SUPPORT_EMAIL,
+                    "deletion_scheduled_date": profile.deletion_scheduled_date,
+                }
+                emails.deletion_scheduled(recipient, ctx)
+                sent += 1
 
-            # Throttle so we don't send too many emails at once:
-            self.pause()
+                # Throttle so we don't send too many emails at once:
+                self.pause()
 
         return f"Done!\nNotices sent: {sent}\n"
diff --git a/hc/accounts/tests/test_senddeletionscheduled.py b/hc/accounts/tests/test_senddeletionscheduled.py
index a44231e0..6d372e47 100644
--- a/hc/accounts/tests/test_senddeletionscheduled.py
+++ b/hc/accounts/tests/test_senddeletionscheduled.py
@@ -8,6 +8,7 @@ from django.core import mail
 from django.utils.timezone import now
 
 from hc.accounts.management.commands.senddeletionscheduled import Command
+from hc.api.models import Check
 from hc.test import BaseTestCase
 
 
@@ -16,19 +17,14 @@ def counts(result):
     return [int(s) for s in re.findall(r"\d+", result)]
 
 
-class SendDeletionNoticesTestCase(BaseTestCase):
-    def test_it_skips_profiles_with_deletion_scheduled_date_not_set(self):
-        cmd = Command(stdout=Mock())
-        cmd.pause = Mock()  # don't pause for 1s
-
-        result = cmd.handle()
-        self.assertEqual(counts(result), [0])
-        self.assertEqual(len(mail.outbox), 0)
-
+class SendDeletionScheduledTestCase(BaseTestCase):
     def test_it_sends_notice(self):
         self.profile.deletion_scheduled_date = now() + td(days=31)
         self.profile.save()
 
+        Check.objects.create(project=self.project)
+        Check.objects.create(project=self.project)
+
         cmd = Command(stdout=Mock())
         cmd.pause = Mock()  # don't pause for 1s
 
@@ -37,6 +33,33 @@ class SendDeletionNoticesTestCase(BaseTestCase):
 
         email = mail.outbox[0]
         self.assertEqual(email.subject, "Account Deletion Warning")
+        self.assertEqual(email.to[0], "alice@example.org")
+        self.assertIn("Owner: alice@example.org", email.body)
+        self.assertIn("Number of checks in the account: 2", email.body)
+
+    def test_it_sends_notice_to_team_members(self):
+        self.profile.deletion_scheduled_date = now() + td(days=31)
+        self.profile.save()
+
+        self.bob.last_login = now()
+        self.bob.save()
+
+        cmd = Command(stdout=Mock())
+        cmd.pause = Mock()  # don't pause for 1s
+
+        result = cmd.handle()
+        self.assertEqual(counts(result), [2])
+
+        self.assertEqual(mail.outbox[0].to[0], "alice@example.org")
+        self.assertEqual(mail.outbox[1].to[0], "bob@example.org")
+
+    def test_it_skips_profiles_with_deletion_scheduled_date_not_set(self):
+        cmd = Command(stdout=Mock())
+        cmd.pause = Mock()  # don't pause for 1s
+
+        result = cmd.handle()
+        self.assertEqual(counts(result), [0])
+        self.assertEqual(len(mail.outbox), 0)
 
     def test_it_skips_profiles_with_deletion_scheduled_date_in_past(self):
         self.profile.deletion_scheduled_date = now() - td(minutes=1)
diff --git a/templates/emails/deletion-scheduled-body-html.html b/templates/emails/deletion-scheduled-body-html.html
index 6b9c6dec..f538a8ff 100644
--- a/templates/emails/deletion-scheduled-body-html.html
+++ b/templates/emails/deletion-scheduled-body-html.html
@@ -2,16 +2,17 @@
 {% load hc_extras %}
 
 {% block content %}
-Hello,<br /><br />
-
-Your {% site_name %} account is
-<strong>scheduled for deletion on {{ deletion_scheduled_date.date }}</strong>.
-
-<br /><br />
-If you wish to keep your account, please contact us at {{ support_email}} as
-soon as possible.
-
-<br /><br />
+Hello,<br />
+<br />
+The {% site_name %} account registered to <strong>{{ owner_email }}</strong>
+is  scheduled for deletion on <strong>{{ deletion_scheduled_date.date }}</strong>.<br />
+<br />
+Account details:<br />
+Owner: <strong>{{ owner_email }}</strong><br />
+Number of checks in the account: <strong>{{ num_checks }}</strong><br />
+<br />
+To prevent deletion, please contact us at {{ support_email}} as soon as possible.<br />
+<br />
 Sincerely,<br />
 The {% site_name %} Team
 {% endblock %}
diff --git a/templates/emails/deletion-scheduled-body-text.html b/templates/emails/deletion-scheduled-body-text.html
index 0536893c..688f7cd6 100644
--- a/templates/emails/deletion-scheduled-body-text.html
+++ b/templates/emails/deletion-scheduled-body-text.html
@@ -1,10 +1,14 @@
 {% load hc_extras %}
 Hello,
 
-Your {% site_name %} account is scheduled for deletion on {{ deletion_scheduled_date.date }}.
+The {% site_name %} account registered to {{ owner_email }} is scheduled for deletion
+on {{ deletion_scheduled_date.date }}.
 
-If you wish to keep your account, please contact us at {{ support_email}} as
-soon as possible.
+Account details:
+Owner: {{ owner_email }}
+Number of checks in the account: {{ num_checks }}
+
+To prevent deletion, please contact us at {{ support_email}} as soon as possible.
 
 --
 Sincerely,