-
-
Notifications
You must be signed in to change notification settings - Fork 775
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5110 from gitcoinco/feature/celery-4976
Feature/celery 4976-In Review
- Loading branch information
Showing
10 changed files
with
202 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from django.conf import settings | ||
from redis import Redis | ||
|
||
|
||
class RedisService: | ||
def __new__(cls): | ||
if not hasattr(cls, 'instance'): | ||
cls.instance = super().__new__(cls) | ||
return cls.instance | ||
|
||
def __init__(self): | ||
redis_url = settings.CELERY_BROKER_URL | ||
self.redis = Redis.from_url(redis_url) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
from django.conf import settings | ||
|
||
from celery import app | ||
from celery.utils.log import get_task_logger | ||
from app.redis_service import RedisService | ||
from dashboard.models import Profile | ||
from marketing.mails import send_mail, func_name | ||
from retail.emails import render_share_bounty | ||
|
||
logger = get_task_logger(__name__) | ||
|
||
redis = RedisService().redis | ||
|
||
# Lock timeout of 2 minutes (just in the case that the application hangs to avoid a redis deadlock) | ||
LOCK_TIMEOUT = 60 * 2 | ||
|
||
|
||
@app.shared_task(bind=True, max_retries=3) | ||
def bounty_emails(self, emails, msg, profile_handle, invite_url=None, kudos_invite=False, retry: bool = True) -> None: | ||
""" | ||
:param self: | ||
:param emails: | ||
:param msg: | ||
:param profile_handle: | ||
:param invite_url: | ||
:param kudos_invite: | ||
:return: | ||
""" | ||
with redis.lock("tasks:bounty_email:%s" % invite_url, timeout=LOCK_TIMEOUT): | ||
# need to look at how to send bulk emails with SG | ||
profile = Profile.objects.get(handle=profile_handle) | ||
try: | ||
for email in emails: | ||
to_email = email | ||
from_email = settings.CONTACT_EMAIL | ||
subject = "You have been invited to work on a bounty." | ||
html, text = render_share_bounty(to_email, msg, profile, invite_url, kudos_invite) | ||
send_mail( | ||
from_email, | ||
to_email, | ||
subject, | ||
text, | ||
html, | ||
from_name=f"@{profile.handle}", | ||
categories=['transactional', func_name()], | ||
) | ||
|
||
except ConnectionError as exc: | ||
print(exc) | ||
self.retry(30) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,13 +102,15 @@ def send_mail(from_email, _to_email, subject, body, html=False, | |
try: | ||
response = sg.client.mail.send.post(request_body=mail.get()) | ||
except UnauthorizedError as e: | ||
logger.debug(f'-- Sendgrid Mail failure - {_to_email} / {categories} - Unauthorized - Check sendgrid credentials') | ||
logger.debug( | ||
f'-- Sendgrid Mail failure - {_to_email} / {categories} - Unauthorized - Check sendgrid credentials') | ||
logger.debug(e) | ||
except HTTPError as e: | ||
logger.debug(f'-- Sendgrid Mail failure - {_to_email} / {categories} - {e}') | ||
|
||
return response | ||
|
||
|
||
def nth_day_email_campaign(nth, subscriber): | ||
firstname = subscriber.email.split('@')[0] | ||
|
||
|
@@ -369,7 +371,8 @@ def tip_email(tip, to_emails, is_new): | |
warning = '' if tip.network == 'mainnet' else "({})".format(tip.network) | ||
subject = gettext("⚡️ New Tip Worth {} {} {}").format(round(tip.amount, round_decimals), warning, tip.tokenName) | ||
if not is_new: | ||
subject = gettext("🕐 Tip Worth {} {} {} Expiring Soon").format(round(tip.amount, round_decimals), warning, tip.tokenName) | ||
subject = gettext("🕐 Tip Worth {} {} {} Expiring Soon").format(round(tip.amount, round_decimals), warning, | ||
tip.tokenName) | ||
|
||
for to_email in to_emails: | ||
cur_language = translation.get_language() | ||
|
@@ -548,7 +551,7 @@ def warn_account_out_of_eth(account, balance, denomination): | |
setup_lang(to_email) | ||
subject = account + str(_(" is out of gas")) | ||
body_str = _("is down to ") | ||
body = f"{account } {body_str} {balance} {denomination}" | ||
body = f"{account} {body_str} {balance} {denomination}" | ||
if not should_suppress_notification_email(to_email, 'admin'): | ||
send_mail( | ||
from_email, | ||
|
@@ -569,7 +572,7 @@ def warn_subscription_failed(subscription): | |
try: | ||
setup_lang(to_email) | ||
subject = str(subscription.pk) + str(_(" subscription failed")) | ||
body = f"{settings.BASE_URL}{subscription.admin_url }\n{subscription.contributor_profile.email}, {subscription.contributor_profile.user.email}<pre>\n\n{subscription.subminer_comments}</pre>" | ||
body = f"{settings.BASE_URL}{subscription.admin_url}\n{subscription.contributor_profile.email}, {subscription.contributor_profile.user.email}<pre>\n\n{subscription.subminer_comments}</pre>" | ||
if not should_suppress_notification_email(to_email, 'admin'): | ||
send_mail( | ||
from_email, | ||
|
@@ -583,7 +586,6 @@ def warn_subscription_failed(subscription): | |
translation.activate(cur_language) | ||
|
||
|
||
|
||
def new_feedback(email, feedback): | ||
to_email = '[email protected]' | ||
from_email = settings.SERVER_EMAIL | ||
|
@@ -660,20 +662,13 @@ def no_applicant_reminder(to_email, bounty): | |
|
||
|
||
def share_bounty(emails, msg, profile, invite_url=None, kudos_invite=False): | ||
for email in emails: | ||
to_email = email | ||
from_email = settings.CONTACT_EMAIL | ||
subject = f"@{profile.handle} invited you to to work on a bounty." | ||
html, text = render_share_bounty(to_email, msg, profile, invite_url, kudos_invite) | ||
send_mail( | ||
from_email, | ||
to_email, | ||
subject, | ||
text, | ||
html, | ||
from_name=f"@{profile.handle}", | ||
categories=['transactional', func_name()], | ||
) | ||
from dashboard.tasks import bounty_emails | ||
# attempt to delay bounty_emails task to a worker | ||
# long on failure to queue | ||
try: | ||
bounty_emails.delay(emails, msg, profile.handle, invite_url, kudos_invite) | ||
except Exception as e: | ||
logger.error(str(e)) | ||
|
||
|
||
def new_reserved_issue(from_email, user, bounty): | ||
|
@@ -717,6 +712,7 @@ def reject_faucet_request(fr): | |
finally: | ||
translation.activate(cur_language) | ||
|
||
|
||
def new_bounty_daily(bounties, old_bounties, to_emails=None): | ||
if not bounties: | ||
return | ||
|
@@ -800,6 +796,7 @@ def weekly_recap(to_emails=None): | |
finally: | ||
translation.activate(cur_language) | ||
|
||
|
||
def unread_notification_email_weekly_roundup(to_emails=None): | ||
if to_emails is None: | ||
to_emails = [] | ||
|
@@ -945,7 +942,6 @@ def bounty_changed(bounty, to_emails=None): | |
|
||
|
||
def new_match(to_emails, bounty, github_username): | ||
|
||
subject = gettext("⚡️ {} Meet {}: {}! ").format(github_username.title(), bounty.org_name.title(), bounty.title) | ||
|
||
to_email = to_emails[0] | ||
|
@@ -1018,7 +1014,8 @@ def bounty_expire_warning(bounty, to_emails=None): | |
unit = _('hour') | ||
num = int(round((bounty.expires_date - timezone.now()).seconds / 3600 / 24, 0)) | ||
unit = unit + ("s" if num != 1 else "") | ||
subject = gettext("😕 Your Funded Issue ({}) Expires In {} {} ... 😕").format(bounty.title_or_desc, num, unit) | ||
subject = gettext("😕 Your Funded Issue ({}) Expires In {} {} ... 😕").format(bounty.title_or_desc, num, | ||
unit) | ||
|
||
from_email = settings.CONTACT_EMAIL | ||
html, text = render_bounty_expire_warning(to_email, bounty) | ||
|
@@ -1208,6 +1205,10 @@ def new_bounty_request(model): | |
|
||
try: | ||
setup_lang(to_email) | ||
subject = _("New Bounty Request") | ||
body_str = _("New Bounty Request from") | ||
body = f"{body_str} {model.requested_by}: " \ | ||
f"{settings.BASE_URL}_administrationbounty_requests/bountyrequest/{model.pk}/change" | ||
html, text, subject = render_bounty_request(to_email, model, settings.BASE_URL) | ||
|
||
send_mail( | ||
|
@@ -1243,21 +1244,21 @@ def new_funding_limit_increase_request(profile, cleaned_data): | |
usdt_per_tx = cleaned_data.get('usdt_per_tx', 0) | ||
usdt_per_week = cleaned_data.get('usdt_per_week', 0) | ||
comment = cleaned_data.get('comment', '') | ||
accept_link = f'{settings.BASE_URL}requestincrease?'\ | ||
f'profile_pk={profile.pk}&'\ | ||
f'usdt_per_tx={usdt_per_tx}&'\ | ||
f'usdt_per_week={usdt_per_week}' | ||
accept_link = f'{settings.BASE_URL}requestincrease?' \ | ||
f'profile_pk={profile.pk}&' \ | ||
f'usdt_per_tx={usdt_per_tx}&' \ | ||
f'usdt_per_week={usdt_per_week}' | ||
|
||
try: | ||
setup_lang(to_email) | ||
subject = _('New Funding Limit Increase Request') | ||
body = f'New Funding Limit Request from {profile} ({profile.absolute_url}).\n\n'\ | ||
f'New Limit in USD per Transaction: {usdt_per_tx}\n'\ | ||
f'New Limit in USD per Week: {usdt_per_week}\n\n'\ | ||
f'To accept the Funding Limit, visit: {accept_link}\n'\ | ||
f'Administration Link: ({settings.BASE_URL}_administrationdashboard/profile/'\ | ||
f'{profile.pk}/change/#id_max_tip_amount_usdt_per_tx)\n\n'\ | ||
f'Comment:\n{comment}' | ||
body = f'New Funding Limit Request from {profile} ({profile.absolute_url}).\n\n' \ | ||
f'New Limit in USD per Transaction: {usdt_per_tx}\n' \ | ||
f'New Limit in USD per Week: {usdt_per_week}\n\n' \ | ||
f'To accept the Funding Limit, visit: {accept_link}\n' \ | ||
f'Administration Link: ({settings.BASE_URL}_administrationdashboard/profile/' \ | ||
f'{profile.pk}/change/#id_max_tip_amount_usdt_per_tx)\n\n' \ | ||
f'Comment:\n{comment}' | ||
|
||
send_mail(from_email, to_email, subject, body, from_name=_("No Reply from Gitcoin.co")) | ||
finally: | ||
|
@@ -1277,17 +1278,17 @@ def bounty_request_feedback(profile): | |
try: | ||
setup_lang(to_email) | ||
subject = _(f'Bounty Request Feedback, @{profile.username} <> Gitcoin') | ||
body = f'Howdy @{profile.username},\n\n'\ | ||
'This is Vivek from Gitcoin. '\ | ||
'I noticed you made a funded Gitcoin Requests '\ | ||
'a few months ago and just wanted to check in. '\ | ||
'How\'d it go? Any feedback for us?\n\n'\ | ||
'Let us know if you have any bounties in your near future '\ | ||
'-- we\'ll pay attention to '\ | ||
'Gitcoin Requests (https://gitcoin.co/requests/) '\ | ||
'from you as we know you\'ve suggested good things '\ | ||
'in the past 🙂\n\n'\ | ||
'Best,\n\nV' | ||
body = f'Howdy @{profile.username},\n\n' \ | ||
'This is Vivek from Gitcoin. ' \ | ||
'I noticed you made a funded Gitcoin Requests ' \ | ||
'a few months ago and just wanted to check in. ' \ | ||
'How\'d it go? Any feedback for us?\n\n' \ | ||
'Let us know if you have any bounties in your near future ' \ | ||
'-- we\'ll pay attention to ' \ | ||
'Gitcoin Requests (https://gitcoin.co/requests/) ' \ | ||
'from you as we know you\'ve suggested good things ' \ | ||
'in the past 🙂\n\n' \ | ||
'Best,\n\nV' | ||
|
||
send_mail( | ||
from_email, | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import os | ||
|
||
from celery import Celery | ||
from celery.signals import setup_logging | ||
from django.apps import AppConfig, apps | ||
|
||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'app.settings') | ||
|
||
app = Celery('app') | ||
|
||
|
||
class CeleryConfig(AppConfig): | ||
name = 'taskapp' | ||
verbose_name = 'Celery Config' | ||
|
||
# Use Django logging instead of celery logger | ||
@setup_logging.connect | ||
def on_celery_setup_logging(**kwargs): | ||
pass | ||
|
||
def ready(self): | ||
# Using a string here means the worker will not have to | ||
app.config_from_object('django.conf:settings', namespace='CELERY') | ||
installed_apps = [app_config.name for app_config in apps.get_app_configs()] | ||
app.autodiscover_tasks(lambda: installed_apps, force=True) |
Oops, something went wrong.