1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
from django import forms
from django.forms.utils import ErrorList
from django.db import transaction
from django.utils import timezone
from .models import Vote
from postgresqleu.membership.models import MemberLog
class VoteForm(forms.Form):
def __init__(self, election, member, *args, **kwargs):
super(VoteForm, self).__init__(*args, **kwargs)
self.saved_and_modified = False
self.election = election
self.member = member
self.candidates = election.candidate_set.all().order_by('name')
self.votes = Vote.objects.filter(election=election, voter=member)
votemap = {}
for vote in self.votes:
votemap[vote.candidate_id] = vote.score
dropdown = [(x, self._votestring(x)) for x in range(1, len(self.candidates) + 1)]
dropdown.insert(0, (-1, '** Please rate this candidate'))
# Dynamically add a dropdown field for each candidate
for candidate in self.candidates:
self.fields['cand%i' % candidate.id] = forms.ChoiceField(choices=dropdown,
label=candidate.name,
required=True,
help_text=candidate.id,
initial=votemap.get(candidate.id, -1))
def _votestring(self, x):
if x == 1:
return "1 - Least favourite"
if x == len(self.candidates):
return "%s - Favourite" % len(self.candidates)
return "%s" % x
def clean(self):
# First, make sure all existing fields are actually filled out
for (k, v) in list(self.cleaned_data.items()):
if k.startswith('cand'):
if v == "-1":
self._errors[k] = ErrorList(["You need to select a score for this candidate!"])
else:
raise Exception("Invalid field name found: %s" % k)
# Second, make sure the fields match the candidates
fields = self.cleaned_data.copy()
for candidate in self.candidates:
k = "cand%i" % candidate.id
if k in fields:
del fields[k]
else:
raise Exception("Data for candidate %i is missing" % candidate.id)
if len(fields) > 0:
raise Exception("Data for candidate not standing for election found!")
# Finally, verify that all options have been found, and none have been duplicated
options = list(range(1, len(self.candidates) + 1))
for k, v in list(self.cleaned_data.items()):
if int(v) in options:
# First use is ok. Take it out of the list, so next attempt generates error
del options[options.index(int(v))]
else:
# Not in the list means it was already used! Bad user!
if k not in self._errors:
# Only add this error in case the other error hasn't already fired
self._errors[k] = ErrorList(["This score has already been given to another candidate"])
if len(options) != 0:
raise forms.ValidationError("One or more scores was not properly assigned!")
return self.cleaned_data
@transaction.atomic
def save(self):
# Let's see if the old votes are here
if len(self.votes) == 0:
# This is completely new, let's create votes for him
for k, v in list(self.cleaned_data.items()):
id = int(k[4:])
Vote(election=self.election, voter=self.member, candidate_id=id, score=v).save()
self.votes = Vote.objects.filter(election=self.election, voter=self.member)
MemberLog(member=self.member, timestamp=timezone.now(),
message="Voted in election '%s'" % self.election.name).save()
self.saved_and_modified = True
elif len(self.votes) == len(self.candidates):
# Ok, we have one vote for each candidate already, so modify them as necessary
changedany = False
for vote in self.votes:
score = int(self.cleaned_data['cand%i' % vote.candidate_id])
if vote.score != score:
vote.score = score
vote.save()
changedany = True
if changedany:
MemberLog(member=self.member, timestamp=timezone.now(),
message="Changed votes in election '%s'" % self.election.name).save()
self.saved_and_modified = True
else:
raise Exception("Invalid number of records found in database, unable to update vote.")
|