]> projects.mako.cc - selectricity-live/blob - app/models/election.rb
merged back from live
[selectricity-live] / app / models / election.rb
1 class Election < ActiveRecord::Base
2   has_many :candidates
3   has_many :voters
4   has_many :votes
5   belongs_to :user
6   validates_presence_of :name, :description
7   
8   #validate that method is one of the listed election types
9   
10   attr_reader :plurality_result
11   attr_reader :approval_result
12   attr_reader :condorcet_result
13   attr_reader :ssd_result
14   attr_reader :borda_result
15   
16   require 'date'
17   
18   def initialize(params={})
19     super
20     self.enddate = read_attribute( :enddate ) || \
21                    Time.now + 30.days - 1.second
22   end
23
24   def other_methods
25     if election_method
26       @other_methods = ELECTION_TYPES.keys.reject {|i| i == election_method}
27     else
28       @other_methods = nil
29     end
30     @other_methods
31   end
32
33   def startdate
34     read_attribute( :startdate ) || Time.now
35   end
36
37   def votes
38     votes = Array.new
39     self.voters.each do |voter|
40       votes << voter.vote
41     end
42     return votes
43   end
44
45   def destroy
46     self.candidates.each do |candidate|
47       candidate.destroy
48     end
49     super
50   end
51
52   def start_blockers
53     reasons = []
54     if self.candidates.length <= 1
55       reasons << "You must have at least two candidates."
56     end
57     
58     if self.voters.length <= 1
59       reasons << "You must have at least two voters."
60     end
61
62     reasons
63   end
64
65   def activate!
66     self.active = 1
67     self.save!
68   end
69   
70   def quickvote?
71     self.class == 'QuickVote'
72   end
73
74   def active?
75     active == 1
76   end 
77
78   def done?
79     active == 2
80   end
81
82   def shortdesc
83     shortdesc = description.split(/\n/)[0]
84   end
85
86   def longdesc
87     longdesc = description.split(/\n/)[1..-1].join("")
88     longdesc.length > 0 ? longdesc : nil 
89   end
90   
91   #Calculate results if not in memcache
92   def results
93     # Assignment is intentional
94     if Cache and c = Cache.get("election_results:#{id}:#{self.votes.length}")
95       @plurality_result = c['plurality']
96       @approval_result = c['approval']
97       @condorcet_result = c['condorcet']
98       @ssd_result = c['ssd']
99       @borda_result = c['borda']
100       return c
101     elsif Cache
102       # memcache is available, but missed.
103       results = self.results!
104       Cache.set("election_results:#{id}:#{self.votes.length}", results)
105       return results
106     else
107       return self.results!
108     end
109   end
110
111   #Always Calculate Election Results
112   def results!
113     # initalize the tallies to empty arrays
114     preference_tally = Array.new
115     plurality_tally = Array.new
116     approval_tally = Array.new
117
118     self.voters.each do |voter|
119       # skip if the voter has not voted or has an unconfirmed vote
120       next unless voter.voted?
121
122       plurality_tally << voter.vote.rankings.sort[0].candidate.id
123       approval_tally << voter.vote.rankings.sort[0..1].collect \
124         { |ranking| ranking.candidate.id }
125       preference_tally << voter.vote.rankings.sort.collect \
126         { |ranking| ranking.candidate.id }
127     end
128     
129     @plurality_result = PluralityVote.new(plurality_tally).result
130     @approval_result = ApprovalVote.new(approval_tally).result
131     @condorcet_result = PureCondorcetVote.new(preference_tally).result
132     @ssd_result = CloneproofSSDVote.new(preference_tally).result
133     @borda_result = BordaVote.new(preference_tally).result
134     
135     { 'plurality' => @plurality_result,
136       'approval' => @approval_result,
137       'condorcet' => @condorcet_result,
138       'ssd' => @ssd_result,
139       'borda' => @borda_result }
140     end
141   
142   def names_by_id
143     names = Hash.new
144     
145     competitors = self.candidates.sort.collect {|candidate| candidate.id}
146     competitors.each do |candidate|
147       names[candidate] = Candidate.find(candidate).name
148     end
149     
150     names
151   end
152 end
153
154

Benjamin Mako Hill || Want to submit a patch?