Added forms for the exceptional "create a new person for this nomination" workflow. Checkpointing before a round of simplification refactors.

- Legacy-Id: 10610
This commit is contained in:
Robert Sparks 2015-12-18 23:11:24 +00:00
parent 6bf4227974
commit 8261507e80
5 changed files with 299 additions and 101 deletions

View file

@ -5,6 +5,8 @@ from django.shortcuts import get_object_or_404, redirect
from django.utils.decorators import method_decorator
from django.shortcuts import render_to_response
from django.template.context import RequestContext
from django.core.urlresolvers import reverse
from django.utils.html import mark_safe
from ietf.dbtemplate.forms import DBTemplateForm
from ietf.group.models import Group, Role
@ -300,7 +302,7 @@ class MergeForm(forms.Form):
class NominateForm(forms.ModelForm):
candidate = SearchableEmailField(only_users=False)
searched_email = SearchableEmailField(only_users=False)
qualifications = forms.CharField(label="Candidate's qualifications for the position",
widget=forms.Textarea())
confirmation = forms.BooleanField(label='Email comments back to me as confirmation.',
@ -314,6 +316,9 @@ class NominateForm(forms.ModelForm):
super(NominateForm, self).__init__(*args, **kwargs)
new_person_url_name = 'nomcom_%s_nominate_newperson' % ('public' if self.public else 'private' )
self.fields['searched_email'].label = 'Candidate email'
self.fields['searched_email'].help_text = 'Search by name or email address. Click <a href="%s">here</a> if the search does not find the candidate you want to nominate.' % reverse(new_person_url_name,kwargs={'year':self.nomcom.year()})
self.fields['nominator_email'].label = 'Nominator email'
if self.nomcom:
self.fields['position'].queryset = Position.objects.get_by_nomcom(self.nomcom).opened()
@ -339,18 +344,15 @@ class NominateForm(forms.ModelForm):
# Create nomination
nomination = super(NominateForm, self).save(commit=False)
nominator_email = self.cleaned_data.get('nominator_email', None)
## TODO - rename this candidate_email after purging the old candidate_email
candidate = self.cleaned_data['candidate']
##candidate_email = self.cleaned_data['candidate_email']
##candidate_name = self.cleaned_data['candidate_name']
searched_email = self.cleaned_data['searched_email']
position = self.cleaned_data['position']
qualifications = self.cleaned_data['qualifications']
confirmation = self.cleaned_data.get('confirmation', False)
share_nominator = self.cleaned_data['share_nominator']
nomcom_template_path = '/nomcom/%s/' % self.nomcom.group.acronym
nomination.candidate_name = candidate.person.plain_name()
nomination.candidate_email = candidate.address
nomination.candidate_name = searched_email.person.plain_name()
nomination.candidate_email = searched_email.address
author = None
if self.public:
@ -359,8 +361,7 @@ class NominateForm(forms.ModelForm):
if nominator_email:
emails = Email.objects.filter(address=nominator_email)
author = emails and emails[0] or None
##nominee = get_or_create_nominee(self.nomcom, candidate_name, candidate_email, position, author)
nominee = get_or_create_nominee_by_person (self.nomcom, candidate.person, position, author)
nominee = get_or_create_nominee_by_person (self.nomcom, searched_email.person, position, author)
# Complete nomination data
feedback = Feedback.objects.create(nomcom=self.nomcom,
@ -399,10 +400,118 @@ class NominateForm(forms.ModelForm):
class Meta:
model = Nomination
fields = ('share_nominator', 'position', 'nominator_email', 'candidate',
fields = ('share_nominator', 'position', 'nominator_email', 'searched_email',
'candidate_phone', 'qualifications', 'confirmation')
##fields = ('share_nominator', 'position', 'nominator_email', 'candidate', 'candidate_name',
## 'candidate_email', 'candidate_phone', 'qualifications', 'confirmation')
class NominateNewPersonForm(forms.ModelForm):
qualifications = forms.CharField(label="Candidate's qualifications for the position",
widget=forms.Textarea())
confirmation = forms.BooleanField(label='Email comments back to me as confirmation.',
help_text="If you want to get a confirmation mail containing your feedback in cleartext, please check the 'email comments back to me as confirmation'.",
required=False)
def __init__(self, *args, **kwargs):
self.nomcom = kwargs.pop('nomcom', None)
self.user = kwargs.pop('user', None)
self.public = kwargs.pop('public', None)
super(NominateNewPersonForm, self).__init__(*args, **kwargs)
self.fields['nominator_email'].label = 'Nominator email'
if self.nomcom:
self.fields['position'].queryset = Position.objects.get_by_nomcom(self.nomcom).opened()
self.fields['qualifications'].help_text = self.nomcom.initial_text
if not self.public:
self.fields.pop('confirmation')
author = get_user_email(self.user)
if author:
self.fields['nominator_email'].initial = author.address
help_text = """(Nomcom Chair/Member: please fill this in. Use your own email address if the person making the
nomination wishes to be anonymous. The confirmation email will be sent to the address given here,
and the address will also be captured as part of the registered nomination.)"""
self.fields['nominator_email'].help_text = help_text
self.fields['share_nominator'].help_text = """(Nomcom Chair/Member: Check this box if the person providing this nomination
has indicated they will allow NomCom to share their name as one of the people
nominating this candidate."""
else:
self.fields.pop('nominator_email')
def clean_candidate_email(self):
candidate_email = self.cleaned_data['candidate_email']
if Email.objects.filter(address=candidate_email).exists():
normal_url_name = 'nomcom_%s_nominate' % 'public' if self.public else 'private'
msg = '%s is already in the datatracker. \
Use the <a href="%s">normal nomination form</a> to nominate the person \
with this address.\
' % (candidate_email,reverse(normal_url_name,kwargs={'year':self.nomcom.year()}))
raise forms.ValidationError(mark_safe(msg))
return candidate_email
def save(self, commit=True):
# Create nomination
nomination = super(NominateNewPersonForm, self).save(commit=False)
nominator_email = self.cleaned_data.get('nominator_email', None)
candidate_email = self.cleaned_data['candidate_email']
candidate_name = self.cleaned_data['candidate_name']
position = self.cleaned_data['position']
qualifications = self.cleaned_data['qualifications']
confirmation = self.cleaned_data.get('confirmation', False)
share_nominator = self.cleaned_data['share_nominator']
nomcom_template_path = '/nomcom/%s/' % self.nomcom.group.acronym
author = None
if self.public:
author = get_user_email(self.user)
else:
if nominator_email:
emails = Email.objects.filter(address=nominator_email)
author = emails and emails[0] or None
## This is where it should change - validation of the email field should fail if the email exists
## The function should become make_nominee_from_newperson)
nominee = get_or_create_nominee(self.nomcom, candidate_name, candidate_email, position, author)
# Complete nomination data
feedback = Feedback.objects.create(nomcom=self.nomcom,
comments=qualifications,
type=FeedbackTypeName.objects.get(slug='nomina'),
user=self.user)
feedback.positions.add(position)
feedback.nominees.add(nominee)
if author:
nomination.nominator_email = author.address
feedback.author = author.address
feedback.save()
nomination.nominee = nominee
nomination.comments = feedback
nomination.share_nominator = share_nominator
nomination.user = self.user
if commit:
nomination.save()
# send receipt email to nominator
if confirmation:
if author:
subject = 'Nomination receipt'
from_email = settings.NOMCOM_FROM_EMAIL
(to_email, cc) = gather_address_lists('nomination_receipt_requested',nominator=author.address)
context = {'nominee': nominee.email.person.name,
'comments': qualifications,
'position': position.name}
path = nomcom_template_path + NOMINATION_RECEIPT_TEMPLATE
send_mail(None, to_email, from_email, subject, path, context, cc=cc)
return nomination
class Meta:
model = Nomination
fields = ('share_nominator', 'position', 'nominator_email', 'candidate_name',
'candidate_email', 'candidate_phone', 'qualifications', 'confirmation')
class FeedbackForm(forms.ModelForm):

View file

@ -75,6 +75,7 @@ class NomcomViewsTest(TestCase):
self.edit_members_url = reverse('nomcom_edit_members', kwargs={'year': self.year})
self.edit_nomcom_url = reverse('nomcom_edit_nomcom', kwargs={'year': self.year})
self.private_nominate_url = reverse('nomcom_private_nominate', kwargs={'year': self.year})
self.private_nominate_newperson_url = reverse('nomcom_private_nominate_newperson', kwargs={'year': self.year})
self.add_questionnaire_url = reverse('nomcom_private_questionnaire', kwargs={'year': self.year})
self.private_feedback_url = reverse('nomcom_private_feedback', kwargs={'year': self.year})
self.positions_url = reverse("nomcom_list_positions", kwargs={'year': self.year})
@ -86,6 +87,7 @@ class NomcomViewsTest(TestCase):
self.questionnaires_url = reverse('nomcom_questionnaires', kwargs={'year': self.year})
self.public_feedback_url = reverse('nomcom_public_feedback', kwargs={'year': self.year})
self.public_nominate_url = reverse('nomcom_public_nominate', kwargs={'year': self.year})
self.public_nominate_newperson_url = reverse('nomcom_public_nominate_newperson', kwargs={'year': self.year})
def tearDown(self):
clean_test_public_keys_dir(self)
@ -507,6 +509,42 @@ class NomcomViewsTest(TestCase):
return self.nominate_view(public=False)
self.client.logout()
def test_public_nominate_newperson(self):
login_testing_unauthorized(self, COMMUNITY_USER, self.public_nominate_url)
messages_before = len(outbox)
self.nominate_newperson_view(public=True,confirmation=True)
self.assertEqual(len(outbox), messages_before + 4)
self.assertEqual('New person is created', outbox[-4]['Subject'])
self.assertTrue('secretariat' in outbox[-4]['To'])
self.assertEqual('IETF Nomination Information', outbox[-3]['Subject'])
self.assertTrue('nominee' in outbox[-3]['To'])
self.assertEqual('Nomination Information', outbox[-2]['Subject'])
self.assertTrue('nomcomchair' in outbox[-2]['To'])
self.assertEqual('Nomination receipt', outbox[-1]['Subject'])
self.assertTrue('plain' in outbox[-1]['To'])
self.assertTrue(u'Comments with accents äöå' in unicode(outbox[-1].get_payload(decode=True),"utf-8","replace"))
# Nominate the same person for the same position again without asking for confirmation
messages_before = len(outbox)
self.nominate_view(public=True)
self.assertEqual(len(outbox), messages_before + 1)
self.assertEqual('Nomination Information', outbox[-1]['Subject'])
self.assertTrue('nomcomchair' in outbox[-1]['To'])
def test_private_nominate_newperson(self):
self.access_member_url(self.private_nominate_url)
return self.nominate_newperson_view(public=False)
self.client.logout()
def test_public_nominate_with_automatic_questionnaire(self):
nomcom = get_nomcom_by_year(self.year)
nomcom.send_questionnaire = True
@ -522,15 +560,15 @@ class NomcomViewsTest(TestCase):
def nominate_view(self, *args, **kwargs):
public = kwargs.pop('public', True)
nominee = kwargs.pop('nominee', None)
searched_email = kwargs.pop('searched_email', None)
nominee_email = kwargs.pop('nominee_email', u'nominee@example.com')
if not nominee:
nominee = Email.objects.filter(address=nominee_email).first()
if not nominee:
nominee = EmailFactory(address=nominee_email,primary=True)
if not nominee.person:
nominee.person = PersonFactory()
nominee.save()
if not searched_email:
searched_email = Email.objects.filter(address=nominee_email).first()
if not searched_email:
searched_email = EmailFactory(address=nominee_email,primary=True)
if not searched_email.person:
searched_email.person = PersonFactory()
searched_email.save()
nominator_email = kwargs.pop('nominator_email', "%s%s" % (COMMUNITY_USER, EMAIL_DOMAIN))
position_name = kwargs.pop('position', 'IAOC')
confirmation = kwargs.pop('confirmation', False)
@ -560,7 +598,7 @@ class NomcomViewsTest(TestCase):
comments = u'Test nominate view. Comments with accents äöåÄÖÅ éáíóú âêîôû ü àèìòù.'
candidate_phone = u'123456'
test_data = {'candidate': nominee.pk,
test_data = {'searched_email': searched_email.pk,
'candidate_phone': candidate_phone,
'position': position.id,
'qualifications': comments,
@ -574,11 +612,10 @@ class NomcomViewsTest(TestCase):
self.assertContains(response, "alert-success")
# check objects
## TODO - straighten this _obj vs _email naming mess out
nominee_obj = Nominee.objects.get(email=nominee)
NomineePosition.objects.get(position=position, nominee=nominee_obj)
nominee = Nominee.objects.get(email=searched_email)
NomineePosition.objects.get(position=position, nominee=nominee)
feedback = Feedback.objects.filter(positions__in=[position],
nominees__in=[nominee_obj],
nominees__in=[nominee],
type=FeedbackTypeName.objects.get(slug='nomina')).latest('id')
if public:
self.assertEqual(feedback.author, nominator_email)
@ -589,84 +626,83 @@ class NomcomViewsTest(TestCase):
self.assertEqual(check_comments(feedback.comments, comments, self.privatekey_file), True)
Nomination.objects.get(position=position,
candidate_name=nominee.person.plain_name(),
candidate_email=nominee.address,
candidate_email=searched_email.address,
candidate_phone=candidate_phone,
nominee=nominee_obj,
nominee=nominee,
comments=feedback,
nominator_email="%s%s" % (COMMUNITY_USER, EMAIL_DOMAIN))
def nominate_newperson_view(self, *args, **kwargs):
public = kwargs.pop('public', True)
nominee_email = kwargs.pop('nominee_email', u'nominee@example.com')
nominator_email = kwargs.pop('nominator_email', "%s%s" % (COMMUNITY_USER, EMAIL_DOMAIN))
position_name = kwargs.pop('position', 'IAOC')
confirmation = kwargs.pop('confirmation', False)
if public:
nominate_url = self.public_nominate_newperson_url
else:
nominate_url = self.private_nominate_newperson_url
response = self.client.get(nominate_url)
self.assertEqual(response.status_code, 200)
nomcom = get_nomcom_by_year(self.year)
if not nomcom.public_key:
q = PyQuery(response.content)
self.assertEqual(len(q("#nominate-form")), 0)
# save the cert file in tmp
nomcom.public_key.storage.location = tempfile.gettempdir()
nomcom.public_key.save('cert', File(open(self.cert_file.name, 'r')))
response = self.client.get(nominate_url)
self.assertEqual(response.status_code, 200)
q = PyQuery(response.content)
self.assertEqual(len(q("#nominate-form")), 1)
position = Position.objects.get(name=position_name)
candidate_email = nominee_email
candidate_name = u'nominee'
comments = u'Test nominate view. Comments with accents äöåÄÖÅ éáíóú âêîôû ü àèìòù.'
candidate_phone = u'123456'
test_data = {'candidate_name': candidate_name,
'candidate_email': candidate_email,
'candidate_phone': candidate_phone,
'position': position.id,
'qualifications': comments,
'confirmation': confirmation}
if not public:
test_data['nominator_email'] = nominator_email
response = self.client.post(nominate_url, test_data)
self.assertEqual(response.status_code, 200)
q = PyQuery(response.content)
self.assertContains(response, "alert-success")
# check objects
email = Email.objects.get(address=candidate_email)
Person.objects.get(name=candidate_name, address=candidate_email)
nominee = Nominee.objects.get(email=email)
NomineePosition.objects.get(position=position, nominee=nominee)
feedback = Feedback.objects.filter(positions__in=[position],
nominees__in=[nominee],
type=FeedbackTypeName.objects.get(slug='nomina')).latest('id')
if public:
self.assertEqual(feedback.author, nominator_email)
# to check feedback comments are saved like enrypted data
self.assertNotEqual(feedback.comments, comments)
self.assertEqual(check_comments(feedback.comments, comments, self.privatekey_file), True)
Nomination.objects.get(position=position,
candidate_name=candidate_name,
candidate_email=candidate_email,
candidate_phone=candidate_phone,
nominee=nominee,
comments=feedback,
nominator_email="%s%s" % (COMMUNITY_USER, EMAIL_DOMAIN))
## Save this for repurposing to test the to-be-created 'by name and address' form
## def nominate_view(self, *args, **kwargs):
## public = kwargs.pop('public', True)
## nominee_email = kwargs.pop('nominee_email', u'nominee@example.com')
## nominator_email = kwargs.pop('nominator_email', "%s%s" % (COMMUNITY_USER, EMAIL_DOMAIN))
## position_name = kwargs.pop('position', 'IAOC')
## confirmation = kwargs.pop('confirmation', False)
##
## if public:
## nominate_url = self.public_nominate_url
## else:
## nominate_url = self.private_nominate_url
## response = self.client.get(nominate_url)
## self.assertEqual(response.status_code, 200)
##
## nomcom = get_nomcom_by_year(self.year)
## if not nomcom.public_key:
## q = PyQuery(response.content)
## self.assertEqual(len(q("#nominate-form")), 0)
##
## # save the cert file in tmp
## nomcom.public_key.storage.location = tempfile.gettempdir()
## nomcom.public_key.save('cert', File(open(self.cert_file.name, 'r')))
##
## response = self.client.get(nominate_url)
## self.assertEqual(response.status_code, 200)
## q = PyQuery(response.content)
## self.assertEqual(len(q("#nominate-form")), 1)
##
## position = Position.objects.get(name=position_name)
## candidate_email = nominee_email
## candidate_name = u'nominee'
## comments = u'Test nominate view. Comments with accents äöåÄÖÅ éáíóú âêîôû ü àèìòù.'
## candidate_phone = u'123456'
##
## test_data = {'candidate_name': candidate_name,
## 'candidate_email': candidate_email,
## 'candidate_phone': candidate_phone,
## 'position': position.id,
## 'qualifications': comments,
## 'confirmation': confirmation}
## if not public:
## test_data['nominator_email'] = nominator_email
##
## response = self.client.post(nominate_url, test_data)
## self.assertEqual(response.status_code, 200)
## q = PyQuery(response.content)
## self.assertContains(response, "alert-success")
##
## # check objects
## email = Email.objects.get(address=candidate_email)
## Person.objects.get(name=candidate_name, address=candidate_email)
## nominee = Nominee.objects.get(email=email)
## NomineePosition.objects.get(position=position, nominee=nominee)
## feedback = Feedback.objects.filter(positions__in=[position],
## nominees__in=[nominee],
## type=FeedbackTypeName.objects.get(slug='nomina')).latest('id')
## if public:
## self.assertEqual(feedback.author, nominator_email)
##
## # to check feedback comments are saved like enrypted data
## self.assertNotEqual(feedback.comments, comments)
##
## self.assertEqual(check_comments(feedback.comments, comments, self.privatekey_file), True)
## Nomination.objects.get(position=position,
## candidate_name=candidate_name,
## candidate_email=candidate_email,
## candidate_phone=candidate_phone,
## nominee=nominee,
## comments=feedback,
## nominator_email="%s%s" % (COMMUNITY_USER, EMAIL_DOMAIN))
##
def test_add_questionnaire(self):
self.access_chair_url(self.add_questionnaire_url)
return self.add_questionnaire()

View file

@ -9,6 +9,7 @@ urlpatterns = patterns('ietf.nomcom.views',
url(r'^(?P<year>\d{4})/private/key/$', 'private_key', name='nomcom_private_key'),
url(r'^(?P<year>\d{4})/private/help/$', 'configuration_help', name='nomcom_chair_help'),
url(r'^(?P<year>\d{4})/private/nominate/$', 'private_nominate', name='nomcom_private_nominate'),
url(r'^(?P<year>\d{4})/private/nominate/newperson$', 'private_nominate_newperson', name='nomcom_private_nominate_newperson'),
url(r'^(?P<year>\d{4})/private/feedback/$', 'private_feedback', name='nomcom_private_feedback'),
url(r'^(?P<year>\d{4})/private/feedback-email/$', 'private_feedback_email', name='nomcom_private_feedback_email'),
url(r'^(?P<year>\d{4})/private/questionnaire-response/$', 'private_questionnaire', name='nomcom_private_questionnaire'),
@ -35,6 +36,7 @@ urlpatterns = patterns('ietf.nomcom.views',
url(r'^(?P<year>\d{4})/questionnaires/$', 'questionnaires', name='nomcom_questionnaires'),
url(r'^(?P<year>\d{4})/feedback/$', 'public_feedback', name='nomcom_public_feedback'),
url(r'^(?P<year>\d{4})/nominate/$', 'public_nominate', name='nomcom_public_nominate'),
url(r'^(?P<year>\d{4})/nominate/newperson$', 'public_nominate_newperson', name='nomcom_public_nominate_newperson'),
url(r'^(?P<year>\d{4})/process-nomination-status/(?P<nominee_position_id>\d+)/(?P<state>[\w]+)/(?P<date>[\d]+)/(?P<hash>[a-f0-9]+)/$', 'process_nomination_status', name='nomcom_process_nomination_status'),
)

View file

@ -363,7 +363,6 @@ def get_or_create_nominee(nomcom, candidate_name, candidate_email, position, aut
email.person = person
email.save()
if created_email:
# send email to secretariat and nomcomchair to warn about the new person
subject = 'New person is created'
from_email = settings.NOMCOM_FROM_EMAIL

View file

@ -22,7 +22,7 @@ from ietf.group.models import Group, GroupEvent
from ietf.message.models import Message
from ietf.nomcom.decorators import nomcom_private_key_required
from ietf.nomcom.forms import (NominateForm, FeedbackForm, QuestionnaireForm,
from ietf.nomcom.forms import (NominateForm, NominateNewPersonForm, FeedbackForm, QuestionnaireForm,
MergeForm, NomComTemplateForm, PositionForm,
PrivateKeyForm, EditNomcomForm, EditNomineeForm,
PendingFeedbackForm, ReminderDatesForm, FullFeedbackFormSet,
@ -361,6 +361,58 @@ def nominate(request, year, public):
'year': year,
'selected': 'nominate'}, RequestContext(request))
@login_required
def public_nominate_newperson(request, year):
return nominate_newperson(request, year, True)
@role_required("Nomcom")
def private_nominate_newperson(request, year):
return nominate_newperson(request, year, False)
def nominate_newperson(request, year, public):
nomcom = get_nomcom_by_year(year)
has_publickey = nomcom.public_key and True or False
if public:
template = 'nomcom/public_nominate.html'
else:
template = 'nomcom/private_nominate.html'
if not has_publickey:
message = ('warning', "This Nomcom is not yet accepting nominations")
return render_to_response(template,
{'message': message,
'nomcom': nomcom,
'year': year,
'selected': 'nominate'}, RequestContext(request))
if nomcom.group.state_id == 'conclude':
message = ('warning', "Nominations to this Nomcom are closed.")
return render_to_response(template,
{'message': message,
'nomcom': nomcom,
'year': year,
'selected': 'nominate'}, RequestContext(request))
message = None
if request.method == 'POST':
form = NominateNewPersonForm(data=request.POST, nomcom=nomcom, user=request.user, public=public)
if form.is_valid():
form.save()
message = ('success', 'Your nomination has been registered. Thank you for the nomination.')
## This needs to redirect to the normal nominate url instead.
## Need to weed out the custom message stuff
form = NominateNewPersonForm(nomcom=nomcom, user=request.user, public=public)
else:
form = NominateNewPersonForm(nomcom=nomcom, user=request.user, public=public)
return render_to_response(template,
{'form': form,
'message': message,
'nomcom': nomcom,
'year': year,
'selected': 'nominate'}, RequestContext(request))
@login_required
def public_feedback(request, year):