views.py 17.4 KB
Newer Older
Sli's avatar
Sli committed
1
from django.shortcuts import redirect, get_object_or_404
Sli's avatar
Sli committed
2
3
4
5
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.utils.translation import ugettext_lazy as _
6
from django.forms.models import modelform_factory
7
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist, ImproperlyConfigured
8
from django.forms import CheckboxSelectMultiple
Sli's avatar
Sli committed
9
from django.utils import timezone
Sli's avatar
Sli committed
10
from django.conf import settings
11
from django import forms
Sli's avatar
Sli committed
12
13

from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
Sli's avatar
Sli committed
14
from django.db.models.query import QuerySet
15
from django.views.generic.edit import FormMixin
16
from core.views.forms import SelectDateTime
Sli's avatar
Sli committed
17
from election.models import Election, Role, Candidature, ElectionList, Vote
Sli's avatar
Sli committed
18

19
20
from ajax_select.fields import AutoCompleteSelectField

21

Sli's avatar
Sli committed
22
23
# Custom form field

Sli's avatar
Sli committed
24
class LimitedCheckboxField(forms.ModelMultipleChoiceField):
Sli's avatar
Sli committed
25
26
27
28
29
30
31
    """
        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):
        self.max_choice = max_choice
Sli's avatar
Sli committed
32
        widget = forms.CheckboxSelectMultiple()
Sli's avatar
Sli committed
33
        super(LimitedCheckboxField, self).__init__(queryset, None, required, widget, label,
Sli's avatar
Sli committed
34
                                                   initial, help_text, *args, **kwargs)
Sli's avatar
Sli committed
35
36

    def clean(self, value):
Sli's avatar
Sli committed
37
        qs = super(LimitedCheckboxField, self).clean(value)
Sli's avatar
Sli committed
38
        self.validate(qs)
Sli's avatar
Sli committed
39
        return qs
Sli's avatar
Sli committed
40
41
42

    def validate(self, qs):
        if qs.count() > self.max_choice:
43
            raise forms.ValidationError(_("You have selected too much candidates."), code='invalid')
Sli's avatar
Sli committed
44
45


46
47
48
# Forms


Sli's avatar
Sli committed
49
class CandidateForm(forms.ModelForm):
50
    """ Form to candidate """
Sli's avatar
Sli committed
51
52
53
54
55
56
57
    class Meta:
        model = Candidature
        fields = ['user', 'role', 'program', 'election_list']
        widgets = {
            'program': forms.Textarea
        }

58
59
    user = AutoCompleteSelectField('users', label=_('User to candidate'), help_text=None, required=True)

Sli's avatar
Sli committed
60
61
62
    def __init__(self, *args, **kwargs):
        election_id = kwargs.pop('election_id', None)
        can_edit = kwargs.pop('can_edit', False)
63
        super(CandidateForm, self).__init__(*args, **kwargs)
Sli's avatar
Sli committed
64
65
66
67
68
        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()
        if not can_edit:
            self.fields['user'].widget = forms.HiddenInput()
69

70
71

class VoteForm(forms.Form):
Sli's avatar
Sli committed
72
    def __init__(self, election, user, *args, **kwargs):
73
        super(VoteForm, self).__init__(*args, **kwargs)
Sli's avatar
Sli committed
74
75
        if not election.has_voted(user):
            for role in election.roles.all():
Sli's avatar
Sli committed
76
                cand = role.candidatures
Sli's avatar
Sli committed
77
                if role.max_choice > 1:
Sli's avatar
Sli committed
78
                    self.fields[role.title] = LimitedCheckboxField(cand, role.max_choice, required=False)
Sli's avatar
Sli committed
79
80
                else:
                    self.fields[role.title] = forms.ModelChoiceField(cand, required=False,
Sli's avatar
Sli committed
81
82
83
84
85
86
87
88
89
                                                                     widget=forms.RadioSelect(), empty_label=_("Blank vote"))


class RoleForm(forms.ModelForm):
    """ Form for creating a role """
    class Meta:
        model = Role
        fields = ['title', 'election', 'description', 'max_choice']

Sli's avatar
Sli committed
90
91
92
93
94
95
    def __init__(self, *args, **kwargs):
        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()

Sli's avatar
Sli committed
96
97
98
99
100
101
    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')
Sli's avatar
Sli committed
102

103

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
class ElectionListForm(forms.ModelForm):
    class Meta:
        model = ElectionList
        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()


class ElectionForm(forms.ModelForm):
    class Meta:
        model = Election
        fields = ['title', 'description', 'start_candidature', 'end_candidature', 'start_date', 'end_date',
Sli's avatar
Sli committed
120
                  'edit_groups', 'view_groups', 'vote_groups', 'candidature_groups']
121
122
123
124
125
        widgets = {
            'edit_groups': CheckboxSelectMultiple,
            'view_groups': CheckboxSelectMultiple,
            'edit_groups': CheckboxSelectMultiple,
            'vote_groups': CheckboxSelectMultiple,
Sli's avatar
Sli committed
126
            'candidature_groups': CheckboxSelectMultiple
127
128
        }

Sli's avatar
Sli committed
129
130
131
132
133
    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)

134

Sli's avatar
Sli committed
135
136
137
138
139
140
141
142
143
# Display elections


class ElectionsListView(CanViewMixin, ListView):
    """
    A list with all responsabilities and their candidates
    """
    model = Election
    template_name = 'election/election_list.jinja'
Sli's avatar
Sli committed
144
145
146
147
148
149
150
151
152
153


class ElectionDetailView(CanViewMixin, DetailView):
    """
    Details an election responsability by responsability
    """
    model = Election
    template_name = 'election/election_detail.jinja'
    pk_url_kwarg = "election_id"

154
155
156
    def get_context_data(self, **kwargs):
        """ Add additionnal data to the template """
        kwargs = super(ElectionDetailView, self).get_context_data(**kwargs)
Sli's avatar
Sli committed
157
158
        kwargs['election_form'] = VoteForm(self.object, self.request.user)
        kwargs['election_results'] = self.object.results
159
160
        return kwargs

Sli's avatar
Sli committed
161
162
163

# Form view

164
165
166
167
168
class VoteFormView(CanCreateMixin, FormView):
    """
    Alows users to vote
    """
    form_class = VoteForm
Sli's avatar
Sli committed
169
    template_name = 'election/election_detail.jinja'
170
171

    def dispatch(self, request, *arg, **kwargs):
Sli's avatar
Sli committed
172
        self.election = get_object_or_404(Election, pk=kwargs['election_id'])
173
174
        return super(VoteFormView, self).dispatch(request, *arg, **kwargs)

175
176
177
178
179
180
    def vote(self, election_data):
        for role_title in election_data.keys():
            # 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)
Sli's avatar
Sli committed
181
                    vote.save()
182
                for el in election_data[role_title]:
Sli's avatar
Sli committed
183
                    vote.candidature.add(el)
184
185
186
            # If we have a single choice
            elif election_data[role_title] is not None:
                vote = Vote(role=election_data[role_title].role)
Sli's avatar
Sli committed
187
                vote.save()
188
                vote.candidature.add(election_data[role_title])
Sli's avatar
Sli committed
189
        self.election.voters.add(self.request.user)
190
191
192

    def get_form_kwargs(self):
        kwargs = super(VoteFormView, self).get_form_kwargs()
Sli's avatar
Sli committed
193
194
        kwargs['election'] = self.election
        kwargs['user'] = self.request.user
195
196
197
198
199
200
201
202
        return kwargs

    def form_valid(self, form):
        """
            Verify that the user is part in a vote group
        """
        data = form.clean()
        res = super(FormView, self).form_valid(form)
Sli's avatar
Sli committed
203
        for grp in self.election.vote_groups.all():
204
205
206
207
208
209
            if self.request.user.is_in_group(grp):
                self.vote(data)
                return res
        return res

    def get_success_url(self, **kwargs):
Sli's avatar
Sli committed
210
211
212
213
214
215
216
217
218
219
        return reverse_lazy('election:detail', kwargs={'election_id': self.election.id})

    def get_context_data(self, **kwargs):
        """ Add additionnal data to the template """
        kwargs = super(VoteFormView, self).get_context_data(**kwargs)
        kwargs['object'] = self.election
        kwargs['election'] = self.election
        kwargs['election_form'] = self.get_form()
        return kwargs

220

221
# Create views
222

Sli's avatar
Sli committed
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
class CandidatureCreateView(CanCreateMixin, CreateView):
    """
    View dedicated to a cundidature creation
    """
    form_class = CandidateForm
    model = Candidature
    template_name = 'election/candidate_form.jinja'

    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)

    def get_initial(self):
        init = {}
        self.can_edit = self.request.user.can_edit(self.election)
        init['user'] = self.request.user.id
        return init

    def get_form_kwargs(self):
        kwargs = super(CandidatureCreateView, self).get_form_kwargs()
        kwargs['election_id'] = self.election.id
        kwargs['can_edit'] = self.can_edit
        return kwargs

    def form_valid(self, form):
        """
            Verify that the selected user is in candidate group
        """
        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):
            return super(CreateView, self).form_valid(form)
        raise PermissionDenied

    def get_context_data(self, **kwargs):
        kwargs = super(CandidatureCreateView, self).get_context_data(**kwargs)
        kwargs['election'] = self.election
        return kwargs

    def get_success_url(self, **kwargs):
        return reverse_lazy('election:detail', kwargs={'election_id': self.election.id})

Sli's avatar
Sli committed
265

266
class ElectionCreateView(CanCreateMixin, CreateView):
267
    model = Election
268
    form_class = ElectionForm
269
    template_name = 'core/create.jinja'
270

271
272
273
274
275
    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_subscribed():
            raise PermissionDenied
        return super(ElectionCreateView, self).dispatch(request, *args, **kwargs)

Sli's avatar
Sli committed
276
277
    def form_valid(self, form):
        """
278
            Allow every users that had passed the dispatch to create an election
Sli's avatar
Sli committed
279
        """
280
        return super(CreateView, self).form_valid(form)
Sli's avatar
Sli committed
281

282
283
    def get_success_url(self, **kwargs):
        return reverse_lazy('election:detail', kwargs={'election_id': self.object.id})
284
285


Sli's avatar
Sli committed
286
287
class RoleCreateView(CanCreateMixin, CreateView):
    model = Role
Sli's avatar
Sli committed
288
    form_class = RoleForm
289
    template_name = 'core/create.jinja'
Sli's avatar
Sli committed
290

Sli's avatar
Sli committed
291
292
    def dispatch(self, request, *arg, **kwargs):
        self.election = get_object_or_404(Election, pk=kwargs['election_id'])
Sli's avatar
Sli committed
293
        if not self.election.is_vote_editable:
294
            raise PermissionDenied
Sli's avatar
Sli committed
295
296
297
298
299
300
301
        return super(RoleCreateView, self).dispatch(request, *arg, **kwargs)

    def get_initial(self):
        init = {}
        init['election'] = self.election
        return init

Sli's avatar
Sli committed
302
303
304
305
306
307
308
309
    def form_valid(self, form):
        """
            Verify that the user can edit proprely
        """
        obj = form.instance
        if obj.election:
            for grp in obj.election.edit_groups.all():
                if self.request.user.is_in_group(grp):
Sli's avatar
Sli committed
310
                    return super(CreateView, self).form_valid(form)
Sli's avatar
Sli committed
311
312
        raise PermissionDenied

Sli's avatar
Sli committed
313
314
315
316
317
    def get_form_kwargs(self):
        kwargs = super(RoleCreateView, self).get_form_kwargs()
        kwargs['election_id'] = self.election.id
        return kwargs

Sli's avatar
Sli committed
318
319
320
321
    def get_success_url(self, **kwargs):
        return reverse_lazy('election:detail', kwargs={'election_id': self.object.election.id})


322
323
class ElectionListCreateView(CanCreateMixin, CreateView):
    model = ElectionList
324
    form_class = ElectionListForm
325
    template_name = 'core/create.jinja'
326

327
328
    def dispatch(self, request, *arg, **kwargs):
        self.election = get_object_or_404(Election, pk=kwargs['election_id'])
Sli's avatar
Sli committed
329
        if not self.election.is_vote_editable:
330
331
332
333
334
335
336
337
338
339
340
341
342
            raise PermissionDenied
        return super(ElectionListCreateView, self).dispatch(request, *arg, **kwargs)

    def get_initial(self):
        init = {}
        init['election'] = self.election
        return init

    def get_form_kwargs(self):
        kwargs = super(ElectionListCreateView, self).get_form_kwargs()
        kwargs['election_id'] = self.election.id
        return kwargs

343
344
345
346
347
348
349
350
    def form_valid(self, form):
        """
            Verify that the user can vote on this election
        """
        obj = form.instance
        if obj.election:
            for grp in obj.election.candidature_groups.all():
                if self.request.user.is_in_group(grp):
Sli's avatar
Sli committed
351
                    return super(CreateView, self).form_valid(form)
352
353
            for grp in obj.election.edit_groups.all():
                if self.request.user.is_in_group(grp):
Sli's avatar
Sli committed
354
                    return super(CreateView, self).form_valid(form)
355
356
357
358
        raise PermissionDenied

    def get_success_url(self, **kwargs):
        return reverse_lazy('election:detail', kwargs={'election_id': self.object.election.id})
359
360
361
362
363
364
365

# Update view


class ElectionUpdateView(CanEditMixin, UpdateView):
    model = Election
    form_class = ElectionForm
366
    template_name = 'core/edit.jinja'
367
368
    pk_url_kwarg = 'election_id'

Sli's avatar
Sli committed
369
370
371
372
373
374
375
    def get_success_url(self, **kwargs):
        return reverse_lazy('election:detail', kwargs={'election_id': self.object.id})


class CandidatureUpdateView(CanEditMixin, UpdateView):
    model = Candidature
    form_class = CandidateForm
376
    template_name = 'core/edit.jinja'
Sli's avatar
Sli committed
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
    pk_url_kwarg = 'candidature_id'

    def dispatch(self, request, *arg, **kwargs):
        self.object = self.get_object()
        if not self.object.role.election.is_vote_editable:
            raise PermissionDenied
        return super(CandidatureUpdateView, self).dispatch(request, *arg, **kwargs)

    def remove_fields(self):
        self.form.fields.pop('role', None)

    def get(self, request, *args, **kwargs):
        self.form = self.get_form()
        self.remove_fields()
        return self.render_to_response(self.get_context_data(form=self.form))

    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():
            return super(CandidatureUpdateView, self).form_valid(self.form)
        return self.form_invalid(self.form)

    def get_form_kwargs(self):
        kwargs = super(CandidatureUpdateView, self).get_form_kwargs()
        kwargs['election_id'] = self.object.role.election.id
        return kwargs

    def get_success_url(self, **kwargs):
        return reverse_lazy('election:detail', kwargs={'election_id': self.object.role.election.id})


class RoleUpdateView(CanEditMixin, UpdateView):
    model = Role
    form_class = RoleForm
412
    template_name = 'core/edit.jinja'
Sli's avatar
Sli committed
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
    pk_url_kwarg = 'role_id'

    def dispatch(self, request, *arg, **kwargs):
        self.object = self.get_object()
        if not self.object.election.is_vote_editable:
            raise PermissionDenied
        return super(RoleUpdateView, self).dispatch(request, *arg, **kwargs)

    def remove_fields(self):
        self.form.fields.pop('election', None)

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.form = self.get_form()
        self.remove_fields()
        return self.render_to_response(self.get_context_data(form=self.form))

    def post(self, request, *args, **kwargs):
        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():
            return super(RoleUpdateView, self).form_valid(self.form)
        return self.form_invalid(self.form)

    def get_form_kwargs(self):
        kwargs = super(RoleUpdateView, self).get_form_kwargs()
        kwargs['election_id'] = self.object.election.id
        return kwargs

    def get_success_url(self, **kwargs):
        return reverse_lazy('election:detail', kwargs={'election_id': self.object.election.id})

# Delete Views


class CandidatureDeleteView(CanEditMixin, DeleteView):
    model = Candidature
451
    template_name = 'core/delete_confirm.jinja'
Sli's avatar
Sli committed
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
    pk_url_kwarg = 'candidature_id'

    def dispatch(self, request, *arg, **kwargs):
        self.object = self.get_object()
        self.election = self.object.role.election
        if not self.election.can_candidate:
            raise PermissionDenied
        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})


class RoleDeleteView(CanEditMixin, DeleteView):
    model = Role
467
    template_name = 'core/delete_confirm.jinja'
Sli's avatar
Sli committed
468
469
470
471
472
473
474
475
476
477
478
    pk_url_kwarg = 'role_id'

    def dispatch(self, request, *arg, **kwargs):
        self.object = self.get_object()
        self.election = self.object.election
        if not self.election.is_vote_editable:
            raise PermissionDenied
        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})