Skip to content

Commit 22f3564

Browse files
committed
Added the ability to send reminders to complete questionnaires to nominees in the accepted state.
Changed the send_reminders management command to not take any arguments and run against all active nomcoms. Removed bugs from the send_reminders management command and added tests for it. Adjusted several dbtemplate titles and content. Fixes bug 1157 Commit ready for merge - Legacy-Id: 6465
1 parent 3814b60 commit 22f3564

9 files changed

Lines changed: 195 additions & 81 deletions

File tree

ietf/dbtemplate/fixtures/nomcom_templates.xml

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ $position: Position</field>
9696
</field>
9797
<field to="group.group" name="group" rel="ManyToOneRel"><None></None></field>
9898
</object>
99-
<object p="8" model="dbtemplate.dbtemplate">
100-
<field type="CharField" name="path">/nomcom/defaults/email/nomination_reminder.txt</field>
99+
<object pk="8" model="dbtemplate.dbtemplate">
100+
<field type="CharField" name="path">/nomcom/defaults/email/nomination_accept_reminder.txt</field>
101101
<field type="CharField" name="title">Email sent to nominees asking them to accept (or decline) the nominations.</field>
102102
<field type="TextField" name="variables">$positions: Nomination positions</field>
103103
<field to="name.dbtemplatetypename" name="type" rel="ManyToOneRel">plain</field>
@@ -114,7 +114,7 @@ If you accept, you will need to fill out a questionnaire.
114114
Best regards,</field>
115115
<field to="group.group" name="group" rel="ManyToOneRel"><None></None></field>
116116
</object>
117-
<object p="9" model="dbtemplate.dbtemplate">
117+
<object pk="9" model="dbtemplate.dbtemplate">
118118
<field type="CharField" name="path">/nomcom/defaults/email/nomination_receipt.txt</field>
119119
<field type="CharField" name="title">Email sent to nominator to get a confirmation mail containing feedback in cleartext</field>
120120
<field type="TextField" name="variables">$nominee: Full name of the nominee
@@ -137,7 +137,7 @@ $comments
137137
Thank you,</field>
138138
<field to="group.group" name="group" rel="ManyToOneRel"><None></None></field>
139139
</object>
140-
<object p="10" model="dbtemplate.dbtemplate">
140+
<object pk="10" model="dbtemplate.dbtemplate">
141141
<field type="CharField" name="path">/nomcom/defaults/email/feedback_receipt.txt</field>
142142
<field type="CharField" name="title">Email sent to feedback author to get a confirmation mail containing feedback in cleartext</field>
143143
<field type="TextField" name="variables">$nominee: Full name of the nominee
@@ -158,4 +158,18 @@ $comments
158158
Thank you,</field>
159159
<field to="group.group" name="group" rel="ManyToOneRel"><None></None></field>
160160
</object>
161+
<object pk="11" model="dbtemplate.dbtemplate">
162+
<field type="CharField" name="path">/nomcom/defaults/email/questionnaire_reminder.txt</field>
163+
<field type="CharField" name="title">Email sent to nominees reminding them to complete a questionnaire</field>
164+
<field type="TextField" name="variables">$positions: Nomination positions</field>
165+
<field to="name.dbtemplatetypename" name="type" rel="ManyToOneRel">plain</field>
166+
<field type="TextField" name="content">
167+
Thank you for accepting your nomination for the position of $position.
168+
169+
Please remember to complete and return the questionnaire for this position at your earliest opportunity.
170+
The questionaire is repeated below for your convenience.
171+
172+
--------</field>
173+
<field to="group.group" name="group" rel="ManyToOneRel"><None></None></field>
174+
</object>
161175
</django-objects>
Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,35 @@
11
import datetime
22
import syslog
33
from optparse import make_option
4+
from django.core.management.base import BaseCommand
5+
from ietf.nomcom.models import NomCom, NomineePosition
6+
from nomcom.utils import send_accept_reminder_to_nominee,send_questionnaire_reminder_to_nominee
47

5-
from django.core.management.base import BaseCommand, CommandError
6-
7-
from ietf.nomcom.models import Nominee, NomCom
8-
from nomcom.utils import send_reminder_to_nominee
8+
def log(message):
9+
syslog.syslog(message)
910

11+
def is_time_to_send(nomcom,send_date,nomination_date):
12+
if nomcom.reminder_interval:
13+
days_passed = (send_date - nomination_date).days
14+
return days_passed > 0 and days_passed % nomcom.reminder_interval == 0
15+
else:
16+
return bool(nomcom.reminderdates_set.filter(date=send_date))
1017

1118
class Command(BaseCommand):
12-
help = (u"Send reminders to nominees")
13-
option_list = BaseCommand.option_list + (
14-
make_option('--nomcom-year', dest='year', help='NomCom year'),)
19+
help = (u"Send acceptance and questionnaire reminders to nominees")
1520

1621
def handle(self, *args, **options):
17-
year = options.get('year', None)
18-
help_message = 'Usage: send_reminders --nomcom-year <nomcom-year>'
19-
20-
if not year:
21-
raise CommandError(help_message)
22-
23-
try:
24-
nomcom = NomCom.objects.get(group__acronym__icontains=year,
25-
group__state__slug='active')
26-
except NomCom.DoesNotExist:
27-
raise CommandError("NomCom %s does not exist or it isn't active" % year)
28-
29-
today = datetime.date.today()
30-
31-
if nomcom.reminder_interval:
32-
nominees = Nominee.objects.get_by_nomcom(nomcom).not_duplicated().filter(nomineeposition__state='pending').distinct()
33-
for nominee in nominees:
34-
for nominee_position in nominee.nomineeposition_set.all():
35-
nomination_date = nominee_position.time.date()
36-
if not (today - nomination_date).days <= 0:
37-
if (today - nomination_date).days % nomcom.reminder_interval == 0:
38-
send_reminder_to_nominee(nominee_position)
39-
syslog.syslog(u"Sent reminder to %s" % nominee_position.nominee.email.address)
40-
print u"Sent reminder to %s" % nominee_position.nominee.email.address
41-
else:
42-
if nomcom.reminderdates_set.filter(date=today):
43-
nominees = Nominee.objects.get_by_nomcom(nomcom).not_duplicated().filter(nomineeposition__state='pending').distinct()
44-
for nominee in nominees:
45-
for nominee_position in nominee.nomineeposition_set.pending():
46-
send_reminder_to_nominee(nominee_position)
47-
syslog.syslog(u"Sent reminder to %s" % nominee_position.nominee.email.address)
48-
print u"Sent reminder (by dates) to %s" % nominee_position.nominee.email.address
22+
for nomcom in NomCom.objects.filter(group__state__slug='active'):
23+
for state in ('pending','accepted'):
24+
for nominee_position in NomineePosition.objects.filter(nominee__nomcom=nomcom,
25+
state=state,
26+
nominee__duplicated__isnull=True):
27+
if is_time_to_send(nomcom, datetime.date.today(), nominee_position.time.date()):
28+
if state=='pending':
29+
send_accept_reminder_to_nominee(nominee_position)
30+
log('Sent accept reminder to %s' % nominee_position.nominee.email.address)
31+
elif state=='accepted':
32+
send_questionnaire_reminder_to_nominee(nominee_position)
33+
log('Sent questionnaire reminder to %s' % nominee_position.nominee.email.address)
34+
else:
35+
pass

ietf/nomcom/tests.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22
import os
33
import tempfile
4+
import datetime
45

56
from ietf.utils import TestCase
67
from django.db import IntegrityError
@@ -9,6 +10,7 @@
910
from django.contrib.formtools.preview import security_hash
1011

1112
from ietf.utils.test_utils import login_testing_unauthorized
13+
from ietf.utils.mail import outbox
1214

1315

1416
from ietf.person.models import Email, Person
@@ -20,7 +22,8 @@
2022
NomineePositionState, Feedback, FeedbackType, \
2123
Nomination
2224
from ietf.nomcom.forms import EditChairForm, EditMembersForm
23-
from ietf.nomcom.utils import get_nomcom_by_year
25+
from ietf.nomcom.utils import get_nomcom_by_year, get_or_create_nominee
26+
from ietf.nomcom.management.commands.send_reminders import Command, is_time_to_send
2427

2528

2629
class NomcomViewsTest(TestCase):
@@ -653,3 +656,63 @@ def test_encrypted_comments(self):
653656

654657
os.unlink(self.privatekey_file.name)
655658
os.unlink(self.cert_file.name)
659+
660+
class ReminderCommandTest(TestCase):
661+
perma_fixtures = ['names', 'nomcom_templates']
662+
663+
def setUp(self):
664+
nomcom_test_data()
665+
self.nomcom = get_nomcom_by_year(NOMCOM_YEAR)
666+
667+
gen = Position.objects.get(nomcom=self.nomcom,name='GEN')
668+
rai = Position.objects.get(nomcom=self.nomcom,name='RAI')
669+
iab = Position.objects.get(nomcom=self.nomcom,name='IAB')
670+
671+
today = datetime.date.today()
672+
t_minus_3 = today - datetime.timedelta(days=3)
673+
t_minus_4 = today - datetime.timedelta(days=4)
674+
n = get_or_create_nominee(self.nomcom,"Nominee 1","nominee1@example.org",gen,None)
675+
np = n.nomineeposition_set.get(position=gen)
676+
np.time = t_minus_3
677+
np.save()
678+
n = get_or_create_nominee(self.nomcom,"Nominee 1","nominee1@example.org",iab,None)
679+
np = n.nomineeposition_set.get(position=iab)
680+
np.state = NomineePositionState.objects.get(slug='accepted')
681+
np.time = t_minus_3
682+
np.save()
683+
n = get_or_create_nominee(self.nomcom,"Nominee 2","nominee2@example.org",rai,None)
684+
np = n.nomineeposition_set.get(position=rai)
685+
np.time = t_minus_4
686+
np.save()
687+
688+
def test_is_time_to_send(self):
689+
self.nomcom.reminder_interval = 4
690+
today = datetime.date.today()
691+
self.assertTrue(is_time_to_send(self.nomcom,today+datetime.timedelta(days=4),today))
692+
for delta in range(4):
693+
self.assertFalse(is_time_to_send(self.nomcom,today+datetime.timedelta(days=delta),today))
694+
self.nomcom.reminder_interval = None
695+
self.assertFalse(is_time_to_send(self.nomcom,today,today))
696+
self.nomcom.reminderdates_set.create(date=today)
697+
self.assertTrue(is_time_to_send(self.nomcom,today,today))
698+
699+
def test_command(self):
700+
c = Command()
701+
messages_before=len(outbox)
702+
self.nomcom.reminder_interval = 3
703+
self.nomcom.save()
704+
c.handle(None,None)
705+
self.assertEqual(len(outbox), messages_before + 2)
706+
self.assertTrue('nominee1@example.org' in outbox[-1]['To'])
707+
self.assertTrue('please complete' in outbox[-1]['Subject'])
708+
self.assertTrue('nominee1@example.org' in outbox[-2]['To'])
709+
self.assertTrue('please accept' in outbox[-2]['Subject'])
710+
messages_before=len(outbox)
711+
self.nomcom.reminder_interval = 4
712+
self.nomcom.save()
713+
c.handle(None,None)
714+
self.assertEqual(len(outbox), messages_before + 1)
715+
self.assertTrue('nominee2@example.org' in outbox[-1]['To'])
716+
self.assertTrue('please accept' in outbox[-1]['Subject'])
717+
718+

ietf/nomcom/urls.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from django.conf.urls.defaults import patterns, url
2-
from django.views.generic.simple import direct_to_template
2+
from django.views.generic.simple import direct_to_template, redirect_to
3+
from ietf.utils.lazy import reverse_lazy
34
from ietf.nomcom.forms import EditChairForm, EditChairFormPreview, \
45
EditMembersForm, EditMembersFormPreview
56

@@ -17,7 +18,8 @@
1718
url(r'^(?P<year>\d{4})/private/view-feedback/nominee/(?P<nominee_id>\d+)$', 'view_feedback_nominee', name='nomcom_view_feedback_nominee'),
1819
url(r'^(?P<year>\d{4})/private/edit/nominee/(?P<nominee_id>\d+)$', 'edit_nominee', name='nomcom_edit_nominee'),
1920
url(r'^(?P<year>\d{4})/private/merge/$', 'private_merge', name='nomcom_private_merge'),
20-
url(r'^(?P<year>\d{4})/private/send-reminder-mail/$', 'send_reminder_mail', name='nomcom_send_reminder_mail'),
21+
# url(r'^(?P<year>\d{4})/private/send-reminder-mail/$', redirect_to, { 'url': reverse_lazy('nomcom_send_reminder_mail',kwargs={'year':year,'type':'accept'})}),
22+
url(r'^(?P<year>\d{4})/private/send-reminder-mail/(?P<type>\w+)/$', 'send_reminder_mail', name='nomcom_send_reminder_mail'),
2123
url(r'^(?P<year>\d{4})/private/edit-members/$', EditMembersFormPreview(EditMembersForm), name='nomcom_edit_members'),
2224
url(r'^(?P<year>\d{4})/private/edit-chair/$', EditChairFormPreview(EditChairForm), name='nomcom_edit_chair'),
2325
url(r'^(?P<year>\d{4})/private/edit-nomcom/$', 'edit_nomcom', name='nomcom_edit_nomcom'),

ietf/nomcom/utils.py

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,17 @@
3232
INEXISTENT_PERSON_TEMPLATE = 'email/inexistent_person.txt'
3333
NOMINEE_EMAIL_TEMPLATE = 'email/new_nominee.txt'
3434
NOMINATION_EMAIL_TEMPLATE = 'email/new_nomination.txt'
35-
NOMINEE_REMINDER_TEMPLATE = 'email/nomination_reminder.txt'
35+
NOMINEE_ACCEPT_REMINDER_TEMPLATE = 'email/nomination_accept_reminder.txt'
36+
NOMINEE_QUESTIONNAIRE_REMINDER_TEMPLATE = 'email/questionnaire_reminder.txt'
3637
NOMINATION_RECEIPT_TEMPLATE = 'email/nomination_receipt.txt'
3738
FEEDBACK_RECEIPT_TEMPLATE = 'email/feedback_receipt.txt'
3839

3940
DEFAULT_NOMCOM_TEMPLATES = [HOME_TEMPLATE,
4041
INEXISTENT_PERSON_TEMPLATE,
4142
NOMINEE_EMAIL_TEMPLATE,
4243
NOMINATION_EMAIL_TEMPLATE,
43-
NOMINEE_REMINDER_TEMPLATE,
44+
NOMINEE_ACCEPT_REMINDER_TEMPLATE,
45+
NOMINEE_QUESTIONNAIRE_REMINDER_TEMPLATE,
4446
NOMINATION_RECEIPT_TEMPLATE,
4547
FEEDBACK_RECEIPT_TEMPLATE]
4648

@@ -107,7 +109,7 @@ def initialize_questionnaire_for_position(position):
107109
content=header_template.content)
108110
questionnaire = DBTemplate.objects.create(
109111
group=position.nomcom.group,
110-
title=template.title + '[%s]' % position.name,
112+
title=template.title + ' [%s]' % position.name,
111113
path='/nomcom/' + position.nomcom.group.acronym + '/' + str(position.id) + '/' + QUESTIONNAIRE_TEMPLATE,
112114
variables=template.variables,
113115
type_id=template.type_id,
@@ -191,15 +193,15 @@ def validate_public_key(public_key):
191193
return (not error, error)
192194

193195

194-
def send_reminder_to_nominee(nominee_position):
196+
def send_accept_reminder_to_nominee(nominee_position):
195197
today = datetime.date.today().strftime('%Y%m%d')
196-
subject = 'IETF Nomination Information'
198+
subject = 'Reminder: please accept (or decline) your nomination.'
197199
from_email = settings.NOMCOM_FROM_EMAIL
198200
domain = Site.objects.get_current().domain
199201
position = nominee_position.position
200202
nomcom = position.nomcom
201203
nomcom_template_path = '/nomcom/%s/' % nomcom.group.acronym
202-
mail_path = nomcom_template_path + NOMINEE_REMINDER_TEMPLATE
204+
mail_path = nomcom_template_path + NOMINEE_ACCEPT_REMINDER_TEMPLATE
203205
nominee = nominee_position.nominee
204206
to_email = nominee.email.address
205207

@@ -229,11 +231,42 @@ def send_reminder_to_nominee(nominee_position):
229231
body += '\n\n%s' % render_to_string(path, context)
230232
send_mail_text(None, to_email, from_email, subject, body)
231233

234+
def send_questionnaire_reminder_to_nominee(nominee_position):
235+
today = datetime.date.today().strftime('%Y%m%d')
236+
subject = 'Reminder: please complete the Nomcom questionnaires for your nomination.'
237+
from_email = settings.NOMCOM_FROM_EMAIL
238+
domain = Site.objects.get_current().domain
239+
position = nominee_position.position
240+
nomcom = position.nomcom
241+
nomcom_template_path = '/nomcom/%s/' % nomcom.group.acronym
242+
mail_path = nomcom_template_path + NOMINEE_QUESTIONNAIRE_REMINDER_TEMPLATE
243+
nominee = nominee_position.nominee
244+
to_email = nominee.email.address
245+
246+
hash = get_hash_nominee_position(today, nominee_position.id)
247+
248+
context = {'nominee': nominee,
249+
'position': position,
250+
'domain': domain,
251+
}
252+
body = render_to_string(mail_path, context)
253+
path = '%s%d/%s' % (nomcom_template_path, position.id, QUESTIONNAIRE_TEMPLATE)
254+
body += '\n\n%s' % render_to_string(path, context)
255+
send_mail_text(None, to_email, from_email, subject, body)
232256

233-
def send_reminder_to_nominees(nominees):
234-
for nominee in nominees:
235-
for nominee_position in nominee.nomineeposition_set.pending():
236-
send_reminder_to_nominee(nominee_position)
257+
def send_reminder_to_nominees(nominees,type):
258+
addrs = []
259+
if type=='accept':
260+
for nominee in nominees:
261+
for nominee_position in nominee.nomineeposition_set.pending():
262+
send_accept_reminder_to_nominee(nominee_position)
263+
addrs.append(nominee_position.nominee.email.address)
264+
elif type=='questionnaire':
265+
for nominee in nominees:
266+
for nominee_position in nominee.nomineeposition_set.accepted():
267+
send_questionnaire_reminder_to_nominee(nominee_position)
268+
addrs.append(nominee_position.nominee.email.address)
269+
return addrs
237270

238271

239272
def get_or_create_nominee(nomcom, candidate_name, candidate_email, position, author):

0 commit comments

Comments
 (0)