Checkpoint: main nomination forms use SearchableEmailField

- Legacy-Id: 10609
This commit is contained in:
Robert Sparks 2015-12-18 20:38:17 +00:00
parent a2af7cfa25
commit 6bf4227974
8 changed files with 190 additions and 70 deletions

View file

@ -92,7 +92,7 @@ class NomComFactory(factory.DjangoModelFactory):
if extracted is None:
extracted = True
if create and extracted:
nominees = [Nominee.objects.create(nomcom=self, email=PersonFactory().email_set.first()) for i in range(4)]
nominees = [NomineeFactory(nomcom=self) for i in range(4)]
positions = [PositionFactory(nomcom=self) for i in range(3)]
def npc(position,nominee,state_id):
@ -134,6 +134,8 @@ class NomineeFactory(factory.DjangoModelFactory):
model = Nominee
nomcom = factory.SubFactory(NomComFactory)
person = factory.SubFactory(PersonFactory)
email = factory.LazyAttribute(lambda n: n.person.email())
class FeedbackFactory(factory.DjangoModelFactory):
class Meta:

View file

@ -2,7 +2,6 @@ from django.conf import settings
from django import forms
from django.contrib.formtools.preview import FormPreview, AUTO_ID
from django.shortcuts import get_object_or_404, redirect
from django.template.loader import render_to_string
from django.utils.decorators import method_decorator
from django.shortcuts import render_to_response
from django.template.context import RequestContext
@ -15,8 +14,10 @@ from ietf.nomcom.models import ( NomCom, Nomination, Nominee, NomineePosition,
Position, Feedback, ReminderDates )
from ietf.nomcom.utils import (NOMINATION_RECEIPT_TEMPLATE, FEEDBACK_RECEIPT_TEMPLATE,
get_user_email, validate_private_key, validate_public_key,
get_or_create_nominee, create_feedback_email)
get_or_create_nominee, get_or_create_nominee_by_person,
create_feedback_email)
from ietf.person.models import Email
from ietf.person.fields import SearchableEmailField
from ietf.utils.fields import MultiEmailField
from ietf.utils.mail import send_mail
from ietf.mailtrigger.utils import gather_address_lists
@ -299,7 +300,8 @@ class MergeForm(forms.Form):
class NominateForm(forms.ModelForm):
comments = forms.CharField(label="Candidate's qualifications for the position",
candidate = 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.',
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'.",
@ -315,9 +317,10 @@ class NominateForm(forms.ModelForm):
self.fields['nominator_email'].label = 'Nominator email'
if self.nomcom:
self.fields['position'].queryset = Position.objects.get_by_nomcom(self.nomcom).opened()
self.fields['comments'].help_text = self.nomcom.initial_text
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
@ -329,21 +332,26 @@ class NominateForm(forms.ModelForm):
has indicated they will allow NomCom to share their name as one of the people
nominating this candidate."""
else:
pass
self.fields.pop('nominator_email')
def save(self, commit=True):
# Create nomination
nomination = super(NominateForm, 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']
## 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']
position = self.cleaned_data['position']
comments = self.cleaned_data['comments']
confirmation = self.cleaned_data['confirmation']
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
author = None
if self.public:
author = get_user_email(self.user)
@ -351,11 +359,12 @@ 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(self.nomcom, candidate_name, candidate_email, position, author)
nominee = get_or_create_nominee_by_person (self.nomcom, candidate.person, position, author)
# Complete nomination data
feedback = Feedback.objects.create(nomcom=self.nomcom,
comments=comments,
comments=qualifications,
type=FeedbackTypeName.objects.get(slug='nomina'),
user=self.user)
feedback.positions.add(position)
@ -381,7 +390,7 @@ class NominateForm(forms.ModelForm):
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': comments,
'comments': qualifications,
'position': position.name}
path = nomcom_template_path + NOMINATION_RECEIPT_TEMPLATE
send_mail(None, to_email, from_email, subject, path, context, cc=cc)
@ -390,8 +399,10 @@ class NominateForm(forms.ModelForm):
class Meta:
model = Nomination
fields = ('share_nominator', 'position', 'nominator_email', 'candidate_name',
'candidate_email', 'candidate_phone')
fields = ('share_nominator', 'position', 'nominator_email', 'candidate',
'candidate_phone', 'qualifications', 'confirmation')
##fields = ('share_nominator', 'position', 'nominator_email', 'candidate', 'candidate_name',
## 'candidate_email', 'candidate_phone', 'qualifications', 'confirmation')
class FeedbackForm(forms.ModelForm):

View file

@ -34,7 +34,7 @@ from ietf.nomcom.management.commands.send_reminders import Command, is_time_to_s
from ietf.nomcom.factories import NomComFactory, FeedbackFactory, \
nomcom_kwargs_for_year, provide_private_key_to_test_client, \
key
from ietf.person.factories import PersonFactory
from ietf.person.factories import PersonFactory, EmailFactory
from ietf.dbtemplate.factories import DBTemplateFactory
from ietf.dbtemplate.models import DBTemplate
@ -481,11 +481,7 @@ class NomcomViewsTest(TestCase):
self.nominate_view(public=True,confirmation=True)
self.assertEqual(len(outbox), messages_before + 4)
self.assertTrue('New person' in outbox[-4]['Subject'])
self.assertTrue('nomcomchair' in outbox[-4]['To'])
self.assertTrue('secretariat' in outbox[-4]['To'])
self.assertEqual(len(outbox), messages_before + 3)
self.assertEqual('IETF Nomination Information', outbox[-3]['Subject'])
self.assertTrue('nominee' in outbox[-3]['To'])
@ -518,15 +514,23 @@ class NomcomViewsTest(TestCase):
login_testing_unauthorized(self, COMMUNITY_USER, self.public_nominate_url)
empty_outbox()
self.nominate_view(public=True)
self.assertEqual(len(outbox), 4)
self.assertEqual(len(outbox), 3)
# test_public_nominate checks the other messages
self.assertTrue('Questionnaire' in outbox[2]['Subject'])
self.assertTrue('nominee@' in outbox[2]['To'])
self.assertTrue('Questionnaire' in outbox[1]['Subject'])
self.assertTrue('nominee@' in outbox[1]['To'])
def nominate_view(self, *args, **kwargs):
public = kwargs.pop('public', True)
nominee = kwargs.pop('nominee', 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()
nominator_email = kwargs.pop('nominator_email', "%s%s" % (COMMUNITY_USER, EMAIL_DOMAIN))
position_name = kwargs.pop('position', 'IAOC')
confirmation = kwargs.pop('confirmation', False)
@ -553,16 +557,13 @@ class NomcomViewsTest(TestCase):
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,
test_data = {'candidate': nominee.pk,
'candidate_phone': candidate_phone,
'position': position.id,
'comments': comments,
'qualifications': comments,
'confirmation': confirmation}
if not public:
test_data['nominator_email'] = nominator_email
@ -573,12 +574,11 @@ class NomcomViewsTest(TestCase):
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)
## TODO - straighten this _obj vs _email naming mess out
nominee_obj = Nominee.objects.get(email=nominee)
NomineePosition.objects.get(position=position, nominee=nominee_obj)
feedback = Feedback.objects.filter(positions__in=[position],
nominees__in=[nominee],
nominees__in=[nominee_obj],
type=FeedbackTypeName.objects.get(slug='nomina')).latest('id')
if public:
self.assertEqual(feedback.author, nominator_email)
@ -588,13 +588,85 @@ class NomcomViewsTest(TestCase):
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_name=nominee.person.plain_name(),
candidate_email=nominee.address,
candidate_phone=candidate_phone,
nominee=nominee,
nominee=nominee_obj,
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()
@ -656,10 +728,10 @@ class NomcomViewsTest(TestCase):
self.feedback_view(public=True,confirmation=True)
# feedback_view does a nomination internally: there is a lot of email related to that - tested elsewhere
# We're interested in the confirmation receipt here
self.assertEqual(len(outbox),4)
self.assertEqual('NomCom comment confirmation', outbox[3]['Subject'])
self.assertTrue('plain' in outbox[3]['To'])
self.assertTrue(u'Comments with accents äöå' in unicode(outbox[3].get_payload(decode=True),"utf-8","replace"))
self.assertEqual(len(outbox),3)
self.assertEqual('NomCom comment confirmation', outbox[2]['Subject'])
self.assertTrue('plain' in outbox[2]['To'])
self.assertTrue(u'Comments with accents äöå' in unicode(outbox[2].get_payload(decode=True),"utf-8","replace"))
empty_outbox()
self.feedback_view(public=True)

View file

@ -271,42 +271,22 @@ def send_reminder_to_nominees(nominees,type):
return addrs
def get_or_create_nominee(nomcom, candidate_name, candidate_email, position, author):
def get_or_create_nominee_by_person(nomcom, candidate, position, author):
from ietf.nomcom.models import Nominee, NomineePosition
nomcom_template_path = '/nomcom/%s/' % nomcom.group.acronym
# Create person and email if candidate email does't exist and send email
email, created_email = Email.objects.get_or_create(address=candidate_email)
if created_email:
person = Person.objects.create(name=candidate_name,
ascii=unaccent.asciify(candidate_name),
address=candidate_email)
email.person = person
email.save()
# Add the nomination for a particular position
nominee, created = Nominee.objects.get_or_create(email=email, nomcom=nomcom)
nominee, created = Nominee.objects.get_or_create(person=candidate,email=candidate.email(), nomcom=nomcom)
while nominee.duplicated:
nominee = nominee.duplicated
nominee_position, nominee_position_created = NomineePosition.objects.get_or_create(position=position, nominee=nominee)
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
(to_email, cc) = gather_address_lists('nomination_created_person',nomcom=nomcom)
context = {'email': email.address,
'fullname': email.person.name,
'person_id': email.person.id}
path = nomcom_template_path + INEXISTENT_PERSON_TEMPLATE
send_mail(None, to_email, from_email, subject, path, context, cc=cc)
if nominee_position_created:
# send email to nominee
subject = 'IETF Nomination Information'
from_email = settings.NOMCOM_FROM_EMAIL
(to_email, cc) = gather_address_lists('nomination_new_nominee',nominee=email.address)
(to_email, cc) = gather_address_lists('nomination_new_nominee',nominee=nominee.email.address)
domain = Site.objects.get_current().domain
today = datetime.date.today().strftime('%Y%m%d')
hash = get_hash_nominee_position(today, nominee_position.id)
@ -325,7 +305,7 @@ def get_or_create_nominee(nomcom, candidate_name, candidate_email, position, aut
today,
hash))
context = {'nominee': email.person.name,
context = {'nominee': nominee.person.name,
'position': position.name,
'domain': domain,
'accept_url': accept_url,
@ -338,8 +318,8 @@ def get_or_create_nominee(nomcom, candidate_name, candidate_email, position, aut
if nomcom.send_questionnaire:
subject = '%s Questionnaire' % position
from_email = settings.NOMCOM_FROM_EMAIL
(to_email, cc) = gather_address_lists('nomcom_questionnaire',nominee=email.address)
context = {'nominee': email.person.name,
(to_email, cc) = gather_address_lists('nomcom_questionnaire',nominee=nominee.email.address)
context = {'nominee': nominee.person.name,
'position': position.name}
path = '%s%d/%s' % (nomcom_template_path,
position.id, HEADER_QUESTIONNAIRE_TEMPLATE)
@ -353,8 +333,8 @@ def get_or_create_nominee(nomcom, candidate_name, candidate_email, position, aut
subject = 'Nomination Information'
from_email = settings.NOMCOM_FROM_EMAIL
(to_email, cc) = gather_address_lists('nomination_received',nomcom=nomcom)
context = {'nominee': email.person.name,
'nominee_email': email.address,
context = {'nominee': nominee.person.name,
'nominee_email': nominee.email.address,
'position': position.name}
if author:
@ -369,6 +349,34 @@ def get_or_create_nominee(nomcom, candidate_name, candidate_email, position, aut
return nominee
def get_or_create_nominee(nomcom, candidate_name, candidate_email, position, author):
## TODO: Assert here that there is no matching email or person, and change the code
## to not possibly stomp on existing things
# Create person and email if candidate email does't exist and send email
email, created_email = Email.objects.get_or_create(address=candidate_email)
if created_email:
person = Person.objects.create(name=candidate_name,
ascii=unaccent.asciify(candidate_name),
address=candidate_email)
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
(to_email, cc) = gather_address_lists('nomination_created_person',nomcom=nomcom)
context = {'email': email.address,
'fullname': email.person.name,
'person_id': email.person.id}
nomcom_template_path = '/nomcom/%s/' % nomcom.group.acronym
path = nomcom_template_path + INEXISTENT_PERSON_TEMPLATE
send_mail(None, to_email, from_email, subject, path, context, cc=cc)
return get_or_create_nominee_by_person(nomcom, email.person, position, author)
def getheader(header_text, default="ascii"):
"""Decode the specified header"""

View file

@ -54,3 +54,5 @@ class EmailFactory(factory.DjangoModelFactory):
django_get_or_create = ('address',)
address = '%s.%s@%s' % (factory.Faker('first_name'),factory.Faker('last_name'),factory.Faker('domain_name'))
active = True
primary = False

View file

@ -59,10 +59,13 @@ class PersonInfo(models.Model):
if e:
return e[0]
return None
def email_address(self):
def email(self):
e = self.email_set.filter(primary=True).first()
if not e:
e = self.email_set.filter(active=True).order_by("-time").first()
return e
def email_address(self):
e = self.email()
if e:
return e.address
else:

View file

@ -1,10 +1,16 @@
{% extends "nomcom/nomcom_private_base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load staticfiles %}
{% load bootstrap3 %}
{% load nomcom_tags %}
{% block pagehead %}
<link rel="stylesheet" href="{% static 'select2/select2.css' %}">
<link rel="stylesheet" href="{% static 'select2-bootstrap-css/select2-bootstrap.min.css' %}">
{% endblock %}
{% block subtitle %} - Nominate{% endblock %}
{% block nomcom_content %}
@ -30,3 +36,8 @@
{% endif %}
{% endblock %}
{% block js %}
<script src="{% static 'select2/select2.min.js' %}"></script>
<script src="{% static 'ietf/js/select2-field.js' %}"></script>
{% endblock %}

View file

@ -1,10 +1,16 @@
{% extends "nomcom/nomcom_public_base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load staticfiles %}
{% load bootstrap3 %}
{% load nomcom_tags %}
{% block pagehead %}
<link rel="stylesheet" href="{% static 'select2/select2.css' %}">
<link rel="stylesheet" href="{% static 'select2-bootstrap-css/select2-bootstrap.min.css' %}">
{% endblock %}
{% block subtitle %} - Nominate{% endblock %}
{% block nomcom_content %}
@ -26,3 +32,8 @@
{% endif %}
{% endblock %}
{% block js %}
<script src="{% static 'select2/select2.min.js' %}"></script>
<script src="{% static 'ietf/js/select2-field.js' %}"></script>
{% endblock %}