Skip to content

Commit 519d628

Browse files
committed
Add command to send reminders
Add fields date an interval in nomcom models to send reminders Refactor some managers Add help_text to pending feedback form Limit types in pending feedback form See ietf-tools#969 ietf-tools#975 - Legacy-Id: 5683
1 parent 6898c51 commit 519d628

7 files changed

Lines changed: 157 additions & 13 deletions

File tree

ietf/nomcom/forms.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def __init__(self, *args, **kwargs):
5151
positions = Position.objects.get_by_nomcom(self.nomcom).opened().order_by('name')
5252
results = []
5353
for position in positions:
54-
nominees = [('%s_%s' % (position.id, i.id), unicode(i)) for i in Nominee.objects.get_by_nomcom(self.nomcom).filter(nominee_position=position)]
54+
nominees = [('%s_%s' % (position.id, i.id), unicode(i)) for i in Nominee.objects.get_by_nomcom(self.nomcom).not_duplicated().filter(nominee_position=position)]
5555
if nominees:
5656
results.append((position.name, nominees))
5757
kwargs['choices'] = results
@@ -247,11 +247,11 @@ def done(self, request, cleaned_data):
247247

248248
class EditNomcomForm(BaseNomcomForm, forms.ModelForm):
249249

250-
fieldsets = [('Edit nomcom', ('public_key', 'send_questionnaire'))]
250+
fieldsets = [('Edit nomcom', ('public_key', 'send_questionnaire', 'reminder_interval'))]
251251

252252
class Meta:
253253
model = NomCom
254-
fields = ('public_key', 'send_questionnaire')
254+
fields = ('public_key', 'send_questionnaire', 'reminder_interval')
255255

256256

257257
class MergeForm(BaseNomcomForm, forms.Form):
@@ -268,7 +268,7 @@ def __init__(self, *args, **kwargs):
268268

269269
def clean_primary_email(self):
270270
email = self.cleaned_data['primary_email']
271-
nominees = Nominee.objects.get_by_nomcom(self.nomcom).filter(email__address=email)
271+
nominees = Nominee.objects.get_by_nomcom(self.nomcom).not_duplicated().filter(email__address=email)
272272
if not nominees:
273273
msg = "Does not exist a nomiee with this email"
274274
self._errors["primary_email"] = self.error_class([msg])
@@ -279,7 +279,7 @@ def clean_secondary_emails(self):
279279
data = self.cleaned_data['secondary_emails']
280280
emails = get_list(data)
281281
for email in emails:
282-
nominees = Nominee.objects.get_by_nomcom(self.nomcom).filter(email__address=email)
282+
nominees = Nominee.objects.get_by_nomcom(self.nomcom).not_duplicated().filter(email__address=email)
283283
if not nominees:
284284
msg = "Does not exist a nomiee with email %s" % email
285285
self._errors["primary_email"] = self.error_class([msg])
@@ -736,15 +736,23 @@ class PrivateKeyForm(BaseNomcomForm, forms.Form):
736736

737737

738738
class PendingFeedbackForm(BaseNomcomForm, forms.ModelForm):
739-
739+
740740
class Meta:
741741
model = Feedback
742742
fields = ('author', 'type', 'nominee')
743743

744+
def __init__(self, *args, **kwargs):
745+
super(PendingFeedbackForm, self).__init__(*args, **kwargs)
746+
self.fields['type'].queryset = FeedbackType.objects.exclude(slug='nomina')
747+
748+
744749
def set_nomcom(self, nomcom, user):
745750
self.nomcom = nomcom
746751
self.user = user
747-
self.fields['nominee'] = MultiplePositionNomineeField(nomcom=self.nomcom, required=True, widget=forms.SelectMultiple)
752+
self.fields['nominee'] = MultiplePositionNomineeField(nomcom=self.nomcom,
753+
required=True,
754+
widget=forms.SelectMultiple,
755+
help_text='Hold down "Control", or "Command" on a Mac, to select more than one.')
748756

749757
def save(self, commit=True):
750758
feedback = super(PendingFeedbackForm, self).save(commit=False)

ietf/nomcom/management/commands/feedback_email.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010

1111
class Command(BaseCommand):
12-
help = (u"Send a remind to each SDO Liaison Manager to update the list of persons authorized to send liaison statements on behalf of his SDO")
12+
help = (u"Registry feedback from email. Usage: feeback_email --nomcom-year <nomcom-year> --email-file <email-file>")
1313
option_list = BaseCommand.option_list + (
1414
make_option('--nomcom-year', dest='year', help='NomCom year'),
1515
make_option('--email-file', dest='email', help='Feedback email'),
@@ -35,7 +35,7 @@ def handle(self, *args, **options):
3535
nomcom = NomCom.objects.get(group__acronym__icontains=year,
3636
group__state__slug='active')
3737
except NomCom.DoesNotExist:
38-
raise CommandError('NomCom %s does not exist' % year)
38+
raise CommandError("NomCom %s does not exist or it isn't active" % year)
3939

4040
by, subject, body = parse_email(msg)
4141
name, addr = parseaddr(by)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import datetime
2+
import syslog
3+
from optparse import make_option
4+
5+
from django.core.management.base import BaseCommand, CommandError
6+
from django.conf import settings
7+
8+
from ietf.utils.mail import send_mail
9+
10+
from ietf.dbtemplate.models import DBTemplate
11+
12+
from ietf.nomcom.models import Nominee, NomCom
13+
from ietf.nomcom.utils import NOMINEE_REMINDER_TEMPLATE
14+
15+
16+
class Command(BaseCommand):
17+
help = (u"Send reminders to nominees")
18+
option_list = BaseCommand.option_list + (
19+
make_option('--nomcom-year', dest='year', help='NomCom year'),
20+
)
21+
22+
def handle(self, *args, **options):
23+
year = options.get('year', None)
24+
help_message = 'Usage: send_reminders --nomcom-year <nomcom-year>'
25+
26+
if not year:
27+
raise CommandError(help_message)
28+
29+
try:
30+
nomcom = NomCom.objects.get(group__acronym__icontains=year,
31+
group__state__slug='active')
32+
except NomCom.DoesNotExist:
33+
raise CommandError("NomCom %s does not exist or it isn't active" % year)
34+
35+
today = datetime.date.today()
36+
nomcom_template_path = '/nomcom/%s/' % nomcom.group.acronym
37+
mail_path = nomcom_template_path + NOMINEE_REMINDER_TEMPLATE
38+
mail_template = DBTemplate.objects.filter(group=nomcom.group, path=mail_path)
39+
mail_template = mail_template and mail_template[0] or None
40+
subject = 'IETF Nomination Information'
41+
from_email = settings.NOMCOM_FROM_EMAIL
42+
43+
if nomcom.reminder_interval:
44+
nominees = Nominee.objects.get_by_nomcom(nomcom).not_duplicated().filter(nomineeposition__state='pending').distinct()
45+
for nominee in nominees:
46+
positions = []
47+
for np in nominee.nomineeposition_set.all():
48+
nomination_date = np.time.date()
49+
if not (today - nomination_date).days <= 0:
50+
if (today - nomination_date).days % nomcom.reminder_interval == 0:
51+
positions.append(np.position)
52+
if positions:
53+
to_email = nominee.email.address
54+
context = {'positions': ', '.join([p.name for p in positions])}
55+
send_mail(None, to_email, from_email, subject, mail_path, context)
56+
syslog.syslog(u"Sent reminder to %s" % to_email)
57+
else:
58+
if nomcom.reminderdates_set.filter(date=today):
59+
nominees = Nominee.objects.get_by_nomcom(nomcom).not_duplicated().filter(nomineeposition__state='pending').distinct()
60+
for nominee in nominees:
61+
to_email = nominee.email.address
62+
positions = ', '.join([nominee_position.position.name for nominee_position in nominee.nomineeposition_set.pending()])
63+
context = {'positions': positions}
64+
send_mail(None, to_email, from_email, subject, mail_path, context)
65+
syslog.syslog(u"Sent reminder to %s" % to_email)

ietf/nomcom/migrations/0001_initial.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,21 @@ class Migration(SchemaMigration):
88

99
def forwards(self, orm):
1010

11+
# Adding model 'ReminderDates'
12+
db.create_table('nomcom_reminderdates', (
13+
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
14+
('date', self.gf('django.db.models.fields.DateField')()),
15+
('nomcom', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['nomcom.NomCom'])),
16+
))
17+
db.send_create_signal('nomcom', ['ReminderDates'])
18+
1119
# Adding model 'NomCom'
1220
db.create_table('nomcom_nomcom', (
1321
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
1422
('public_key', self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True, blank=True)),
1523
('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['group.Group'])),
1624
('send_questionnaire', self.gf('django.db.models.fields.BooleanField')(default=False)),
25+
('reminder_interval', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)),
1726
))
1827
db.send_create_signal('nomcom', ['NomCom'])
1928

@@ -108,6 +117,9 @@ def backwards(self, orm):
108117
# Removing unique constraint on 'Nominee', fields ['email', 'nomcom']
109118
db.delete_unique('nomcom_nominee', ['email_id', 'nomcom_id'])
110119

120+
# Deleting model 'ReminderDates'
121+
db.delete_table('nomcom_reminderdates')
122+
111123
# Deleting model 'NomCom'
112124
db.delete_table('nomcom_nomcom')
113125

@@ -365,6 +377,7 @@ def backwards(self, orm):
365377
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']"}),
366378
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
367379
'public_key': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
380+
'reminder_interval': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
368381
'send_questionnaire': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
369382
},
370383
'nomcom.nomination': {
@@ -408,6 +421,12 @@ def backwards(self, orm):
408421
'questionnaire': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questionnaire'", 'null': 'True', 'to': "orm['dbtemplate.DBTemplate']"}),
409422
'requirement': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'requirement'", 'null': 'True', 'to': "orm['dbtemplate.DBTemplate']"})
410423
},
424+
'nomcom.reminderdates': {
425+
'Meta': {'object_name': 'ReminderDates'},
426+
'date': ('django.db.models.fields.DateField', [], {}),
427+
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
428+
'nomcom': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['nomcom.NomCom']"})
429+
},
411430
'person.email': {
412431
'Meta': {'object_name': 'Email'},
413432
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),

ietf/nomcom/models.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,22 @@ def upload_path_handler(instance, filename):
2727
return os.path.join(instance.group.acronym, 'public.cert')
2828

2929

30+
class ReminderDates(models.Model):
31+
date = models.DateField()
32+
nomcom = models.ForeignKey('NomCom')
33+
34+
3035
class NomCom(models.Model):
3136
public_key = models.FileField(storage=FileSystemStorage(location=settings.PUBLIC_KEYS_URL),
3237
upload_to=upload_path_handler, blank=True, null=True)
3338

3439
group = models.ForeignKey(Group)
3540
send_questionnaire = models.BooleanField(verbose_name='Send automatically questionnaires"',
3641
help_text='If you check this box, questionnaires are sent automatically after nominations')
42+
reminder_interval = models.PositiveIntegerField(help_text='If the nomcom user the interval field then a cron command will \
43+
send reminders to the nominees who have not responded using \
44+
the following formula: (today - nomination_date) % interval == 0',
45+
blank=True, null=True)
3746

3847
class Meta:
3948
verbose_name_plural = 'NomComs'

ietf/nomcom/views.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from django.template.loader import render_to_string
1212
from django.utils import simplejson
1313
from django.db.models import Count, Q
14-
from django.forms.models import modelformset_factory
14+
from django.forms.models import modelformset_factory, inlineformset_factory
1515

1616
from ietf.utils.mail import send_mail
1717

@@ -23,7 +23,7 @@
2323
from ietf.nomcom.forms import (NominateForm, FeedbackForm, QuestionnaireForm,
2424
MergeForm, NomComTemplateForm, PositionForm,
2525
PrivateKeyForm, EditNomcomForm, PendingFeedbackForm)
26-
from ietf.nomcom.models import Position, NomineePosition, Nominee, Feedback, NomCom
26+
from ietf.nomcom.models import Position, NomineePosition, Nominee, Feedback, NomCom, ReminderDates
2727
from ietf.nomcom.utils import (get_nomcom_by_year, HOME_TEMPLATE,
2828
store_nomcom_private_key, get_hash_nominee_position,
2929
NOMINEE_REMINDER_TEMPLATE)
@@ -129,7 +129,7 @@ def private_index(request, year):
129129
@member_required(role='chair')
130130
def send_reminder_mail(request, year):
131131
nomcom = get_nomcom_by_year(year)
132-
nominees = Nominee.objects.get_by_nomcom(nomcom).filter(nomineeposition__state='pending').distinct()
132+
nominees = Nominee.objects.get_by_nomcom(nomcom).not_duplicated().filter(nomineeposition__state='pending').distinct()
133133
nomcom_template_path = '/nomcom/%s/' % nomcom.group.acronym
134134
mail_path = nomcom_template_path + NOMINEE_REMINDER_TEMPLATE
135135
mail_template = DBTemplate.objects.filter(group=nomcom.group, path=mail_path)
@@ -442,18 +442,25 @@ def edit_nomcom(request, year):
442442
nomcom = get_nomcom_by_year(year)
443443

444444
message = ('warning', 'Previous data will remain encrypted with the old key')
445+
446+
ReminderDateInlineFormSet = inlineformset_factory(NomCom, ReminderDates)
445447
if request.method == 'POST':
448+
formset = ReminderDateInlineFormSet(request.POST, instance=nomcom)
446449
form = EditNomcomForm(request.POST,
447450
request.FILES,
448451
instance=nomcom)
449-
if form.is_valid():
452+
if form.is_valid() and formset.is_valid():
450453
form.save()
454+
formset.save()
455+
formset = ReminderDateInlineFormSet(instance=nomcom)
451456
message = ('success', 'The nomcom has been changed')
452457
else:
458+
formset = ReminderDateInlineFormSet(instance=nomcom)
453459
form = EditNomcomForm(instance=nomcom)
454460

455461
return render_to_response('nomcom/edit_nomcom.html',
456462
{'form': form,
463+
'formset': formset,
457464
'nomcom': nomcom,
458465
'message': message,
459466
'year': year,

ietf/templates/nomcom/edit_nomcom.html

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,42 @@ <h2>Edit Nomcom</h2>
1515
<table>
1616
{{ form }}
1717
</table>
18+
19+
<h3>Reminder Dates</h3>
20+
21+
<p>If the "reminder interval" field of nomcom isn't filled, the following dates will be used to send reminders.</p>
22+
23+
<p>The valid format: <strong>YYYY-MM-DD</strong></p>
24+
25+
{{ formset.management_form }}
26+
{% for form in formset.forms %}
27+
{% if form.errors %}<div class="info-message-error">Please correct the following errors</div>{% endif %}
28+
<table style="width: 100%;"><tr><td>
29+
<div class="baseform">
30+
<div class="fieldset">
31+
{% for field in form %}
32+
<div id="baseform-fieldname-{{ field.html_name }}"
33+
{% if field.field.widget.is_hidden %}style="display: none;"{% endif %}
34+
class="{% if field.errors %}fieldError {% endif %}field BaseFormStringWidget{% if field.field.column_style %} {{ field.field.column_style }}{% endif %}">
35+
<label for="id_{{ field.html_name }}">{{ field.label }}
36+
{% if field.field.required %}
37+
<span class="fieldRequired" title="Required">*</span>
38+
{% endif %}
39+
</label>
40+
<div class="fieldWidget">
41+
<div id="{{ field.html_name }}_help" class="formHelp"> {{ field.help_text }}</div>
42+
{{ field }}
43+
{{ field.errors }}
44+
</div>
45+
<div class="endfield"></div>
46+
</div>
47+
{% endfor %}
48+
</div>
49+
</div>
50+
</td><td style="width: 50%; vertical-align: top;">
51+
</td></tr></table>
52+
{% endfor %}
53+
1854
<p><input type="submit" value="Save" /></p>
1955
</form>
2056

0 commit comments

Comments
 (0)