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
5 # This program is free software. Please see the COPYING file for
8 class Election < ActiveRecord::Base
13 validates_presence_of :name, :description
15 #validate that method is one of the listed election types
17 attr_reader :plurality_result
18 attr_reader :approval_result
19 attr_reader :condorcet_result
20 attr_reader :ssd_result
21 attr_reader :borda_result
25 def initialize(params={})
27 self.enddate = read_attribute( :enddate ) || \
28 Time.now + 30.days - 1.second
33 @other_methods = ELECTION_TYPES.keys.reject {|i| i == election_method}
41 read_attribute( :startdate ) || Time.now
46 self.voters.each do |voter|
53 self.candidates.each do |candidate|
61 if self.candidates.length <= 1
62 reasons << "You must have at least two candidates."
65 if self.voters.length <= 1 and self.authenticated?
66 reasons << "You must have at least two voters."
78 self.class == QuickVote
94 shortdesc = description.split(/\n/)[0]
98 longdesc = description.split(/\n/)[1..-1].join("")
99 longdesc.length > 0 ? longdesc : nil
102 #Calculate results if not in memcache
104 # Assignment is intentional
105 if Cache and c = Cache.get("election_results:#{id}:#{self.votes.length}")
106 @plurality_result = c['plurality']
107 @approval_result = c['approval']
108 @condorcet_result = c['condorcet']
109 @ssd_result = c['ssd']
110 @borda_result = c['borda']
113 # memcache is available, but missed.
114 results = self.results!
115 Cache.set("election_results:#{id}:#{self.votes.length}", results)
122 #Always Calculate Election Results
124 # initalize the tallies to empty arrays
125 preference_tally = Array.new
126 plurality_tally = Array.new
127 approval_tally = Array.new
129 self.voters.each do |voter|
130 # skip if the voter has not voted or has an unconfirmed vote
131 next unless voter.voted?
133 plurality_tally << voter.vote.rankings.sort[0].candidate.id
134 approval_tally << voter.vote.rankings.sort[0..1].collect \
135 { |ranking| ranking.candidate.id }
136 preference_tally << voter.vote.rankings.sort.collect \
137 { |ranking| ranking.candidate.id }
140 @plurality_result = PluralityVote.new(plurality_tally).result
141 @approval_result = ApprovalVote.new(approval_tally).result
142 @condorcet_result = PureCondorcetVote.new(preference_tally).result
143 @ssd_result = CloneproofSSDVote.new(preference_tally).result
144 @borda_result = BordaVote.new(preference_tally).result
146 { 'plurality' => @plurality_result,
147 'approval' => @approval_result,
148 'condorcet' => @condorcet_result,
149 'ssd' => @ssd_result,
150 'borda' => @borda_result }
156 competitors = self.candidates.sort.collect {|candidate| candidate.id}
157 competitors.each do |candidate|
158 names[candidate] = Candidate.find(candidate).name