diff options
author | Vitor Freitas | 2019-01-13 13:32:17 +0200 |
---|---|---|
committer | Vitor Freitas | 2019-01-13 13:32:17 +0200 |
commit | b8a0cf965307989f2749554353b73adcca023a33 (patch) | |
tree | ed27af06d546e4f528f46599c80cd99f7de5a310 | |
parent | 4544c0423b4c9ac316d48232a78d74e66ff0189b (diff) | |
download | colossus-b8a0cf965307989f2749554353b73adcca023a33.tar.gz colossus-b8a0cf965307989f2749554353b73adcca023a33.tar.bz2 colossus-b8a0cf965307989f2749554353b73adcca023a33.zip |
Add send to tag shortcut
-rw-r--r-- | colossus/apps/campaigns/forms.py | 44 | ||||
-rw-r--r-- | colossus/apps/campaigns/tests/test_forms.py | 66 | ||||
-rw-r--r-- | colossus/apps/campaigns/tests/test_views.py | 36 | ||||
-rw-r--r-- | colossus/apps/campaigns/views.py | 11 | ||||
-rw-r--r-- | colossus/apps/lists/templates/lists/tag_list.html | 2 |
5 files changed, 152 insertions, 7 deletions
diff --git a/colossus/apps/campaigns/forms.py b/colossus/apps/campaigns/forms.py index 6d865c5..6f675cf 100644 --- a/colossus/apps/campaigns/forms.py +++ b/colossus/apps/campaigns/forms.py @@ -13,7 +13,49 @@ from .models import Campaign class CreateCampaignForm(forms.ModelForm): class Meta: model = Campaign - fields = ('name',) + fields = ('name', 'mailing_list', 'tag') + widgets = { + 'mailing_list': forms.HiddenInput(), + 'tag': forms.HiddenInput() + } + + def save(self, commit=True) -> Campaign: + """ + Before saving check the mailing_list and tag data for consistency + This is done during the save method instead of the clean because + both fields are hidden fields, used when the user clicks the "Send" + button on a tag (as a shortcut to create and campaign for subscribers + in a given tag). + + If the mailing_list + tag combo is invalid there is nothing the user + can do in the user interface, so just fallback to create a campaign + with the Name only. Later on the user can set the mailing_list and tag. + + :param commit: Boolean to indicate if the data should be saved + on the database or not + :return: A new Campaign instance + """ + + campaign = super().save(commit=False) + mailing_list = self.cleaned_data.get('mailing_list') + tag = self.cleaned_data.get('tag') + + if tag is not None and mailing_list is None: + # Remove the tag if there was no mailing list associated with + # This is just to keep the consistency of the data + # In normal cases this should never happen, unless the user is injecting/forging + # a POST request manually + campaign.tag = None + + if tag is not None and mailing_list is not None: + # Remove the tag if it is not associated with the mailing list + if not mailing_list.tags.filter(pk=tag.pk).exists(): + campaign.tag = None + + if commit: + campaign.save() + + return campaign class CampaignRecipientsForm(forms.ModelForm): diff --git a/colossus/apps/campaigns/tests/test_forms.py b/colossus/apps/campaigns/tests/test_forms.py new file mode 100644 index 0000000..bd67c9f --- /dev/null +++ b/colossus/apps/campaigns/tests/test_forms.py @@ -0,0 +1,66 @@ +from colossus.apps.campaigns.forms import CreateCampaignForm +from colossus.apps.campaigns.models import Campaign +from colossus.apps.lists.tests.factories import MailingListFactory +from colossus.apps.subscribers.tests.factories import TagFactory +from colossus.test.testcases import TestCase + + +class CreateCampaignFormTests(TestCase): + def setUp(self): + self.mailing_list_1 = MailingListFactory(name='list_1') + self.tag_1 = TagFactory(name='tag_1', mailing_list=self.mailing_list_1) + self.mailing_list_2 = MailingListFactory(name='list_2') + self.tag_2 = TagFactory(name='tag_2', mailing_list=self.mailing_list_2) + + def test_campaign_creation(self): + form = CreateCampaignForm({'name': 'Test campaign'}) + self.assertTrue(form.is_valid()) + form.save() + self.assertTrue(Campaign.objects.filter(name='Test campaign').exists()) + + def test_form_validation(self): + form = CreateCampaignForm() + self.assertFalse(form.is_valid()) + + def test_campaign_with_list_and_tag(self): + form = CreateCampaignForm({ + 'name': 'Test campaign', + 'mailing_list': self.mailing_list_1.pk, + 'tag': self.tag_1.pk + }) + self.assertTrue(form.is_valid()) + campaign = form.save() + self.assertEqual('Test campaign', campaign.name) + self.assertEqual('list_1', campaign.mailing_list.name) + self.assertEqual('tag_1', campaign.tag.name) + + def test_campaign_with_list_and_invalid_tag(self): + """ + The tag should belong to the mailing list + """ + form = CreateCampaignForm({ + 'name': 'Test campaign', + 'mailing_list': self.mailing_list_1.pk, + 'tag': self.tag_2.pk + }) + self.assertTrue(form.is_valid()) + campaign = form.save() + self.assertEqual('Test campaign', campaign.name) + self.assertEqual('list_1', campaign.mailing_list.name) + self.assertIsNone(campaign.tag) + + def test_campaign_with_tag_and_without_list(self): + """ + To include a tag the campaign should have a mailing list associated with + so we can validate if the tag belong to the mailing list + """ + form = CreateCampaignForm({ + 'name': 'Test campaign', + 'mailing_list': '', + 'tag': self.tag_1.pk + }) + self.assertTrue(form.is_valid()) + campaign = form.save() + self.assertEqual('Test campaign', campaign.name) + self.assertIsNone(campaign.mailing_list) + self.assertIsNone(campaign.tag) diff --git a/colossus/apps/campaigns/tests/test_views.py b/colossus/apps/campaigns/tests/test_views.py index e049f76..48f314b 100644 --- a/colossus/apps/campaigns/tests/test_views.py +++ b/colossus/apps/campaigns/tests/test_views.py @@ -1,8 +1,9 @@ from django.urls import reverse -from colossus.apps.accounts.tests.factories import UserFactory from colossus.apps.campaigns.models import Campaign -from colossus.test.testcases import TestCase +from colossus.apps.lists.tests.factories import MailingListFactory +from colossus.apps.subscribers.tests.factories import TagFactory +from colossus.test.testcases import AuthenticatedTestCase, TestCase from .factories import CampaignFactory @@ -40,16 +41,15 @@ class CampaignsLoginRequiredTests(TestCase): self.assertRedirectsLoginRequired(response, url) -class CampaignListViewSuccessTests(TestCase): +class CampaignListViewSuccessTests(AuthenticatedTestCase): @classmethod def setUpClass(cls): super().setUpClass() CampaignFactory.create_batch(5) def setUp(self): + super().setUp() self.campaigns = Campaign.objects.all() - self.user = UserFactory(username='alex') - self.client.login(username='alex', password='123') self.url = reverse('campaigns:campaigns') self.response = self.client.get(self.url) @@ -66,3 +66,29 @@ class CampaignListViewSuccessTests(TestCase): for content in contents: with self.subTest(content=content): self.assertContains(self.response, content) + + +class CampaignCreateViewTests(AuthenticatedTestCase): + def setUp(self): + super().setUp() + self.mailing_list_1 = MailingListFactory(name='list_1') + self.tag_1 = TagFactory(name='tag_1', mailing_list=self.mailing_list_1) + self.url = reverse('campaigns:campaign_add') + + def test_initial_data(self): + url = '{0}?mailing_list={1}&tag={2}'.format(self.url, self.mailing_list_1.pk, self.tag_1.pk) + response = self.client.get(url) + form = response.context['form'] + self.assertEqual(form.initial['mailing_list'], self.mailing_list_1.pk) + self.assertEqual(form.initial['tag'], self.tag_1.pk) + + def test_invalid_initial_data(self): + url = '{0}?mailing_list=xxx&tag=yyyy'.format(self.url) + response = self.client.get(url) + form = response.context['form'] + self.assertEqual(form.initial, {}) + + def test_html(self): + response = self.client.get(self.url) + self.assertContains(response, '<input type="hidden"', 3) # csrf_token, mailing_list, tag + self.assertContains(response, '<input type="text"', 1) # name tag diff --git a/colossus/apps/campaigns/views.py b/colossus/apps/campaigns/views.py index 2d2b2fa..50986ee 100644 --- a/colossus/apps/campaigns/views.py +++ b/colossus/apps/campaigns/views.py @@ -71,6 +71,17 @@ class CampaignCreateView(CampaignMixin, CreateView): model = Campaign form_class = CreateCampaignForm + def get_initial(self): + initial = super().get_initial() + mailing_list_id = self.request.GET.get('mailing_list', '') + tag_id = self.request.GET.get('tag', '') + try: + initial['mailing_list'] = int(mailing_list_id) + initial['tag'] = int(tag_id) + except (TypeError, ValueError): + pass + return initial + @method_decorator(login_required, name='dispatch') class CampaignEditView(CampaignMixin, DetailView): diff --git a/colossus/apps/lists/templates/lists/tag_list.html b/colossus/apps/lists/templates/lists/tag_list.html index 60e5383..35f7e7b 100644 --- a/colossus/apps/lists/templates/lists/tag_list.html +++ b/colossus/apps/lists/templates/lists/tag_list.html @@ -60,7 +60,7 @@ <span class="sr-only">{% trans 'Toggle Dropdown' %}</span> </button> <div class="dropdown-menu dropdown-menu-right"> - <a class="dropdown-item" href="#">{% trans 'Send' %}</a> + <a class="dropdown-item" href="{% url 'campaigns:campaign_add' %}?mailing_list={{ mailing_list.pk }}&tag={{ tag.pk }}">{% trans 'Send' %}</a> <a class="dropdown-item" href="{% url 'lists:edit_tag' mailing_list.pk tag.pk %}">{% trans 'Edit' %}</a> <div class="dropdown-divider"></div> <a class="dropdown-item" href="{% url 'lists:delete_tag' mailing_list.pk tag.pk %}">{% trans 'Delete' %}</a> |