diff --git a/CHANGELOG.md b/CHANGELOG.md index 84d48d35..c13589a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. - Django 3.1 - Handle status callbacks from Twilio, show delivery failures in Integrations - Removing unused /api/v1/notifications/{uuid}/bounce endpoint +- Less verbose output in the `senddeletionnotices` command ## Bug Fixes - Handle excessively long email addresses in the signup form. diff --git a/hc/accounts/management/commands/senddeletionnotices.py b/hc/accounts/management/commands/senddeletionnotices.py index 59c0da87..ecf7213c 100644 --- a/hc/accounts/management/commands/senddeletionnotices.py +++ b/hc/accounts/management/commands/senddeletionnotices.py @@ -16,6 +16,8 @@ class Command(BaseCommand): - deletion notice has not been sent recently - last login more than a year ago - none of the owned projects has invited team members + - none of the owned projects has pings in the last year + - is on a free plan """ @@ -38,17 +40,21 @@ class Command(BaseCommand): q = q.exclude(sms_limit__gt=5) sent = 0 + skipped_has_team = 0 + skipped_has_pings = 0 + for profile in q: members = Member.objects.filter(project__owner_id=profile.user_id) if members.exists(): - self.stdout.write("Skipping %s, has team members" % profile) + # Don't send deletion notice: this account has team members + skipped_has_team += 1 continue - pings = Ping.objects - pings = pings.filter(owner__project__owner_id=profile.user_id) + pings = Ping.objects.filter(owner__project__owner_id=profile.user_id) pings = pings.filter(created__gt=year_ago) if pings.exists(): - self.stdout.write("Skipping %s, has pings in last year" % profile) + # Don't send deletion notice: this account has pings in the last year + skipped_has_pings += 1 continue self.stdout.write("Sending notice to %s" % profile.user.email) @@ -58,10 +64,14 @@ class Command(BaseCommand): ctx = {"email": profile.user.email, "support_email": settings.SUPPORT_EMAIL} emails.deletion_notice(profile.user.email, ctx) + sent += 1 # Throttle so we don't send too many emails at once: self.pause() - sent += 1 - - return "Done! Sent %d notices" % sent + return ( + f"Done!\n" + f"* Notices sent: {sent}\n" + f"* Skipped (has team members): {skipped_has_team}\n" + f"* Skipped (has pings in the last year): {skipped_has_pings}\n" + ) diff --git a/hc/accounts/tests/test_senddeletionnotices.py b/hc/accounts/tests/test_senddeletionnotices.py index 79f5800c..668f8489 100644 --- a/hc/accounts/tests/test_senddeletionnotices.py +++ b/hc/accounts/tests/test_senddeletionnotices.py @@ -1,4 +1,5 @@ from datetime import timedelta as td +import re from unittest.mock import Mock from django.core import mail @@ -9,6 +10,11 @@ from hc.api.models import Check, Ping from hc.test import BaseTestCase +def counts(result): + """ Extract integer values from command's return value. """ + return [int(s) for s in re.findall(r"\d+", result)] + + class SendDeletionNoticesTestCase(BaseTestCase): def setUp(self): super(SendDeletionNoticesTestCase, self).setUp() @@ -28,7 +34,7 @@ class SendDeletionNoticesTestCase(BaseTestCase): cmd.pause = Mock() # don't pause for 1s result = cmd.handle() - self.assertEqual(result, "Done! Sent 1 notices") + self.assertEqual(counts(result), [1, 0, 0]) self.profile.refresh_from_db() self.assertTrue(self.profile.deletion_notice_date) @@ -42,7 +48,7 @@ class SendDeletionNoticesTestCase(BaseTestCase): self.alice.save() result = Command(stdout=Mock()).handle() - self.assertEqual(result, "Done! Sent 0 notices") + self.assertEqual(counts(result), [0, 0, 0]) self.profile.refresh_from_db() self.assertIsNone(self.profile.deletion_notice_date) @@ -53,7 +59,7 @@ class SendDeletionNoticesTestCase(BaseTestCase): self.alice.save() result = Command(stdout=Mock()).handle() - self.assertEqual(result, "Done! Sent 0 notices") + self.assertEqual(counts(result), [0, 0, 0]) self.profile.refresh_from_db() self.assertIsNone(self.profile.deletion_notice_date) @@ -64,7 +70,7 @@ class SendDeletionNoticesTestCase(BaseTestCase): self.profile.save() result = Command(stdout=Mock()).handle() - self.assertEqual(result, "Done! Sent 0 notices") + self.assertEqual(counts(result), [0, 0, 0]) def test_it_checks_sms_limit(self): # alice has a paid account @@ -72,7 +78,7 @@ class SendDeletionNoticesTestCase(BaseTestCase): self.profile.save() result = Command(stdout=Mock()).handle() - self.assertEqual(result, "Done! Sent 0 notices") + self.assertEqual(counts(result), [0, 0, 0]) self.profile.refresh_from_db() self.assertIsNone(self.profile.deletion_notice_date) @@ -82,7 +88,7 @@ class SendDeletionNoticesTestCase(BaseTestCase): Member.objects.create(user=self.bob, project=self.project) result = Command(stdout=Mock()).handle() - self.assertEqual(result, "Done! Sent 0 notices") + self.assertEqual(counts(result), [0, 1, 0]) self.profile.refresh_from_db() self.assertIsNone(self.profile.deletion_notice_date) @@ -92,7 +98,7 @@ class SendDeletionNoticesTestCase(BaseTestCase): Ping.objects.create(owner=check) result = Command(stdout=Mock()).handle() - self.assertEqual(result, "Done! Sent 0 notices") + self.assertEqual(counts(result), [0, 0, 1]) self.profile.refresh_from_db() self.assertIsNone(self.profile.deletion_notice_date) @@ -103,4 +109,4 @@ class SendDeletionNoticesTestCase(BaseTestCase): self.profile.save() result = Command(stdout=Mock()).handle() - self.assertEqual(result, "Done! Sent 0 notices") + self.assertEqual(counts(result), [0, 0, 0])