views.py 19.1 KB
Newer Older
Sli's avatar
Sli committed
1
2
3
4
5
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
Sli's avatar
Sli committed
6
from django.utils.translation import ugettext_lazy as _
Sli's avatar
Sli committed
7
8
from django.core.exceptions import PermissionDenied
from django.db import transaction
9
10
from django.forms import CheckboxSelectMultiple
from django import forms
Sli's avatar
Sli committed
11

Sli's avatar
Sli committed
12
from core.views import CanViewMixin, CanEditMixin, CanCreateMixin
Sli's avatar
Sli committed
13
from django.db.models.query import QuerySet
14
from core.views.forms import SelectDateTime
Sli's avatar
Sli committed
15
from election.models import Election, Role, Candidature, ElectionList, Vote
Sli's avatar
Sli committed
16

17
18
from ajax_select.fields import AutoCompleteSelectField

19

Sli's avatar
Sli committed
20
21
# Custom form field

Sli's avatar
Sli committed
22
class LimitedCheckboxField(forms.ModelMultipleChoiceField):
Sli's avatar
Sli committed
23
24
25
26
    """
        Used to replace ModelMultipleChoiceField but with
        automatic backend verification
    """
Sli's avatar
Sli committed
27
28
29

    def __init__(self, queryset, max_choice, required=True, widget=None,
                 label=None, initial=None, help_text='', *args, **kwargs):
Sli's avatar
Sli committed
30
        self.max_choice = max_choice
Sli's avatar
Sli committed
31
        widget = forms.CheckboxSelectMultiple()
Sli's avatar
Sli committed
32
33
34
35
36
        super(LimitedCheckboxField, self).__init__(queryset,
                                                   None, required,
                                                   widget, label,
                                                   initial, help_text,
                                                   *args, **kwargs)
Sli's avatar
Sli committed
37
38

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

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


49
50
51
# Forms


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

Sli's avatar
Sli committed
61
62
    user = AutoCompleteSelectField('users', label=_(
        'User to candidate'), help_text=None, required=True)
63

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

76
77

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


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

Sli's avatar
Sli committed
99
100
101
102
    def __init__(self, *args, **kwargs):
        election_id = kwargs.pop('election_id', None)
        super(RoleForm, self).__init__(*args, **kwargs)
        if election_id:
Sli's avatar
Sli committed
103
104
            self.fields['election'].queryset = Election.objects.filter(
                id=election_id).all()
Sli's avatar
Sli committed
105

Sli's avatar
Sli committed
106
107
108
109
110
    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():
Sli's avatar
Sli committed
111
112
113
            raise forms.ValidationError(
                _("This role already exists for this election"),
                code='invalid')
Sli's avatar
Sli committed
114

115

116
117
118
class ElectionListForm(forms.ModelForm):
    class Meta:
        model = ElectionList
Sli's avatar
Sli committed
119
        fields = ('title', 'election')
120
121
122
123
124

    def __init__(self, *args, **kwargs):
        election_id = kwargs.pop('election_id', None)
        super(ElectionListForm, self).__init__(*args, **kwargs)
        if election_id:
Sli's avatar
Sli committed
125
126
            self.fields['election'].queryset = Election.objects.filter(
                id=election_id).all()
127
128
129
130
131


class ElectionForm(forms.ModelForm):
    class Meta:
        model = Election
Sli's avatar
Sli committed
132
133
134
135
136
        fields = ['title', 'description',
                  'start_candidature', 'end_candidature',
                  'start_date', 'end_date',
                  'edit_groups', 'view_groups',
                  'vote_groups', 'candidature_groups']
137
138
139
140
141
        widgets = {
            'edit_groups': CheckboxSelectMultiple,
            'view_groups': CheckboxSelectMultiple,
            'edit_groups': CheckboxSelectMultiple,
            'vote_groups': CheckboxSelectMultiple,
Sli's avatar
Sli committed
142
            'candidature_groups': CheckboxSelectMultiple
143
144
        }

Sli's avatar
Sli committed
145
146
147
148
149
150
151
152
153
154
155
156
    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)
Sli's avatar
Sli committed
157

158

Sli's avatar
Sli committed
159
160
161
162
163
164
165
166
167
# 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
168
169
170
171
172
173
174
175
176
177


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

178
179
180
    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
181
182
        kwargs['election_form'] = VoteForm(self.object, self.request.user)
        kwargs['election_results'] = self.object.results
183
184
        return kwargs

Sli's avatar
Sli committed
185
186
187

# Form view

188
189
190
191
192
class VoteFormView(CanCreateMixin, FormView):
    """
    Alows users to vote
    """
    form_class = VoteForm
Sli's avatar
Sli committed
193
    template_name = 'election/election_detail.jinja'
194
195

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

199
    def vote(self, election_data):
200
201
202
203
204
        with transaction.atomic():
            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:
Sli's avatar
Sli committed
205
206
                        vote = Vote(role=election_data[
                                    role_title].first().role)
207
208
209
210
211
212
                        vote.save()
                    for el in election_data[role_title]:
                        vote.candidature.add(el)
                # 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
213
                    vote.save()
214
215
                    vote.candidature.add(election_data[role_title])
            self.election.voters.add(self.request.user)
216
217
218

    def get_form_kwargs(self):
        kwargs = super(VoteFormView, self).get_form_kwargs()
Sli's avatar
Sli committed
219
220
        kwargs['election'] = self.election
        kwargs['user'] = self.request.user
221
222
223
224
225
226
227
228
        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
229
        for grp in self.election.vote_groups.all():
230
231
232
233
234
235
            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
236
237
        return reverse_lazy('election:detail',
                            kwargs={'election_id': self.election.id})
Sli's avatar
Sli committed
238
239
240
241
242
243
244
245
246

    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

247

248
# Create views
249

Sli's avatar
Sli committed
250
251
252
253
254
255
256
257
258
259
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'])
Sli's avatar
Sli committed
260
261
        return super(CandidatureCreateView,
                     self).dispatch(request, *arg, **kwargs)
Sli's avatar
Sli committed
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280

    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)
Sli's avatar
Sli committed
281
282
        if(obj.election.can_candidate(obj.user)) and \
                (obj.user == self.request.user or self.can_edit):
Sli's avatar
Sli committed
283
284
285
286
287
288
289
290
291
            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):
Sli's avatar
Sli committed
292
293
        return reverse_lazy('election:detail',
                            kwargs={'election_id': self.election.id})
Sli's avatar
Sli committed
294

Sli's avatar
Sli committed
295

296
class ElectionCreateView(CanCreateMixin, CreateView):
297
    model = Election
298
    form_class = ElectionForm
299
    template_name = 'core/create.jinja'
300

301
    def dispatch(self, request, *args, **kwargs):
302
        if not request.user.is_subscribed:
303
            raise PermissionDenied
Sli's avatar
Sli committed
304
305
        return super(ElectionCreateView,
                     self).dispatch(request, *args, **kwargs)
306

Sli's avatar
Sli committed
307
308
    def form_valid(self, form):
        """
Sli's avatar
Sli committed
309
310
            Allow every users that had passed the dispatch
            to create an election
Sli's avatar
Sli committed
311
        """
312
        return super(CreateView, self).form_valid(form)
Sli's avatar
Sli committed
313

314
    def get_success_url(self, **kwargs):
Sli's avatar
Sli committed
315
316
        return reverse_lazy('election:detail',
                            kwargs={'election_id': self.object.id})
317
318


Sli's avatar
Sli committed
319
320
class RoleCreateView(CanCreateMixin, CreateView):
    model = Role
Sli's avatar
Sli committed
321
    form_class = RoleForm
322
    template_name = 'core/create.jinja'
Sli's avatar
Sli committed
323

Sli's avatar
Sli committed
324
325
    def dispatch(self, request, *arg, **kwargs):
        self.election = get_object_or_404(Election, pk=kwargs['election_id'])
Sli's avatar
Sli committed
326
        if not self.election.is_vote_editable:
327
            raise PermissionDenied
Sli's avatar
Sli committed
328
329
330
331
332
333
334
        return super(RoleCreateView, self).dispatch(request, *arg, **kwargs)

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

Sli's avatar
Sli committed
335
336
337
338
339
340
341
342
    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
343
                    return super(CreateView, self).form_valid(form)
Sli's avatar
Sli committed
344
345
        raise PermissionDenied

Sli's avatar
Sli committed
346
347
348
349
350
    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
351
    def get_success_url(self, **kwargs):
Sli's avatar
Sli committed
352
353
        return reverse_lazy('election:detail',
                            kwargs={'election_id': self.object.election.id})
Sli's avatar
Sli committed
354
355


356
357
class ElectionListCreateView(CanCreateMixin, CreateView):
    model = ElectionList
358
    form_class = ElectionListForm
359
    template_name = 'core/create.jinja'
360

361
362
    def dispatch(self, request, *arg, **kwargs):
        self.election = get_object_or_404(Election, pk=kwargs['election_id'])
Sli's avatar
Sli committed
363
        if not self.election.is_vote_editable:
364
            raise PermissionDenied
Sli's avatar
Sli committed
365
366
        return super(ElectionListCreateView,
                     self).dispatch(request, *arg, **kwargs)
367
368
369
370
371
372
373
374
375
376
377

    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

378
379
380
381
382
383
384
385
    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
386
                    return super(CreateView, self).form_valid(form)
387
388
            for grp in obj.election.edit_groups.all():
                if self.request.user.is_in_group(grp):
Sli's avatar
Sli committed
389
                    return super(CreateView, self).form_valid(form)
390
391
392
        raise PermissionDenied

    def get_success_url(self, **kwargs):
Sli's avatar
Sli committed
393
394
        return reverse_lazy('election:detail',
                            kwargs={'election_id': self.object.election.id})
395
396
397
398
399
400
401

# Update view


class ElectionUpdateView(CanEditMixin, UpdateView):
    model = Election
    form_class = ElectionForm
402
    template_name = 'core/edit.jinja'
403
404
    pk_url_kwarg = 'election_id'

405
406
407
    def get_initial(self):
        init = {}
        try:
Sli's avatar
Sli committed
408
409
410
411
            init['start_date'] = self.object.start_date.strftime(
                '%Y-%m-%d %H:%M:%S')
        except Exception:
            pass
412
        try:
Sli's avatar
Sli committed
413
414
415
416
            init['end_date'] = self.object.end_date.strftime(
                '%Y-%m-%d %H:%M:%S')
        except Exception:
            pass
417
        try:
Sli's avatar
Sli committed
418
419
420
421
            init['start_candidature'] = self.object.start_candidature.strftime(
                '%Y-%m-%d %H:%M:%S')
        except Exception:
            pass
422
        try:
Sli's avatar
Sli committed
423
424
425
426
            init['end_candidature'] = self.object.end_candidature.strftime(
                '%Y-%m-%d %H:%M:%S')
        except Exception:
            pass
427
428
        return init

Sli's avatar
Sli committed
429
    def get_success_url(self, **kwargs):
Sli's avatar
Sli committed
430
431
        return reverse_lazy('election:detail',
                            kwargs={'election_id': self.object.id})
Sli's avatar
Sli committed
432
433
434
435
436


class CandidatureUpdateView(CanEditMixin, UpdateView):
    model = Candidature
    form_class = CandidateForm
437
    template_name = 'core/edit.jinja'
Sli's avatar
Sli committed
438
439
440
441
442
443
    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
Sli's avatar
Sli committed
444
445
        return super(CandidatureUpdateView,
                     self).dispatch(request, *arg, **kwargs)
Sli's avatar
Sli committed
446
447
448
449
450
451
452
453
454
455
456
457

    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()
Sli's avatar
Sli committed
458
459
        if request.user.is_authenticated() and \
                request.user.can_edit(self.object) and self.form.is_valid():
Sli's avatar
Sli committed
460
461
462
463
464
465
466
467
468
            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):
Sli's avatar
Sli committed
469
470
471
        return reverse_lazy('election:detail',
                            kwargs={'election_id':
                                    self.object.role.election.id})
Sli's avatar
Sli committed
472
473
474
475
476


class RoleUpdateView(CanEditMixin, UpdateView):
    model = Role
    form_class = RoleForm
477
    template_name = 'core/edit.jinja'
Sli's avatar
Sli committed
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
    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()
Sli's avatar
Sli committed
499
500
        if request.user.is_authenticated() and \
                request.user.can_edit(self.object) and self.form.is_valid():
Sli's avatar
Sli committed
501
502
503
504
505
506
507
508
509
            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):
Sli's avatar
Sli committed
510
511
        return reverse_lazy('election:detail',
                            kwargs={'election_id': self.object.election.id})
Sli's avatar
Sli committed
512
513
514
515
516
517

# Delete Views


class CandidatureDeleteView(CanEditMixin, DeleteView):
    model = Candidature
518
    template_name = 'core/delete_confirm.jinja'
Sli's avatar
Sli committed
519
520
521
522
523
    pk_url_kwarg = 'candidature_id'

    def dispatch(self, request, *arg, **kwargs):
        self.object = self.get_object()
        self.election = self.object.role.election
Sli's avatar
Sli committed
524
525
        if not self.election.can_candidate \
                or not self.election.is_vote_editable:
Sli's avatar
Sli committed
526
            raise PermissionDenied
Sli's avatar
Sli committed
527
528
        return super(CandidatureDeleteView,
                     self).dispatch(request, *arg, **kwargs)
Sli's avatar
Sli committed
529
530

    def get_success_url(self, **kwargs):
Sli's avatar
Sli committed
531
532
        return reverse_lazy('election:detail',
                            kwargs={'election_id': self.election.id})
Sli's avatar
Sli committed
533
534
535
536


class RoleDeleteView(CanEditMixin, DeleteView):
    model = Role
537
    template_name = 'core/delete_confirm.jinja'
Sli's avatar
Sli committed
538
539
540
541
542
543
544
545
546
547
    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):
Sli's avatar
Sli committed
548
549
        return reverse_lazy('election:detail',
                            kwargs={'election_id': self.election.id})