Skip to content

Commit 87cdec6

Browse files
authored
Merge pull request #526 from rahulxxarora/master
Enhancement: Sending emails via Celery in Proposals
2 parents 03e6eaa + a4ff804 commit 87cdec6

File tree

8 files changed

+129
-66
lines changed

8 files changed

+129
-66
lines changed

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,9 @@ notifications:
3131
email:
3232
on_success: change # [always|never|change]
3333
on_failure: always # [always|never|change]
34+
35+
services:
36+
- redis-server
37+
38+
before_script:
39+
- celery -A junction worker -l info &

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@ It is advised to install all the requirements inside [virtualenv], use [virtuale
1515
[virtualenvwrapper]: https://virtualenvwrapper.readthedocs.org/en/latest/
1616

1717
```
18-
sudo apt-get install libpq-dev python-dev
18+
sudo apt-get update
19+
sudo apt-get upgrade
20+
sudo apt-get install libpq-dev python-dev build-essential tcl
1921
pip install -r requirements-dev.txt
2022
cp settings/dev.py.sample settings/dev.py
2123
python manage.py migrate --noinput
2224
python manage.py sample_data
25+
sudo apt-get -y install redis-server
2326
```
2427

2528
Initial auth data: admin/123123
@@ -161,6 +164,7 @@ Contributing
161164
<td align=center><img width=100 src=https://avatars.githubusercontent.com/u/889999?v=3><br>Vignesh Sarma K (<a href=https://github.com/vigneshsarma>@vigneshsarma</a>)</td>
162165
<td align=center><img width=100 src=https://avatars.githubusercontent.com/u/316253?v=3><br>Vijay (<a href=https://github.com/vnbang2003>@vnbang2003</a>)</td>
163166
<td align=center><img width=100 src=https://avatars.githubusercontent.com/u/6693374?v=3><br>Vinay Singh (<a href=https://github.com/vinay13>@vinay13</a>)</td>
167+
<td align=center><img width=100 src=https://avatars.githubusercontent.com/u/7351791?v=3><br>Rahul Arora (<a href=https://github.com/rahulxxarora>@rahulxxarora</a>)</td>
164168
</tr>
165169
</table>
166170

junction/proposals/comments_views.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ def create_proposal_comment(request, conference_slug, proposal_slug):
4141
)
4242
host = '{}://{}'.format(settings.SITE_PROTOCOL,
4343
request.META.get('HTTP_HOST'))
44-
send_mail_for_new_comment(proposal_comment, host)
44+
45+
if settings.USE_ASYNC_FOR_EMAIL:
46+
send_mail_for_new_comment.delay(proposal_comment.id, host)
47+
else:
48+
send_mail_for_new_comment(proposal_comment.id, host)
4549

4650
redirect_url = reverse('proposal-detail',
4751
args=[conference.slug, proposal.slug])

junction/proposals/services.py

Lines changed: 65 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,51 @@
99
# Third Party Stuff
1010
from django.conf import settings
1111
from markdown2 import markdown
12+
from celery import shared_task
1213

1314
# Junction Stuff
1415
from junction.base.emailer import send_email
1516
from junction.base.constants import ProposalStatus
1617

17-
from .models import ProposalSection, ProposalSectionReviewer
18+
from .models import Proposal, ProposalComment, ProposalSection, ProposalSectionReviewer
19+
from junction.conferences.models import Conference
1820

1921
logger = logging.getLogger(__name__)
2022

2123

24+
def _get_proposal_section_reviewers(proposal):
25+
proposal_reviewers = set(ProposalSectionReviewer.objects.filter(
26+
proposal_section=proposal.proposal_section))
27+
recipients = {proposal_reviewer.conference_reviewer.reviewer
28+
for proposal_reviewer in proposal_reviewers}
29+
return recipients
30+
31+
32+
def _arrange_proposals_by_section(proposal_qs):
33+
res = collections.defaultdict(list)
34+
for proposal in proposal_qs:
35+
res[proposal.proposal_section.name].append(proposal)
36+
return res
37+
38+
39+
def group_proposals_by_reveiew_state(conf, state='reviewed'):
40+
reviewed_qs = conf.proposal_set.filter(
41+
status=ProposalStatus.PUBLIC).select_related(
42+
'proposal_type', 'proposal_section',
43+
'proposalsection').filter(proposalcomment__private=True,
44+
proposalcomment__deleted=False)
45+
if state == 'reviewed':
46+
proposal_qs = reviewed_qs.distinct()
47+
return _arrange_proposals_by_section(proposal_qs)
48+
else:
49+
ids = reviewed_qs.values_list('id').distinct()
50+
qs = conf.proposal_set.filter(
51+
status=ProposalStatus.PUBLIC).select_related(
52+
'proposal_type', 'proposal_section',
53+
'proposalsection').exclude(pk__in=ids)
54+
return _arrange_proposals_by_section(qs)
55+
56+
2257
def markdown_to_html(md):
2358
"""
2459
Convert given markdown to html.
@@ -28,7 +63,28 @@ def markdown_to_html(md):
2863
return markdown(md)
2964

3065

31-
def send_mail_for_new_comment(proposal_comment, host):
66+
def comment_recipients(proposal_comment):
67+
proposal = proposal_comment.proposal
68+
if proposal_comment.reviewer:
69+
recipients = _get_proposal_section_reviewers(
70+
proposal=proposal)
71+
elif proposal_comment.private:
72+
recipients = _get_proposal_section_reviewers(
73+
proposal=proposal)
74+
recipients.add(proposal.author)
75+
else:
76+
recipients = {
77+
comment.commenter
78+
for comment in proposal.proposalcomment_set
79+
.all().select_related('commenter')}
80+
recipients.add(proposal.author)
81+
82+
return recipients
83+
84+
85+
@shared_task(ignore_result=True)
86+
def send_mail_for_new_comment(proposal_comment_id, host):
87+
proposal_comment = ProposalComment.objects.get(id=proposal_comment_id)
3288
proposal = proposal_comment.proposal
3389
login_url = '{}?next={}'.format(settings.LOGIN_URL, proposal.get_absolute_url())
3490
send_to = comment_recipients(proposal_comment)
@@ -51,26 +107,9 @@ def send_mail_for_new_comment(proposal_comment, host):
51107
'comment_type': comment_type})
52108

53109

54-
def comment_recipients(proposal_comment):
55-
proposal = proposal_comment.proposal
56-
if proposal_comment.reviewer:
57-
recipients = _get_proposal_section_reviewers(
58-
proposal=proposal)
59-
elif proposal_comment.private:
60-
recipients = _get_proposal_section_reviewers(
61-
proposal=proposal)
62-
recipients.add(proposal.author)
63-
else:
64-
recipients = {
65-
comment.commenter
66-
for comment in proposal.proposalcomment_set
67-
.all().select_related('commenter')}
68-
recipients.add(proposal.author)
69-
70-
return recipients
71-
72-
73-
def send_mail_for_new_proposal(proposal, host):
110+
@shared_task(ignore_result=True)
111+
def send_mail_for_new_proposal(proposal_id, host):
112+
proposal = Proposal.objects.get(id=proposal_id)
74113
proposal_section = ProposalSection.objects.get(
75114
pk=proposal.proposal_section_id)
76115
send_to = [p.conference_reviewer.reviewer for p in
@@ -92,18 +131,13 @@ def send_mail_for_new_proposal(proposal, host):
92131
'login_url': login_url})
93132

94133

95-
def _get_proposal_section_reviewers(proposal):
96-
proposal_reviewers = set(ProposalSectionReviewer.objects.filter(
97-
proposal_section=proposal.proposal_section))
98-
recipients = {proposal_reviewer.conference_reviewer.reviewer
99-
for proposal_reviewer in proposal_reviewers}
100-
return recipients
101-
102-
103-
def send_mail_for_proposal_content(conference, proposal, host):
134+
@shared_task(ignore_result=True)
135+
def send_mail_for_proposal_content(conference_id, proposal_id, host):
104136
"""
105137
Send mail to proposal author to upload content for proposal.
106138
"""
139+
conference = Conference.objects.get(id=conference_id)
140+
proposal = Proposal.objects.get(id=proposal_id)
107141
login_url = '{}?next={}'.format(settings.LOGIN_URL, proposal.get_absolute_url())
108142
author = proposal.author
109143
author_name = author.get_full_name() or author.username
@@ -116,28 +150,3 @@ def send_mail_for_proposal_content(conference, proposal, host):
116150
}
117151
return send_email(to=author, template_dir='proposals/email/upload_content',
118152
context=context)
119-
120-
121-
def group_proposals_by_reveiew_state(conf, state='reviewed'):
122-
reviewed_qs = conf.proposal_set.filter(
123-
status=ProposalStatus.PUBLIC).select_related(
124-
'proposal_type', 'proposal_section',
125-
'proposalsection').filter(proposalcomment__private=True,
126-
proposalcomment__deleted=False)
127-
if state == 'reviewed':
128-
proposal_qs = reviewed_qs.distinct()
129-
return _arrange_proposals_by_section(proposal_qs)
130-
else:
131-
ids = reviewed_qs.values_list('id').distinct()
132-
qs = conf.proposal_set.filter(
133-
status=ProposalStatus.PUBLIC).select_related(
134-
'proposal_type', 'proposal_section',
135-
'proposalsection').exclude(pk__in=ids)
136-
return _arrange_proposals_by_section(qs)
137-
138-
139-
def _arrange_proposals_by_section(proposal_qs):
140-
res = collections.defaultdict(list)
141-
for proposal in proposal_qs:
142-
res[proposal.proposal_section.name].append(proposal)
143-
return res

junction/proposals/views.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,11 @@ def create_proposal(request, conference_slug):
131131
proposal_type_id=form.cleaned_data['proposal_type'],
132132
proposal_section_id=form.cleaned_data['proposal_section'])
133133
host = '{}://{}'.format(settings.SITE_PROTOCOL, request.META.get('HTTP_HOST'))
134-
send_mail_for_new_proposal(proposal, host)
134+
135+
if settings.USE_ASYNC_FOR_EMAIL:
136+
send_mail_for_new_proposal.delay(proposal.id, host)
137+
else:
138+
send_mail_for_new_proposal(proposal.id, host)
135139

136140
return HttpResponseRedirect(reverse('proposal-detail',
137141
args=[conference.slug, proposal.slug]))
@@ -387,12 +391,16 @@ def proposal_upload_content(request, conference_slug, slug):
387391
raise PermissionDenied
388392

389393
host = '{}://{}'.format(settings.SITE_PROTOCOL, request.META['HTTP_HOST'])
390-
response = send_mail_for_proposal_content(conference, proposal, host)
391394

392-
if response == 1:
395+
if settings.USE_ASYNC_FOR_EMAIL:
396+
send_mail_for_proposal_content.delay(conference.id, proposal.id, host)
393397
message = 'Email sent successfully.'
394398
else:
395-
message = 'There is problem in sending mail. Please contact conference chair.'
399+
response = send_mail_for_proposal_content(conference.id, proposal.id, host)
400+
if response == 1:
401+
message = 'Email sent successfully.'
402+
else:
403+
message = 'There is problem in sending mail. Please contact conference chair.'
396404

397405
return HttpResponse(message)
398406

settings/common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,5 @@
244244
EXPLARA_API_TOKEN = "shjbalkfbdskjlbdskljbdskaljfb"
245245

246246
QR_CODES_DIR = ROOT_DIR + '/qr_files'
247+
248+
USE_ASYNC_FOR_EMAIL = False

settings/dev.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import absolute_import, unicode_literals
3+
4+
import os
5+
6+
from .common import * # noqa
7+
8+
DEBUG = True
9+
TEMPLATE_DEBUG = DEBUG
10+
11+
DATABASES = {
12+
'default': {
13+
'ENGINE': 'django.db.backends.sqlite3',
14+
'NAME': os.path.join(ROOT_DIR, 'db.sqlite3'),
15+
}
16+
}
17+
18+
ACCOUNT_DEFAULT_HTTP_PROTOCOL = 'http'
19+
20+
TEMPLATE_CONTEXT_PROCESSORS += (
21+
"django.core.context_processors.debug",
22+
)
23+
24+
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
25+
26+
INSTALLED_APPS += ('django_extensions',)
27+
28+
# settings for celery
29+
BROKER_URL = os.environ.get("BROKER_URL", "redis://redis:6379/0")
30+
CELERY_RESULT_BACKEND = os.environ.get("CELERY_RESULT_BACKEND", 'redis://redis:6379/0')

settings/dev.py.sample

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ DATABASES = {
1515
}
1616
}
1717

18-
ACCOUNT_DEFAULT_HTTP_PROTOCOL='http'
18+
ACCOUNT_DEFAULT_HTTP_PROTOCOL = 'http'
1919

2020
TEMPLATE_CONTEXT_PROCESSORS += (
2121
"django.core.context_processors.debug",
@@ -25,6 +25,6 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
2525

2626
INSTALLED_APPS += ('django_extensions',)
2727

28-
#settings for celery
29-
BROKER_URL = os.environ.get("BROKER_URL", "redis://redis:6379/0")
30-
CELERY_RESULT_BACKEND = os.environ.get("CELERY_RESULT_BACKEND", 'redis://redis:6379/0')
28+
# settings for celery
29+
BROKER_URL = os.environ.get("BROKER_URL", "redis://127.0.0.1:6379/0")
30+
CELERY_RESULT_BACKEND = os.environ.get("CELERY_RESULT_BACKEND", 'redis://127.0.0.1:6379/0')

0 commit comments

Comments
 (0)