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 #969 #975
 - Legacy-Id: 5683
This commit is contained in:
Emilio Jiménez 2013-04-23 08:24:21 +00:00
parent 6898c51490
commit 519d628cde
7 changed files with 157 additions and 13 deletions

View file

@ -51,7 +51,7 @@ class PositionNomineeField(forms.ChoiceField):
positions = Position.objects.get_by_nomcom(self.nomcom).opened().order_by('name')
results = []
for position in positions:
nominees = [('%s_%s' % (position.id, i.id), unicode(i)) for i in Nominee.objects.get_by_nomcom(self.nomcom).filter(nominee_position=position)]
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)]
if nominees:
results.append((position.name, nominees))
kwargs['choices'] = results
@ -247,11 +247,11 @@ class EditChairFormPreview(FormPreview):
class EditNomcomForm(BaseNomcomForm, forms.ModelForm):
fieldsets = [('Edit nomcom', ('public_key', 'send_questionnaire'))]
fieldsets = [('Edit nomcom', ('public_key', 'send_questionnaire', 'reminder_interval'))]
class Meta:
model = NomCom
fields = ('public_key', 'send_questionnaire')
fields = ('public_key', 'send_questionnaire', 'reminder_interval')
class MergeForm(BaseNomcomForm, forms.Form):
@ -268,7 +268,7 @@ class MergeForm(BaseNomcomForm, forms.Form):
def clean_primary_email(self):
email = self.cleaned_data['primary_email']
nominees = Nominee.objects.get_by_nomcom(self.nomcom).filter(email__address=email)
nominees = Nominee.objects.get_by_nomcom(self.nomcom).not_duplicated().filter(email__address=email)
if not nominees:
msg = "Does not exist a nomiee with this email"
self._errors["primary_email"] = self.error_class([msg])
@ -279,7 +279,7 @@ class MergeForm(BaseNomcomForm, forms.Form):
data = self.cleaned_data['secondary_emails']
emails = get_list(data)
for email in emails:
nominees = Nominee.objects.get_by_nomcom(self.nomcom).filter(email__address=email)
nominees = Nominee.objects.get_by_nomcom(self.nomcom).not_duplicated().filter(email__address=email)
if not nominees:
msg = "Does not exist a nomiee with email %s" % email
self._errors["primary_email"] = self.error_class([msg])
@ -736,15 +736,23 @@ class PrivateKeyForm(BaseNomcomForm, forms.Form):
class PendingFeedbackForm(BaseNomcomForm, forms.ModelForm):
class Meta:
model = Feedback
fields = ('author', 'type', 'nominee')
def __init__(self, *args, **kwargs):
super(PendingFeedbackForm, self).__init__(*args, **kwargs)
self.fields['type'].queryset = FeedbackType.objects.exclude(slug='nomina')
def set_nomcom(self, nomcom, user):
self.nomcom = nomcom
self.user = user
self.fields['nominee'] = MultiplePositionNomineeField(nomcom=self.nomcom, required=True, widget=forms.SelectMultiple)
self.fields['nominee'] = MultiplePositionNomineeField(nomcom=self.nomcom,
required=True,
widget=forms.SelectMultiple,
help_text='Hold down "Control", or "Command" on a Mac, to select more than one.')
def save(self, commit=True):
feedback = super(PendingFeedbackForm, self).save(commit=False)

View file

@ -9,7 +9,7 @@ from ietf.nomcom.models import Nominee, NomCom, Feedback
class Command(BaseCommand):
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")
help = (u"Registry feedback from email. Usage: feeback_email --nomcom-year <nomcom-year> --email-file <email-file>")
option_list = BaseCommand.option_list + (
make_option('--nomcom-year', dest='year', help='NomCom year'),
make_option('--email-file', dest='email', help='Feedback email'),
@ -35,7 +35,7 @@ class Command(BaseCommand):
nomcom = NomCom.objects.get(group__acronym__icontains=year,
group__state__slug='active')
except NomCom.DoesNotExist:
raise CommandError('NomCom %s does not exist' % year)
raise CommandError("NomCom %s does not exist or it isn't active" % year)
by, subject, body = parse_email(msg)
name, addr = parseaddr(by)

View file

@ -0,0 +1,65 @@
import datetime
import syslog
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings
from ietf.utils.mail import send_mail
from ietf.dbtemplate.models import DBTemplate
from ietf.nomcom.models import Nominee, NomCom
from ietf.nomcom.utils import NOMINEE_REMINDER_TEMPLATE
class Command(BaseCommand):
help = (u"Send reminders to nominees")
option_list = BaseCommand.option_list + (
make_option('--nomcom-year', dest='year', help='NomCom year'),
)
def handle(self, *args, **options):
year = options.get('year', None)
help_message = 'Usage: send_reminders --nomcom-year <nomcom-year>'
if not year:
raise CommandError(help_message)
try:
nomcom = NomCom.objects.get(group__acronym__icontains=year,
group__state__slug='active')
except NomCom.DoesNotExist:
raise CommandError("NomCom %s does not exist or it isn't active" % year)
today = datetime.date.today()
nomcom_template_path = '/nomcom/%s/' % nomcom.group.acronym
mail_path = nomcom_template_path + NOMINEE_REMINDER_TEMPLATE
mail_template = DBTemplate.objects.filter(group=nomcom.group, path=mail_path)
mail_template = mail_template and mail_template[0] or None
subject = 'IETF Nomination Information'
from_email = settings.NOMCOM_FROM_EMAIL
if nomcom.reminder_interval:
nominees = Nominee.objects.get_by_nomcom(nomcom).not_duplicated().filter(nomineeposition__state='pending').distinct()
for nominee in nominees:
positions = []
for np in nominee.nomineeposition_set.all():
nomination_date = np.time.date()
if not (today - nomination_date).days <= 0:
if (today - nomination_date).days % nomcom.reminder_interval == 0:
positions.append(np.position)
if positions:
to_email = nominee.email.address
context = {'positions': ', '.join([p.name for p in positions])}
send_mail(None, to_email, from_email, subject, mail_path, context)
syslog.syslog(u"Sent reminder to %s" % to_email)
else:
if nomcom.reminderdates_set.filter(date=today):
nominees = Nominee.objects.get_by_nomcom(nomcom).not_duplicated().filter(nomineeposition__state='pending').distinct()
for nominee in nominees:
to_email = nominee.email.address
positions = ', '.join([nominee_position.position.name for nominee_position in nominee.nomineeposition_set.pending()])
context = {'positions': positions}
send_mail(None, to_email, from_email, subject, mail_path, context)
syslog.syslog(u"Sent reminder to %s" % to_email)

View file

@ -8,12 +8,21 @@ class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'ReminderDates'
db.create_table('nomcom_reminderdates', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('date', self.gf('django.db.models.fields.DateField')()),
('nomcom', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['nomcom.NomCom'])),
))
db.send_create_signal('nomcom', ['ReminderDates'])
# Adding model 'NomCom'
db.create_table('nomcom_nomcom', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('public_key', self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True, blank=True)),
('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['group.Group'])),
('send_questionnaire', self.gf('django.db.models.fields.BooleanField')(default=False)),
('reminder_interval', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)),
))
db.send_create_signal('nomcom', ['NomCom'])
@ -108,6 +117,9 @@ class Migration(SchemaMigration):
# Removing unique constraint on 'Nominee', fields ['email', 'nomcom']
db.delete_unique('nomcom_nominee', ['email_id', 'nomcom_id'])
# Deleting model 'ReminderDates'
db.delete_table('nomcom_reminderdates')
# Deleting model 'NomCom'
db.delete_table('nomcom_nomcom')
@ -365,6 +377,7 @@ class Migration(SchemaMigration):
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'public_key': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'reminder_interval': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
'send_questionnaire': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
},
'nomcom.nomination': {
@ -408,6 +421,12 @@ class Migration(SchemaMigration):
'questionnaire': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questionnaire'", 'null': 'True', 'to': "orm['dbtemplate.DBTemplate']"}),
'requirement': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'requirement'", 'null': 'True', 'to': "orm['dbtemplate.DBTemplate']"})
},
'nomcom.reminderdates': {
'Meta': {'object_name': 'ReminderDates'},
'date': ('django.db.models.fields.DateField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'nomcom': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['nomcom.NomCom']"})
},
'person.email': {
'Meta': {'object_name': 'Email'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),

View file

@ -27,6 +27,11 @@ def upload_path_handler(instance, filename):
return os.path.join(instance.group.acronym, 'public.cert')
class ReminderDates(models.Model):
date = models.DateField()
nomcom = models.ForeignKey('NomCom')
class NomCom(models.Model):
public_key = models.FileField(storage=FileSystemStorage(location=settings.PUBLIC_KEYS_URL),
upload_to=upload_path_handler, blank=True, null=True)
@ -34,6 +39,10 @@ class NomCom(models.Model):
group = models.ForeignKey(Group)
send_questionnaire = models.BooleanField(verbose_name='Send automatically questionnaires"',
help_text='If you check this box, questionnaires are sent automatically after nominations')
reminder_interval = models.PositiveIntegerField(help_text='If the nomcom user the interval field then a cron command will \
send reminders to the nominees who have not responded using \
the following formula: (today - nomination_date) % interval == 0',
blank=True, null=True)
class Meta:
verbose_name_plural = 'NomComs'

View file

@ -11,7 +11,7 @@ from django.template import RequestContext
from django.template.loader import render_to_string
from django.utils import simplejson
from django.db.models import Count, Q
from django.forms.models import modelformset_factory
from django.forms.models import modelformset_factory, inlineformset_factory
from ietf.utils.mail import send_mail
@ -23,7 +23,7 @@ from ietf.nomcom.decorators import member_required, private_key_required
from ietf.nomcom.forms import (NominateForm, FeedbackForm, QuestionnaireForm,
MergeForm, NomComTemplateForm, PositionForm,
PrivateKeyForm, EditNomcomForm, PendingFeedbackForm)
from ietf.nomcom.models import Position, NomineePosition, Nominee, Feedback, NomCom
from ietf.nomcom.models import Position, NomineePosition, Nominee, Feedback, NomCom, ReminderDates
from ietf.nomcom.utils import (get_nomcom_by_year, HOME_TEMPLATE,
store_nomcom_private_key, get_hash_nominee_position,
NOMINEE_REMINDER_TEMPLATE)
@ -129,7 +129,7 @@ def private_index(request, year):
@member_required(role='chair')
def send_reminder_mail(request, year):
nomcom = get_nomcom_by_year(year)
nominees = Nominee.objects.get_by_nomcom(nomcom).filter(nomineeposition__state='pending').distinct()
nominees = Nominee.objects.get_by_nomcom(nomcom).not_duplicated().filter(nomineeposition__state='pending').distinct()
nomcom_template_path = '/nomcom/%s/' % nomcom.group.acronym
mail_path = nomcom_template_path + NOMINEE_REMINDER_TEMPLATE
mail_template = DBTemplate.objects.filter(group=nomcom.group, path=mail_path)
@ -442,18 +442,25 @@ def edit_nomcom(request, year):
nomcom = get_nomcom_by_year(year)
message = ('warning', 'Previous data will remain encrypted with the old key')
ReminderDateInlineFormSet = inlineformset_factory(NomCom, ReminderDates)
if request.method == 'POST':
formset = ReminderDateInlineFormSet(request.POST, instance=nomcom)
form = EditNomcomForm(request.POST,
request.FILES,
instance=nomcom)
if form.is_valid():
if form.is_valid() and formset.is_valid():
form.save()
formset.save()
formset = ReminderDateInlineFormSet(instance=nomcom)
message = ('success', 'The nomcom has been changed')
else:
formset = ReminderDateInlineFormSet(instance=nomcom)
form = EditNomcomForm(instance=nomcom)
return render_to_response('nomcom/edit_nomcom.html',
{'form': form,
'formset': formset,
'nomcom': nomcom,
'message': message,
'year': year,

View file

@ -15,6 +15,42 @@
<table>
{{ form }}
</table>
<h3>Reminder Dates</h3>
<p>If the "reminder interval" field of nomcom isn't filled, the following dates will be used to send reminders.</p>
<p>The valid format: <strong>YYYY-MM-DD</strong></p>
{{ formset.management_form }}
{% for form in formset.forms %}
{% if form.errors %}<div class="info-message-error">Please correct the following errors</div>{% endif %}
<table style="width: 100%;"><tr><td>
<div class="baseform">
<div class="fieldset">
{% for field in form %}
<div id="baseform-fieldname-{{ field.html_name }}"
{% if field.field.widget.is_hidden %}style="display: none;"{% endif %}
class="{% if field.errors %}fieldError {% endif %}field BaseFormStringWidget{% if field.field.column_style %} {{ field.field.column_style }}{% endif %}">
<label for="id_{{ field.html_name }}">{{ field.label }}
{% if field.field.required %}
<span class="fieldRequired" title="Required">*</span>
{% endif %}
</label>
<div class="fieldWidget">
<div id="{{ field.html_name }}_help" class="formHelp"> {{ field.help_text }}</div>
{{ field }}
{{ field.errors }}
</div>
<div class="endfield"></div>
</div>
{% endfor %}
</div>
</div>
</td><td style="width: 50%; vertical-align: top;">
</td></tr></table>
{% endfor %}
<p><input type="submit" value="Save" /></p>
</form>