Skip to content

Commit bf0747a

Browse files
committed
Add duplicated field for nominees, it is necesary when chair marks duplicate nominations
Change questionnaires field in NomineePosition model to manytomany, becouse it is necessary whe chair marks duplicate nominations. Add timestamp in NomineePosition objects to see wich nomineeposition object is the last one. Better merge nominations, update questionnaires and state of primary nomineposition object See ietf-tools#930 - Legacy-Id: 5187
1 parent 69a93c9 commit bf0747a

File tree

5 files changed

+71
-28
lines changed

5 files changed

+71
-28
lines changed

ietf/nomcom/forms.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ def __init__(self, *args, **kwargs):
203203
def clean_primary_email(self):
204204
email = self.cleaned_data['primary_email']
205205
nominees = Nominee.objects.filter(email__address=email,
206-
nomine_position__nomcom=self.nomcom)
206+
nominee_position__nomcom=self.nomcom)
207207
if not nominees:
208208
msg = "Does not exist a nomiee with this email"
209209
self._errors["primary_email"] = self.error_class([msg])
@@ -215,7 +215,7 @@ def clean_secondary_emails(self):
215215
emails = get_list(data)
216216
for email in emails:
217217
nominees = Nominee.objects.filter(email__address=email,
218-
nomine_position__nomcom=self.nomcom)
218+
nominee_position__nomcom=self.nomcom)
219219
if not nominees:
220220
msg = "Does not exist a nomiee with email %s" % email
221221
self._errors["primary_email"] = self.error_class([msg])
@@ -237,23 +237,40 @@ def save(self):
237237
secondary_emails = get_list(self.cleaned_data.get("secondary_emails"))
238238

239239
primary_nominee = Nominee.objects.get(email__address=primary_email,
240-
nomine_position__nomcom=self.nomcom)
240+
nominee_position__nomcom=self.nomcom)
241241
secondary_nominees = Nominee.objects.filter(email__address__in=secondary_emails,
242-
nomine_position__nomcom=self.nomcom)
242+
nominee_position__nomcom=self.nomcom)
243243
for nominee in secondary_nominees:
244244
# move nominations
245245
nominee.nomination_set.all().update(nominee=primary_nominee)
246246
# move feedback
247247
nominee.feedback_set.all().update(nominee=primary_nominee)
248248
# move nomineepositions
249249
for nominee_position in nominee.nomineeposition_set.all():
250-
if not NomineePosition.objects.filter(position=nominee_position.position,
251-
nominee=primary_nominee):
252-
250+
primary_nominee_positions = NomineePosition.objects.filter(position=nominee_position.position,
251+
nominee=primary_nominee)
252+
primary_nominee_position = primary_nominee_positions and primary_nominee_positions[0] or None
253+
254+
if primary_nominee_position:
255+
# if already a nomineeposition object for a position and nominee,
256+
# update the nomineepostion of primary nominee with the state and questionnaire
257+
if nominee_position.time > primary_nominee_position.time:
258+
primary_nominee_position.state = nominee_position.state
259+
primary_nominee_position.save()
260+
questionnaires = nominee_position.questionnaires.all()
261+
if questionnaires:
262+
primary_nominee_position.questionnaires.add(*questionnaires)
263+
264+
else:
265+
# It is not allowed two or more nomineeposition objects with same position and nominee
266+
# move nominee_position object to primary nominee
253267
nominee_position.nominee = primary_nominee
254268
nominee_position.save()
255269

256-
secondary_nominees.delete()
270+
nominee.duplicated = primary_nominee
271+
nominee.save()
272+
273+
secondary_nominees.update(duplicated=primary_nominee)
257274

258275

259276
class NominateForm(BaseNomcomForm, forms.ModelForm):

ietf/nomcom/migrations/0001_initial.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# encoding: utf-8
2+
import datetime
23
from south.db import db
34
from south.v2 import SchemaMigration
45
from django.db import models
56

6-
77
class Migration(SchemaMigration):
88

99
def forwards(self, orm):
10-
10+
1111
# Adding model 'NomCom'
1212
db.create_table('nomcom_nomcom', (
1313
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
@@ -34,6 +34,7 @@ def forwards(self, orm):
3434
db.create_table('nomcom_nominee', (
3535
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
3636
('email', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['person.Email'])),
37+
('duplicated', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['nomcom.Nominee'], null=True, blank=True)),
3738
))
3839
db.send_create_signal('nomcom', ['Nominee'])
3940

@@ -43,13 +44,21 @@ def forwards(self, orm):
4344
('position', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['nomcom.Position'])),
4445
('nominee', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['nomcom.Nominee'])),
4546
('state', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.NomineePositionState'])),
46-
('questionnaire', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='questionnaire', null=True, to=orm['nomcom.Feedback'])),
47+
('time', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
4748
))
4849
db.send_create_signal('nomcom', ['NomineePosition'])
4950

5051
# Adding unique constraint on 'NomineePosition', fields ['position', 'nominee']
5152
db.create_unique('nomcom_nomineeposition', ['position_id', 'nominee_id'])
5253

54+
# Adding M2M table for field questionnaires on 'NomineePosition'
55+
db.create_table('nomcom_nomineeposition_questionnaires', (
56+
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
57+
('nomineeposition', models.ForeignKey(orm['nomcom.nomineeposition'], null=False)),
58+
('feedback', models.ForeignKey(orm['nomcom.feedback'], null=False))
59+
))
60+
db.create_unique('nomcom_nomineeposition_questionnaires', ['nomineeposition_id', 'feedback_id'])
61+
5362
# Adding M2M table for field feedback on 'NomineePosition'
5463
db.create_table('nomcom_nomineeposition_feedback', (
5564
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
@@ -75,7 +84,7 @@ def forwards(self, orm):
7584
# Adding model 'Feedback'
7685
db.create_table('nomcom_feedback', (
7786
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
78-
('author', self.gf('django.db.models.fields.EmailField')(max_length=75)),
87+
('author', self.gf('django.db.models.fields.EmailField')(max_length=75, blank=True)),
7988
('position', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['nomcom.Position'])),
8089
('nominee', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['nomcom.Nominee'])),
8190
('comments', self.gf('ietf.nomcom.fields.EncryptedTextField')()),
@@ -84,8 +93,9 @@ def forwards(self, orm):
8493
))
8594
db.send_create_signal('nomcom', ['Feedback'])
8695

87-
def backwards(self, orm):
8896

97+
def backwards(self, orm):
98+
8999
# Removing unique constraint on 'NomineePosition', fields ['position', 'nominee']
90100
db.delete_unique('nomcom_nomineeposition', ['position_id', 'nominee_id'])
91101

@@ -101,6 +111,9 @@ def backwards(self, orm):
101111
# Deleting model 'NomineePosition'
102112
db.delete_table('nomcom_nomineeposition')
103113

114+
# Removing M2M table for field questionnaires on 'NomineePosition'
115+
db.delete_table('nomcom_nomineeposition_questionnaires')
116+
104117
# Removing M2M table for field feedback on 'NomineePosition'
105118
db.delete_table('nomcom_nomineeposition_feedback')
106119

@@ -110,6 +123,7 @@ def backwards(self, orm):
110123
# Deleting model 'Feedback'
111124
db.delete_table('nomcom_feedback')
112125

126+
113127
models = {
114128
'auth.group': {
115129
'Meta': {'object_name': 'Group'},
@@ -327,7 +341,7 @@ def backwards(self, orm):
327341
},
328342
'nomcom.feedback': {
329343
'Meta': {'object_name': 'Feedback'},
330-
'author': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
344+
'author': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
331345
'comments': ('ietf.nomcom.fields.EncryptedTextField', [], {}),
332346
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
333347
'nominee': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['nomcom.Nominee']"}),
@@ -355,18 +369,20 @@ def backwards(self, orm):
355369
},
356370
'nomcom.nominee': {
357371
'Meta': {'object_name': 'Nominee'},
372+
'duplicated': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['nomcom.Nominee']", 'null': 'True', 'blank': 'True'}),
358373
'email': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
359374
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
360-
'nomine_position': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['nomcom.Position']", 'through': "orm['nomcom.NomineePosition']", 'symmetrical': 'False'})
375+
'nominee_position': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['nomcom.Position']", 'through': "orm['nomcom.NomineePosition']", 'symmetrical': 'False'})
361376
},
362377
'nomcom.nomineeposition': {
363378
'Meta': {'unique_together': "(('position', 'nominee'),)", 'object_name': 'NomineePosition'},
364379
'feedback': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['nomcom.Feedback']", 'null': 'True', 'blank': 'True'}),
365380
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
366381
'nominee': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['nomcom.Nominee']"}),
367382
'position': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['nomcom.Position']"}),
368-
'questionnaire': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'questionnaire'", 'null': 'True', 'to': "orm['nomcom.Feedback']"}),
369-
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.NomineePositionState']"})
383+
'questionnaires': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'questionnaires'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['nomcom.Feedback']"}),
384+
'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.NomineePositionState']"}),
385+
'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
370386
},
371387
'nomcom.position': {
372388
'Meta': {'object_name': 'Position'},

ietf/nomcom/models.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ def __unicode__(self):
6262
class Nominee(models.Model):
6363

6464
email = models.ForeignKey(Email)
65-
nomine_position = models.ManyToManyField('Position', through='NomineePosition')
65+
nominee_position = models.ManyToManyField('Position', through='NomineePosition')
66+
duplicated = models.ForeignKey('Nominee', blank=True, null=True)
6667

6768
class Meta:
6869
verbose_name_plural = 'Nominees'
@@ -76,10 +77,11 @@ class NomineePosition(models.Model):
7677
position = models.ForeignKey('Position')
7778
nominee = models.ForeignKey('Nominee')
7879
state = models.ForeignKey(NomineePositionState)
79-
questionnaire = models.ForeignKey('Feedback',
80-
related_name='questionnaire',
81-
blank=True, null=True)
80+
questionnaires = models.ManyToManyField('Feedback',
81+
related_name='questionnaires',
82+
blank=True, null=True)
8283
feedback = models.ManyToManyField('Feedback', blank=True, null=True)
84+
time = models.DateTimeField(auto_now_add=True)
8385

8486
class Meta:
8587
verbose_name = 'Nominee position'

ietf/nomcom/test_data.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33

44
from django.contrib.auth.models import User
5+
from django.core.files import File
56
from django.conf import settings
67

78
from ietf.utils.pipe import pipe
@@ -15,6 +16,7 @@
1516
MEMBER_USER = 'member'
1617
SECRETARIAT_USER = 'secretariat'
1718
EMAIL_DOMAIN = '@example.com'
19+
NOMCOM_YEAR = "2013"
1820

1921
USERS = [COMMUNITY_USER, CHAIR_USER, MEMBER_USER, SECRETARIAT_USER]
2022

@@ -98,10 +100,13 @@ def nomcom_test_data():
98100
group, created = Group.objects.get_or_create(name='IAB/IESG Nominating Committee 2013/2014',
99101
state_id='active',
100102
type_id='nomcom',
101-
acronym='nomcom2013')
103+
acronym='nomcom%s' % NOMCOM_YEAR)
102104
nomcom, created = NomCom.objects.get_or_create(group=group)
103105

104-
secretariat, created = Group.objects.get_or_create(name="Secretariat",
106+
cert_file, privatekey_file = generate_cert()
107+
nomcom.public_key.save('cert', File(open(cert_file.name, 'r')))
108+
109+
secretariat, created = Group.objects.get_or_create(name=u'IETF Secretariat',
105110
acronym="secretariat",
106111
state_id="active",
107112
type_id="ietf",

ietf/nomcom/tests.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from ietf.nomcom.test_data import nomcom_test_data, generate_cert, check_comments, \
1616
COMMUNITY_USER, CHAIR_USER, \
17-
MEMBER_USER, SECRETARIAT_USER, EMAIL_DOMAIN
17+
MEMBER_USER, SECRETARIAT_USER, EMAIL_DOMAIN, NOMCOM_YEAR
1818
from ietf.nomcom.models import NomineePosition, Position, Nominee, \
1919
NomineePositionState, Feedback, FeedbackType, \
2020
Nomination
@@ -33,7 +33,7 @@ def check_url_status(self, url, status):
3333
def setUp(self):
3434
nomcom_test_data()
3535
self.cert_file, self.privatekey_file = generate_cert()
36-
self.year = 2013
36+
self.year = NOMCOM_YEAR
3737

3838
# private urls
3939
self.private_index_url = reverse('nomcom_private_index', kwargs={'year': self.year})
@@ -133,9 +133,12 @@ def test_private_merge_view(self):
133133
self.assertEqual(response.status_code, 200)
134134
self.assertContains(response, "info-message-success")
135135

136-
self.assertEqual(Nominee.objects.filter(email__address='nominee2@example.com').count(), 0)
137-
self.assertEqual(Nominee.objects.filter(email__address='nominee3@example.com').count(), 0)
138-
self.assertEqual(Nominee.objects.filter(email__address='nominee4@example.com').count(), 0)
136+
self.assertEqual(Nominee.objects.filter(email__address='nominee2@example.com',
137+
duplicated__isnull=False).count(), 1)
138+
self.assertEqual(Nominee.objects.filter(email__address='nominee3@example.com',
139+
duplicated__isnull=False).count(), 1)
140+
self.assertEqual(Nominee.objects.filter(email__address='nominee4@example.com',
141+
duplicated__isnull=False).count(), 1)
139142

140143
nominee = Nominee.objects.get(email__address='nominee@example.com')
141144
self.assertEqual(Nomination.objects.filter(nominee=nominee).count(), 4)

0 commit comments

Comments
 (0)