Merge lp:~ronnie.vd.c/ubuntu-nl-website/forms-views into lp:~registry/ubuntu-nl-website/voting

Proposed by Ronnie
Status: Merged
Merged at revision: 6
Proposed branch: lp:~ronnie.vd.c/ubuntu-nl-website/forms-views
Merge into: lp:~registry/ubuntu-nl-website/voting
Diff against target: 389 lines (+257/-37)
9 files modified
elections/forms.py (+104/-0)
elections/models.py (+21/-1)
elections/urls.py (+4/-4)
elections/views.py (+87/-26)
settings.py (+1/-1)
templates/election_new.html (+10/-0)
templates/election_results.html (+19/-0)
templates/registration/login.html (+5/-0)
urls.py (+6/-5)
To merge this branch: bzr merge lp:~ronnie.vd.c/ubuntu-nl-website/forms-views
Reviewer Review Type Date Requested Status
Registry Administrators Pending
Review via email: mp+49581@code.launchpad.net
To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'elections/forms.py'
2--- elections/forms.py 1970-01-01 00:00:00 +0000
3+++ elections/forms.py 2011-02-14 00:39:53 +0000
4@@ -0,0 +1,104 @@
5+from django import forms
6+
7+from elections.models import Election, Candidate, Voter
8+
9+
10+class CreateElectionForm(forms.ModelForm):
11+ class Meta:
12+ model = Election
13+
14+ def clean(self, *args, **kwargs):
15+ super(CreateElectionForm, self).clean(*args, **kwargs)
16+ cleaned_data = self.cleaned_data
17+
18+ candidacy_start = cleaned_data.get('candidacy_start')
19+ candidacy_end = cleaned_data.get('candidacy_end')
20+ election_start = cleaned_data.get('election_start')
21+ election_end = cleaned_data.get('election_end')
22+
23+ if candidacy_start > candidacy_end:
24+ self._errors['candidacy_end'] = self.error_class(['Eind-datum is eerder dan begin-datum'])
25+
26+ if candidacy_end > election_start:
27+ self._errors['election_start'] = self.error_class(['De kandidaten moeten gekozen worden voordat de verkiezing begint'])
28+
29+ if election_start > election_end:
30+ self._errors['election_end'] = self.error_class(['Eind-datum is eerder dan begin-datum'])
31+
32+ # Always return the full collection of cleaned data.
33+ return cleaned_data
34+
35+
36+class ApplyForElectionForm(forms.ModelForm):
37+ class Meta:
38+ model = Candidate
39+ fields = ('chair_desired', 'council_desired')
40+
41+
42+class VoteForm(forms.Form):
43+ chair = forms.ModelChoiceField(queryset=Candidate.objects.all())
44+ council1 = forms.ModelChoiceField(queryset=Candidate.objects.all())
45+ council2 = forms.ModelChoiceField(queryset=Candidate.objects.all())
46+ council3 = forms.ModelChoiceField(queryset=Candidate.objects.all())
47+ council4 = forms.ModelChoiceField(queryset=Candidate.objects.all())
48+ council5 = forms.ModelChoiceField(queryset=Candidate.objects.all())
49+ council6 = forms.ModelChoiceField(queryset=Candidate.objects.all())
50+
51+ def __init__(self, election, *args, **kwargs):
52+ super(VoteForm, self).__init__(*args, **kwargs)
53+ chair_choices = Candidate.objects.filter(election=election, chair_desired=True)
54+ council_choices = Candidate.objects.filter(election=election, council_desired=True)
55+ self.fields['chair'].queryset = chair_choices
56+ self.fields['council1'].queryset = council_choices
57+ self.fields['council2'].queryset = council_choices
58+ self.fields['council3'].queryset = council_choices
59+ self.fields['council4'].queryset = council_choices
60+ self.fields['council5'].queryset = council_choices
61+ self.fields['council6'].queryset = council_choices
62+
63+ def clean(self, *args, **kwargs):
64+ cleaned_data = self.cleaned_data
65+
66+ council = [cleaned_data.get('council1'),
67+ cleaned_data.get('council2'),
68+ cleaned_data.get('council3'),
69+ cleaned_data.get('council4'),
70+ cleaned_data.get('council5'),
71+ cleaned_data.get('council6')]
72+ council.reverse()
73+
74+ for candidate in council:
75+ if council.count(candidate) > 1:
76+ for same in range(council.count(candidate) - 1):
77+ index = council.index(candidate)
78+ self._errors['council%d' % (len(council)-index)] = self.error_class(['Je hebt deze persoon al gekozen'])
79+ council.pop(index)
80+
81+ # Always return the full collection of cleaned data.
82+ return cleaned_data
83+
84+
85+ def save(self, *args, **kwargs):
86+ cleaned_data = self.cleaned_data
87+
88+ votes = [cleaned_data.get('council1'),
89+ cleaned_data.get('council2'),
90+ cleaned_data.get('council3'),
91+ cleaned_data.get('council4'),
92+ cleaned_data.get('council5'),
93+ cleaned_data.get('council6')]
94+
95+ for c in range(len(votes)):
96+ candidate = votes[c]
97+ candidate.council_votes += len(votes) - c
98+ candidate.save()
99+
100+ chair = cleaned_data.get('chair')
101+ if chair in votes: # If the chair is in the votes, the new version is not stored in cleaned_data, so use the database version
102+ chair = Candidate.objects.get(pk=cleaned_data.get('chair').pk)
103+
104+ chair.chair_votes += 1
105+ chair.save()
106+
107+ return True
108+
109
110=== modified file 'elections/models.py'
111--- elections/models.py 2011-02-11 23:43:01 +0000
112+++ elections/models.py 2011-02-14 00:39:53 +0000
113@@ -1,16 +1,20 @@
114 from django.db import models
115+from django.contrib import admin
116 from django.contrib.auth.models import User
117
118 import datetime
119
120
121 class Election(models.Model):
122- name = models.CharField(max_length=128, default="Verkiezing")
123+ name = models.CharField(max_length=128, default='Verkiezing')
124 candidacy_start = models.DateTimeField(default=datetime.datetime.now())
125 candidacy_end = models.DateTimeField(default=datetime.datetime.now()+datetime.timedelta(days=28))
126 election_start = models.DateTimeField(default=datetime.datetime.now()+datetime.timedelta(days=28))
127 election_end = models.DateTimeField(default=datetime.datetime.now()+datetime.timedelta(days=35))
128 live_results = models.BooleanField(default=False) # If true, the votes can be seen immediatly, else the results are shown after the election
129+
130+ def __unicode__(self):
131+ return self.name
132
133
134 class Candidate(models.Model):
135@@ -21,6 +25,22 @@
136 chair_votes = models.IntegerField(default=0)
137 council_votes = models.IntegerField(default=0) # 6 for first choice, 1 for 6th choice
138
139+ class Meta:
140+ unique_together = ('user', 'election')
141+
142+ def __unicode__(self):
143+ return '%s (%s)' % (self.user.username, self.election.name)
144+
145+
146 class Voter(models.Model):
147 election = models.ForeignKey(Election)
148 voter = models.CharField(max_length=12) # StoreOneWayHashOfOpenIDHere
149+
150+ class Meta:
151+ unique_together = ('voter', 'election')
152+
153+
154+admin.site.register(Election)
155+admin.site.register(Candidate)
156+admin.site.register(Voter)
157+
158
159=== modified file 'elections/urls.py'
160--- elections/urls.py 2011-02-11 23:45:49 +0000
161+++ elections/urls.py 2011-02-14 00:39:53 +0000
162@@ -2,9 +2,9 @@
163
164
165 urlpatterns = patterns('',
166- url('^nieuw/$', 'elections.views.election_new', name='election-new'), # Create a new election (check permissions trough admin group)
167+ url(r'^nieuw/$', 'elections.views.election_new', name='election-new'), # Create a new election (check permissions trough admin group)
168 # Kunnen deze 3 pagina's ook 1 url worden, afhankelijk van de datum (voor,tijdens,na verkiezing?)
169- url('^(?P<electionId>\d+)/(?P<electionName>\w+)/verkiesbaar$', 'elections.views.election_apply', name='election-apply'), # Check/Uncheck (before election starts) if you apply for this
170- url('^(?P<electionId>\d+)/(?P<electionName>\w+)/stem$', 'elections.views.election_vote', name='election-vote'), # Voting happens here (or view / edit? your vote, if already voted)
171- url('^(?P<electionId>\d+)/(?P<electionName>\w+)/uitslag/$', 'elections.views.election_results', name='election-results'), # The link where the results are shown (maybe with some nice effects)
172+ url(r'^(?P<electionId>\d+)/(?P<electionName>\w+)/verkiesbaar$', 'elections.views.election_apply', name='election-apply'), # Check/Uncheck (before election starts) if you apply for this
173+ url(r'^(?P<electionId>\d+)/(?P<electionName>\w+)/stem$', 'elections.views.election_vote', name='election-vote'), # Voting happens here (or view / edit? your vote, if already voted)
174+ url(r'^(?P<electionId>\d+)/(?P<electionName>\w+)/uitslag/$', 'elections.views.election_results', name='election-results'), # The link where the results are shown (maybe with some nice effects)
175 )
176
177=== modified file 'elections/views.py'
178--- elections/views.py 2011-02-11 23:45:49 +0000
179+++ elections/views.py 2011-02-14 00:39:53 +0000
180@@ -1,29 +1,90 @@
181+from django.contrib.auth.decorators import login_required
182+from django.db import IntegrityError
183 from django.views.generic.simple import direct_to_template
184+from django.shortcuts import redirect
185 from django.template import RequestContext
186-
187-from elections.forms import CreateElectionForm, VoteForm
188-
189+from django.http import Http404
190+
191+from elections.models import Election, Candidate, Voter
192+from elections.forms import CreateElectionForm, ApplyForElectionForm, VoteForm
193+
194+import datetime
195+
196+def account(request):
197+ # TODO: Write this view
198+ return direct_to_template(request, 'election_new.html', {})
199+
200+@login_required
201 def election_new(request):
202- form = None
203- if request.POST:
204- pass
205- form = CreateElectionForm(request.POST)
206-
207- if not form:
208- pass
209- form = CreateElectionForm()
210-
211- context = {
212- 'form': form,
213- }
214-
215- return direct_to_template(request, 'election_new.html', context)
216-
217-def election_apply(request):
218- form = ApplyForElectionForm(request.POST)
219-
220-def election_vote(request):
221- pass
222-
223-def election_results(request):
224- pass
225+ # TODO: limit only to admins, to prevent other users to create different elections (for now)
226+ form = CreateElectionForm(data=request.POST or None)
227+
228+ if request.POST and form.is_valid():
229+ election = form.save()
230+ # TODO: Link to a succes page
231+ return redirect(election_apply, electionId=election.pk, electionName=election.name)
232+
233+ return direct_to_template(request, 'election_new.html', {'form': form})
234+
235+@login_required
236+def election_apply(request, electionId, electionName):
237+ election = Election.objects.get(pk=electionId)
238+ if datetime.datetime.now() < election.candidacy_start:
239+ raise Http404('De kandidaatstelling is nog niet begonnen')
240+ if datetime.datetime.now() > election.candidacy_end:
241+ raise Http404('De kandidaatstelling is al afgelopen')
242+
243+ # TODO: check if user has rights
244+ candidate, created = Candidate.objects.get_or_create(election=election, user=request.user)
245+ form = ApplyForElectionForm(data=request.POST or None, instance=candidate)
246+
247+ if request.POST and form.is_valid():
248+ form.save()
249+ # TODO: Link to a success page and overview page of candidates
250+ return redirect(election_vote, electionId=election.pk, electionName=election.name)
251+
252+ return direct_to_template(request, 'election_new.html', {'form': form})
253+
254+@login_required
255+def election_vote(request, electionId, electionName):
256+ election = Election.objects.get(pk=electionId)
257+ if datetime.datetime.now() < election.election_start:
258+ raise Http404('De verkiezing is nog niet begonnen')
259+ if datetime.datetime.now() > election.election_end:
260+ raise Http404('De verkiezing is al afgelopen')
261+
262+ # TODO: check if user has rights to vote
263+ user_hash = hash(request.user) # TODO: Do a better hash, and not of the user, but the openid
264+ try: # If the voter already exists, it has already vote, raise an exception!
265+ Voter.objects.get(election=election, voter=user_hash)
266+ except Voter.DoesNotExist:
267+ pass
268+ else:
269+ raise Http404('Je heb al gestemd voor deze verkiezing')
270+
271+ form = VoteForm(data=request.POST or None, election=election)
272+
273+ if request.POST and form.is_valid():
274+ form.save()
275+ Voter.objects.create(election=election, voter=user_hash)
276+ return redirect(election_results, electionId=election.pk, electionName=election.name)
277+
278+ return direct_to_template(request, 'election_new.html', {'form': form})
279+
280+
281+def election_results(request, electionId, electionName):
282+ election = Election.objects.get(pk=electionId)
283+ if datetime.datetime.now() < election.election_start:
284+ if election.live_results:
285+ raise Http404('Het stemming is nog niet begonnen, de eerste resultaten zijn pas zichtbaar op: %s' % election.election_start)
286+ else:
287+ raise Http404('Het stemming is nog niet begonnen, de eerste resultaten zijn pas zichtbaar op: %s' % election.election_end)
288+ if datetime.datetime.now() < election.election_end and not election.live_results:
289+ raise Http404('De resultaten zijn pas zichtbaar na de stemming (%s)' % election.election_end)
290+
291+ top_chairs = Candidate.objects.filter(chair_desired=True).order_by('-chair_votes')
292+ top_council = Candidate.objects.filter(council_desired=True).order_by('-council_votes')
293+ return direct_to_template(request, 'election_results.html', {'chairs': top_chairs,
294+ 'council': top_council})
295+
296+
297
298=== modified file 'settings.py'
299--- settings.py 2011-02-11 23:45:49 +0000
300+++ settings.py 2011-02-14 00:39:53 +0000
301@@ -95,7 +95,7 @@
302 'django.contrib.sites',
303 'django.contrib.messages',
304 # Uncomment the next line to enable the admin:
305- # 'django.contrib.admin',
306+ 'django.contrib.admin',
307 # Uncomment the next line to enable admin documentation:
308 # 'django.contrib.admindocs',
309
310
311=== added directory 'templates'
312=== added file 'templates/election_new.html'
313--- templates/election_new.html 1970-01-01 00:00:00 +0000
314+++ templates/election_new.html 2011-02-14 00:39:53 +0000
315@@ -0,0 +1,10 @@
316+<html>
317+<body>
318+<form method="POST" action="">
319+{% csrf_token %}
320+{{ form.as_ul }}
321+<input type="submit" value="create" />
322+</form>
323+</body>
324+</html>
325+
326
327=== added file 'templates/election_results.html'
328--- templates/election_results.html 1970-01-01 00:00:00 +0000
329+++ templates/election_results.html 2011-02-14 00:39:53 +0000
330@@ -0,0 +1,19 @@
331+<html>
332+<body>
333+
334+<h3>=== Chairs ===</h3>
335+<ul>
336+{% for candidate in chairs %}
337+ <li>{{ candidate.user.username }} ({{ candidate.chair_votes }} votes)</li>
338+{% endfor %}
339+</ul>
340+
341+<h3>=== Council ===</h3>
342+<ul>
343+{% for candidate in council %}
344+ <li>{{ candidate.user.username }} ({{ candidate.council_votes }} votes)</li>
345+{% endfor %}
346+</ul>
347+
348+</body>
349+</html>
350
351=== added directory 'templates/registration'
352=== added file 'templates/registration/login.html'
353--- templates/registration/login.html 1970-01-01 00:00:00 +0000
354+++ templates/registration/login.html 2011-02-14 00:39:53 +0000
355@@ -0,0 +1,5 @@
356+<form method="POST">
357+ {% csrf_token %}
358+ {{ form.as_ul }}
359+ <input type="submit" name="Inloggen" />
360+</form>
361
362=== modified file 'urls.py'
363--- urls.py 2011-02-11 23:45:49 +0000
364+++ urls.py 2011-02-14 00:39:53 +0000
365@@ -1,12 +1,13 @@
366 from django.conf.urls.defaults import *
367
368 # Uncomment the next two lines to enable the admin:
369-# from django.contrib import admin
370-# admin.autodiscover()
371+from django.contrib import admin
372+admin.autodiscover()
373
374 urlpatterns = patterns('',
375- url('^account/$', 'elections.views.account'), # to fill in your Full name, because the LP name is not always the right
376- url('^verkiezing/$', include('ubuntu_nl_voting.elections.urls')),
377+ url(r'^account/$', 'elections.views.account', name='account'), # to fill in your Full name, because the LP name is not always the right
378+ url(r'^accounts/login/$', 'django.contrib.auth.views.login', name='login'),
379+ url(r'^verkiezing/', include('ubuntu_nl_voting.elections.urls')),
380 # Example:
381 # (r'^ubuntu_nl_voting/', include('ubuntu_nl_voting.foo.urls')),
382
383@@ -14,5 +15,5 @@
384 # (r'^admin/doc/', include('django.contrib.admindocs.urls')),
385
386 # Uncomment the next line to enable the admin:
387- # (r'^admin/', include(admin.site.urls)),
388+ (r'^admin/', include(admin.site.urls)),
389 )

Subscribers

People subscribed via source and target branches