Captured when nomcom members reviewed feedback for a given nominee last. Added badges when new feedback is available. Improved layout of feedback index page. Fixes #1850.

- Legacy-Id: 10535
This commit is contained in:
Robert Sparks 2015-12-02 23:25:42 +00:00
parent d65987b935
commit f566a83d1d
9 changed files with 162 additions and 14 deletions

View file

@ -1,7 +1,7 @@
import factory
import random
from ietf.nomcom.models import NomCom, Position, Nominee, NomineePosition
from ietf.nomcom.models import NomCom, Position, Feedback, Nominee, NomineePosition
from ietf.group.factories import GroupFactory
from ietf.person.factories import PersonFactory
@ -128,3 +128,17 @@ class PositionFactory(factory.DjangoModelFactory):
description = factory.Faker('paragraph',nb_sentences=4)
is_open = True
class NomineeFactory(factory.DjangoModelFactory):
class Meta:
model = Nominee
nomcom = factory.SubFactory(NomComFactory)
class FeedbackFactory(factory.DjangoModelFactory):
class Meta:
model = Feedback
nomcom = factory.SubFactory(NomComFactory)
subject = factory.Faker('sentence')
comments = factory.Faker('paragraph')
type_id = 'comment'

View file

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.db import migrations
def set_new_template_content(apps, schema_editor):

View file

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('person', '0004_auto_20150308_0440'),
('nomcom', '0006_improve_default_questionnaire_templates'),
]
operations = [
migrations.CreateModel(
name='FeedbackLastSeen',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('time', models.DateTimeField(auto_now=True)),
('nominee', models.ForeignKey(to='nomcom.Nominee')),
('reviewer', models.ForeignKey(to='person.Person')),
],
options={
},
bases=(models.Model,),
),
]

View file

@ -9,7 +9,7 @@ from django.contrib.auth.models import User
from django.template.loader import render_to_string
from ietf.nomcom.fields import EncryptedTextField
from ietf.person.models import Email
from ietf.person.models import Person,Email
from ietf.group.models import Group
from ietf.name.models import NomineePositionStateName, FeedbackTypeName
from ietf.dbtemplate.models import DBTemplate
@ -224,4 +224,7 @@ class Feedback(models.Model):
class Meta:
ordering = ['time']
class FeedbackLastSeen(models.Model):
reviewer = models.ForeignKey(Person)
nominee = models.ForeignKey(Nominee)
time = models.DateTimeField(auto_now=True)

View file

@ -145,3 +145,17 @@ class NominationResource(ModelResource):
}
api.nomcom.register(NominationResource())
from ietf.person.resources import PersonResource
class FeedbackLastSeenResource(ModelResource):
reviewer = ToOneField(PersonResource, 'reviewer')
nominee = ToOneField(NomineeResource, 'nominee')
class Meta:
queryset = FeedbackLastSeen.objects.all()
serializer = api.Serializer()
filtering = {
"id": ALL,
"time": ALL,
"reviewer": ALL_WITH_RELATIONS,
"nominee": ALL_WITH_RELATIONS,
}
api.nomcom.register(FeedbackLastSeenResource())

View file

@ -25,12 +25,13 @@ from ietf.nomcom.test_data import nomcom_test_data, generate_cert, check_comment
MEMBER_USER, SECRETARIAT_USER, EMAIL_DOMAIN, NOMCOM_YEAR
from ietf.nomcom.models import NomineePosition, Position, Nominee, \
NomineePositionStateName, Feedback, FeedbackTypeName, \
Nomination
Nomination, FeedbackLastSeen
from ietf.nomcom.forms import EditMembersForm, EditMembersFormPreview
from ietf.nomcom.utils import get_nomcom_by_year, get_or_create_nominee, get_hash_nominee_position
from ietf.nomcom.management.commands.send_reminders import Command, is_time_to_send
from ietf.nomcom.factories import NomComFactory, nomcom_kwargs_for_year, provide_private_key_to_test_client
from ietf.nomcom.factories import NomComFactory, FeedbackFactory, \
nomcom_kwargs_for_year, provide_private_key_to_test_client
from ietf.person.factories import PersonFactory
from ietf.dbtemplate.factories import DBTemplateFactory
@ -1114,3 +1115,69 @@ class InactiveNomcomTests(TestCase):
q = PyQuery(response.content)
self.assertFalse( q('#templateform') )
class FeedbackLastSeenTests(TestCase):
def setUp(self):
self.nc = NomComFactory.create(**nomcom_kwargs_for_year(group__state_id='conclude'))
self.author = PersonFactory.create().email_set.first().address
self.member = self.nc.group.role_set.filter(name='member').first().person
self.nominee = self.nc.nominee_set.first()
self.position = self.nc.position_set.first()
for type_id in ['comment','nomina','questio']:
f = FeedbackFactory.create(author=self.author,nomcom=self.nc,type_id=type_id)
f.positions.add(self.position)
f.nominees.add(self.nominee)
now = datetime.datetime.now()
self.hour_ago = now - datetime.timedelta(hours=1)
self.half_hour_ago = now - datetime.timedelta(minutes=30)
self.second_from_now = now + datetime.timedelta(seconds=1)
def test_feedback_index_badges(self):
url = reverse('nomcom_view_feedback',kwargs={'year':self.nc.year()})
login_testing_unauthorized(self, self.member.user.username, url)
provide_private_key_to_test_client(self)
response = self.client.get(url)
self.assertEqual(response.status_code,200)
q = PyQuery(response.content)
self.assertEqual( len(q('.label-success')), 3 )
f = self.nc.feedback_set.first()
f.time = self.hour_ago
f.save()
FeedbackLastSeen.objects.create(reviewer=self.member,nominee=self.nominee)
FeedbackLastSeen.objects.update(time=self.half_hour_ago)
response = self.client.get(url)
self.assertEqual(response.status_code,200)
q = PyQuery(response.content)
self.assertEqual( len(q('.label-success')), 2 )
FeedbackLastSeen.objects.update(time=self.second_from_now)
response = self.client.get(url)
self.assertEqual(response.status_code,200)
q = PyQuery(response.content)
self.assertEqual( len(q('.label-success')), 0 )
def test_feedback_nominee_badges(self):
url = reverse('nomcom_view_feedback_nominee',kwargs={'year':self.nc.year(),'nominee_id':self.nominee.id})
login_testing_unauthorized(self, self.member.user.username, url)
provide_private_key_to_test_client(self)
response = self.client.get(url)
self.assertEqual(response.status_code,200)
q = PyQuery(response.content)
self.assertEqual( len(q('.label-success')), 3 )
f = self.nc.feedback_set.first()
f.time = self.hour_ago
f.save()
FeedbackLastSeen.objects.create(reviewer=self.member,nominee=self.nominee)
FeedbackLastSeen.objects.update(time=self.half_hour_ago)
response = self.client.get(url)
self.assertEqual(response.status_code,200)
q = PyQuery(response.content)
self.assertEqual( len(q('.label-success')), 2 )
FeedbackLastSeen.objects.update(time=self.second_from_now)
response = self.client.get(url)
self.assertEqual(response.status_code,200)
q = PyQuery(response.content)
self.assertEqual( len(q('.label-success')), 0 )

View file

@ -26,7 +26,7 @@ from ietf.nomcom.forms import (NominateForm, FeedbackForm, QuestionnaireForm,
PrivateKeyForm, EditNomcomForm, EditNomineeForm,
PendingFeedbackForm, ReminderDatesForm, FullFeedbackFormSet,
FeedbackEmailForm)
from ietf.nomcom.models import Position, NomineePosition, Nominee, Feedback, NomCom, ReminderDates
from ietf.nomcom.models import Position, NomineePosition, Nominee, Feedback, NomCom, ReminderDates, FeedbackLastSeen
from ietf.nomcom.utils import (get_nomcom_by_year, store_nomcom_private_key,
get_hash_nominee_position, send_reminder_to_nominees,
HOME_TEMPLATE, NOMINEE_ACCEPT_REMINDER_TEMPLATE,NOMINEE_QUESTIONNAIRE_REMINDER_TEMPLATE)
@ -579,7 +579,18 @@ def view_feedback(request, year):
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]
last_seen = FeedbackLastSeen.objects.filter(reviewer=request.user.person,nominee=nominee).first()
nominee_feedback = []
for ft in feedback_types:
qs = nominee.feedback_set.by_type(ft.slug)
count = qs.count()
if not count:
newflag = False
elif not last_seen:
newflag = True
else:
newflag = qs.filter(time__gt=last_seen.time).exists()
nominee_feedback.append( (ft.name,count,newflag) )
nominees_feedback.update({nominee: nominee_feedback})
independent_feedback = [ft.feedback_set.get_by_nomcom(nomcom).count() for ft in independent_feedback_types]
@ -736,11 +747,19 @@ def view_feedback_nominee(request, year, nominee_id):
nominee = get_object_or_404(Nominee, id=nominee_id)
feedback_types = FeedbackTypeName.objects.filter(slug__in=settings.NOMINEE_FEEDBACK_TYPES)
last_seen = FeedbackLastSeen.objects.filter(reviewer=request.user.person,nominee=nominee).first()
last_seen_time = (last_seen and last_seen.time) or datetime.datetime(year=1,month=1,day=1)
if last_seen:
last_seen.save()
else:
FeedbackLastSeen.objects.create(reviewer=request.user.person,nominee=nominee)
return render_to_response('nomcom/view_feedback_nominee.html',
{'year': year,
'selected': 'view_feedback',
'nominee': nominee,
'feedback_types': feedback_types,
'last_seen_time' : last_seen_time,
'nomcom': nomcom}, RequestContext(request))

View file

@ -13,20 +13,24 @@
<table class="table table-condensed table-striped">
<thead>
<tr>
<th>Nominee</th>
<th class="col-sm-9">Nominee</th>
{% for ft in feedback_types %}
<th>{{ ft.name }}</th>
<th class="col-sm-1 text-center">{{ ft.name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for nominee, feedback in nominees_feedback.items %}
{% for nominee, feedback in nominees_feedback.items %}
<tr>
<td>
<a href="{% url "nomcom_view_feedback_nominee" year nominee.id %}#comment">{{ nominee }}
<a href="{% url "nomcom_view_feedback_nominee" year nominee.id %}">{{ nominee.name }}</a>
<span class="hidden-xs">&lt;{{nominee.email.address}}&gt;</span>
</td>
{% for f in feedback %}
<td>{{ f.1 }}</td>
<td class="text-right">
{% if f.2 %}<span class="label label-success">New</span>{% endif %}
{{ f.1 }}
</td>
{% endfor %}
</tr>
{% endfor %}

View file

@ -23,7 +23,7 @@
{% if feedback.type.slug == ft.slug %}
{% if forloop.first %}<p></p>{% else %}<hr>{% endif %}
<dl class="dl-horizontal">
<dt>From</dt>
<dt>{% if feedback.time > last_seen_time %}<span class="label label-success">New</span>{% endif %}From</dt>
<dd>{{ feedback.author|formatted_email|default:"Anonymous" }}
{% if ft.slug == "nomina" and feedback.nomination_set.first.share_nominator %}
<span class="bg-info"> OK to share name with nominee</span>