From 7a5ca7c4d135fbde0e9186844972fcac03cd4890 Mon Sep 17 00:00:00 2001 From: Tarun Bansal Date: Mon, 18 Jan 2021 18:37:01 +0530 Subject: [PATCH 1/2] Send one mail instead of n mail to supporter on checkout of n grants - Made process_grant_contribution task return grant and created subscription objects - Added batch_process_grant_contributions task to process multiple grants in one task - Added batch_thank_you_for_supporting method to render and send email for multiple grant subscriptions - Added batch_thank_you_for_supporting.html and batch_thank_you_for_supporting.txt templates - Updated grants/views.py to use new batch_process_grant_contributions task Fixes: https://github.com/gitcoinco/web/issues/8238 --- app/grants/tasks.py | 36 ++++- app/grants/views.py | 16 +- app/marketing/mails.py | 24 ++- app/retail/emails.py | 8 + .../batch_thank_you_for_supporting.html | 152 ++++++++++++++++++ .../grants/batch_thank_you_for_supporting.txt | 30 ++++ 6 files changed, 256 insertions(+), 10 deletions(-) create mode 100644 app/retail/templates/emails/grants/batch_thank_you_for_supporting.html create mode 100644 app/retail/templates/emails/grants/batch_thank_you_for_supporting.txt diff --git a/app/grants/tasks.py b/app/grants/tasks.py index 30d18d1b6f1..ba572702e31 100644 --- a/app/grants/tasks.py +++ b/app/grants/tasks.py @@ -13,7 +13,10 @@ from celery.utils.log import get_task_logger from dashboard.models import Profile from grants.models import Grant, Subscription -from marketing.mails import new_grant, new_grant_admin, new_supporter, thank_you_for_supporting +from marketing.mails import ( + new_grant, new_grant_admin, new_supporter, thank_you_for_supporting, + batch_thank_you_for_supporting +) from marketing.models import Stat from perftools.models import JSONStore from townsquare.models import Comment @@ -159,13 +162,14 @@ def update_grant_metadata(self, grant_id, retry: bool = True) -> None: @app.shared_task(bind=True, max_retries=1) -def process_grant_contribution(self, grant_id, grant_slug, profile_id, package, retry: bool = True) -> None: +def process_grant_contribution(self, grant_id, grant_slug, profile_id, package, send_supporter_mail:bool = True, retry: bool = True): """ :param self: :param grant_id: :param grant_slug: :param profile_id: :param package: + :param send_supporter_mail: :return: """ from grants.views import record_subscription_activity_helper @@ -252,8 +256,34 @@ def process_grant_contribution(self, grant_id, grant_slug, profile_id, package, new_supporter(grant, subscription) # emails to contributor - thank_you_for_supporting(grant, subscription) + if send_supporter_mail: + thank_you_for_supporting(grant, subscription) + update_grant_metadata.delay(grant_id) + return grant, subscription + + +@app.shared_task(bind=True, max_retries=1) +def batch_process_grant_contributions(self, grants_with_payload, profile_id, retry: bool = True) -> None: + """ + :param self: + :param grants_with_payload: list of dicts with grant_id, grant_slug and payload + :param profile_id: + :return: + """ + grants_with_subscription = [] + for grant_with_payload in grants_with_payload: + grant_id = grant_with_payload["grant_id"] + grant_slug = grant_with_payload["grant_slug"] + payload = grant_with_payload["payload"] + grant, subscription = process_grant_contribution( + grant_id, grant_slug, profile_id, payload, send_supporter_mail=False, retry=retry + ) + grants_with_subscription.append({ + "grant": grant, + "subscription": subscription + }) + batch_thank_you_for_supporting(grants_with_subscription) @app.shared_task(bind=True, max_retries=1) diff --git a/app/grants/views.py b/app/grants/views.py index c396b3d02b5..e5103121dbf 100644 --- a/app/grants/views.py +++ b/app/grants/views.py @@ -2057,8 +2057,9 @@ def bulk_fund(request): successes = [] failures = [] - batch_grants_mail = [] profile = get_profile(request) + grants_with_payload = [] + for (index, grant_id) in enumerate(grant_ids_list): try: grant = Grant.objects.get(pk=grant_id) @@ -2103,7 +2104,6 @@ def bulk_fund(request): ) try: - from grants.tasks import process_grant_contribution payload = { # Values that are constant for all donations 'checkout_type': request.POST.get('checkout_type'), @@ -2139,7 +2139,11 @@ def bulk_fund(request): 'token_symbol': request.POST.get('token_symbol').split(',')[index], 'include_for_clr': json.loads(request.POST.get('include_for_clr', 'true')) } - process_grant_contribution.delay(grant_id, grant.slug, profile.pk, payload) + grants_with_payload.append({ + 'grant_id': grant_id, + 'grant_slug': grant.slug, + 'payload': payload + }) except Exception as e: failures.append({ 'active': 'grant_error', @@ -2153,13 +2157,13 @@ def bulk_fund(request): successes.append({ 'title': _('Fund - Grant Funding Processed Successfully'), - 'grant':grant_id, + 'grant': grant_id, 'text': _('Funding for this grant was successfully processed and saved.'), 'success': True }) - batch_grants_mail.append(grant_id) - # thank_you_for_supporting(batch_grants_mail, profile) + from grants.tasks import batch_process_grant_contributions + batch_process_grant_contributions.delay(grants_with_payload, profile.pk) return JsonResponse({ 'success': True, diff --git a/app/marketing/mails.py b/app/marketing/mails.py index 55253a56b19..fc70c450f2f 100644 --- a/app/marketing/mails.py +++ b/app/marketing/mails.py @@ -48,7 +48,7 @@ render_subscription_terminated_email, render_successful_contribution_email, render_support_cancellation_email, render_tax_report, render_thank_you_for_supporting_email, render_tip_email, render_tribe_hackathon_prizes, render_unread_notification_email_weekly_roundup, render_wallpost, render_weekly_recap, -) + render_batch_thank_you_for_supporting_email) from sendgrid.helpers.mail import Attachment, Content, Email, Mail, Personalization from sendgrid.helpers.stats import Category from townsquare.utils import is_email_townsquare_enabled, is_there_an_action_available @@ -322,6 +322,28 @@ def thank_you_for_supporting(grant, subscription): translation.activate(cur_language) +def batch_thank_you_for_supporting(grants_with_subscription): + positive_subscriptions = list(filter(lambda gws: not gws["subscription"].negative, grants_with_subscription)) + if len(positive_subscriptions) == 0: + return + + from_email = settings.CONTACT_EMAIL + to_email = positive_subscriptions[0]["subscription"].contributor_profile.email + if not to_email: + to_email = positive_subscriptions[0]["subscription"].contributor_profile.user.email + + cur_language = translation.get_language() + + try: + setup_lang(to_email) + html, text, subject = render_batch_thank_you_for_supporting_email(grants_with_subscription) + + if not should_suppress_notification_email(to_email, 'thank_you_for_supporting'): + send_mail(from_email, to_email, subject, text, html, categories=['transactional', func_name()]) + finally: + translation.activate(cur_language) + + def support_cancellation(grant, subscription): if subscription and subscription.negative: return diff --git a/app/retail/emails.py b/app/retail/emails.py index e36bbb40855..5f108329b52 100644 --- a/app/retail/emails.py +++ b/app/retail/emails.py @@ -139,6 +139,14 @@ def render_thank_you_for_supporting_email(grant, subscription): return response_html, response_txt, subject +def render_batch_thank_you_for_supporting_email(grants_with_subscription): + params = {'grants_with_subscription': grants_with_subscription} + response_html = premailer_transform(render_to_string("emails/grants/batch_thank_you_for_supporting.html", params)) + response_txt = render_to_string("emails/grants/batch_thank_you_for_supporting.txt", params) + subject = _("Thank you for supporting Grants on Gitcoin!") + return response_html, response_txt, subject + + def render_support_cancellation_email(grant, subscription): params = {'grant': grant, 'subscription': subscription} response_html = premailer_transform(render_to_string("emails/grants/support_cancellation.html", params)) diff --git a/app/retail/templates/emails/grants/batch_thank_you_for_supporting.html b/app/retail/templates/emails/grants/batch_thank_you_for_supporting.html new file mode 100644 index 00000000000..a00ec72ca26 --- /dev/null +++ b/app/retail/templates/emails/grants/batch_thank_you_for_supporting.html @@ -0,0 +1,152 @@ +{% extends 'emails/template.html' %} +{% comment %} + Copyright (C) 2020 Gitcoin Core + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +{% endcomment %} +{% load i18n static grants_extra humanize %} + +{% block content %} + + +{% for grant_with_subscription in grants_with_subscription %} +{% with subscription=grant_with_subscription.subscription grant=grant_with_subscription.grant %} +
+
+ {% trans +
+
+

{% trans "Thank you for supporting" %} {{ grant.title }}! +

+

{% trans "The world of open source is a better place because of you." %} +

+
+
+ +
+
+
+ + + +
+ + {{ grant.title }} + +

+ {{ grant.description|truncatechars:300 }} +

+

+ {% trans "You have contributed" %} +

+

+ {{ subscription.amount_per_period|floatformat:4|intcomma }} {{ subscription.token_symbol }} +

+

{% trans "You can see all the grants you support and your transaction history " %} + {% trans "here." %} +

+
+ +
+{% trans "View Updates" %} +
+
+

{% trans "If you ever need to, you can cancel your support" %} + {% trans "here." %} +

+
+{% endwith %} +{% endfor %} +{% endblock %} diff --git a/app/retail/templates/emails/grants/batch_thank_you_for_supporting.txt b/app/retail/templates/emails/grants/batch_thank_you_for_supporting.txt new file mode 100644 index 00000000000..65aa58fe6d1 --- /dev/null +++ b/app/retail/templates/emails/grants/batch_thank_you_for_supporting.txt @@ -0,0 +1,30 @@ +{% load i18n humanize %} + +{% trans "Gitcoin Heart Robot" %} + +{% for grant_with_subscription in grants_with_subscription %} +{% with subscription=grant_with_subscription.subscription grant=grant_with_subscription.grant %} +{% trans "Thank you for supporting" %} {{ subscription.grant.title }} + +{% trans "The world of open source is a better place because of you." %} + +{{ subscription.grant.description }} + +{% trans "You have contributed" %} + +{{ subscription.amount_per_period|floatformat:4|intcomma }} {{ subscription.token_symbol }} + +{% trans "You can view the transaction" %} at +{% if subscription.token_symbol == 'ZEC' %} + {% url 'grants:details' grant.id grant.slug %} +{% elif subscription.network == 'mainnet' %} + http://etherscan.io/tx/{{ subscription.sub_new_approve_tx_id }} +{% else %} + http://{{ grant.network }}.etherscan.io/tx/{{ subscription.sub_new_approve_tx_id }} +{% endif %} + +{% trans "View Updates" %} + +{% trans "If you ever need to you can cancel your support" %} {% trans "here." %} +{% endwith %} +{% endfor %} From 00280fa4e11896ae55e7eda1252d8f26ab8a9588 Mon Sep 17 00:00:00 2001 From: Tarun Bansal Date: Wed, 20 Jan 2021 10:19:25 +0530 Subject: [PATCH 2/2] Remove duplicate templates for rendering multiple grants in thank_you_for_supporting email --- app/grants/sync/helpers.py | 6 +- app/grants/tasks.py | 11 +- app/marketing/mails.py | 27 +--- app/retail/emails.py | 12 +- .../batch_thank_you_for_supporting.html | 152 ------------------ .../grants/batch_thank_you_for_supporting.txt | 30 ---- .../grants/thank_you_for_supporting.html | 4 + .../grants/thank_you_for_supporting.txt | 4 +- 8 files changed, 24 insertions(+), 222 deletions(-) delete mode 100644 app/retail/templates/emails/grants/batch_thank_you_for_supporting.html delete mode 100644 app/retail/templates/emails/grants/batch_thank_you_for_supporting.txt diff --git a/app/grants/sync/helpers.py b/app/grants/sync/helpers.py index 0649768a0a8..e3c055c1435 100644 --- a/app/grants/sync/helpers.py +++ b/app/grants/sync/helpers.py @@ -75,7 +75,11 @@ def record_contribution_activity(contribution): # successful_contribution(grant, subscription, contribution) # update_grant_metadata.delay(grant.pk) new_supporter(grant, subscription) - thank_you_for_supporting(grant, subscription) + grants_with_subscription = [{ + 'grant': grant, + 'subscription': subscription + }] + thank_you_for_supporting(grants_with_subscription) except Exception as e: logger.error(f"error in record_contribution_activity: {e} - {contribution}") diff --git a/app/grants/tasks.py b/app/grants/tasks.py index ba572702e31..a357d07ae8a 100644 --- a/app/grants/tasks.py +++ b/app/grants/tasks.py @@ -14,8 +14,7 @@ from dashboard.models import Profile from grants.models import Grant, Subscription from marketing.mails import ( - new_grant, new_grant_admin, new_supporter, thank_you_for_supporting, - batch_thank_you_for_supporting + new_grant, new_grant_admin, new_supporter, thank_you_for_supporting ) from marketing.models import Stat from perftools.models import JSONStore @@ -257,7 +256,11 @@ def process_grant_contribution(self, grant_id, grant_slug, profile_id, package, # emails to contributor if send_supporter_mail: - thank_you_for_supporting(grant, subscription) + grants_with_subscription = [{ + 'grant': grant, + 'subscription': subscription + }] + thank_you_for_supporting(grants_with_subscription) update_grant_metadata.delay(grant_id) return grant, subscription @@ -283,7 +286,7 @@ def batch_process_grant_contributions(self, grants_with_payload, profile_id, ret "grant": grant, "subscription": subscription }) - batch_thank_you_for_supporting(grants_with_subscription) + thank_you_for_supporting(grants_with_subscription) @app.shared_task(bind=True, max_retries=1) diff --git a/app/marketing/mails.py b/app/marketing/mails.py index fc70c450f2f..b583cb6277b 100644 --- a/app/marketing/mails.py +++ b/app/marketing/mails.py @@ -48,7 +48,7 @@ render_subscription_terminated_email, render_successful_contribution_email, render_support_cancellation_email, render_tax_report, render_thank_you_for_supporting_email, render_tip_email, render_tribe_hackathon_prizes, render_unread_notification_email_weekly_roundup, render_wallpost, render_weekly_recap, - render_batch_thank_you_for_supporting_email) +) from sendgrid.helpers.mail import Attachment, Content, Email, Mail, Personalization from sendgrid.helpers.stats import Category from townsquare.utils import is_email_townsquare_enabled, is_there_an_action_available @@ -301,28 +301,7 @@ def new_supporter(grant, subscription): translation.activate(cur_language) -def thank_you_for_supporting(grant, subscription): - if subscription and subscription.negative: - return - - from_email = settings.CONTACT_EMAIL - to_email = subscription.contributor_profile.email - if not to_email: - to_email = subscription.contributor_profile.user.email - - cur_language = translation.get_language() - - try: - setup_lang(to_email) - html, text, subject = render_thank_you_for_supporting_email(grant, subscription) - - if not should_suppress_notification_email(to_email, 'thank_you_for_supporting'): - send_mail(from_email, to_email, subject, text, html, categories=['transactional', func_name()]) - finally: - translation.activate(cur_language) - - -def batch_thank_you_for_supporting(grants_with_subscription): +def thank_you_for_supporting(grants_with_subscription): positive_subscriptions = list(filter(lambda gws: not gws["subscription"].negative, grants_with_subscription)) if len(positive_subscriptions) == 0: return @@ -336,7 +315,7 @@ def batch_thank_you_for_supporting(grants_with_subscription): try: setup_lang(to_email) - html, text, subject = render_batch_thank_you_for_supporting_email(grants_with_subscription) + html, text, subject = render_thank_you_for_supporting_email(grants_with_subscription) if not should_suppress_notification_email(to_email, 'thank_you_for_supporting'): send_mail(from_email, to_email, subject, text, html, categories=['transactional', func_name()]) diff --git a/app/retail/emails.py b/app/retail/emails.py index 5f108329b52..16403a0ddf5 100644 --- a/app/retail/emails.py +++ b/app/retail/emails.py @@ -131,22 +131,14 @@ def render_new_supporter_email(grant, subscription): return response_html, response_txt, subject -def render_thank_you_for_supporting_email(grant, subscription): - params = {'grant': grant, 'subscription': subscription} +def render_thank_you_for_supporting_email(grants_with_subscription): + params = {'grants_with_subscription': grants_with_subscription} response_html = premailer_transform(render_to_string("emails/grants/thank_you_for_supporting.html", params)) response_txt = render_to_string("emails/grants/thank_you_for_supporting.txt", params) subject = _("Thank you for supporting Grants on Gitcoin!") return response_html, response_txt, subject -def render_batch_thank_you_for_supporting_email(grants_with_subscription): - params = {'grants_with_subscription': grants_with_subscription} - response_html = premailer_transform(render_to_string("emails/grants/batch_thank_you_for_supporting.html", params)) - response_txt = render_to_string("emails/grants/batch_thank_you_for_supporting.txt", params) - subject = _("Thank you for supporting Grants on Gitcoin!") - return response_html, response_txt, subject - - def render_support_cancellation_email(grant, subscription): params = {'grant': grant, 'subscription': subscription} response_html = premailer_transform(render_to_string("emails/grants/support_cancellation.html", params)) diff --git a/app/retail/templates/emails/grants/batch_thank_you_for_supporting.html b/app/retail/templates/emails/grants/batch_thank_you_for_supporting.html deleted file mode 100644 index a00ec72ca26..00000000000 --- a/app/retail/templates/emails/grants/batch_thank_you_for_supporting.html +++ /dev/null @@ -1,152 +0,0 @@ -{% extends 'emails/template.html' %} -{% comment %} - Copyright (C) 2020 Gitcoin Core - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -{% endcomment %} -{% load i18n static grants_extra humanize %} - -{% block content %} - - -{% for grant_with_subscription in grants_with_subscription %} -{% with subscription=grant_with_subscription.subscription grant=grant_with_subscription.grant %} -
-
- {% trans -
-
-

{% trans "Thank you for supporting" %} {{ grant.title }}! -

-

{% trans "The world of open source is a better place because of you." %} -

-
-
- -
-
-
- - - -
- - {{ grant.title }} - -

- {{ grant.description|truncatechars:300 }} -

-

- {% trans "You have contributed" %} -

-

- {{ subscription.amount_per_period|floatformat:4|intcomma }} {{ subscription.token_symbol }} -

-

{% trans "You can see all the grants you support and your transaction history " %} - {% trans "here." %} -

-
- -
-{% trans "View Updates" %} -
-
-

{% trans "If you ever need to, you can cancel your support" %} - {% trans "here." %} -

-
-{% endwith %} -{% endfor %} -{% endblock %} diff --git a/app/retail/templates/emails/grants/batch_thank_you_for_supporting.txt b/app/retail/templates/emails/grants/batch_thank_you_for_supporting.txt deleted file mode 100644 index 65aa58fe6d1..00000000000 --- a/app/retail/templates/emails/grants/batch_thank_you_for_supporting.txt +++ /dev/null @@ -1,30 +0,0 @@ -{% load i18n humanize %} - -{% trans "Gitcoin Heart Robot" %} - -{% for grant_with_subscription in grants_with_subscription %} -{% with subscription=grant_with_subscription.subscription grant=grant_with_subscription.grant %} -{% trans "Thank you for supporting" %} {{ subscription.grant.title }} - -{% trans "The world of open source is a better place because of you." %} - -{{ subscription.grant.description }} - -{% trans "You have contributed" %} - -{{ subscription.amount_per_period|floatformat:4|intcomma }} {{ subscription.token_symbol }} - -{% trans "You can view the transaction" %} at -{% if subscription.token_symbol == 'ZEC' %} - {% url 'grants:details' grant.id grant.slug %} -{% elif subscription.network == 'mainnet' %} - http://etherscan.io/tx/{{ subscription.sub_new_approve_tx_id }} -{% else %} - http://{{ grant.network }}.etherscan.io/tx/{{ subscription.sub_new_approve_tx_id }} -{% endif %} - -{% trans "View Updates" %} - -{% trans "If you ever need to you can cancel your support" %} {% trans "here." %} -{% endwith %} -{% endfor %} diff --git a/app/retail/templates/emails/grants/thank_you_for_supporting.html b/app/retail/templates/emails/grants/thank_you_for_supporting.html index adbd78c0ad0..a00ec72ca26 100644 --- a/app/retail/templates/emails/grants/thank_you_for_supporting.html +++ b/app/retail/templates/emails/grants/thank_you_for_supporting.html @@ -101,6 +101,8 @@ } +{% for grant_with_subscription in grants_with_subscription %} +{% with subscription=grant_with_subscription.subscription grant=grant_with_subscription.grant %}
{% trans @@ -145,4 +147,6 @@

{% trans "The world of open source is a better {% trans "here." %}


+{% endwith %} +{% endfor %} {% endblock %} diff --git a/app/retail/templates/emails/grants/thank_you_for_supporting.txt b/app/retail/templates/emails/grants/thank_you_for_supporting.txt index ad2dcfb0a0d..65aa58fe6d1 100644 --- a/app/retail/templates/emails/grants/thank_you_for_supporting.txt +++ b/app/retail/templates/emails/grants/thank_you_for_supporting.txt @@ -2,7 +2,8 @@ {% trans "Gitcoin Heart Robot" %} -{% for subscription in subscriptions %} +{% for grant_with_subscription in grants_with_subscription %} +{% with subscription=grant_with_subscription.subscription grant=grant_with_subscription.grant %} {% trans "Thank you for supporting" %} {{ subscription.grant.title }} {% trans "The world of open source is a better place because of you." %} @@ -25,4 +26,5 @@ {% trans "View Updates" %} {% trans "If you ever need to you can cancel your support" %} {% trans "here." %} +{% endwith %} {% endfor %}