+# Selectricity: Voting Machinery for the Masses
+# Copyright (C) 2007, 2008 Benjamin Mako Hill <mako@atdot.cc>
+# Copyright (C) 2007 Massachusetts Institute of Technology
+#
+# This program is free software. Please see the COPYING file for
+# details.
+
class Election < ActiveRecord::Base
has_many :candidates
has_many :voters
has_many :votes
belongs_to :user
validates_presence_of :name, :description
-
+
+ # enforce constraints associated with dependencies (i.e., a kiosk
+ # election can't also be unauthenticated)
+ before_save :enforce_constraints
+
#validate that method is one of the listed election types
-
attr_reader :plurality_result
attr_reader :approval_result
attr_reader :condorcet_result
attr_reader :borda_result
require 'date'
-
+
def initialize(params={})
super
self.enddate = read_attribute( :enddate ) || \
def other_methods
if election_method
- @other_methods = ELECTION_TYPES.reject {|i| i == election_method}
+ @other_methods = ELECTION_TYPES.keys.reject {|i| i == election_method}
else
@other_methods = nil
end
reasons << "You must have at least two candidates."
end
- if self.voters.length <= 1
+ if self.voters.length <= 1 and self.authenticated?
reasons << "You must have at least two voters."
end
def activate!
self.active = 1
- self.save!
+ self.save
end
def quickvote?
- self.class == 'QuickVote'
+ self.class == QuickVote
end
def active?
active == 2
end
+ def authenticated?
+ authenticated
+ end
+
+ def kiosk?
+ kiosk
+ end
+
def shortdesc
shortdesc = description.split(/\n/)[0]
end
# skip if the voter has not voted or has an unconfirmed vote
next unless voter.voted?
- plurality_tally << voter.vote.rankings.sort[0].candidate.id
+ plurality_tally << voter.vote.rankings.sort[0].candidate_id
approval_tally << voter.vote.rankings.sort[0..1].collect \
- { |ranking| ranking.candidate.id }
+ { |ranking| ranking.candidate_id }
preference_tally << voter.vote.rankings.sort.collect \
- { |ranking| ranking.candidate.id }
+ { |ranking| ranking.candidate_id }
end
@plurality_result = PluralityVote.new(plurality_tally).result
names
end
-end
+ def candidate_hash
+ hash = {}
+ self.candidates.each {|c| hash[c.id] = c}
+ return hash
+ end
+
+
+ # TODO now that this code is in here, we should go ahead and remove
+ # date checking from other places in the code
+ def after_find
+ if self.active < 2 and self.enddate < Time.now
+ self.active = 2
+ self.save
+ end
+ end
+ def just_before_midnight(datetime)
+ if datetime.instance_of? DateTime
+ off = datetime.offset
+ else
+ off = Rational(datetime.utc_offset, 60*60*24)
+ end
+ return DateTime.new(datetime.year, datetime.month, datetime.day,
+ 23, 59, 59, off)
+ end
+
+ private
+ def enforce_constraints
+ # elections end just before midnight
+ self.enddate = just_before_midnight(self.enddate)
+ # kiosks can't be authenticated
+ self.authenticated = false if kiosk?
+ return true
+ end
+
+end