diff --git a/hc/accounts/admin.py b/hc/accounts/admin.py index ed082a41..02db5c7c 100644 --- a/hc/accounts/admin.py +++ b/hc/accounts/admin.py @@ -43,7 +43,7 @@ class ProfileFieldset(Fieldset): name = "User Profile" fields = ( "email", - "reports_allowed", + "reports", "next_report_date", "nag_period", "next_nag_date", @@ -109,7 +109,7 @@ class ProfileAdmin(admin.ModelAdmin): "projects", "invited", "sms", - "reports_allowed", + "reports", ) list_filter = ( "user__date_joined", diff --git a/hc/accounts/forms.py b/hc/accounts/forms.py index 4f7bb2b2..6f714c0b 100644 --- a/hc/accounts/forms.py +++ b/hc/accounts/forms.py @@ -6,6 +6,7 @@ from django import forms from django.core.exceptions import ValidationError from django.contrib.auth import authenticate from django.contrib.auth.models import User +from hc.accounts.models import REPORT_CHOICES from hc.api.models import TokenBucket @@ -84,7 +85,7 @@ class PasswordLoginForm(forms.Form): class ReportSettingsForm(forms.Form): - reports_allowed = forms.BooleanField(required=False) + reports = forms.ChoiceField(choices=REPORT_CHOICES) nag_period = forms.IntegerField(min_value=0, max_value=86400) def clean_nag_period(self): diff --git a/hc/accounts/migrations/0035_profile_reports.py b/hc/accounts/migrations/0035_profile_reports.py new file mode 100644 index 00000000..da5bc70a --- /dev/null +++ b/hc/accounts/migrations/0035_profile_reports.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.2 on 2021-05-24 07:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0034_credential'), + ] + + operations = [ + migrations.AddField( + model_name='profile', + name='reports', + field=models.CharField(choices=[('off', 'Off'), ('weekly', 'Weekly'), ('monthly', 'Monthly')], default='monthly', max_length=10), + ), + ] diff --git a/hc/accounts/migrations/0036_fill_profile_reports.py b/hc/accounts/migrations/0036_fill_profile_reports.py new file mode 100644 index 00000000..adf081ae --- /dev/null +++ b/hc/accounts/migrations/0036_fill_profile_reports.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.2 on 2021-05-24 07:38 + +from django.db import migrations + + +def fill_reports_field(apps, schema_editor): + Profile = apps.get_model("accounts", "Profile") + Profile.objects.filter(reports_allowed=False).update(reports="off") + Profile.objects.filter(reports_allowed=True).update(reports="monthly") + + +class Migration(migrations.Migration): + + dependencies = [ + ("accounts", "0035_profile_reports"), + ] + + operations = [migrations.RunPython(fill_reports_field, migrations.RunPython.noop)] diff --git a/hc/accounts/models.py b/hc/accounts/models.py index cf84abf9..f3c36ee8 100644 --- a/hc/accounts/models.py +++ b/hc/accounts/models.py @@ -23,6 +23,8 @@ NAG_PERIODS = ( (timedelta(days=1), "Daily"), ) +REPORT_CHOICES = (("off", "Off"), ("weekly", "Weekly"), ("monthly", "Monthly")) + def month(dt): """ For a given datetime, return the matching first-day-of-month date. """ @@ -50,6 +52,7 @@ class Profile(models.Model): user = models.OneToOneField(User, models.CASCADE, blank=True, null=True) next_report_date = models.DateTimeField(null=True, blank=True) reports_allowed = models.BooleanField(default=True) + reports = models.CharField(max_length=10, default="monthly", choices=REPORT_CHOICES) nag_period = models.DurationField(default=NO_NAG, choices=NAG_PERIODS) next_nag_date = models.DateTimeField(null=True, blank=True) ping_log_limit = models.IntegerField(default=100) diff --git a/hc/accounts/tests/test_notifications.py b/hc/accounts/tests/test_notifications.py index 194ce51f..e68f89da 100644 --- a/hc/accounts/tests/test_notifications.py +++ b/hc/accounts/tests/test_notifications.py @@ -6,33 +6,37 @@ from hc.test import BaseTestCase class NotificationsTestCase(BaseTestCase): - def test_it_saves_reports_allowed_true(self): + def test_it_saves_reports_monthly(self): + self.profile.reports = "off" self.profile.reports_allowed = False self.profile.save() self.client.login(username="alice@example.org", password="password") - form = {"reports_allowed": "on", "nag_period": "0"} + form = {"reports": "monthly", "nag_period": "0"} r = self.client.post("/accounts/profile/notifications/", form) self.assertEqual(r.status_code, 200) self.profile.refresh_from_db() self.assertTrue(self.profile.reports_allowed) + self.assertEqual(self.profile.reports, "monthly") self.assertIsNotNone(self.profile.next_report_date) - def test_it_saves_reports_allowed_false(self): + def test_it_saves_reports_off(self): self.profile.reports_allowed = True + self.profile.reports = "monthly" self.profile.next_report_date = now() self.profile.save() self.client.login(username="alice@example.org", password="password") - form = {"nag_period": "0"} + form = {"reports": "off", "nag_period": "0"} r = self.client.post("/accounts/profile/notifications/", form) self.assertEqual(r.status_code, 200) self.profile.refresh_from_db() self.assertFalse(self.profile.reports_allowed) + self.assertEqual(self.profile.reports, "off") self.assertIsNone(self.profile.next_report_date) def test_it_sets_next_nag_date_when_setting_hourly_nag_period(self): @@ -40,7 +44,7 @@ class NotificationsTestCase(BaseTestCase): self.client.login(username="alice@example.org", password="password") - form = {"nag_period": "3600"} + form = {"reports": "off", "nag_period": "3600"} r = self.client.post("/accounts/profile/notifications/", form) self.assertEqual(r.status_code, 200) @@ -54,7 +58,7 @@ class NotificationsTestCase(BaseTestCase): self.client.login(username="alice@example.org", password="password") - form = {"nag_period": "3600"} + form = {"reports": "off", "nag_period": "3600"} r = self.client.post("/accounts/profile/notifications/", form) self.assertEqual(r.status_code, 200) @@ -68,7 +72,7 @@ class NotificationsTestCase(BaseTestCase): self.client.login(username="alice@example.org", password="password") - form = {"nag_period": "1234"} + form = {"reports": "off", "nag_period": "1234"} r = self.client.post("/accounts/profile/notifications/", form) self.assertEqual(r.status_code, 200) diff --git a/hc/accounts/tests/test_unsubscribe_reports.py b/hc/accounts/tests/test_unsubscribe_reports.py index 4b43de84..4e976109 100644 --- a/hc/accounts/tests/test_unsubscribe_reports.py +++ b/hc/accounts/tests/test_unsubscribe_reports.py @@ -22,6 +22,7 @@ class UnsubscribeReportsTestCase(BaseTestCase): self.profile.refresh_from_db() self.assertFalse(self.profile.reports_allowed) + self.assertEqual(self.profile.reports, "off") self.assertIsNone(self.profile.next_report_date) self.assertEqual(self.profile.nag_period.total_seconds(), 0) diff --git a/hc/accounts/views.py b/hc/accounts/views.py index b77a2ed9..d7b2daf6 100644 --- a/hc/accounts/views.py +++ b/hc/accounts/views.py @@ -447,8 +447,9 @@ def notifications(request): if request.method == "POST": form = forms.ReportSettingsForm(request.POST) if form.is_valid(): - if profile.reports_allowed != form.cleaned_data["reports_allowed"]: - profile.reports_allowed = form.cleaned_data["reports_allowed"] + if profile.reports != form.cleaned_data["reports"]: + profile.reports = form.cleaned_data["reports"] + profile.reports_allowed = profile.reports == "monthly" if profile.reports_allowed: profile.next_report_date = choose_next_report_date() else: @@ -542,6 +543,7 @@ def unsubscribe_reports(request, signed_username): user = User.objects.get(username=username) profile = Profile.objects.for_user(user) + profile.reports = "off" profile.reports_allowed = False profile.next_report_date = None profile.nag_period = td() diff --git a/templates/accounts/notifications.html b/templates/accounts/notifications.html index e05b0f43..51a70228 100644 --- a/templates/accounts/notifications.html +++ b/templates/accounts/notifications.html @@ -31,14 +31,24 @@ {% csrf_token %}

Email Reports

-

Send me monthly emails about:

-