create new open voter to allow open full elections
[selectricity-live] / app / models / vote.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 Vote < ActiveRecord::Base
20   # relationships to other classes
21   belongs_to :voter
22   has_many :rankings
23   has_one :token
24   
25   # callbacks
26   after_update :save_rankings
27   before_destroy :destroy_rankings
28   
29   def to_s
30     votes.join("")
31   end
32
33   def each
34     self.votes.each {|vote| yield vote}
35   end
36
37   def votes
38     unless @votes
39       if rankings.empty?
40         @votes = Array.new
41       else
42         @votes = self.rankings.sort.collect { |ranking| ranking.candidate.id }
43       end
44     end
45
46     @votes
47   end
48
49   def votes=(array)
50     @votes = array
51   end
52
53   def save_rankings
54     self.votes # i need to initalize this before destroying rankings
55                # or else the ranks themselves show up as nil
56
57     destroy_rankings
58     self.votes.each_with_index do |candidate_id, index| 
59       ranking = Ranking.new
60       ranking.rank = index
61       ranking.candidate =  Candidate.find(candidate_id)
62       self.rankings << ranking
63     end
64   end
65   
66   def destroy
67     self.destroy_rankings
68     super
69   end
70
71   def destroy_rankings 
72     rankings.each { |ranking| ranking.destroy }
73   end
74
75   def confirm!
76     self.confirmed = 1
77     self.time = Time.now
78     self.save
79     
80     unless self.voter.election.quickvote?
81       token.destroy and token.reload if token
82       self.token = Token.new
83       self.save
84     end
85   end
86
87   def confirm?
88     confirmed == 1
89   end
90   
91   def votestring
92     # create a mapping of candidates ids and the relative order of the
93     # candidates as they appear when sorted alphabetically
94     cand_relnums = {}
95     self.voter.election.candidates.sort.each_with_index do |c, i|
96       cand_relnums[c.id] = i + 1
97     end
98
99     # assemble the votestring
100     self.votes.collect {|v| (cand_relnums[v] + 64).chr}.join("")
101   end
102
103   # the following subroutine is used for quickvotes, but need for elections now
104   # too. It creates a vote with the candidates listed in order of preference 
105   # based on alphabetical order. Meant to be manipulated and then confirmed
106   def set_defaults!  
107     self.votes = self.voter.election.candidates.sort_by { rand }.collect {|c| c.id }
108     self.save
109   end
110          
111 end

Benjamin Mako Hill || Want to submit a patch?