Skip to content

Commit ee1eec7

Browse files
committed
Refactored the feedback pending list. Now you can classify feedback as any type of feedback.
The feedback of a type that is not related to a nominee is classified inmediatly. The feedback of a type that is related to a nominee must be completed with the nominee information. Created a view to list feedback that is not related to nominees (e.g. offtopic) Changed the command that retrieves feedback from email to add the subject inside the comment and to set the author using the from field of the email. Fixes ietf-tools#1036. Fixes ietf-tools#1035 - Legacy-Id: 5738
1 parent 0bc7e18 commit ee1eec7

13 files changed

Lines changed: 482 additions & 171 deletions

File tree

ietf/name/fixtures/names.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,12 @@
553553
<field type="BooleanField" name="used">True</field>
554554
<field type="IntegerField" name="order">0</field>
555555
</object>
556+
<object pk="offtopic" model="name.feedbacktype">
557+
<field type="CharField" name="name">Offtopic</field>
558+
<field type="TextField" name="desc"></field>
559+
<field type="BooleanField" name="used">True</field>
560+
<field type="IntegerField" name="order">0</field>
561+
</object>
556562
<object pk="rst" model="name.dbtemplatetypename">
557563
<field type="CharField" name="name">reStructuredText</field>
558564
<field type="TextField" name="desc"></field>
@@ -1846,4 +1852,4 @@
18461852
<field type="IntegerField" name="order">3</field>
18471853
<field to="name.ballotpositionname" name="positions" rel="ManyToManyRel"><object pk="yes"></object><object pk="noobj"></object><object pk="block"></object><object pk="abstain"></object><object pk="norecord"></object></field>
18481854
</object>
1849-
</django-objects>
1855+
</django-objects>

ietf/nomcom/decorators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from django.http import HttpResponseRedirect
33
from django.utils.http import urlquote
44

5-
from ietf.ietfauth.decorators import passes_test_decorator, has_role
5+
from ietf.ietfauth.decorators import passes_test_decorator
66

77
from ietf.nomcom.utils import get_nomcom_by_year
88

ietf/nomcom/forms.py

Lines changed: 115 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import datetime
2-
31
from django.conf import settings
42
from django import forms
53
from django.contrib.formtools.preview import FormPreview, AUTO_ID
@@ -10,24 +8,19 @@
108
from django.utils.decorators import method_decorator
119
from django.shortcuts import render_to_response
1210
from django.template.context import RequestContext
13-
from django.contrib.sites.models import Site
1411

1512
from ietf.dbtemplate.forms import DBTemplateForm
16-
from ietf.utils import unaccent
17-
from ietf.utils.mail import send_mail, send_mail_text
13+
from ietf.utils.mail import send_mail
1814
from ietf.ietfauth.decorators import role_required
1915
from ietf.utils import fields as custom_fields
2016
from ietf.group.models import Group, Role
2117
from ietf.name.models import RoleName, FeedbackType, NomineePositionState
22-
from ietf.person.models import Email, Person
18+
from ietf.person.models import Email
2319
from ietf.nomcom.models import NomCom, Nomination, Nominee, NomineePosition, \
2420
Position, Feedback, ReminderDates
25-
from ietf.nomcom.utils import QUESTIONNAIRE_TEMPLATE, NOMINATION_EMAIL_TEMPLATE, \
26-
INEXISTENT_PERSON_TEMPLATE, NOMINEE_EMAIL_TEMPLATE, \
27-
NOMINATION_RECEIPT_TEMPLATE, FEEDBACK_RECEIPT_TEMPLATE, \
28-
get_user_email, get_hash_nominee_position, get_year_by_nomcom, \
29-
HEADER_QUESTIONNAIRE_TEMPLATE, validate_private_key, \
30-
validate_public_key
21+
from ietf.nomcom.utils import (NOMINATION_RECEIPT_TEMPLATE, FEEDBACK_RECEIPT_TEMPLATE,
22+
get_user_email, validate_private_key, validate_public_key,
23+
get_or_create_nominee)
3124
from ietf.nomcom.decorators import nomcom_member_required
3225

3326

@@ -441,37 +434,23 @@ def save(self, commit=True):
441434
comments = self.cleaned_data['comments']
442435
confirmation = self.cleaned_data['confirmation']
443436
nomcom_template_path = '/nomcom/%s/' % self.nomcom.group.acronym
444-
nomcom_chair = self.nomcom.group.get_chair()
445-
nomcom_chair_mail = nomcom_chair and nomcom_chair.email.address or None
446-
447-
# Create person and email if candidate email does't exist and send email
448-
email, created_email = Email.objects.get_or_create(address=candidate_email)
449-
if created_email:
450-
email.person = Person.objects.create(name=candidate_name,
451-
ascii=unaccent.asciify(candidate_name),
452-
address=candidate_email)
453-
email.save()
454-
455-
# Add the nomination for a particular position
456-
nominee, created = Nominee.objects.get_or_create(email=email, nomcom=self.nomcom)
457-
while nominee.duplicated:
458-
nominee = nominee.duplicated
459-
nominee_position, nominee_position_created = NomineePosition.objects.get_or_create(position=position, nominee=nominee)
460437

461-
# Complete nomination data
462-
feedback = Feedback.objects.create(nomcom=self.nomcom,
463-
comments=comments,
464-
type=FeedbackType.objects.get(slug='nomina'),
465-
user=self.user)
466-
feedback.positions.add(position)
467-
feedback.nominees.add(nominee)
468438
author = None
469439
if self.public:
470440
author = get_user_email(self.user)
471441
else:
472442
if nominator_email:
473443
emails = Email.objects.filter(address=nominator_email)
474444
author = emails and emails[0] or None
445+
nominee = get_or_create_nominee(self.nomcom, candidate_name, candidate_email, position, author)
446+
447+
# Complete nomination data
448+
feedback = Feedback.objects.create(nomcom=self.nomcom,
449+
comments=comments,
450+
type=FeedbackType.objects.get(slug='nomina'),
451+
user=self.user)
452+
feedback.positions.add(position)
453+
feedback.nominees.add(nominee)
475454

476455
if author:
477456
nomination.nominator_email = author.address
@@ -485,87 +464,13 @@ def save(self, commit=True):
485464
if commit:
486465
nomination.save()
487466

488-
if created_email:
489-
# send email to secretariat and nomcomchair to warn about the new person
490-
subject = 'New person is created'
491-
from_email = settings.NOMCOM_FROM_EMAIL
492-
to_email = [settings.NOMCOM_ADMIN_EMAIL]
493-
context = {'email': email.address,
494-
'fullname': email.person.name,
495-
'person_id': email.person.id}
496-
path = nomcom_template_path + INEXISTENT_PERSON_TEMPLATE
497-
if nomcom_chair_mail:
498-
to_email.append(nomcom_chair_mail)
499-
send_mail(None, to_email, from_email, subject, path, context)
500-
501-
# send email to nominee
502-
if nominee_position_created:
503-
subject = 'IETF Nomination Information'
504-
from_email = settings.NOMCOM_FROM_EMAIL
505-
to_email = email.address
506-
domain = Site.objects.get_current().domain
507-
today = datetime.date.today().strftime('%Y%m%d')
508-
hash = get_hash_nominee_position(today, nominee_position.id)
509-
accept_url = reverse('nomcom_process_nomination_status',
510-
None,
511-
args=(get_year_by_nomcom(self.nomcom),
512-
nominee_position.id,
513-
'accepted',
514-
today,
515-
hash))
516-
decline_url = reverse('nomcom_process_nomination_status',
517-
None,
518-
args=(get_year_by_nomcom(self.nomcom),
519-
nominee_position.id,
520-
'declined',
521-
today,
522-
hash))
523-
524-
context = {'nominee': email.person.name,
525-
'position': position.name,
526-
'domain': domain,
527-
'accept_url': accept_url,
528-
'decline_url': decline_url}
529-
530-
path = nomcom_template_path + NOMINEE_EMAIL_TEMPLATE
531-
send_mail(None, to_email, from_email, subject, path, context)
532-
533-
# send email to nominee with questionnaire
534-
if nominee_position_created:
535-
if self.nomcom.send_questionnaire:
536-
subject = '%s Questionnaire' % position
537-
from_email = settings.NOMCOM_FROM_EMAIL
538-
to_email = email.address
539-
context = {'nominee': email.person.name,
540-
'position': position.name}
541-
path = '%s%d/%s' % (nomcom_template_path,
542-
position.id, HEADER_QUESTIONNAIRE_TEMPLATE)
543-
body = render_to_string(path, context)
544-
path = '%s%d/%s' % (nomcom_template_path,
545-
position.id, QUESTIONNAIRE_TEMPLATE)
546-
body += '\n\n%s' % render_to_string(path, context)
547-
send_mail_text(None, to_email, from_email, subject, body)
548-
549-
# send emails to nomcom chair
550-
subject = 'Nomination Information'
551-
from_email = settings.NOMCOM_FROM_EMAIL
552-
to_email = nomcom_chair_mail
553-
context = {'nominee': email.person.name,
554-
'nominee_email': email.address,
555-
'position': position.name}
556-
if author:
557-
context.update({'nominator': author.person.name,
558-
'nominator_email': author.address})
559-
path = nomcom_template_path + NOMINATION_EMAIL_TEMPLATE
560-
send_mail(None, to_email, from_email, subject, path, context)
561-
562467
# send receipt email to nominator
563468
if confirmation:
564469
if author:
565470
subject = 'Nomination Receipt'
566471
from_email = settings.NOMCOM_FROM_EMAIL
567472
to_email = author.address
568-
context = {'nominee': email.person.name,
473+
context = {'nominee': nominee.email.person.name,
569474
'comments': comments,
570475
'position': position.name}
571476
path = nomcom_template_path + NOMINATION_RECEIPT_TEMPLATE
@@ -795,37 +700,43 @@ def clean_key(self):
795700

796701
class PendingFeedbackForm(BaseNomcomForm, forms.ModelForm):
797702

703+
type = forms.ModelChoiceField(queryset=FeedbackType.objects.all(), widget=forms.RadioSelect, empty_label='Unclassified', required=False)
704+
798705
class Meta:
799706
model = Feedback
800-
fields = ('author', 'type', 'nominee')
707+
fields = ('type', )
801708

802709
def __init__(self, *args, **kwargs):
803710
super(PendingFeedbackForm, self).__init__(*args, **kwargs)
804-
self.fields['type'].queryset = FeedbackType.objects.exclude(slug='nomina')
711+
try:
712+
self.default_type = FeedbackType.objects.get(slug=settings.DEFAULT_FEEDBACK_TYPE)
713+
except FeedbackType.DoesNotExist:
714+
self.default_type = None
805715

806716
def set_nomcom(self, nomcom, user):
807717
self.nomcom = nomcom
808718
self.user = user
809-
self.fields['nominee'] = MultiplePositionNomineeField(nomcom=self.nomcom,
810-
required=True,
811-
widget=forms.SelectMultiple,
812-
help_text='Hold down "Control", or "Command" on a Mac, to select more than one.')
719+
#self.fields['nominee'] = MultiplePositionNomineeField(nomcom=self.nomcom,
720+
#required=True,
721+
#widget=forms.SelectMultiple,
722+
#help_text='Hold down "Control", or "Command" on a Mac, to select more than one.')
813723

814724
def save(self, commit=True):
815725
feedback = super(PendingFeedbackForm, self).save(commit=False)
726+
feedback.nomcom = self.nomcom
727+
feedback.user = self.user
728+
feedback.save()
729+
return feedback
816730

817-
author = get_user_email(self.user)
818-
819-
if author:
820-
feedback.author = author
821-
731+
def move_to_default(self):
732+
if not self.default_type or self.cleaned_data.get('type', None):
733+
return None
734+
feedback = super(PendingFeedbackForm, self).save(commit=False)
822735
feedback.nomcom = self.nomcom
823736
feedback.user = self.user
737+
feedback.type = self.default_type
824738
feedback.save()
825-
self.save_m2m()
826-
for (position, nominee) in self.cleaned_data['nominee']:
827-
feedback.nominees.add(nominee)
828-
feedback.positions.add(position)
739+
return feedback
829740

830741

831742
class ReminderDatesForm(forms.ModelForm):
@@ -837,3 +748,82 @@ class Meta:
837748
def __init__(self, *args, **kwargs):
838749
super(ReminderDatesForm, self).__init__(*args, **kwargs)
839750
self.fields['date'].required = False
751+
752+
753+
class MutableFeedbackForm(forms.ModelForm):
754+
755+
type = forms.ModelChoiceField(queryset=FeedbackType.objects.all(), widget=forms.HiddenInput)
756+
757+
class Meta:
758+
model = Feedback
759+
fields = ('type', )
760+
761+
def set_nomcom(self, nomcom, user, instances=None):
762+
self.nomcom = nomcom
763+
self.user = user
764+
instances = instances or []
765+
self.feedback_type = None
766+
for i in instances:
767+
if i.id == self.instance.id:
768+
self.feedback_type = i.type
769+
break
770+
self.feedback_type = self.feedback_type or self.fields['type'].clean(self.fields['type'].widget.value_from_datadict(self.data, self.files, self.add_prefix('type')))
771+
772+
self.initial['type'] = self.feedback_type
773+
774+
if self.feedback_type.slug != 'nomina':
775+
self.fields['nominee'] = MultiplePositionNomineeField(nomcom=self.nomcom,
776+
required=True,
777+
widget=forms.SelectMultiple,
778+
help_text='Hold down "Control", or "Command" on a Mac, to select more than one.')
779+
else:
780+
self.fields['position'] = forms.ModelChoiceField(queryset=Position.objects.get_by_nomcom(self.nomcom).opened(), label="Position")
781+
self.fields['candidate_name'] = forms.CharField(label="Candidate name")
782+
self.fields['candidate_email'] = forms.EmailField(label="Candidate email")
783+
self.fields['candidate_phone'] = forms.CharField(label="Candidate phone", required=False)
784+
785+
def save(self, commit=True):
786+
feedback = super(MutableFeedbackForm, self).save(commit=False)
787+
if self.instance.type.slug == 'nomina':
788+
candidate_email = self.cleaned_data['candidate_email']
789+
candidate_name = self.cleaned_data['candidate_name']
790+
candidate_phone = self.cleaned_data['candidate_phone']
791+
position = self.cleaned_data['position']
792+
793+
nominator_email = feedback.author
794+
feedback.save()
795+
796+
emails = Email.objects.filter(address=nominator_email)
797+
author = emails and emails[0] or None
798+
799+
nominee = get_or_create_nominee(self.nomcom, candidate_name, candidate_email, position, author)
800+
feedback.nominees.add(nominee)
801+
feedback.positions.add(position)
802+
Nomination.objects.create(
803+
position=self.cleaned_data.get('position'),
804+
candidate_name=candidate_name,
805+
candidate_email=candidate_email,
806+
candidate_phone=candidate_phone,
807+
nominee=nominee,
808+
comments=feedback,
809+
nominator_email=nominator_email,
810+
user=self.user,
811+
)
812+
return feedback
813+
else:
814+
feedback.save()
815+
self.save_m2m()
816+
for (position, nominee) in self.cleaned_data['nominee']:
817+
feedback.nominees.add(nominee)
818+
feedback.positions.add(position)
819+
return feedback
820+
821+
822+
class FullFeedbackFormSet(forms.models.BaseModelFormSet):
823+
824+
model = Feedback
825+
extra = 0
826+
max_num = 0
827+
form = MutableFeedbackForm
828+
can_order = False
829+
can_delete = False

ietf/nomcom/management/commands/feedback_email.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from django.core.management.base import BaseCommand, CommandError
66

77
from ietf.nomcom.utils import parse_email
8-
from ietf.nomcom.models import Nominee, NomCom, Feedback
8+
from ietf.nomcom.models import NomCom, Feedback
99

1010

1111
class Command(BaseCommand):
@@ -19,7 +19,6 @@ def handle(self, *args, **options):
1919
email = options.get('email', None)
2020
year = options.get('year', None)
2121
msg = None
22-
nominee = None
2322
nomcom = None
2423
help_message = 'Usage: feeback_email --nomcom-year <nomcom-year> --email-file <email-file>'
2524

@@ -38,16 +37,12 @@ def handle(self, *args, **options):
3837
raise CommandError("NomCom %s does not exist or it isn't active" % year)
3938

4039
by, subject, body = parse_email(msg)
40+
body = 'Subject: %s\n\n%s' % (subject, body)
4141
name, addr = parseaddr(by)
42-
try:
43-
nominee = Nominee.objects.get_by_nomcom(nomcom).not_duplicated().get(email__address__icontains=addr)
44-
except Nominee.DoesNotExist:
45-
pass
4642

4743
feedback = Feedback(nomcom=nomcom,
44+
author=addr,
4845
comments=body)
4946
feedback.save()
50-
if nominee:
51-
feedback.nominees.add(nominee)
5247

5348
syslog.syslog(u"Read feedback email by %s" % by)

ietf/nomcom/templatetags/nomcom_tags.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def formatted_email(address):
6060

6161

6262
@register.simple_tag
63-
def decrypt(string, request, year):
63+
def decrypt(string, request, year, plain=False):
6464
key = retrieve_nomcom_private_key(request, year)
6565

6666
if not key:
@@ -79,4 +79,6 @@ def decrypt(string, request, year):
7979
if error:
8080
return '<-Encripted text [Your private key is invalid]->'
8181

82-
return linebreaksbr(out)
82+
if not plain:
83+
return linebreaksbr(out)
84+
return out

0 commit comments

Comments
 (0)