production.log
server.log
test.log
+tmp
-require 'uniq_token'
-
class ElectionsController < ApplicationController
model :raw_voter_list, :voter, :vote, :candidate
voter.destroy
end
+ def summary_results
+ end
+
+ def detailed_results
+ @election = Election.find( params[:id] )
+ @voting_rolls = []
+ @election.voters.each do |voter|
+ if voter.vote and voter.vote.confirmed?
+ @voting_rolls << voter
+ end
+ end
+ end
+
private
+ def randomize_order
+ end
def process_incoming_voters(raw_voter_list)
incoming_voters = RawVoterList.new( raw_voter_list )
- token_generator = UniqueTokenGenerator.new( 16 )
unless incoming_voters.entries.empty?
incoming_voters.each do |new_voter|
- until new_voter.password and \
- Voter.find_all( [ "password = ?", new_voter.password ]).empty?
- new_voter.password = token_generator.token
- end
-
- breakpoint
if incoming_voters.email == 0
new_voter.contacted = 1
elsif incoming_voters.email == 1
def index
password = params[:id]
- @voter = Voter.find_all( [ "password = ?", password ] )[0]
+ password = params[:vote][:password] if params[:vote]
+ if @voter = Voter.find_all( [ "password = ?", password ] )[0]
+ render :action => 'vote'
+ end
end
def review
- password = params[:id]
- @voter = Voter.find_all( [ "password = ?", password ] )[0]
+ if authenticate
+ # remove any existing votes and reload
+ if @voter.vote
+ @voter.vote.destroy
+ @voter.reload
+ end
- # destroy the old vote if that's what we need to do
- @voter.vote.destroy if @voter.vote
- @voter.reload
+ @vote = Vote.new
+ @voter.vote = @vote
+ @vote.votestring = params[:vote][:votestring]
+ @vote.save
+ else
+ redirect_to :action => 'index'
+ end
+ end
- @voter.vote = Vote.new
- @voter.vote.votestring = params[:vote][:votestring]
- @voter.vote.save
- render_text "success"
+ def confirm
+ if authenticate
+ @voter.vote.confirm!
+ render :action => 'thanks'
+ else
+ redirect_to :action => 'index'
+ end
end
+ private
+ def authenticate
+ password = params[:id]
+ @voter = Voter.find_all( [ "password = ?", password ] )[0]
+ end
end
def <=>(other)
self.name <=> other.name
end
+
+ def to_s
+ name
+ end
end
class Ranking < ActiveRecord::Base
belongs_to :candidate
belongs_to :vote
+
+ def <=>(other)
+ self.rank <=> other.rank
+ end
+
end
--- /dev/null
+class Token < ActiveRecord::Base
+ belongs_to :vote
+
+ def initialize
+ super
+
+ token_generator = UniqueTokenGenerator.new( 16 )
+ until not token.empty? and Token.find_all( [ "token = ?", token ]).empty?
+ self.token = token_generator.token
+ end
+
+ self
+ end
+
+ def to_s
+ self.token
+ end
+end
class Vote < ActiveRecord::Base
+ # relationships to other classes
belongs_to :voter
has_many :rankings
+ has_one :token
- def initialize
- super
- @votes = []
+ # callbacks
+ after_update :save_rankings
+ before_destroy :destroy_rankings
+
+ def to_s
+ votes.join("")
end
-
- def votestring=(string="")
- rel_votes = string.split("").collect { |vote| vote.to_i }
-
- # covert relative orders to absolute candidate ids
- candidate_ids = voter.election.candidates.sort
- candidate_ids.collect! { |candidate| candidate.id.to_i }
-
- rel_votes.collect! { |vote| candidate_ids[ vote - 1 ] }
- @votes = rel_votes
+
+ def each
+ votes.each {|vote| yield vote}
+ end
+
+ def votes
+ unless @votes
+ if rankings.empty?
+ @votes = Array.new
+ else
+ @votes = rankings.sort.collect { |ranking| ranking.candidate.id }
+ end
+ end
+
+ @votes
+ end
+
+ def votes=(array)
+ @votes = array
end
- def save
- rankings.each { destroy } unless rankings.empty?
- @votes.each_with_index do |candidate, index|
+ def save_rankings
+ destroy_rankings
+ self.votes.each_with_index do |candidate, index|
ranking = Ranking.new
ranking.rank = index + 1
ranking.candidate = Candidate.find(candidate)
self.rankings << ranking
end
-
- super
end
- def destroy
- rankings.each { destroy }
- super
+ def destroy_rankings
+ rankings.each { |ranking| ranking.destroy }
+ end
+
+ def confirm!
+ self.confirmed = 1
+ self.save
+
+ token.destroy and token.reload if token
+ self.token = Token.new
+ self.save
+ end
+
+ def confirm?
+ if confirm == 1
+ return true
+ else
+ return false
+ end
+ end
+
+ def votestring=(string="")
+ candidate_ids = voter.election.candidates.sort.collect \
+ { |candidate| candidate.id.to_i }
+
+ rel_votes = string.split("").collect { |vote| vote.to_i }
+
+ # covert relative orders to absolute candidate ids
+ self.votes = rel_votes.collect { |vote| candidate_ids[ vote - 1 ] }
end
end
class Voter < ActiveRecord::Base
belongs_to :election
has_one :vote
+
+ def initialize(args)
+ super(args)
+
+ token_generator = UniqueTokenGenerator.new( 16 )
+ until password and Voter.find_all( [ "password = ?", password ]).empty?
+ self.password = token_generator.token
+ end
+ end
+
end
+
+
+
--- /dev/null
+<p>The voting rolls for the last election are are follows.</p>
+
+<h2>Voters</h2>
+<table>
+<% for voter in @voting_rolls.randomize %>
+<tr>
+<td><%= voter.email %></td>
+</tr>
+<% end %>
+</table>
+
+<h2>Votes (by Token)</h2>
+<table border="1">
+<tr>
+ <th rowspan="2">Token</th>
+ <th colspan="<%= @election.candidates.length %>">Rank of Candidates</th>
+</tr>
+<tr>
+<% for candidate in @election.candidates.sort %>
+ <th><%= candidate %></th>
+<% end %>
+</tr>
+<% for voter in @voting_rolls.randomize %>
+<tr>
+<td><%= voter.vote.token %></td>
+<% for ranking in voter.vote %>
+<td><%= ranking %></td>
+<% end %>
+</tr>
+<% end %>
+</table>
<% %>
-<h1>Vote Below the Line</h1>
+<p>Please enter your password/token to log in and vote:</p>
-<p><strong>Election:</strong> <%= @voter.election.name %></p>
-
-<p><strong>Voter:</strong> <%= @voter.email %></p>
-
-<p><strong>Candidates:</strong></p>
-
-<ol>
-<% for candidate in @voter.election.candidates %>
- <li><%= candidate.name %></li>
-<% end %>
-</ol>
-
-<p>If this information is incorrect, please notify the vote
-administrator immediatedly!</p>
-
-<hr />
-
-<h2>Place Your Vote Here</h2>
-
-<p>Rank each candidate in order of more preferred to least
-preferred. (e.g., 123 or 321 or 213, etc.)</p>
-
-<%= form_tag :action => 'review', :id => @voter.password %>
-<%= text_field :vote, :votestring -%>
-<%= submit_tag "Submit!" %>
+<%= form_tag :action => 'index' %>
+<%= text_field :vote, :password %>
+<%= submit_tag "Log In" %>
<%= end_form_tag %>
-
-
-
-
-
--- /dev/null
+<% %>
+
+<h1>Please review your vote carefully before confirming it.</h1>
+
+<p>You have ranked the candidates in the following order (from most
+preferred to least preferred:</p>
+
+<ol>
+ <% for rank in @vote.rankings.sort %>
+ <li><%= rank.candidate.name %> </li>
+ <% end %>
+</ol>
+
+<table>
+<tr>
+ <td valign="top"><%= button_to 'Confirm', :action => 'confirm', :id => @voter.password %></td>
+ <td valign="top">Confirm this vote now. You will be able to go back and
+ change it.</td>
+</tr>
+
+<tr>
+ <td valign="top"><%= button_to 'Change', :action => 'index', :id => @voter.password %></td>
+ <td valign="top">Go back to the voting page and vote again.</td>
+</tr>
+
+<tr>
+ <td valign="top"><%= button_to 'Discard', :action => 'discard' %></td>
+ <td valign="top">Discard this tentative vote and log out.</td>
+</tr>
+
+</table>
--- /dev/null
+<% %>
+
+<p>Your vote has been recorded.</p>
+
+<p>Your unique token for this vote is: <strong><%= @voter.vote.token %></strong></p>
+
+<p>Please record this token for your records and keep it secret. You
+will be able to use this token to verify that your vote was used in the
+election and that your vote was recorded correctly.</p>
--- /dev/null
+<% %>
+
+<h1>Vote Below the Line</h1>
+
+<p><strong>Election:</strong> <%= @voter.election.name %></p>
+
+<p><strong>Voter:</strong> <%= @voter.email %></p>
+
+<p><strong>Candidates:</strong></p>
+
+<ol>
+<% for candidate in @voter.election.candidates.sort %>
+ <li><%= candidate.name %></li>
+<% end %>
+</ol>
+
+<p>If this information is incorrect, please notify the vote
+administrator immediatedly!</p>
+
+<hr />
+
+<h2>Place Your Vote Here</h2>
+
+<p>Rank each candidate in order of more preferred to least
+preferred. (e.g., 123 or 321 or 213, etc.)</p>
+
+<%= form_tag :action => 'review', :id => @voter.password %>
+<%= text_field :vote, :votestring -%>
+<%= submit_tag "Submit!" %>
+<%= end_form_tag %>
+
+
+
+
+
# inflect.uncountable %w( fish sheep )
# end
-# Include your application configuration below
\ No newline at end of file
+# Include your application configuration below
+
+require 'uniq_token'
+require 'randarray'
create table tokens(
id int NOT NULL auto_increment,
token varchar(100) NOT NULL,
+ vote_id int NOT NULL,
+ constraint fk_vote_token foreign key (vote_id) references vote(id),
primary key (id)
);
create table votes (
id int NOT NULL auto_increment,
voter_id int DEFAULT NULL,
- token_id int DEFAULT NULL,
+ confirmed tinyint NOT NULL DEFAULT 0,
constraint fk_vote_voter foreign key (voter_id) references voters(id),
- constraint fk_vote_token foreign key (token_id) references token(id),
primary key (id)
);
--- /dev/null
+class Array
+ # Chooses a random array element from the receiver based on the weights
+ # provided. If _weights_ is nil, then each element is weighed equally.
+ #
+ # [1,2,3].random #=> 2
+ # [1,2,3].random #=> 1
+ # [1,2,3].random #=> 3
+ #
+ # If _weights_ is an array, then each element of the receiver gets its
+ # weight from the corresponding element of _weights_. Notice that it
+ # favors the element with the highest weight.
+ #
+ # [1,2,3].random([1,4,1]) #=> 2
+ # [1,2,3].random([1,4,1]) #=> 1
+ # [1,2,3].random([1,4,1]) #=> 2
+ # [1,2,3].random([1,4,1]) #=> 2
+ # [1,2,3].random([1,4,1]) #=> 3
+ #
+ # If _weights_ is a symbol, the weight array is constructed by calling
+ # the appropriate method on each array element in turn. Notice that
+ # it favors the longer word when using :length.
+ #
+ # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "hippopotamus"
+ # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "dog"
+ # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "hippopotamus"
+ # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "hippopotamus"
+ # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "cat"
+ def random(weights=nil)
+ return random(map {|n| n.send(weights)}) if weights.is_a? Symbol
+
+ weights ||= Array.new(length, 1.0)
+ total = weights.inject(0.0) {|t,w| t+w}
+ point = rand * total
+
+ zip(weights).each do |n,w|
+ return n if w >= point
+ point -= w
+ end
+ end
+
+ # Generates a permutation of the receiver based on _weights_ as in
+ # Array#random. Notice that it favors the element with the highest
+ # weight.
+ #
+ # [1,2,3].randomize #=> [2,1,3]
+ # [1,2,3].randomize #=> [1,3,2]
+ # [1,2,3].randomize([1,4,1]) #=> [2,1,3]
+ # [1,2,3].randomize([1,4,1]) #=> [2,3,1]
+ # [1,2,3].randomize([1,4,1]) #=> [1,2,3]
+ # [1,2,3].randomize([1,4,1]) #=> [2,3,1]
+ # [1,2,3].randomize([1,4,1]) #=> [3,2,1]
+ # [1,2,3].randomize([1,4,1]) #=> [2,1,3]
+ def randomize(weights=nil)
+ return randomize(map {|n| n.send(weights)}) if weights.is_a? Symbol
+
+ weights = weights.nil? ? Array.new(length, 1.0) : weights.dup
+
+ # pick out elements until there are none left
+ list, result = self.dup, []
+ until list.empty?
+ # pick an element
+ result << list.random(weights)
+ # remove the element from the temporary list and its weight
+ weights.delete_at(list.index(result.last))
+ list.delete result.last
+ end
+
+ result
+ end
+end
--- /dev/null
+class Array
+ # Chooses a random array element from the receiver based on the weights
+ # provided. If _weights_ is nil, then each element is weighed equally.
+ #
+ # [1,2,3].random #=> 2
+ # [1,2,3].random #=> 1
+ # [1,2,3].random #=> 3
+ #
+ # If _weights_ is an array, then each element of the receiver gets its
+ # weight from the corresponding element of _weights_. Notice that it
+ # favors the element with the highest weight.
+ #
+ # [1,2,3].random([1,4,1]) #=> 2
+ # [1,2,3].random([1,4,1]) #=> 1
+ # [1,2,3].random([1,4,1]) #=> 2
+ # [1,2,3].random([1,4,1]) #=> 2
+ # [1,2,3].random([1,4,1]) #=> 3
+ #
+ # If _weights_ is a symbol, the weight array is constructed by calling
+ # the appropriate method on each array element in turn. Notice that
+ # it favors the longer word when using :length.
+ #
+ # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "hippopotamus"
+ # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "dog"
+ # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "hippopotamus"
+ # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "hippopotamus"
+ # ['dog', 'cat', 'hippopotamus'].random(:length) #=> "cat"
+ def random(weights=nil)
+ return random(map {|n| n.send(weights)}) if weights.is_a? Symbol
+
+ weights ||= Array.new(length, 1.0)
+ total = weights.inject(0.0) {|t,w| t+w}
+ point = rand * total
+
+ zip(weights).each do |n,w|
+ return n if w >= point
+ point -= w
+ end
+ end
+
+ # Generates a permutation of the receiver based on _weights_ as in
+ # Array#random. Notice that it favors the element with the highest
+ # weight.
+ #
+ # [1,2,3].randomize #=> [2,1,3]
+ # [1,2,3].randomize #=> [1,3,2]
+ # [1,2,3].randomize([1,4,1]) #=> [2,1,3]
+ # [1,2,3].randomize([1,4,1]) #=> [2,3,1]
+ # [1,2,3].randomize([1,4,1]) #=> [1,2,3]
+ # [1,2,3].randomize([1,4,1]) #=> [2,3,1]
+ # [1,2,3].randomize([1,4,1]) #=> [3,2,1]
+ # [1,2,3].randomize([1,4,1]) #=> [2,1,3]
+ def randomize(weights=nil)
+ return randomize(map {|n| n.send(weights)}) if weights.is_a? Symbol
+
+ weights = weights.nil? ? Array.new(length, 1.0) : weights.dup
+
+ # pick out elements until there are none left
+ list, result = self.dup, []
+ until list.empty?
+ # pick an element
+ result << list.random(weights)
+ # remove the element from the temporary list and its weight
+ weights.delete_at(list.index(result.last))
+ list.delete result.last
+ end
+
+ result
+ end
+end
--- /dev/null
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+first:
+ id: 1
+another:
+ id: 2
--- /dev/null
+require File.dirname(__FILE__) + '/../test_helper'
+
+class TokenTest < Test::Unit::TestCase
+ fixtures :tokens
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end