Merge pull request #1802 from normanjaeckel/Blocked

Removed possibility to block candidates. Fixes #1708.
This commit is contained in:
Norman Jäckel 2016-01-09 18:10:19 +01:00
commit 1f72ef4099
6 changed files with 38 additions and 55 deletions

View File

@ -15,6 +15,7 @@ Agenda:
- Removed mptt.
Assignments:
- Renamed app from assignment to assignments.
- Removed possibility to block candidates.
- Massive refactoring and cleanup of the app.
Motions:
- Renamed app from motion to motions.

View File

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assignments', '0004_auto_20160109_1329'),
]
operations = [
migrations.RemoveField(
model_name='assignmentrelateduser',
name='status',
),
migrations.AddField(
model_name='assignmentrelateduser',
name='elected',
field=models.BooleanField(default=False),
),
]

View File

@ -24,15 +24,6 @@ class AssignmentRelatedUser(RESTModelMixin, models.Model):
"""
Many to Many table between an assignment and user.
"""
STATUS_CANDIDATE = 1
STATUS_ELECTED = 2
STATUS_BLOCKED = 3
STATUSES = (
(STATUS_CANDIDATE, ugettext_lazy('candidate')),
(STATUS_ELECTED, ugettext_lazy('elected')),
(STATUS_BLOCKED, ugettext_lazy('blocked')),
)
assignment = models.ForeignKey(
'Assignment',
on_delete=models.CASCADE,
@ -40,9 +31,7 @@ class AssignmentRelatedUser(RESTModelMixin, models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
status = models.IntegerField(
choices=STATUSES,
default=STATUS_CANDIDATE)
elected = models.BooleanField(default=False)
class Meta:
default_permissions = ()
@ -106,9 +95,9 @@ class Assignment(RESTModelMixin, models.Model):
settings.AUTH_USER_MODEL,
through='AssignmentRelatedUser')
"""
Users that are candidates, elected or blocked as candidate.
Users that are candidates or elected.
See AssignmentRelatedUser for more infos.
See AssignmentRelatedUser for more information.
"""
tags = models.ManyToManyField(Tag, blank=True)
@ -145,7 +134,7 @@ class Assignment(RESTModelMixin, models.Model):
Queryset that represents the candidates for the assignment.
"""
return self.related_users.filter(
assignmentrelateduser__status=AssignmentRelatedUser.STATUS_CANDIDATE)
assignmentrelateduser__elected=False)
@property
def elected(self):
@ -153,15 +142,7 @@ class Assignment(RESTModelMixin, models.Model):
Queryset that represents all elected users for the assignment.
"""
return self.related_users.filter(
assignmentrelateduser__status=AssignmentRelatedUser.STATUS_ELECTED)
@property
def blocked(self):
"""
Queryset that represents all blocked users for the assignment.
"""
return self.related_users.filter(
assignmentrelateduser__status=AssignmentRelatedUser.STATUS_BLOCKED)
assignmentrelateduser__elected=True)
def is_candidate(self, user):
"""
@ -179,21 +160,13 @@ class Assignment(RESTModelMixin, models.Model):
"""
return self.elected.filter(pk=user.pk).exists()
def is_blocked(self, user):
"""
Returns True if the user is blocked for candidature.
Costs one database query.
"""
return self.blocked.filter(pk=user.pk).exists()
def set_candidate(self, user):
"""
Adds the user as candidate.
"""
related_user, __ = self.assignment_related_users.update_or_create(
user=user,
defaults={'status': AssignmentRelatedUser.STATUS_CANDIDATE})
defaults={'elected': False})
def set_elected(self, user):
"""
@ -201,15 +174,7 @@ class Assignment(RESTModelMixin, models.Model):
"""
related_user, __ = self.assignment_related_users.update_or_create(
user=user,
defaults={'status': AssignmentRelatedUser.STATUS_ELECTED})
def set_blocked(self, user):
"""
Block user from this assignment, so he can not get an candidate.
"""
related_user, __ = self.assignment_related_users.update_or_create(
user=user,
defaults={'status': AssignmentRelatedUser.STATUS_BLOCKED})
defaults={'elected': True})
def delete_related_user(self, user):
"""

View File

@ -29,7 +29,7 @@ class AssignmentRelatedUserSerializer(ModelSerializer):
fields = (
'id',
'user',
'status',
'elected',
'assignment') # js-data needs the assignment-id in the nested object to define relations.

View File

@ -107,7 +107,7 @@ class AssignmentViewSet(ModelViewSet):
return _('You were nominated successfully.')
def withdraw_self(self, request, assignment):
# Withdraw candidature and set self blocked.
# Withdraw candidature.
if assignment.phase == assignment.PHASE_FINISHED:
raise ValidationError({'detail': _('You can not withdraw your candidature to this election because it is finished.')})
if assignment.phase == assignment.PHASE_VOTING and not request.user.has_perm('assignments.can_manage'):
@ -115,16 +115,14 @@ class AssignmentViewSet(ModelViewSet):
self.permission_denied(request)
if not assignment.is_candidate(request.user):
raise ValidationError({'detail': _('You are not a candidate of this election.')})
assignment.set_blocked(request.user)
return _(
'You have withdrawn your candidature successfully. '
'You can not be nominated by other participants anymore.')
assignment.delete_related_user(request.user)
return _('You have withdrawn your candidature successfully.')
def get_user_from_request_data(self, request):
"""
Helper method to get a specific user from request data (not the
request.user) so that the views self.candidature_other or
self.mark_elected can play with him.
self.mark_elected can play with it.
"""
if not isinstance(request.data, dict):
detail = _('Invalid data. Expected dictionary, got %s.') % type(request.data)
@ -165,8 +163,6 @@ class AssignmentViewSet(ModelViewSet):
# To nominate another user during voting you have to be a manager.
self.permission_denied(request)
if not request.user.has_perm('assignments.can_manage'):
if assignment.is_blocked(user):
raise ValidationError({'detail': _('User %s does not want to be a candidate. Only a manager can do this.') % user})
if assignment.is_elected(user):
raise ValidationError({'detail': _('User %s is already elected.') % user})
if assignment.is_candidate(user):
@ -181,10 +177,10 @@ class AssignmentViewSet(ModelViewSet):
if assignment.phase == assignment.PHASE_FINISHED:
detail = _('You can not delete someones candidature to this election because it is finished.')
raise ValidationError({'detail': detail})
if not assignment.is_candidate(user) and not assignment.is_blocked(user):
if not assignment.is_candidate(user):
raise ValidationError({'detail': _('User %s has no status in this election.') % user})
assignment.delete_related_user(user)
return _('Candidate %s was withdrawn/unblocked successfully.') % user
return _('Candidate %s was withdrawn successfully.') % user
@detail_route(methods=['post', 'delete'])
def mark_elected(self, request, pk=None):

View File

@ -65,7 +65,6 @@ class CanidatureSelf(TestCase):
self.assertEqual(response.status_code, 200)
self.assertFalse(Assignment.objects.get(pk=self.assignment.pk).candidates.filter(username='admin').exists())
self.assertTrue(Assignment.objects.get(pk=self.assignment.pk).blocked.filter(username='admin').exists())
def test_withdraw_self_twice(self):
response = self.client.delete(reverse('assignment-candidature-self', args=[self.assignment.pk]))
@ -90,7 +89,6 @@ class CanidatureSelf(TestCase):
self.assertEqual(response.status_code, 200)
self.assertFalse(Assignment.objects.get(pk=self.assignment.pk).candidates.exists())
self.assertTrue(Assignment.objects.get(pk=self.assignment.pk).blocked.filter(username='admin').exists())
def test_withdraw_self_during_voting_non_admin(self):
self.assignment.set_candidate(get_user_model().objects.get(username='admin'))