merged back from production
[selectricity] / 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   # add filter to verify that the person working on or looking at
58   # something is the owner
59   def edit
60     @election = Election.find(params[:id])
61   end
62
63   def show
64     @sidebar_content = render_to_string :partial => 'progress',
65                                         :locals => { :page => 'review' }
66
67     @election = Election.find(params[:id])
68   end
69
70   def update
71     @election = Election.find(params[:id])
72     if @election.update_attributes(params[:election])
73       flash[:notice] = 'Election was successfully updated.'
74       redirect_to :action => 'show', :id => @election
75     else
76       render :action => 'edit'
77     end
78   end
79
80   def start_election
81     @election = Election.find(params[:id])
82     @election.voters.each do |voter|
83       voter.vote = Vote.new
84       email_voter voter
85     end
86
87     @election.activate!
88     redirect_to :action => 'show', :id => @election.id
89   end
90
91   # methods fod display, adding, deleting, and manipulating candidate
92   # information for elections
93   ####################################################################
94   def edit_candidates
95     @sidebar_content = render_to_string :partial => 'progress',
96                                         :locals => { :page => 'candidates' }
97     @election = Election.find( params[:id] )
98   end
99
100   def add_candidate
101     @election = Election.find(params[:id])
102     @candidate = Candidate.new(params[:candidate])
103     @election.candidates << @candidate
104
105     if @candidate.save
106       @candidate = Candidate.new
107       redirect_to :action => 'edit_candidates', :id => @election.id
108     else
109       render :action => 'edit_candidates', :id => @election.id
110     end
111   end
112   
113   def delete_candidate
114     candidate = Candidate.find( params[:id] )
115     candidate.destroy
116   end
117
118   def candidate_picture
119     candidate = Candidate.find( params[:id] )
120     send_data( candidate.picture.data,
121                :filename => candidate.picture.filename,
122                :type => candidate.picture.filetype,
123                :disposition => 'inline' )
124   end
125
126   ## methods for displaying, adding, deleting, and manipulating voters
127   ## for a particular election
128   ####################################################################
129   def new_voters
130     redirect_to :action => 'edit_voters', :id => params[:id]
131   end
132   
133   def edit_voters
134     @sidebar_content = render_to_string :partial => 'progress',
135                                         :locals => { :page => 'voters' }
136
137     @election = Election.find( params[:id] )
138     if params.has_key?( :raw_voter_list )
139       process_incoming_voters( params[:raw_voter_list] )
140     end
141     @raw_voter_list = RawVoterList.new
142   end
143   
144   def delete_voter
145     voter = Voter.find( params[:id] )
146     voter.destroy
147   end
148   
149   ## methods for computing and printing results
150   ####################################################################
151   def results
152     @election = Election.find( params[:id] )
153     votes = []
154
155     @election.voters.each do |voter|
156       if voter.vote and voter.vote.confirmed?
157         votes << voter.vote.rankings.sort.collect {|vote| vote.candidate_id}
158       end
159     end
160     
161     @voteobj = CloneproofSSDVote.new(votes)
162     @resultobj = @voteobj.result
163     @winners = @resultobj.winners
164     
165     @candidates_by_id = {}
166     @election.candidates.each {|cand| @candidates_by_id[cand.id] = cand}
167   end
168   
169   def detailed_results
170    
171     self.results
172
173     @voter_list = []
174     @vote_list = []
175     @election.voters. each do |voter|
176       if voter.vote and voter.vote.confirmed?
177         @voter_list << voter.email
178               @vote_list << voter.vote
179       end
180     end
181
182     @vote_list.sort!
183     @vote_list.sort! { |a,b| a.token <=> b.token }
184   end
185
186   ## private methods
187   ####################################################################
188   private
189
190     def process_incoming_voters(raw_voter_list)
191       incoming_voters = RawVoterList.new( raw_voter_list )
192
193       unless incoming_voters.entries.empty?
194         incoming_voters.each do |new_voter|
195           new_voter.email.strip! # There's a trailing \r on all but the last in
196                                  # the list!
197           if new_voter.valid?
198             # the new voter should be in good shape. save add to the election
199             @election.voters << new_voter
200                   new_voter.save
201           end
202           # TODO: Can we do some kind of AJAX error message for the voter being invalid?
203         end
204         @election.save
205       end
206  
207       # reset the next time to have a the same default value for emailing
208       @raw_voter_list = RawVoterList.new
209       @raw_voter_list.email = incoming_voters.email
210     end
211
212     def email_voter(voter=nil)
213       if voter
214         VoterNotify.deliver_votestart(voter)
215         voter.contacted=1
216         voter.save
217       end
218     end
219
220 end

Benjamin Mako Hill || Want to submit a patch?