Add public and private complete views to do comments

Add new manager for position model
Add templatetag to see the total number of nominations by user on a position
Add feedback receipt template 
See 
 - Legacy-Id: 5554
This commit is contained in:
Emilio Jiménez 2013-03-12 15:47:49 +00:00
parent 04ab58d1ae
commit b1f1ceb826
11 changed files with 397 additions and 113 deletions

View file

@ -125,6 +125,27 @@ The following comments have also been registered:
$comments
--------------------------------------------------------------------------
Thank you,</field>
<field to="group.group" name="group" rel="ManyToOneRel"><None></None></field>
</object>
<object p="10" model="dbtemplate.dbtemplate">
<field type="CharField" name="path">/nomcom/defaults/email/feedback_receipt.txt</field>
<field type="CharField" name="title">Email sent to feedback author to get a confirmation mail containing feedback in cleartext</field>
<field type="TextField" name="variables">$nominee: Full name of the nominee
$position: Nomination position
$comments: Comments on this candidate</field>
<field to="name.dbtemplatetypename" name="type" rel="ManyToOneRel">plain</field>
<field type="TextField" name="content">Hi,
Your input regarding $nominee for the position of
$position has been received and registered.
The following comments have been registered:
--------------------------------------------------------------------------
$comments
--------------------------------------------------------------------------
Thank you,</field>
<field to="group.group" name="group" rel="ManyToOneRel"><None></None></field>
</object>

View file

@ -21,7 +21,8 @@ from ietf.nomcom.models import NomCom, Nomination, Nominee, NomineePosition, \
Position, Feedback
from ietf.nomcom.utils import QUESTIONNAIRE_TEMPLATE, NOMINATION_EMAIL_TEMPLATE, \
INEXISTENT_PERSON_TEMPLATE, NOMINEE_EMAIL_TEMPLATE, \
NOMINATION_RECEIPT_TEMPLATE, get_user_email
NOMINATION_RECEIPT_TEMPLATE, FEEDBACK_RECEIPT_TEMPLATE, \
get_user_email
from ietf.nomcom.decorators import member_required
ROLODEX_URL = getattr(settings, 'ROLODEX_URL', None)
@ -288,7 +289,7 @@ class NominateForm(BaseNomcomForm, forms.ModelForm):
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' box below.",
please check the 'email comments back to me as confirmation'",
required=False)
fieldsets = [('Candidate Nomination', ('position', 'candidate_name',
@ -300,8 +301,16 @@ class NominateForm(BaseNomcomForm, forms.ModelForm):
self.public = kwargs.pop('public', None)
super(NominateForm, self).__init__(*args, **kwargs)
fieldset = ['nominator_email',
'position',
'candidate_name',
'candidate_email', 'candidate_phone',
'comments']
if self.nomcom:
self.fields['position'].queryset = Position.objects.filter(nomcom=self.nomcom)
self.fields['position'].queryset = Position.objects.get_by_nomcom(self.nomcom).opened()
if not self.public:
author = get_user_email(self.user)
if author:
@ -310,11 +319,10 @@ class NominateForm(BaseNomcomForm, forms.ModelForm):
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.fieldsets = [('Candidate Nomination', ('nominator_email',
'position',
'candidate_name',
'candidate_email', 'candidate_phone',
'comments'))]
else:
fieldset.append('confirmation')
self.fieldsets = [('Candidate Nomination', fieldset)]
def save(self, commit=True):
# Create nomination
@ -413,6 +421,7 @@ class NominateForm(BaseNomcomForm, forms.ModelForm):
path = nomcom_template_path + NOMINATION_EMAIL_TEMPLATE
send_mail(None, to_email, from_email, subject, path, context)
# send receipt email to nominator
if confirmation or not self.public:
if author:
subject = 'Nomination Receipt'
@ -437,39 +446,130 @@ class NominateForm(BaseNomcomForm, forms.ModelForm):
class FeedbackForm(BaseNomcomForm, forms.ModelForm):
position = forms.CharField(label='position')
nominee_name = forms.CharField(label='nominee name')
nominee_email = forms.CharField(label='nominee email')
position_name = forms.CharField(label='position',
widget=forms.TextInput(attrs={'size': '40'}))
nominee_name = forms.CharField(label='your name',
widget=forms.TextInput(attrs={'size': '40'}))
nominee_email = forms.CharField(label='your email',
widget=forms.TextInput(attrs={'size': '40'}))
nominator_name = forms.CharField(label='nominator name')
nominator_email = forms.CharField(label='nominator email')
comments = forms.CharField(label='Comments on this candidate', widget=forms.Textarea())
fieldsets = [('Provide comments', ('position',
'nominee_name',
'nominee_email',
'nominator_name',
'nominator_email',
'comments'))]
comments = forms.CharField(label='Comments on this candidate',
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)
self.position = kwargs.pop('position', None)
self.nominee = kwargs.pop('nominee', None)
super(FeedbackForm, self).__init__(*args, **kwargs)
readonly_fields = ['position_name',
'nominee_name',
'nominee_email']
fieldset = ['position_name',
'nominee_name',
'nominee_email',
'nominator_name',
'nominator_email',
'comments',
'position',
'type',
'nominee']
if self.public:
readonly_fields += ['nominator_name', 'nominator_email']
fieldset.append('confirmation')
else:
help_text = """(Nomcom Chair/Member: please fill this in. Use your own email address if the person making the
comments 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
hidden_fields = ['position', 'type', 'nominee']
author = get_user_email(self.user)
if author:
self.fields['nominator_email'].initial = author.address
self.fields['nominator_name'].initial = author.person.name
if self.position and self.nominee:
self.fields['type'].initial = "comment"
self.fields['position'].initial = self.position
self.fields['position_name'].initial = self.position.name
self.fields['nominee'].initial = self.nominee
self.fields['nominee_name'].initial = self.nominee.email.person.name
self.fields['nominee_email'].initial = self.nominee.email.address
else:
help_text = "Please pick a name on the nominees list"
self.fields['position_name'].initial = help_text
self.fields['nominee_name'].initial = help_text
self.fields['nominee_email'].initial = help_text
self.fields['comments'].initial = help_text
readonly_fields += ['comments']
self.fields['confirmation'].widget.attrs['disabled'] = "disabled"
for field in readonly_fields:
self.fields[field].widget.attrs['readonly'] = True
for field in hidden_fields:
self.fields[field].widget = forms.HiddenInput()
self.fieldsets = [('Provide comments', fieldset)]
def save(self, commit=True):
pass
feedback = super(FeedbackForm, self).save(commit=False)
confirmation = self.cleaned_data['confirmation']
nominee = self.cleaned_data['nominee']
position = self.cleaned_data['position']
comments = self.cleaned_data['comments']
nominator_email = self.cleaned_data['nominator_email']
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
if author:
feedback.author = author
feedback.save()
# send receipt email to feedback author
if confirmation or not self.public:
if author:
subject = "NomCom comment confirmation"
from_email = settings.NOMCOM_FROM_EMAIL
to_email = author.address
context = {'nominee': nominee.email.person.name,
'comments': comments,
'position': position.name}
path = nomcom_template_path + FEEDBACK_RECEIPT_TEMPLATE
send_mail(None, to_email, from_email, subject, path, context)
class Meta:
model = Feedback
fields = ('position',
fields = ('author',
'position',
'nominee',
'nominee_name',
'nominee_email',
'nominator_name',
'nominator_email',
'comments')
'confirmation',
'comments',
'type')
class Media:
js = ("/js/jquery-1.5.1.min.js",

View file

@ -37,3 +37,28 @@ class NomineePositionManager(models.Manager):
class NomineeManager(models.Manager):
def get_by_nomcom(self, nomcom):
return self.filter(nominee_position__nomcom=nomcom)
class PositionQuerySet(QuerySet):
def get_by_nomcom(self, nomcom):
return self.filter(nomcom=nomcom)
def opened(self):
""" only opened positions """
return self.filter(is_open=True)
def closed(self):
""" only closed positions """
return self.filter(is_open=False)
class PositionManager(models.Manager):
def get_query_set(self):
return PositionQuerySet(self.model)
def __getattr__(self, attr, *args):
try:
return getattr(self.__class__, attr, *args)
except AttributeError:
return getattr(self.get_query_set(), attr, *args)

View file

@ -12,7 +12,7 @@ from ietf.group.models import Group
from ietf.name.models import NomineePositionState, FeedbackType
from ietf.dbtemplate.models import DBTemplate
from ietf.nomcom.managers import NomineePositionManager, NomineeManager
from ietf.nomcom.managers import NomineePositionManager, NomineeManager, PositionManager
from ietf.nomcom.utils import (initialize_templates_for_group,
initialize_questionnaire_for_position,
initialize_requirements_for_position)
@ -114,6 +114,8 @@ class Position(models.Model):
is_open = models.BooleanField(verbose_name='Is open')
incumbent = models.ForeignKey(Email)
objects = PositionManager()
class Meta:
verbose_name_plural = 'Positions'

View file

@ -4,9 +4,12 @@ import tempfile
from django import template
from django.conf import settings
from ietf.ietfauth.decorators import has_role
from ietf.nomcom.utils import get_nomcom_by_year
from ietf.utils.pipe import pipe
from ietf.ietfauth.decorators import has_role
from ietf.nomcom.models import Feedback
from ietf.nomcom.utils import get_nomcom_by_year, get_user_email
register = template.Library()
@ -21,6 +24,21 @@ def is_chair(user, year):
return nomcom.group.is_chair(user)
@register.simple_tag
def add_num_nominations(user, position, nominee):
author = get_user_email(user)
count = Feedback.objects.filter(position=position,
nominee=nominee,
author=author,
type='comment').count()
if count:
mark = """<span style="white-space: pre; color: red;">*</span>"""
else:
mark = """<span style="white-space: pre;"> </span> """
return '<span title="%d earlier comments from you on %s as %s">%s</span>&nbsp;' % (count, nominee, position, mark)
@register.filter
def decrypt(string, key=None):
if not key:
@ -34,7 +52,6 @@ def decrypt(string, key=None):
code, out, error = pipe(command % (settings.OPENSSL_COMMAND,
encrypted_file.name), key)
os.unlink(encrypted_file.name)
if error:

View file

@ -16,12 +16,15 @@ NOMINEE_EMAIL_TEMPLATE = 'email/new_nominee.txt'
NOMINATION_EMAIL_TEMPLATE = 'email/new_nomination.txt'
NOMINEE_REMINDER_TEMPLATE = 'email/nomination_reminder.txt'
NOMINATION_RECEIPT_TEMPLATE = 'email/nomination_receipt.txt'
FEEDBACK_RECEIPT_TEMPLATE = 'email/feedback_receipt.txt'
DEFAULT_NOMCOM_TEMPLATES = [HOME_TEMPLATE,
INEXISTENT_PERSON_TEMPLATE,
NOMINEE_EMAIL_TEMPLATE,
NOMINATION_EMAIL_TEMPLATE,
NOMINEE_REMINDER_TEMPLATE,
NOMINATION_RECEIPT_TEMPLATE]
NOMINATION_RECEIPT_TEMPLATE,
FEEDBACK_RECEIPT_TEMPLATE]
def get_nomcom_by_year(year):

View file

@ -14,6 +14,7 @@ from ietf.utils.mail import send_mail
from ietf.dbtemplate.models import DBTemplate
from ietf.dbtemplate.views import template_edit
from ietf.name.models import NomineePositionState
from ietf.nomcom.decorators import member_required, private_key_required
from ietf.nomcom.forms import (EditPublicKeyForm, NominateForm, FeedbackForm, MergeForm,
NomComTemplateForm, PositionForm, PrivateKeyForm)
@ -252,6 +253,18 @@ def private_feedback(request, year):
def feedback(request, year, public):
nomcom = get_nomcom_by_year(year)
has_publickey = nomcom.public_key and True or False
submit_disabled = True
nominee = None
position = None
selected_nominee = request.GET.get('nominee')
selected_position = request.GET.get('position')
if selected_nominee and selected_position:
nominee = Nominee.objects.get(id=selected_nominee)
position = Position.objects.get(id=selected_position)
submit_disabled = False
positions = Position.objects.get_by_nomcom(nomcom=nomcom).opened()
if public:
template = 'nomcom/public_feedback.html'
else:
@ -268,12 +281,14 @@ def feedback(request, year, public):
message = None
if request.method == 'POST':
form = FeedbackForm(data=request.POST, nomcom=nomcom, user=request.user, public=public)
form = FeedbackForm(data=request.POST, nomcom=nomcom, user=request.user,
public=public, position=position, nominee=nominee)
if form.is_valid():
form.save()
message = ('success', 'Your nomination has been registered. Thank you for the nomination.')
message = ('success', 'Your feedback has been registered.')
else:
form = FeedbackForm(nomcom=nomcom, user=request.user, public=public)
form = FeedbackForm(nomcom=nomcom, user=request.user, public=public,
position=position, nominee=nominee)
return render_to_response(template,
{'has_publickey': has_publickey,
@ -281,6 +296,8 @@ def feedback(request, year, public):
'message': message,
'nomcom': nomcom,
'year': year,
'positions': positions,
'submit_disabled': submit_disabled,
'selected': 'feedback'}, RequestContext(request))

View file

@ -8,6 +8,7 @@
{% for field in fieldset.fields %}
<div id="baseform-fieldname-{{ field.html_name }}"
{% if field.field.widget.is_hidden %}style="display: none;"{% endif %}
class="{% if field.errors %}fieldError {% endif %}field BaseFormStringWidget{% if field.field.column_style %} {{ field.field.column_style }}{% endif %}">
<label for="id_{{ field.html_name }}">{{ field.label }}
{% if field.field.required %}

View file

@ -1,5 +1,18 @@
{% extends "nomcom/nomcom_private_base.html" %}
{% block morecss %}
.content .primary {
width: 550px;
padding-right: 20px;
float: left;
display: inline;
}
table.nominees tr td {
padding-left: 15px;
}
{% endblock %}
{% block subtitle %} - Feedback{% endblock %}
{% block pagehead %}
@ -8,9 +21,12 @@
{% block nomcom_content %}
<p>Select a nominee from the list of nominees to provide input about that nominee.
(This will fill in the non-editable fields in the form).</p>
{% if message %}
<div class="info-message-{{ message.0 }}">{{ message.1 }}</div>
<div class="info-message-{{ message.0 }}">{{ message.1 }}</div>
{% endif %}
{% if has_publickey %}
@ -24,17 +40,38 @@
</script>
</div>
<div class="content">
<div class="primary">
{% if form.errors %}<div class="info-message-error">Please correct the following errors</div>{% endif %}
{% if form.errors %}<div class="info-message-error">Please correct the following errors</div>{% endif %}
<form id="privateform" action="" method="post">{% csrf_token %}
{{ form }}
<form id="feedbackform" action="" method="post">{% csrf_token %}
{{ form }}
<div class="submitrow">
<input type="submit" value="Save" name="save" />
<input type="submit" value="Save" name="save" {% if submit_disabled %}disabled="disabled"{% endif %}/>
</div>
</form>
</form>
</div>
<div>
<h3> Nominees </h3>
<table class="nominees">
{% for p in positions %}
{% if p.nomineeposition_set.accepted %}
<tr><th>{{ p.name }}:</th></tr>
{% for np in p.nomineeposition_set.accepted %}
<tr>
<td><a href="{% url nomcom_private_feedback year %}?nominee={{np.nominee.id}}&position={{ np.position.id}}">{{ np.nominee.email.person.name }}</td>
</tr>
{% endfor %}
{% endif %}
{% endfor %}
</table>
</div>
</div>
{% endif %}
{% endblock %}

View file

@ -1,5 +1,14 @@
{% extends "nomcom/nomcom_private_base.html" %}
{% block morecss %}
.content .primary {
width: 550px;
padding-right: 20px;
float: left;
display: inline;
}
{% endblock %}
{% block subtitle %} - Administration {% endblock %}
{% block nomcom_content %}
@ -29,41 +38,6 @@
<div style="padding: 8px 0 8px 0;"></div>
<div class="ietf-box diffTool">
<h2>Select Filters</h2>
<form action="" method="get">
<table>
<tr>
<td>
<label>State</label>
<select name="state">
<option value="">All</option>
{% for state in states %}
<option value="{{ state.slug }}"
{% ifequal state.slug selected_state %}selected="selected"{% endifequal%}>
{{ state.name }}
</option>
{% endfor %}
</select>
</td>
<td rowspan="2" valign="top">
<label>Position:</label>
<select name="position">
<option value="">All</option>
{% for position in positions %}
<option value="{{ position.position__id }}"
{% ifequal position.position__id selected_position %}selected="selected"{% endifequal%}>
{{ position.position__name }}
</option>
{% endfor %}
</select>
<input name="submit" value="Go!" type="submit" />
</td>
</tr>
</table>
</form>
</div>
<h2>List of nominees</h2>
@ -71,44 +45,87 @@
{% if is_chair %}(You can <a href="{% url nomcom_send_reminder_mail year %}">request confirmation</a> from nominees if they haven't
replied to the nomination notification they have received.){% endif %}</p>
{% if is_chair %}
<form id="batch-action-form" method="post" action="">{% csrf_token %}
{% if message %}
<div class="info-message-{{ message.0 }}">{{ message.1 }}</div>
{% endif %}
<div class="actions">
<label>Action:
<select name="action">
<option value="" selected="selected">---------</option>
<option value="set_as_accepted">Set as accepted</option>
<option value="set_as_pending">Set as pending</option>
<option value="set_as_declined">Set as declined</option>
</select>
</label>
<button type="submit" title="Run action">Go</button>
</div>
{% endif %}
<table class="ietf-table ietf-doctable">
<tr>
{% if is_chair %}<th>&#x2713;</th>{% endif %}
<th>Nominees</th>
<th>Position</th>
<th>State</th>
<th>Questionnaire</th>
</tr>
{% for np in nominee_positions %}
<tr class="{{ forloop.counter|divisibleby:2|yesno:"oddrow,evenrow" }}">
{% if is_chair %}
<td><input class="batch-select" type="checkbox" value="{{ np.id }}" name="selected"/></td>
<form id="batch-action-form" method="post" action="">{% csrf_token %}
{% if message %}
<div class="info-message-{{ message.0 }}">{{ message.1 }}</div>
{% endif %}
<div class="actions">
<label>Action:
<select name="action">
<option value="" selected="selected">---------</option>
<option value="set_as_accepted">Set as accepted</option>
<option value="set_as_pending">Set as pending</option>
<option value="set_as_declined">Set as declined</option>
</select>
</label>
<button type="submit" title="Run action">Go</button>
</div>
{% endif %}
<td>{{ np.nominee }}</td>
<td>{{ np.position.name }}</td>
<td>{{ np.state }}</td>
<td>{{ np.questionnaires.all|yesno:">&#x2713;,No,No" }}
</tr>
{% endfor %}
</table>
</div>
<div class="content">
<div class="primary">
<table class="ietf-table ietf-doctable">
<tr>
{% if is_chair %}<th>&#x2713;</th>{% endif %}
<th>Nominees</th>
<th>Position</th>
<th>State</th>
<th>Questionnaire</th>
</tr>
{% for np in nominee_positions %}
<tr class="{{ forloop.counter|divisibleby:2|yesno:"oddrow,evenrow" }}">
{% if is_chair %}
<td><input class="batch-select" type="checkbox" value="{{ np.id }}" name="selected"/></td>
{% endif %}
<td>{{ np.nominee }}</td>
<td>{{ np.position.name }}</td>
<td>{{ np.state }}</td>
<td>{{ np.questionnaires.all|yesno:">&#x2713;,No,No" }}
</tr>
{% endfor %}
</table>
</div>
<div>
<h3>Select Filters</h3>
<form action="" method="get">
<table>
<tr>
<td>
<label>State</label>
<select name="state">
<option value="">All</option>
{% for state in states %}
<option value="{{ state.slug }}"
{% ifequal state.slug selected_state %}selected="selected"{% endifequal%}>
{{ state.name }}
</option>
{% endfor %}
</select>
</td>
<td rowspan="2" valign="top">
<label>Position:</label>
<select name="position">
<option value="">All</option>
{% for position in positions %}
<option value="{{ position.position__id }}"
{% ifequal position.position__id selected_position %}selected="selected"{% endifequal%}>
{{ position.position__name }}
</option>
{% endfor %}
</select>
<input name="submit" value="Go!" type="submit" />
</td>
</tr>
</table>
</form>
</div>
</div
{% if is_chair %}
</form>

View file

@ -1,5 +1,20 @@
{% extends "nomcom/nomcom_public_base.html" %}
{% load nomcom_tags %}
{% block morecss %}
.content .primary {
width: 550px;
padding-right: 20px;
float: left;
display: inline;
}
table.nominees tr td {
padding-left: 15px;
}
{% endblock %}
{% block subtitle %} - Feedback{% endblock %}
{% block pagehead %}
@ -8,6 +23,9 @@
{% block nomcom_content %}
<p>Select a nominee from the list of nominees to provide input about that nominee.
(This will fill in the non-editable fields in the form).</p>
{% if message %}
<div class="info-message-{{ message.0 }}">{{ message.1 }}</div>
@ -24,17 +42,43 @@
</script>
</div>
<div class="content">
<div class="primary">
{% if form.errors %}<div class="info-message-error">Please correct the following errors</div>{% endif %}
{% if form.errors %}<div class="info-message-error">Please correct the following errors</div>{% endif %}
<form id="feedbackform" action="" method="post">{% csrf_token %}
{{ form }}
<form id="feedbackform" action="" method="post">{% csrf_token %}
{{ form }}
<div class="submitrow">
<input type="submit" value="Save" name="save" {% if submit_disabled %}disabled="disabled"{% endif %}/>
</div>
<div class="submitrow">
<input type="submit" value="Save" name="save" />
</form>
</div>
<div>
<h3> Nominees </h3>
<table class="nominees">
{% for p in positions %}
{% if p.nomineeposition_set.accepted %}
<tr><th>{{ p.name }}:</th></tr>
{% for np in p.nomineeposition_set.accepted %}
<tr>
<td>{% add_num_nominations user np.position np.nominee %}<a href="{% url nomcom_public_feedback year %}?nominee={{np.nominee.id}}&position={{ np.position.id}}">{{ np.nominee.email.person.name }}</td>
</tr>
{% endfor %}
{% endif %}
{% endfor %}
</table>
<p>An asterisk <span style="white-space: pre; color: red;">*</span> in front of a name indicates
that you have given comments on this nominee
earlier. If you position the mouse pointer over
the asterisk you should see how many comments
exist from you for this nominee.</p>
</div>
</div>
</form>
{% endif %}
{% endblock %}