Pep8 for elections

This commit is contained in:
Antoine Bartuccio 2017-06-07 16:22:04 +02:00
parent 969d5699fa
commit d3fbc65cdc
4 changed files with 186 additions and 102 deletions

View File

@ -1,9 +1,7 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.conf import settings
from datetime import timedelta
from core.models import User, Group
@ -13,16 +11,26 @@ class Election(models.Model):
"""
title = models.CharField(_('title'), max_length=255)
description = models.TextField(_('description'), null=True, blank=True)
start_candidature = models.DateTimeField(_('start candidature'), blank=False)
start_candidature = models.DateTimeField(
_('start candidature'), blank=False)
end_candidature = models.DateTimeField(_('end candidature'), blank=False)
start_date = models.DateTimeField(_('start date'), blank=False)
end_date = models.DateTimeField(_('end date'), blank=False)
edit_groups = models.ManyToManyField(Group, related_name="editable_elections", verbose_name=_("edit groups"), blank=True)
view_groups = models.ManyToManyField(Group, related_name="viewable_elections", verbose_name=_("view groups"), blank=True)
vote_groups = models.ManyToManyField(Group, related_name="votable_elections", verbose_name=_("vote groups"), blank=True)
candidature_groups = models.ManyToManyField(Group, related_name="candidate_elections", verbose_name=_("candidature groups"), blank=True)
voters = models.ManyToManyField(User, verbose_name=('voters'), related_name='voted_elections')
edit_groups = models.ManyToManyField(
Group, related_name="editable_elections",
verbose_name=_("edit groups"), blank=True)
view_groups = models.ManyToManyField(
Group, related_name="viewable_elections",
verbose_name=_("view groups"), blank=True)
vote_groups = models.ManyToManyField(
Group, related_name="votable_elections",
verbose_name=_("vote groups"), blank=True)
candidature_groups = models.ManyToManyField(
Group, related_name="candidate_elections",
verbose_name=_("candidature groups"), blank=True)
voters = models.ManyToManyField(User, verbose_name=(
'voters'), related_name='voted_elections')
def __str__(self):
return self.title
@ -39,7 +47,8 @@ class Election(models.Model):
@property
def is_candidature_active(self):
now = timezone.now()
return bool(now <= self.end_candidature and now >= self.start_candidature)
return bool(now <= self.end_candidature and
now >= self.start_candidature)
@property
def is_vote_editable(self):
@ -77,7 +86,8 @@ class Role(models.Model):
"""
This class allows to create a new role avaliable for a candidature
"""
election = models.ForeignKey(Election, related_name='roles', verbose_name=_("election"))
election = models.ForeignKey(
Election, related_name='roles', verbose_name=_("election"))
title = models.CharField(_('title'), max_length=255)
description = models.TextField(_('description'), null=True, blank=True)
max_choice = models.IntegerField(_('max choice'), default=1)
@ -88,11 +98,13 @@ class Role(models.Model):
non_blank = 0
for candidature in self.candidatures.all():
cand_results = {}
cand_results['vote'] = self.votes.filter(candidature=candidature).count()
cand_results['vote'] = self.votes.filter(
candidature=candidature).count()
if total_vote == 0:
cand_results['percent'] = 0
else:
cand_results['percent'] = cand_results['vote'] * 100 / total_vote
cand_results['percent'] = cand_results[
'vote'] * 100 / total_vote
non_blank += cand_results['vote']
results[candidature.user.username] = cand_results
results['total vote'] = total_vote
@ -100,7 +112,8 @@ class Role(models.Model):
results['blank vote'] = {'vote': 0, 'percent': 0}
else:
results['blank vote'] = {'vote': total_vote - non_blank,
'percent': (total_vote - non_blank) * 100 / total_vote}
'percent': (total_vote -
non_blank) * 100 / total_vote}
return results
@property
@ -116,7 +129,8 @@ class ElectionList(models.Model):
To allow per list vote
"""
title = models.CharField(_('title'), max_length=255)
election = models.ForeignKey(Election, related_name='election_lists', verbose_name=_("election"))
election = models.ForeignKey(
Election, related_name='election_lists', verbose_name=_("election"))
def __str__(self):
return self.title
@ -126,10 +140,14 @@ class Candidature(models.Model):
"""
This class is a component of responsability
"""
role = models.ForeignKey(Role, related_name='candidatures', verbose_name=_("role"))
user = models.ForeignKey(User, verbose_name=_('user'), related_name='candidates', blank=True)
role = models.ForeignKey(
Role, related_name='candidatures', verbose_name=_("role"))
user = models.ForeignKey(User, verbose_name=_(
'user'), related_name='candidates', blank=True)
program = models.TextField(_('description'), null=True, blank=True)
election_list = models.ForeignKey(ElectionList, related_name='candidatures', verbose_name=_('election list'))
election_list = models.ForeignKey(
ElectionList, related_name='candidatures',
verbose_name=_('election list'))
def can_be_edited_by(self, user):
return (user == self.user) or user.can_edit(self.role.election)
@ -142,8 +160,10 @@ class Vote(models.Model):
"""
This class allows to vote for candidates
"""
role = models.ForeignKey(Role, related_name='votes', verbose_name=_("role"))
candidature = models.ManyToManyField(Candidature, related_name='votes', verbose_name=_("candidature"))
role = models.ForeignKey(
Role, related_name='votes', verbose_name=_("role"))
candidature = models.ManyToManyField(
Candidature, related_name='votes', verbose_name=_("candidature"))
def __str__(self):
return "Vote"

View File

@ -1,11 +1,10 @@
from django.test import Client, TestCase
from django.test import TestCase
from django.core.urlresolvers import reverse
from django.contrib.auth.models import Group
from django.core.management import call_command
from django.conf import settings
from datetime import date, datetime
from core.models import User, Group
from core.models import User
from election.models import Election
@ -15,8 +14,10 @@ class MainElection(TestCase):
self.election = Election.objects.all().first()
self.public_group = Group.objects.get(id=settings.SITH_GROUP_PUBLIC_ID)
self.subscriber_group = Group.objects.get(name=settings.SITH_MAIN_MEMBERS_GROUP)
self.ae_board_group = Group.objects.get(name=settings.SITH_MAIN_BOARD_GROUP)
self.subscriber_group = Group.objects.get(
name=settings.SITH_MAIN_MEMBERS_GROUP)
self.ae_board_group = Group.objects.get(
name=settings.SITH_MAIN_BOARD_GROUP)
self.sli = User.objects.get(username='sli')
self.subscriber = User.objects.get(username='subscriber')
self.public = User.objects.get(username='public')

View File

@ -5,14 +5,24 @@ from election.views import *
urlpatterns = [
url(r'^$', ElectionsListView.as_view(), name='list'),
url(r'^add$', ElectionCreateView.as_view(), name='create'),
url(r'^(?P<election_id>[0-9]+)/edit$', ElectionUpdateView.as_view(), name='update'),
url(r'^(?P<election_id>[0-9]+)/list/add$', ElectionListCreateView.as_view(), name='create_list'),
url(r'^(?P<election_id>[0-9]+)/role/create$', RoleCreateView.as_view(), name='create_role'),
url(r'^(?P<role_id>[0-9]+)/role/edit$', RoleUpdateView.as_view(), name='update_role'),
url(r'^(?P<role_id>[0-9]+)/role/delete$', RoleDeleteView.as_view(), name='delete_role'),
url(r'^(?P<election_id>[0-9]+)/candidate/add$', CandidatureCreateView.as_view(), name='candidate'),
url(r'^(?P<candidature_id>[0-9]+)/candidate/edit$', CandidatureUpdateView.as_view(), name='update_candidate'),
url(r'^(?P<candidature_id>[0-9]+)/candidate/delete$', CandidatureDeleteView.as_view(), name='delete_candidate'),
url(r'^(?P<election_id>[0-9]+)/vote$', VoteFormView.as_view(), name='vote'),
url(r'^(?P<election_id>[0-9]+)/detail$', ElectionDetailView.as_view(), name='detail'),
url(r'^(?P<election_id>[0-9]+)/edit$',
ElectionUpdateView.as_view(), name='update'),
url(r'^(?P<election_id>[0-9]+)/list/add$',
ElectionListCreateView.as_view(), name='create_list'),
url(r'^(?P<election_id>[0-9]+)/role/create$',
RoleCreateView.as_view(), name='create_role'),
url(r'^(?P<role_id>[0-9]+)/role/edit$',
RoleUpdateView.as_view(), name='update_role'),
url(r'^(?P<role_id>[0-9]+)/role/delete$',
RoleDeleteView.as_view(), name='delete_role'),
url(r'^(?P<election_id>[0-9]+)/candidate/add$',
CandidatureCreateView.as_view(), name='candidate'),
url(r'^(?P<candidature_id>[0-9]+)/candidate/edit$',
CandidatureUpdateView.as_view(), name='update_candidate'),
url(r'^(?P<candidature_id>[0-9]+)/candidate/delete$',
CandidatureDeleteView.as_view(), name='delete_candidate'),
url(r'^(?P<election_id>[0-9]+)/vote$',
VoteFormView.as_view(), name='vote'),
url(r'^(?P<election_id>[0-9]+)/detail$',
ElectionDetailView.as_view(), name='detail'),
]

View File

@ -1,19 +1,16 @@
from django.shortcuts import redirect, get_object_or_404
from django.views.generic import ListView, DetailView, RedirectView
from django.views.generic.edit import UpdateView, CreateView, DeleteView, FormView
from django.core.urlresolvers import reverse_lazy, reverse
from django.shortcuts import get_object_or_404
from django.views.generic import ListView, DetailView
from django.views.generic.edit import UpdateView, CreateView
from django.views.generic.edit import DeleteView, FormView
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from django.forms.models import modelform_factory
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist, ImproperlyConfigured
from django.db import DataError, transaction
from django.core.exceptions import PermissionDenied
from django.db import transaction
from django.forms import CheckboxSelectMultiple
from django.utils import timezone
from django.conf import settings
from django import forms
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
from core.views import CanViewMixin, CanEditMixin, CanCreateMixin
from django.db.models.query import QuerySet
from django.views.generic.edit import FormMixin
from core.views.forms import SelectDateTime
from election.models import Election, Role, Candidature, ElectionList, Vote
@ -27,12 +24,16 @@ class LimitedCheckboxField(forms.ModelMultipleChoiceField):
Used to replace ModelMultipleChoiceField but with
automatic backend verification
"""
def __init__(self, queryset, max_choice, required=True, widget=None, label=None,
initial=None, help_text='', *args, **kwargs):
def __init__(self, queryset, max_choice, required=True, widget=None,
label=None, initial=None, help_text='', *args, **kwargs):
self.max_choice = max_choice
widget = forms.CheckboxSelectMultiple()
super(LimitedCheckboxField, self).__init__(queryset, None, required, widget, label,
initial, help_text, *args, **kwargs)
super(LimitedCheckboxField, self).__init__(queryset,
None, required,
widget, label,
initial, help_text,
*args, **kwargs)
def clean(self, value):
qs = super(LimitedCheckboxField, self).clean(value)
@ -41,7 +42,8 @@ class LimitedCheckboxField(forms.ModelMultipleChoiceField):
def validate(self, qs):
if qs.count() > self.max_choice:
raise forms.ValidationError(_("You have selected too much candidates."), code='invalid')
raise forms.ValidationError(
_("You have selected too much candidates."), code='invalid')
# Forms
@ -56,15 +58,18 @@ class CandidateForm(forms.ModelForm):
'program': forms.Textarea
}
user = AutoCompleteSelectField('users', label=_('User to candidate'), help_text=None, required=True)
user = AutoCompleteSelectField('users', label=_(
'User to candidate'), help_text=None, required=True)
def __init__(self, *args, **kwargs):
election_id = kwargs.pop('election_id', None)
can_edit = kwargs.pop('can_edit', False)
super(CandidateForm, self).__init__(*args, **kwargs)
if election_id:
self.fields['role'].queryset = Role.objects.filter(election__id=election_id).all()
self.fields['election_list'].queryset = ElectionList.objects.filter(election__id=election_id).all()
self.fields['role'].queryset = Role.objects.filter(
election__id=election_id).all()
self.fields['election_list'].queryset \
= ElectionList.objects.filter(election__id=election_id).all()
if not can_edit:
self.fields['user'].widget = forms.HiddenInput()
@ -76,10 +81,13 @@ class VoteForm(forms.Form):
for role in election.roles.all():
cand = role.candidatures
if role.max_choice > 1:
self.fields[role.title] = LimitedCheckboxField(cand, role.max_choice, required=False)
self.fields[role.title] = LimitedCheckboxField(
cand, role.max_choice, required=False)
else:
self.fields[role.title] = forms.ModelChoiceField(cand, required=False,
widget=forms.RadioSelect(), empty_label=_("Blank vote"))
self.fields[role.title] \
= forms.ModelChoiceField(cand, required=False,
widget=forms.RadioSelect(),
empty_label=_("Blank vote"))
class RoleForm(forms.ModelForm):
@ -92,33 +100,40 @@ class RoleForm(forms.ModelForm):
election_id = kwargs.pop('election_id', None)
super(RoleForm, self).__init__(*args, **kwargs)
if election_id:
self.fields['election'].queryset = Election.objects.filter(id=election_id).all()
self.fields['election'].queryset = Election.objects.filter(
id=election_id).all()
def clean(self):
cleaned_data = super(RoleForm, self).clean()
title = cleaned_data.get('title')
election = cleaned_data.get('election')
if Role.objects.filter(title=title, election=election).exists():
raise forms.ValidationError(_("This role already exists for this election"), code='invalid')
raise forms.ValidationError(
_("This role already exists for this election"),
code='invalid')
class ElectionListForm(forms.ModelForm):
class Meta:
model = ElectionList
fields = ('title','election')
fields = ('title', 'election')
def __init__(self, *args, **kwargs):
election_id = kwargs.pop('election_id', None)
super(ElectionListForm, self).__init__(*args, **kwargs)
if election_id:
self.fields['election'].queryset = Election.objects.filter(id=election_id).all()
self.fields['election'].queryset = Election.objects.filter(
id=election_id).all()
class ElectionForm(forms.ModelForm):
class Meta:
model = Election
fields = ['title', 'description', 'start_candidature', 'end_candidature', 'start_date', 'end_date',
'edit_groups', 'view_groups', 'vote_groups', 'candidature_groups']
fields = ['title', 'description',
'start_candidature', 'end_candidature',
'start_date', 'end_date',
'edit_groups', 'view_groups',
'vote_groups', 'candidature_groups']
widgets = {
'edit_groups': CheckboxSelectMultiple,
'view_groups': CheckboxSelectMultiple,
@ -127,10 +142,18 @@ class ElectionForm(forms.ModelForm):
'candidature_groups': CheckboxSelectMultiple
}
start_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Start date"), widget=SelectDateTime, required=True)
end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), widget=SelectDateTime, required=True)
start_candidature = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Start candidature"), widget=SelectDateTime, required=True)
end_candidature = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End candidature"), widget=SelectDateTime, required=True)
start_date = forms.DateTimeField(
['%Y-%m-%d %H:%M:%S'], label=_("Start date"),
widget=SelectDateTime, required=True)
end_date = forms.DateTimeField(
['%Y-%m-%d %H:%M:%S'], label=_("End date"),
widget=SelectDateTime, required=True)
start_candidature = forms.DateTimeField(
['%Y-%m-%d %H:%M:%S'], label=_("Start candidature"),
widget=SelectDateTime, required=True)
end_candidature = forms.DateTimeField(
['%Y-%m-%d %H:%M:%S'], label=_("End candidature"),
widget=SelectDateTime, required=True)
# Display elections
@ -179,7 +202,8 @@ class VoteFormView(CanCreateMixin, FormView):
# If we have a multiple choice field
if isinstance(election_data[role_title], QuerySet):
if election_data[role_title].count() > 0:
vote = Vote(role=election_data[role_title].first().role)
vote = Vote(role=election_data[
role_title].first().role)
vote.save()
for el in election_data[role_title]:
vote.candidature.add(el)
@ -209,7 +233,8 @@ class VoteFormView(CanCreateMixin, FormView):
return res
def get_success_url(self, **kwargs):
return reverse_lazy('election:detail', kwargs={'election_id': self.election.id})
return reverse_lazy('election:detail',
kwargs={'election_id': self.election.id})
def get_context_data(self, **kwargs):
""" Add additionnal data to the template """
@ -232,7 +257,8 @@ class CandidatureCreateView(CanCreateMixin, CreateView):
def dispatch(self, request, *arg, **kwargs):
self.election = get_object_or_404(Election, pk=kwargs['election_id'])
return super(CandidatureCreateView, self).dispatch(request, *arg, **kwargs)
return super(CandidatureCreateView,
self).dispatch(request, *arg, **kwargs)
def get_initial(self):
init = {}
@ -252,7 +278,8 @@ class CandidatureCreateView(CanCreateMixin, CreateView):
"""
obj = form.instance
obj.election = Election.objects.get(id=self.election.id)
if(obj.election.can_candidate(obj.user)) and (obj.user == self.request.user or self.can_edit):
if(obj.election.can_candidate(obj.user)) and \
(obj.user == self.request.user or self.can_edit):
return super(CreateView, self).form_valid(form)
raise PermissionDenied
@ -262,7 +289,8 @@ class CandidatureCreateView(CanCreateMixin, CreateView):
return kwargs
def get_success_url(self, **kwargs):
return reverse_lazy('election:detail', kwargs={'election_id': self.election.id})
return reverse_lazy('election:detail',
kwargs={'election_id': self.election.id})
class ElectionCreateView(CanCreateMixin, CreateView):
@ -273,16 +301,19 @@ class ElectionCreateView(CanCreateMixin, CreateView):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_subscribed:
raise PermissionDenied
return super(ElectionCreateView, self).dispatch(request, *args, **kwargs)
return super(ElectionCreateView,
self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
"""
Allow every users that had passed the dispatch to create an election
Allow every users that had passed the dispatch
to create an election
"""
return super(CreateView, self).form_valid(form)
def get_success_url(self, **kwargs):
return reverse_lazy('election:detail', kwargs={'election_id': self.object.id})
return reverse_lazy('election:detail',
kwargs={'election_id': self.object.id})
class RoleCreateView(CanCreateMixin, CreateView):
@ -318,7 +349,8 @@ class RoleCreateView(CanCreateMixin, CreateView):
return kwargs
def get_success_url(self, **kwargs):
return reverse_lazy('election:detail', kwargs={'election_id': self.object.election.id})
return reverse_lazy('election:detail',
kwargs={'election_id': self.object.election.id})
class ElectionListCreateView(CanCreateMixin, CreateView):
@ -330,7 +362,8 @@ class ElectionListCreateView(CanCreateMixin, CreateView):
self.election = get_object_or_404(Election, pk=kwargs['election_id'])
if not self.election.is_vote_editable:
raise PermissionDenied
return super(ElectionListCreateView, self).dispatch(request, *arg, **kwargs)
return super(ElectionListCreateView,
self).dispatch(request, *arg, **kwargs)
def get_initial(self):
init = {}
@ -357,7 +390,8 @@ class ElectionListCreateView(CanCreateMixin, CreateView):
raise PermissionDenied
def get_success_url(self, **kwargs):
return reverse_lazy('election:detail', kwargs={'election_id': self.object.election.id})
return reverse_lazy('election:detail',
kwargs={'election_id': self.object.election.id})
# Update view
@ -371,21 +405,30 @@ class ElectionUpdateView(CanEditMixin, UpdateView):
def get_initial(self):
init = {}
try:
init['start_date'] = self.object.start_date.strftime('%Y-%m-%d %H:%M:%S')
except:pass
init['start_date'] = self.object.start_date.strftime(
'%Y-%m-%d %H:%M:%S')
except Exception:
pass
try:
init['end_date'] = self.object.end_date.strftime('%Y-%m-%d %H:%M:%S')
except:pass
init['end_date'] = self.object.end_date.strftime(
'%Y-%m-%d %H:%M:%S')
except Exception:
pass
try:
init['start_candidature'] = self.object.start_candidature.strftime('%Y-%m-%d %H:%M:%S')
except:pass
init['start_candidature'] = self.object.start_candidature.strftime(
'%Y-%m-%d %H:%M:%S')
except Exception:
pass
try:
init['end_candidature'] = self.object.end_candidature.strftime('%Y-%m-%d %H:%M:%S')
except:pass
init['end_candidature'] = self.object.end_candidature.strftime(
'%Y-%m-%d %H:%M:%S')
except Exception:
pass
return init
def get_success_url(self, **kwargs):
return reverse_lazy('election:detail', kwargs={'election_id': self.object.id})
return reverse_lazy('election:detail',
kwargs={'election_id': self.object.id})
class CandidatureUpdateView(CanEditMixin, UpdateView):
@ -398,7 +441,8 @@ class CandidatureUpdateView(CanEditMixin, UpdateView):
self.object = self.get_object()
if not self.object.role.election.is_vote_editable:
raise PermissionDenied
return super(CandidatureUpdateView, self).dispatch(request, *arg, **kwargs)
return super(CandidatureUpdateView,
self).dispatch(request, *arg, **kwargs)
def remove_fields(self):
self.form.fields.pop('role', None)
@ -411,7 +455,8 @@ class CandidatureUpdateView(CanEditMixin, UpdateView):
def post(self, request, *args, **kwargs):
self.form = self.get_form()
self.remove_fields()
if request.user.is_authenticated() and request.user.can_edit(self.object) and self.form.is_valid():
if request.user.is_authenticated() and \
request.user.can_edit(self.object) and self.form.is_valid():
return super(CandidatureUpdateView, self).form_valid(self.form)
return self.form_invalid(self.form)
@ -421,7 +466,9 @@ class CandidatureUpdateView(CanEditMixin, UpdateView):
return kwargs
def get_success_url(self, **kwargs):
return reverse_lazy('election:detail', kwargs={'election_id': self.object.role.election.id})
return reverse_lazy('election:detail',
kwargs={'election_id':
self.object.role.election.id})
class RoleUpdateView(CanEditMixin, UpdateView):
@ -449,7 +496,8 @@ class RoleUpdateView(CanEditMixin, UpdateView):
self.object = self.get_object()
self.form = self.get_form()
self.remove_fields()
if request.user.is_authenticated() and request.user.can_edit(self.object) and self.form.is_valid():
if request.user.is_authenticated() and \
request.user.can_edit(self.object) and self.form.is_valid():
return super(RoleUpdateView, self).form_valid(self.form)
return self.form_invalid(self.form)
@ -459,7 +507,8 @@ class RoleUpdateView(CanEditMixin, UpdateView):
return kwargs
def get_success_url(self, **kwargs):
return reverse_lazy('election:detail', kwargs={'election_id': self.object.election.id})
return reverse_lazy('election:detail',
kwargs={'election_id': self.object.election.id})
# Delete Views
@ -472,12 +521,15 @@ class CandidatureDeleteView(CanEditMixin, DeleteView):
def dispatch(self, request, *arg, **kwargs):
self.object = self.get_object()
self.election = self.object.role.election
if not self.election.can_candidate or not self.election.is_vote_editable:
if not self.election.can_candidate \
or not self.election.is_vote_editable:
raise PermissionDenied
return super(CandidatureDeleteView, self).dispatch(request, *arg, **kwargs)
return super(CandidatureDeleteView,
self).dispatch(request, *arg, **kwargs)
def get_success_url(self, **kwargs):
return reverse_lazy('election:detail', kwargs={'election_id': self.election.id})
return reverse_lazy('election:detail',
kwargs={'election_id': self.election.id})
class RoleDeleteView(CanEditMixin, DeleteView):
@ -493,4 +545,5 @@ class RoleDeleteView(CanEditMixin, DeleteView):
return super(RoleDeleteView, self).dispatch(request, *arg, **kwargs)
def get_success_url(self, **kwargs):
return reverse_lazy('election:detail', kwargs={'election_id': self.election.id})
return reverse_lazy('election:detail',
kwargs={'election_id': self.election.id})