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 #1036. Fixes #1035 - Legacy-Id: 5738
This commit is contained in:
parent
0bc7e181cd
commit
ee1eec7de8
|
@ -553,6 +553,12 @@
|
|||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="IntegerField" name="order">0</field>
|
||||
</object>
|
||||
<object pk="offtopic" model="name.feedbacktype">
|
||||
<field type="CharField" name="name">Offtopic</field>
|
||||
<field type="TextField" name="desc"></field>
|
||||
<field type="BooleanField" name="used">True</field>
|
||||
<field type="IntegerField" name="order">0</field>
|
||||
</object>
|
||||
<object pk="rst" model="name.dbtemplatetypename">
|
||||
<field type="CharField" name="name">reStructuredText</field>
|
||||
<field type="TextField" name="desc"></field>
|
||||
|
@ -1846,4 +1852,4 @@
|
|||
<field type="IntegerField" name="order">3</field>
|
||||
<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>
|
||||
</object>
|
||||
</django-objects>
|
||||
</django-objects>
|
||||
|
|
|
@ -2,7 +2,7 @@ from django.core.urlresolvers import reverse
|
|||
from django.http import HttpResponseRedirect
|
||||
from django.utils.http import urlquote
|
||||
|
||||
from ietf.ietfauth.decorators import passes_test_decorator, has_role
|
||||
from ietf.ietfauth.decorators import passes_test_decorator
|
||||
|
||||
from ietf.nomcom.utils import get_nomcom_by_year
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django import forms
|
||||
from django.contrib.formtools.preview import FormPreview, AUTO_ID
|
||||
|
@ -10,24 +8,19 @@ 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
|
||||
from django.contrib.sites.models import Site
|
||||
|
||||
from ietf.dbtemplate.forms import DBTemplateForm
|
||||
from ietf.utils import unaccent
|
||||
from ietf.utils.mail import send_mail, send_mail_text
|
||||
from ietf.utils.mail import send_mail
|
||||
from ietf.ietfauth.decorators import role_required
|
||||
from ietf.utils import fields as custom_fields
|
||||
from ietf.group.models import Group, Role
|
||||
from ietf.name.models import RoleName, FeedbackType, NomineePositionState
|
||||
from ietf.person.models import Email, Person
|
||||
from ietf.person.models import Email
|
||||
from ietf.nomcom.models import NomCom, Nomination, Nominee, NomineePosition, \
|
||||
Position, Feedback, ReminderDates
|
||||
from ietf.nomcom.utils import QUESTIONNAIRE_TEMPLATE, NOMINATION_EMAIL_TEMPLATE, \
|
||||
INEXISTENT_PERSON_TEMPLATE, NOMINEE_EMAIL_TEMPLATE, \
|
||||
NOMINATION_RECEIPT_TEMPLATE, FEEDBACK_RECEIPT_TEMPLATE, \
|
||||
get_user_email, get_hash_nominee_position, get_year_by_nomcom, \
|
||||
HEADER_QUESTIONNAIRE_TEMPLATE, validate_private_key, \
|
||||
validate_public_key
|
||||
from ietf.nomcom.utils import (NOMINATION_RECEIPT_TEMPLATE, FEEDBACK_RECEIPT_TEMPLATE,
|
||||
get_user_email, validate_private_key, validate_public_key,
|
||||
get_or_create_nominee)
|
||||
from ietf.nomcom.decorators import nomcom_member_required
|
||||
|
||||
|
||||
|
@ -441,22 +434,15 @@ class NominateForm(BaseNomcomForm, forms.ModelForm):
|
|||
comments = self.cleaned_data['comments']
|
||||
confirmation = self.cleaned_data['confirmation']
|
||||
nomcom_template_path = '/nomcom/%s/' % self.nomcom.group.acronym
|
||||
nomcom_chair = self.nomcom.group.get_chair()
|
||||
nomcom_chair_mail = nomcom_chair and nomcom_chair.email.address or None
|
||||
|
||||
# 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:
|
||||
email.person = Person.objects.create(name=candidate_name,
|
||||
ascii=unaccent.asciify(candidate_name),
|
||||
address=candidate_email)
|
||||
email.save()
|
||||
|
||||
# Add the nomination for a particular position
|
||||
nominee, created = Nominee.objects.get_or_create(email=email, nomcom=self.nomcom)
|
||||
while nominee.duplicated:
|
||||
nominee = nominee.duplicated
|
||||
nominee_position, nominee_position_created = NomineePosition.objects.get_or_create(position=position, nominee=nominee)
|
||||
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
|
||||
nominee = get_or_create_nominee(self.nomcom, candidate_name, candidate_email, position, author)
|
||||
|
||||
# Complete nomination data
|
||||
feedback = Feedback.objects.create(nomcom=self.nomcom,
|
||||
|
@ -465,13 +451,6 @@ class NominateForm(BaseNomcomForm, forms.ModelForm):
|
|||
user=self.user)
|
||||
feedback.positions.add(position)
|
||||
feedback.nominees.add(nominee)
|
||||
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
|
||||
|
||||
if author:
|
||||
nomination.nominator_email = author.address
|
||||
|
@ -485,87 +464,13 @@ class NominateForm(BaseNomcomForm, forms.ModelForm):
|
|||
if commit:
|
||||
nomination.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 = [settings.NOMCOM_ADMIN_EMAIL]
|
||||
context = {'email': email.address,
|
||||
'fullname': email.person.name,
|
||||
'person_id': email.person.id}
|
||||
path = nomcom_template_path + INEXISTENT_PERSON_TEMPLATE
|
||||
if nomcom_chair_mail:
|
||||
to_email.append(nomcom_chair_mail)
|
||||
send_mail(None, to_email, from_email, subject, path, context)
|
||||
|
||||
# send email to nominee
|
||||
if nominee_position_created:
|
||||
subject = 'IETF Nomination Information'
|
||||
from_email = settings.NOMCOM_FROM_EMAIL
|
||||
to_email = 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)
|
||||
accept_url = reverse('nomcom_process_nomination_status',
|
||||
None,
|
||||
args=(get_year_by_nomcom(self.nomcom),
|
||||
nominee_position.id,
|
||||
'accepted',
|
||||
today,
|
||||
hash))
|
||||
decline_url = reverse('nomcom_process_nomination_status',
|
||||
None,
|
||||
args=(get_year_by_nomcom(self.nomcom),
|
||||
nominee_position.id,
|
||||
'declined',
|
||||
today,
|
||||
hash))
|
||||
|
||||
context = {'nominee': email.person.name,
|
||||
'position': position.name,
|
||||
'domain': domain,
|
||||
'accept_url': accept_url,
|
||||
'decline_url': decline_url}
|
||||
|
||||
path = nomcom_template_path + NOMINEE_EMAIL_TEMPLATE
|
||||
send_mail(None, to_email, from_email, subject, path, context)
|
||||
|
||||
# send email to nominee with questionnaire
|
||||
if nominee_position_created:
|
||||
if self.nomcom.send_questionnaire:
|
||||
subject = '%s Questionnaire' % position
|
||||
from_email = settings.NOMCOM_FROM_EMAIL
|
||||
to_email = email.address
|
||||
context = {'nominee': email.person.name,
|
||||
'position': position.name}
|
||||
path = '%s%d/%s' % (nomcom_template_path,
|
||||
position.id, HEADER_QUESTIONNAIRE_TEMPLATE)
|
||||
body = render_to_string(path, context)
|
||||
path = '%s%d/%s' % (nomcom_template_path,
|
||||
position.id, QUESTIONNAIRE_TEMPLATE)
|
||||
body += '\n\n%s' % render_to_string(path, context)
|
||||
send_mail_text(None, to_email, from_email, subject, body)
|
||||
|
||||
# send emails to nomcom chair
|
||||
subject = 'Nomination Information'
|
||||
from_email = settings.NOMCOM_FROM_EMAIL
|
||||
to_email = nomcom_chair_mail
|
||||
context = {'nominee': email.person.name,
|
||||
'nominee_email': email.address,
|
||||
'position': position.name}
|
||||
if author:
|
||||
context.update({'nominator': author.person.name,
|
||||
'nominator_email': author.address})
|
||||
path = nomcom_template_path + NOMINATION_EMAIL_TEMPLATE
|
||||
send_mail(None, to_email, from_email, subject, path, context)
|
||||
|
||||
# send receipt email to nominator
|
||||
if confirmation:
|
||||
if author:
|
||||
subject = 'Nomination Receipt'
|
||||
from_email = settings.NOMCOM_FROM_EMAIL
|
||||
to_email = author.address
|
||||
context = {'nominee': email.person.name,
|
||||
context = {'nominee': nominee.email.person.name,
|
||||
'comments': comments,
|
||||
'position': position.name}
|
||||
path = nomcom_template_path + NOMINATION_RECEIPT_TEMPLATE
|
||||
|
@ -795,37 +700,43 @@ class PrivateKeyForm(BaseNomcomForm, forms.Form):
|
|||
|
||||
class PendingFeedbackForm(BaseNomcomForm, forms.ModelForm):
|
||||
|
||||
type = forms.ModelChoiceField(queryset=FeedbackType.objects.all(), widget=forms.RadioSelect, empty_label='Unclassified', required=False)
|
||||
|
||||
class Meta:
|
||||
model = Feedback
|
||||
fields = ('author', 'type', 'nominee')
|
||||
fields = ('type', )
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PendingFeedbackForm, self).__init__(*args, **kwargs)
|
||||
self.fields['type'].queryset = FeedbackType.objects.exclude(slug='nomina')
|
||||
try:
|
||||
self.default_type = FeedbackType.objects.get(slug=settings.DEFAULT_FEEDBACK_TYPE)
|
||||
except FeedbackType.DoesNotExist:
|
||||
self.default_type = None
|
||||
|
||||
def set_nomcom(self, nomcom, user):
|
||||
self.nomcom = nomcom
|
||||
self.user = user
|
||||
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.')
|
||||
#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)
|
||||
|
||||
author = get_user_email(self.user)
|
||||
|
||||
if author:
|
||||
feedback.author = author
|
||||
|
||||
feedback.nomcom = self.nomcom
|
||||
feedback.user = self.user
|
||||
feedback.save()
|
||||
self.save_m2m()
|
||||
for (position, nominee) in self.cleaned_data['nominee']:
|
||||
feedback.nominees.add(nominee)
|
||||
feedback.positions.add(position)
|
||||
return feedback
|
||||
|
||||
def move_to_default(self):
|
||||
if not self.default_type or self.cleaned_data.get('type', None):
|
||||
return None
|
||||
feedback = super(PendingFeedbackForm, self).save(commit=False)
|
||||
feedback.nomcom = self.nomcom
|
||||
feedback.user = self.user
|
||||
feedback.type = self.default_type
|
||||
feedback.save()
|
||||
return feedback
|
||||
|
||||
|
||||
class ReminderDatesForm(forms.ModelForm):
|
||||
|
@ -837,3 +748,82 @@ class ReminderDatesForm(forms.ModelForm):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super(ReminderDatesForm, self).__init__(*args, **kwargs)
|
||||
self.fields['date'].required = False
|
||||
|
||||
|
||||
class MutableFeedbackForm(forms.ModelForm):
|
||||
|
||||
type = forms.ModelChoiceField(queryset=FeedbackType.objects.all(), widget=forms.HiddenInput)
|
||||
|
||||
class Meta:
|
||||
model = Feedback
|
||||
fields = ('type', )
|
||||
|
||||
def set_nomcom(self, nomcom, user, instances=None):
|
||||
self.nomcom = nomcom
|
||||
self.user = user
|
||||
instances = instances or []
|
||||
self.feedback_type = None
|
||||
for i in instances:
|
||||
if i.id == self.instance.id:
|
||||
self.feedback_type = i.type
|
||||
break
|
||||
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')))
|
||||
|
||||
self.initial['type'] = self.feedback_type
|
||||
|
||||
if self.feedback_type.slug != 'nomina':
|
||||
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.')
|
||||
else:
|
||||
self.fields['position'] = forms.ModelChoiceField(queryset=Position.objects.get_by_nomcom(self.nomcom).opened(), label="Position")
|
||||
self.fields['candidate_name'] = forms.CharField(label="Candidate name")
|
||||
self.fields['candidate_email'] = forms.EmailField(label="Candidate email")
|
||||
self.fields['candidate_phone'] = forms.CharField(label="Candidate phone", required=False)
|
||||
|
||||
def save(self, commit=True):
|
||||
feedback = super(MutableFeedbackForm, self).save(commit=False)
|
||||
if self.instance.type.slug == 'nomina':
|
||||
candidate_email = self.cleaned_data['candidate_email']
|
||||
candidate_name = self.cleaned_data['candidate_name']
|
||||
candidate_phone = self.cleaned_data['candidate_phone']
|
||||
position = self.cleaned_data['position']
|
||||
|
||||
nominator_email = feedback.author
|
||||
feedback.save()
|
||||
|
||||
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)
|
||||
feedback.nominees.add(nominee)
|
||||
feedback.positions.add(position)
|
||||
Nomination.objects.create(
|
||||
position=self.cleaned_data.get('position'),
|
||||
candidate_name=candidate_name,
|
||||
candidate_email=candidate_email,
|
||||
candidate_phone=candidate_phone,
|
||||
nominee=nominee,
|
||||
comments=feedback,
|
||||
nominator_email=nominator_email,
|
||||
user=self.user,
|
||||
)
|
||||
return feedback
|
||||
else:
|
||||
feedback.save()
|
||||
self.save_m2m()
|
||||
for (position, nominee) in self.cleaned_data['nominee']:
|
||||
feedback.nominees.add(nominee)
|
||||
feedback.positions.add(position)
|
||||
return feedback
|
||||
|
||||
|
||||
class FullFeedbackFormSet(forms.models.BaseModelFormSet):
|
||||
|
||||
model = Feedback
|
||||
extra = 0
|
||||
max_num = 0
|
||||
form = MutableFeedbackForm
|
||||
can_order = False
|
||||
can_delete = False
|
||||
|
|
|
@ -5,7 +5,7 @@ import syslog
|
|||
from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
from ietf.nomcom.utils import parse_email
|
||||
from ietf.nomcom.models import Nominee, NomCom, Feedback
|
||||
from ietf.nomcom.models import NomCom, Feedback
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
@ -19,7 +19,6 @@ class Command(BaseCommand):
|
|||
email = options.get('email', None)
|
||||
year = options.get('year', None)
|
||||
msg = None
|
||||
nominee = None
|
||||
nomcom = None
|
||||
help_message = 'Usage: feeback_email --nomcom-year <nomcom-year> --email-file <email-file>'
|
||||
|
||||
|
@ -38,16 +37,12 @@ class Command(BaseCommand):
|
|||
raise CommandError("NomCom %s does not exist or it isn't active" % year)
|
||||
|
||||
by, subject, body = parse_email(msg)
|
||||
body = 'Subject: %s\n\n%s' % (subject, body)
|
||||
name, addr = parseaddr(by)
|
||||
try:
|
||||
nominee = Nominee.objects.get_by_nomcom(nomcom).not_duplicated().get(email__address__icontains=addr)
|
||||
except Nominee.DoesNotExist:
|
||||
pass
|
||||
|
||||
feedback = Feedback(nomcom=nomcom,
|
||||
author=addr,
|
||||
comments=body)
|
||||
feedback.save()
|
||||
if nominee:
|
||||
feedback.nominees.add(nominee)
|
||||
|
||||
syslog.syslog(u"Read feedback email by %s" % by)
|
||||
|
|
|
@ -60,7 +60,7 @@ def formatted_email(address):
|
|||
|
||||
|
||||
@register.simple_tag
|
||||
def decrypt(string, request, year):
|
||||
def decrypt(string, request, year, plain=False):
|
||||
key = retrieve_nomcom_private_key(request, year)
|
||||
|
||||
if not key:
|
||||
|
@ -79,4 +79,6 @@ def decrypt(string, request, year):
|
|||
if error:
|
||||
return '<-Encripted text [Your private key is invalid]->'
|
||||
|
||||
return linebreaksbr(out)
|
||||
if not plain:
|
||||
return linebreaksbr(out)
|
||||
return out
|
||||
|
|
|
@ -10,6 +10,7 @@ urlpatterns = patterns('ietf.nomcom.views',
|
|||
url(r'^(?P<year>\d{4})/private/feedback/$', 'private_feedback', name='nomcom_private_feedback'),
|
||||
url(r'^(?P<year>\d{4})/private/questionnaire-response/$', 'private_questionnaire', name='nomcom_private_questionnaire'),
|
||||
url(r'^(?P<year>\d{4})/private/view-feedback/$', 'view_feedback', name='nomcom_view_feedback'),
|
||||
url(r'^(?P<year>\d{4})/private/view-feedback/unrelated/$', 'view_feedback_unrelated', name='nomcom_view_feedback_unrelated'),
|
||||
url(r'^(?P<year>\d{4})/private/view-feedback/pending/$', 'view_feedback_pending', name='nomcom_view_feedback_pending'),
|
||||
url(r'^(?P<year>\d{4})/private/view-feedback/nominee/(?P<nominee_id>\d+)$', 'view_feedback_nominee', name='nomcom_view_feedback_nominee'),
|
||||
url(r'^(?P<year>\d{4})/private/merge/$', 'private_merge', name='nomcom_private_merge'),
|
||||
|
|
|
@ -13,9 +13,11 @@ from django.template.loader import render_to_string
|
|||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from ietf.dbtemplate.models import DBTemplate
|
||||
from ietf.person.models import Email
|
||||
from ietf.person.models import Email, Person
|
||||
from ietf.utils.pipe import pipe
|
||||
from ietf.utils.mail import send_mail_text
|
||||
from ietf.utils import unaccent
|
||||
from ietf.utils.mail import send_mail_text, send_mail
|
||||
|
||||
|
||||
MAIN_NOMCOM_TEMPLATE_PATH = '/nomcom/defaults/'
|
||||
QUESTIONNAIRE_TEMPLATE = 'position/questionnaire.txt'
|
||||
|
@ -157,10 +159,14 @@ def extract_body(payload):
|
|||
|
||||
|
||||
def parse_email(text):
|
||||
msg = email.message_from_string(text.encode("utf-8"))
|
||||
msg = email.message_from_string(text)
|
||||
|
||||
# comment
|
||||
body = extract_body(msg.get_payload())
|
||||
#body = quopri.decodestring(extract_body(msg.get_payload()))
|
||||
charset = msg.get_content_charset()
|
||||
body = extract_body(msg.get_payload(decode=True))
|
||||
if charset:
|
||||
body = body.decode(charset)
|
||||
|
||||
return msg['From'], msg['Subject'], body
|
||||
|
||||
|
@ -235,3 +241,101 @@ def send_reminder_to_nominees(nominees):
|
|||
for nominee in nominees:
|
||||
for nominee_position in nominee.nomineeposition_set.pending():
|
||||
send_reminder_to_nominee(nominee_position)
|
||||
|
||||
|
||||
def get_or_create_nominee(nomcom, candidate_name, candidate_email, position, author):
|
||||
from ietf.nomcom.models import Nominee, NomineePosition
|
||||
|
||||
nomcom_template_path = '/nomcom/%s/' % nomcom.group.acronym
|
||||
nomcom_chair = nomcom.group.get_chair()
|
||||
nomcom_chair_mail = nomcom_chair and nomcom_chair.email.address or None
|
||||
|
||||
# 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:
|
||||
email.person = Person.objects.create(name=candidate_name,
|
||||
ascii=unaccent.asciify(candidate_name),
|
||||
address=candidate_email)
|
||||
email.save()
|
||||
|
||||
# Add the nomination for a particular position
|
||||
nominee, created = Nominee.objects.get_or_create(email=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 = [settings.NOMCOM_ADMIN_EMAIL]
|
||||
context = {'email': email.address,
|
||||
'fullname': email.person.name,
|
||||
'person_id': email.person.id}
|
||||
path = nomcom_template_path + INEXISTENT_PERSON_TEMPLATE
|
||||
if nomcom_chair_mail:
|
||||
to_email.append(nomcom_chair_mail)
|
||||
send_mail(None, to_email, from_email, subject, path, context)
|
||||
|
||||
if nominee_position_created:
|
||||
# send email to nominee
|
||||
subject = 'IETF Nomination Information'
|
||||
from_email = settings.NOMCOM_FROM_EMAIL
|
||||
to_email = 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)
|
||||
accept_url = reverse('nomcom_process_nomination_status',
|
||||
None,
|
||||
args=(get_year_by_nomcom(nomcom),
|
||||
nominee_position.id,
|
||||
'accepted',
|
||||
today,
|
||||
hash))
|
||||
decline_url = reverse('nomcom_process_nomination_status',
|
||||
None,
|
||||
args=(get_year_by_nomcom(nomcom),
|
||||
nominee_position.id,
|
||||
'declined',
|
||||
today,
|
||||
hash))
|
||||
|
||||
context = {'nominee': email.person.name,
|
||||
'position': position.name,
|
||||
'domain': domain,
|
||||
'accept_url': accept_url,
|
||||
'decline_url': decline_url}
|
||||
|
||||
path = nomcom_template_path + NOMINEE_EMAIL_TEMPLATE
|
||||
send_mail(None, to_email, from_email, subject, path, context)
|
||||
|
||||
# send email to nominee with questionnaire
|
||||
if nomcom.send_questionnaire:
|
||||
subject = '%s Questionnaire' % position
|
||||
from_email = settings.NOMCOM_FROM_EMAIL
|
||||
to_email = email.address
|
||||
context = {'nominee': email.person.name,
|
||||
'position': position.name}
|
||||
path = '%s%d/%s' % (nomcom_template_path,
|
||||
position.id, HEADER_QUESTIONNAIRE_TEMPLATE)
|
||||
body = render_to_string(path, context)
|
||||
path = '%s%d/%s' % (nomcom_template_path,
|
||||
position.id, QUESTIONNAIRE_TEMPLATE)
|
||||
body += '\n\n%s' % render_to_string(path, context)
|
||||
send_mail_text(None, to_email, from_email, subject, body)
|
||||
|
||||
# send emails to nomcom chair
|
||||
subject = 'Nomination Information'
|
||||
from_email = settings.NOMCOM_FROM_EMAIL
|
||||
to_email = nomcom_chair_mail
|
||||
context = {'nominee': email.person.name,
|
||||
'nominee_email': email.address,
|
||||
'position': position.name}
|
||||
|
||||
if author:
|
||||
context.update({'nominator': author.person.name,
|
||||
'nominator_email': author.address})
|
||||
path = nomcom_template_path + NOMINATION_EMAIL_TEMPLATE
|
||||
send_mail(None, to_email, from_email, subject, path, context)
|
||||
|
||||
return nominee
|
||||
|
|
|
@ -5,13 +5,14 @@ import datetime
|
|||
from django.views.generic.create_update import delete_object
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.messages.api import success, get_messages
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponse, Http404, HttpResponseRedirect, HttpResponseForbidden
|
||||
from django.shortcuts import render_to_response, get_object_or_404
|
||||
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.db.models import Count
|
||||
from django.forms.models import modelformset_factory, inlineformset_factory
|
||||
|
||||
|
||||
|
@ -23,7 +24,7 @@ from ietf.nomcom.decorators import nomcom_member_required, nomcom_private_key_re
|
|||
from ietf.nomcom.forms import (NominateForm, FeedbackForm, QuestionnaireForm,
|
||||
MergeForm, NomComTemplateForm, PositionForm,
|
||||
PrivateKeyForm, EditNomcomForm, PendingFeedbackForm,
|
||||
ReminderDatesForm)
|
||||
ReminderDatesForm, FullFeedbackFormSet)
|
||||
from ietf.nomcom.models import Position, NomineePosition, Nominee, Feedback, NomCom, ReminderDates
|
||||
from ietf.nomcom.utils import (get_nomcom_by_year, store_nomcom_private_key,
|
||||
get_hash_nominee_position, send_reminder_to_nominees,
|
||||
|
@ -380,17 +381,26 @@ def process_nomination_status(request, year, nominee_position_id, state, date, h
|
|||
def view_feedback(request, year):
|
||||
nomcom = get_nomcom_by_year(year)
|
||||
nominees = Nominee.objects.get_by_nomcom(nomcom).not_duplicated().distinct()
|
||||
feedback_types = FeedbackType.objects.all()
|
||||
independent_feedback_types = []
|
||||
feedback_types = []
|
||||
for ft in FeedbackType.objects.all():
|
||||
if ft.slug in settings.NOMINEE_FEEDBACK_TYPES:
|
||||
feedback_types.append(ft)
|
||||
else:
|
||||
independent_feedback_types.append(ft)
|
||||
nominees_feedback = {}
|
||||
for nominee in nominees:
|
||||
nominee_feedback = [(ft.name, nominee.feedback_set.by_type(ft.slug).count()) for ft in feedback_types]
|
||||
nominees_feedback.update({nominee: nominee_feedback})
|
||||
independent_feedback = [ft.feedback_set.get_by_nomcom(nomcom).count() for ft in independent_feedback_types]
|
||||
|
||||
return render_to_response('nomcom/view_feedback.html',
|
||||
{'year': year,
|
||||
'selected': 'view_feedback',
|
||||
'nominees': nominees,
|
||||
'feedback_types': feedback_types,
|
||||
'independent_feedback_types': independent_feedback_types,
|
||||
'independent_feedback': independent_feedback,
|
||||
'nominees_feedback': nominees_feedback,
|
||||
'nomcom': nomcom}, RequestContext(request))
|
||||
|
||||
|
@ -400,23 +410,63 @@ def view_feedback(request, year):
|
|||
def view_feedback_pending(request, year):
|
||||
nomcom = get_nomcom_by_year(year)
|
||||
message = None
|
||||
for message in get_messages(request):
|
||||
message = ('success', message.message)
|
||||
FeedbackFormSet = modelformset_factory(Feedback,
|
||||
form=PendingFeedbackForm,
|
||||
exclude=('nomcom', 'comments'),
|
||||
extra=0)
|
||||
feedbacks = Feedback.objects.filter(Q(type__isnull=True) |
|
||||
Q(nominees__isnull=True) |
|
||||
Q(positions__isnull=True))
|
||||
if request.method == 'POST':
|
||||
feedbacks = Feedback.objects.filter(type__isnull=True, nomcom=nomcom)
|
||||
|
||||
try:
|
||||
default_type = FeedbackType.objects.get(slug=settings.DEFAULT_FEEDBACK_TYPE)
|
||||
except FeedbackType.DoesNotExist:
|
||||
default_type = None
|
||||
|
||||
extra_step = False
|
||||
if request.method == 'POST' and request.POST.get('move_to_default'):
|
||||
formset = FeedbackFormSet(request.POST)
|
||||
if formset.is_valid():
|
||||
for form in formset.forms:
|
||||
form.set_nomcom(nomcom, request.user)
|
||||
form.move_to_default()
|
||||
formset = FeedbackFormSet(queryset=feedbacks)
|
||||
for form in formset.forms:
|
||||
form.set_nomcom(nomcom, request.user)
|
||||
success(request, 'Feedback saved')
|
||||
return HttpResponseRedirect(reverse('nomcom_view_feedback_pending', None, args=(year, )))
|
||||
elif request.method == 'POST' and request.POST.get('end'):
|
||||
extra_step = True
|
||||
formset = FullFeedbackFormSet(request.POST)
|
||||
for form in formset.forms:
|
||||
form.set_nomcom(nomcom, request.user)
|
||||
if formset.is_valid():
|
||||
formset.save()
|
||||
message = ('success', 'The feedbacks has been saved.')
|
||||
formset = FeedbackFormSet(queryset=feedbacks)
|
||||
success(request, 'Feedback saved')
|
||||
return HttpResponseRedirect(reverse('nomcom_view_feedback_pending', None, args=(year, )))
|
||||
elif request.method == 'POST':
|
||||
formset = FeedbackFormSet(request.POST)
|
||||
for form in formset.forms:
|
||||
form.set_nomcom(nomcom, request.user)
|
||||
if formset.is_valid():
|
||||
extra = []
|
||||
moved = 0
|
||||
for form in formset.forms:
|
||||
form.set_nomcom(nomcom, request.user)
|
||||
if form.instance.type and form.instance.type.slug in settings.NOMINEE_FEEDBACK_TYPES:
|
||||
extra.append(form.instance)
|
||||
else:
|
||||
if form.instance.type:
|
||||
moved += 1
|
||||
form.save()
|
||||
if extra:
|
||||
extra_step = True
|
||||
formset = FullFeedbackFormSet(queryset=Feedback.objects.filter(id__in=[i.id for i in extra]))
|
||||
for form in formset.forms:
|
||||
form.set_nomcom(nomcom, request.user, extra)
|
||||
if moved:
|
||||
message = ('success', '%s messages classified. You must enter more information for the following feedback.' % moved)
|
||||
else:
|
||||
success(request, 'Feedback saved')
|
||||
return HttpResponseRedirect(reverse('nomcom_view_feedback_pending', None, args=(year, )))
|
||||
else:
|
||||
formset = FeedbackFormSet(queryset=feedbacks)
|
||||
for form in formset.forms:
|
||||
|
@ -426,6 +476,24 @@ def view_feedback_pending(request, year):
|
|||
'selected': 'view_feedback',
|
||||
'formset': formset,
|
||||
'message': message,
|
||||
'extra_step': extra_step,
|
||||
'default_type': default_type,
|
||||
'nomcom': nomcom}, RequestContext(request))
|
||||
|
||||
|
||||
@nomcom_member_required(role='member')
|
||||
@nomcom_private_key_required
|
||||
def view_feedback_unrelated(request, year):
|
||||
nomcom = get_nomcom_by_year(year)
|
||||
feedback_types = []
|
||||
for ft in FeedbackType.objects.exclude(slug__in=settings.NOMINEE_FEEDBACK_TYPES):
|
||||
feedback_types.append({'ft': ft,
|
||||
'feedback': ft.feedback_set.get_by_nomcom(nomcom)})
|
||||
|
||||
return render_to_response('nomcom/view_feedback_unrelated.html',
|
||||
{'year': year,
|
||||
'selected': 'view_feedback',
|
||||
'feedback_types': feedback_types,
|
||||
'nomcom': nomcom}, RequestContext(request))
|
||||
|
||||
|
||||
|
@ -434,7 +502,7 @@ def view_feedback_pending(request, year):
|
|||
def view_feedback_nominee(request, year, nominee_id):
|
||||
nomcom = get_nomcom_by_year(year)
|
||||
nominee = get_object_or_404(Nominee, id=nominee_id)
|
||||
feedback_types = FeedbackType.objects.all()
|
||||
feedback_types = FeedbackType.objects.filter(slug__in=settings.NOMINEE_FEEDBACK_TYPES)
|
||||
|
||||
return render_to_response('nomcom/view_feedback_nominee.html',
|
||||
{'year': year,
|
||||
|
|
|
@ -102,6 +102,7 @@ MIDDLEWARE_CLASSES = (
|
|||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.auth.middleware.RemoteUserMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.doc.XViewMiddleware',
|
||||
'ietf.middleware.SQLLogMiddleware',
|
||||
'ietf.middleware.SMTPExceptionMiddleware',
|
||||
|
@ -135,6 +136,7 @@ INSTALLED_APPS = (
|
|||
'django.contrib.admin',
|
||||
'django.contrib.admindocs',
|
||||
'django.contrib.humanize',
|
||||
'django.contrib.messages',
|
||||
'south',
|
||||
'workflows',
|
||||
'permissions',
|
||||
|
@ -266,6 +268,8 @@ NOMCOM_FROM_EMAIL = DEFAULT_FROM_EMAIL
|
|||
NOMCOM_ADMIN_EMAIL = DEFAULT_FROM_EMAIL
|
||||
OPENSSL_COMMAND = '/usr/bin/openssl'
|
||||
DAYS_TO_EXPIRE_NOMINATION_LINK = ''
|
||||
DEFAULT_FEEDBACK_TYPE = 'offtopic'
|
||||
NOMINEE_FEEDBACK_TYPES = ['comment', 'questio', 'nomina']
|
||||
|
||||
# Days from meeting to cut off dates on submit
|
||||
FIRST_CUTOFF_DAYS = 19
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<p><a href="{% url nomcom_view_feedback_pending year %}">Feedback pending</a></p>
|
||||
{% endif %}
|
||||
|
||||
<h2>List of Nominees</h2>
|
||||
<h2>Feedback related to Nominees</h2>
|
||||
|
||||
<table class="ietf-table ietf-doctable">
|
||||
<tr>
|
||||
|
@ -29,4 +29,21 @@
|
|||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
{% if independent_feedback_types %}
|
||||
<h2>Feedback not related to Nominees</h2>
|
||||
<table class="ietf-table ietf-doctable">
|
||||
<tr>
|
||||
<th></th>
|
||||
{% for ft in independent_feedback_types %}
|
||||
<th>{{ ft.name }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
<tr class="evenrow">
|
||||
<td><a href="{% url nomcom_view_feedback_unrelated year %}">View feedback not related to nominees</td>
|
||||
{% for count in independent_feedback %}
|
||||
<td>{{ count }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -15,26 +15,40 @@
|
|||
<div class="info-message-{{ message.0 }}">{{ message.1 }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if formset.forms %}
|
||||
{% if extra_step %}
|
||||
<p>Please, provide the following information about nominees to complete the classification of this feedback.</p>
|
||||
{% endif %}
|
||||
|
||||
<div>
|
||||
<form id="feedbackformset" action="" method="post">{% csrf_token %}
|
||||
<div class="sumit-row">
|
||||
{% if extra_step %}
|
||||
<input type="submit" value="Save feedback" name="end" />
|
||||
<a href="{% url nomcom_view_feedback_pending year %}">Cancel and leave the following feedback unclassified</a>
|
||||
{% else %}
|
||||
<input type="submit" value="Classify" />
|
||||
{% if default_type %}<input type="submit" name="move_to_default" value="Move all unclassified feedback to {{ default_type }}" />{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{{ formset.management_form }}
|
||||
{% for form in formset.forms %}
|
||||
<h3 class="ietf-divider" style="margin-bottom: 0px;">{{ form.instance.time|date:"Y-m-d" }} id:{{ form.instance.id }}</h3>
|
||||
{% 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">
|
||||
<h2>Comment meta data</h2>
|
||||
<h2>{{ form.instance.time|date:"Y-m-d" }} id:{{ form.instance.id }}</h2>
|
||||
{% for field in form %}
|
||||
<div id="baseform-fieldname-{{ field.html_name }}"
|
||||
<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 %}">
|
||||
{% if extra_step %}
|
||||
<label for="id_{{ field.html_name }}">{{ field.label }}
|
||||
{% if field.field.required %}
|
||||
<span class="fieldRequired" title="Required">*</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
<div class="fieldWidget">
|
||||
{% endif %}
|
||||
<div class="fieldWidget{% if extra_step %} defaultPos{% endif %}">
|
||||
<div id="{{ field.html_name }}_help" class="formHelp"> {{ field.help_text }}</div>
|
||||
{{ field }}
|
||||
{{ field.errors }}
|
||||
|
@ -42,20 +56,27 @@
|
|||
<div class="endfield"></div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<h3>Type</h3>
|
||||
<pre>{{ form.feedback_type }}</pre>
|
||||
<h3>Author</h3>
|
||||
<pre>{{ form.instance.author }}</pre>
|
||||
<h3>Feedback body</h3>
|
||||
<pre>{% decrypt form.instance.comments request year 1 %}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</td><td style="width: 50%; vertical-align: top;">
|
||||
<div class="baseform">
|
||||
<div class="fieldset">
|
||||
<h2>Comment body</h2>
|
||||
<pre style="padding: 1em;">{% decrypt form.instance.comments request year %}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</td></tr></table>
|
||||
{% endfor %}
|
||||
<div class="ietf-divider"></div>
|
||||
<input type="submit" value="Submit" />
|
||||
<div class="sumit-row">
|
||||
{% if extra_step %}
|
||||
<input type="submit" value="Save feedback" name="end" />
|
||||
<a href="{% url nomcom_view_feedback_pending year %}">Cancel and leave the following feedback unclassified</a>
|
||||
{% else %}
|
||||
<input type="submit" value="Classify" />
|
||||
{% if default_type %}<input type="submit" name="move_to_default" value="Move all unclassified feedback to {{ default_type }}" />{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<p>There is no pending feedback.</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
72
ietf/templates/nomcom/view_feedback_unrelated.html
Normal file
72
ietf/templates/nomcom/view_feedback_unrelated.html
Normal file
|
@ -0,0 +1,72 @@
|
|||
{% extends "nomcom/nomcom_private_base.html" %}
|
||||
|
||||
{% load nomcom_tags %}
|
||||
|
||||
{% block pagehead %}
|
||||
{{ block.super }}
|
||||
<script type="text/javascript" src="/js/lib/jquery-1.4.2.min.js"></script>
|
||||
<script type="text/javascript" src="/js/yui/yui-20100305.js"></script>
|
||||
<script type="text/javascript" src="/js/base.js"></script>
|
||||
{% endblock pagehead %}
|
||||
|
||||
{% block subtitle %} - View unrelated feedback{% endblock %}
|
||||
|
||||
{% block nomcom_content %}
|
||||
|
||||
<p>Back to list of <a href="{% url nomcom_view_feedback year %}">feedback</a></p>
|
||||
|
||||
<h2>Feedback not related to nominees</h2>
|
||||
|
||||
<div id="mytabs" class="yui-navset">
|
||||
<ul class="yui-nav">
|
||||
{% for ft in feedback_types %}
|
||||
<li><a href="#{{ ft.ft.slug }}"><em>{{ ft.ft.name }}</em></a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
Pick the feedback type to view from the list immediately above
|
||||
<div class="yui-content">
|
||||
{% for ft in feedback_types %}
|
||||
<div id="#{{ ft.ft.slug }}">
|
||||
{% for feedback in ft.feedback %}
|
||||
<div>
|
||||
<h3 class="ietf-divider">From {{ feedback.author|formatted_email|default:"Anonymous" }} ({{ feedback.time|date:"Y-m-d" }})</h3>
|
||||
{% ifequal ft.slug "nomina" %}
|
||||
{% for fn in feedback.nomination_set.all %}
|
||||
{% if fn.candidate_name %}
|
||||
<p><b>Candidate name:</b> {{ fn.candidate_name }}</p>
|
||||
{% endif %}
|
||||
{% if fn.candidate_phone %}
|
||||
<p><b>Candidate phone:</b> {{ fn.candidate_phone }}</p>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endifequal %}
|
||||
<b>Positions:</b> {{ feedback.positions.all|join:"," }}
|
||||
<p>
|
||||
{% decrypt feedback.comments request year %}
|
||||
</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var tabView = new YAHOO.widget.TabView('mytabs');
|
||||
var url = location.href.split('#');
|
||||
if (url[1]) {
|
||||
url[1] = "#"+url[1];
|
||||
var tabs = tabView.get('tabs');
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
if (url[1].indexOf(tabs[i].get('href')) == 0) {
|
||||
tabView.set('activeIndex', i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
|
@ -249,6 +249,37 @@ div.info-message-error { border: 1px solid red; background-color: #ffeebb; paddi
|
|||
margin-left: 150px;
|
||||
}
|
||||
|
||||
#feedbackformset .fieldWidget {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
#feedbackformset .fieldWidget.defaultPos {
|
||||
margin-left: 150px;
|
||||
}
|
||||
|
||||
#feedbackformset ul {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#feedbackformset ul li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#feedbackformset ul li label {
|
||||
display: inline;
|
||||
float: none;
|
||||
}
|
||||
|
||||
#feedbackformset .baseform h3 {
|
||||
margin-left: 12px;
|
||||
margin-top: 24px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
#feedbackformset pre {
|
||||
margin: 12px;
|
||||
}
|
||||
|
||||
.baseform select,
|
||||
.baseform textarea,
|
||||
.baseform input {
|
||||
|
|
Loading…
Reference in a new issue