merge from live
[selectricity-live] / app / controllers / election_controller.rb
1 # Selectricity: Voting Machinery for the Masses
2 # Copyright (C) 2007, 2008 Benjamin Mako Hill <mako@atdot.cc>
3 # Copyright (C) 2007 Massachusetts Institute of Technology
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Affero General Public License for more details.
14 #
15 # You should have received a copy of the GNU Affero General Public
16 # License along with this program.  If not, see
17 # <http://www.gnu.org/licenses/>.
18
19 class ElectionController < ApplicationController
20   require_dependency "raw_voter_list"
21   require_dependency "voter"
22   require_dependency "vote"
23   require_dependency "candidate"
24   layout 'main'
25
26   ## methods for displaying, creating,
27   ## and manipulating election overview data
28   ####################################################################
29
30   def new
31     redirect_to :action => 'general_information'
32   end
33   
34   def general_information
35     @sidebar_content = render_to_string :partial => 'progress',
36                                         :locals => { :page => 'overview' }
37     @election = Election.new
38     render :action => 'general_information'
39   end
40   
41   def create_election
42     @election = Election.new(params[:election])
43     
44     # default options
45     @election.user = session[:user]
46     @election.anonymous = 1
47     @election.startdate = Time.now
48
49     if @election.save
50       flash[:notice] = 'Election was successfully created.'
51       redirect_to :action => 'edit_candidates', :id => @election.id
52     else
53       render :action => 'general_information'
54     end
55   end
56   
57   # TODO add filter to verify that the person working on or looking at
58   # something is the owner
59   def edit_general_information
60     @election = Election.find(params[:id])
61   end
62   
63   def update_general_information
64     @election = Election.find(params[:id])
65     if @election.update_attributes(params[:election])
66       flash[:notice] = 'Election was successfully updated.'
67       redirect_to :action => 'show', :id => @election
68     else
69       render :action => 'edit'
70     end
71   end
72
73
74   def show
75     @sidebar_content = render_to_string :partial => 'progress',
76                                         :locals => { :page => 'review' }
77
78     @election = Election.find(params[:id])
79   end
80
81   def start_election
82     @election = Election.find(params[:id])
83     @election.voters.each do |voter|
84       voter.vote = Vote.new
85       email_voter voter
86     end
87
88     @election.activate!
89     redirect_to :action => 'show', :id => @election.id
90   end
91
92   # methods fod display, adding, deleting, and manipulating candidate
93   # information for elections
94   ####################################################################
95   def edit_candidates
96     @sidebar_content = render_to_string :partial => 'progress',
97                                         :locals => { :page => 'candidates' }
98     @election = Election.find( params[:id] )
99   end
100
101   def add_candidate
102     @election = Election.find(params[:id])
103     @candidate = Candidate.new(params[:candidate])
104     @election.candidates << @candidate
105
106     if @candidate.save
107       # check to see if they've uploaded a picture
108       if params[:picture][:uploaded_data]
109         picture = Picture.new(params[:picture])
110         @candidate.picture = picture if picture.save
111       end
112
113       @candidate = Candidate.new
114       redirect_to :action => 'edit_candidates', :id => @election.id
115     else
116       render :action => 'edit_candidates', :id => @election.id
117     end
118   end
119   
120   def delete_candidate
121     candidate = Candidate.find( params[:id] )
122     candidate.destroy
123   end
124
125   def candidate_picture
126     candidate = Candidate.find( params[:id] )
127     send_data( candidate.picture.data,
128                :filename => candidate.picture.filename,
129                :type => candidate.picture.filetype,
130                :disposition => 'inline' )
131   end
132
133   ## methods for displaying, adding, deleting, and manipulating voters
134   ## for a particular election
135   ####################################################################
136   def new_voters
137     redirect_to :action => 'edit_voters', :id => params[:id]
138   end
139   
140   def edit_voters
141     @sidebar_content = render_to_string :partial => 'progress',
142                                         :locals => { :page => 'voters' }
143
144     @election = Election.find( params[:id] )
145     if params.has_key?( :raw_voter_list )
146       process_incoming_voters( params[:raw_voter_list] )
147     end
148     @raw_voter_list = RawVoterList.new
149   end
150   
151   def delete_voter
152     voter = Voter.find( params[:id] )
153     voter.destroy
154   end
155
156   def toggle_authenticated
157     @election = Election.find(params[:id])
158     if params[:authenticated] == "1"
159       @election.authenticated = true
160     else
161       @election.authenticated = false
162     end
163     @election.save
164   end
165   
166   ## methods for computing and printing results
167   ####################################################################
168   def results
169     @election = Election.find( params[:id] )
170     votes = []
171
172     @election.voters.each do |voter|
173       if voter.vote and voter.vote.confirmed?
174         votes << voter.vote.rankings.sort.collect {|vote| vote.candidate_id}
175       end
176     end
177     
178     @voteobj = CloneproofSSDVote.new(votes)
179     @resultobj = @voteobj.result
180     @winners = @resultobj.winners
181     
182     @candidates_by_id = {}
183     @election.candidates.each {|cand| @candidates_by_id[cand.id] = cand}
184   end
185   
186   def detailed_results
187    
188     self.results
189
190     @voter_list = []
191     @vote_list = []
192     @election.voters. each do |voter|
193       if voter.vote and voter.vote.confirmed?
194         @voter_list << voter.email
195               @vote_list << voter.vote
196       end
197     end
198
199     @vote_list.sort!
200     @vote_list.sort! { |a,b| a.token <=> b.token }
201   end
202
203   ## private methods
204   ####################################################################
205   private
206
207     def process_incoming_voters(raw_voter_list)
208       incoming_voters = RawVoterList.new( raw_voter_list )
209
210       unless incoming_voters.entries.empty?
211         incoming_voters.each do |new_voter|
212           new_voter.email.strip! # There's a trailing \r on all but the last in
213                                  # the list!
214           if new_voter.valid?
215             # the new voter should be in good shape. save add to the election
216             @election.voters << new_voter
217                   new_voter.save
218           end
219           # TODO: Can we do some kind of AJAX error message for the voter being invalid?
220         end
221         @election.save
222       end
223  
224       # reset the next time to have a the same default value for emailing
225       @raw_voter_list = RawVoterList.new
226       @raw_voter_list.email = incoming_voters.email
227     end
228
229     def email_voter(voter=nil)
230       if voter
231         VoterNotify.deliver_votestart(voter)
232         voter.contacted=1
233         voter.save
234       end
235     end
236
237 end

Benjamin Mako Hill || Want to submit a patch?