From: Benjamin Mako Hill
Date: Wed, 17 Jun 2009 05:22:34 +0000 (-0400)
Subject: fixed a large number of bugs in the software (see wiki) over a days work
X-Git-Url: https://projects.mako.cc/source/selectricity/commitdiff_plain/83a39529b634c0344a7884d5813e3f7e1a7dbcb4
fixed a large number of bugs in the software (see wiki) over a days work
---
diff --git a/app/controllers/election_controller.rb b/app/controllers/election_controller.rb
index ad74a1b..58c5c47 100644
--- a/app/controllers/election_controller.rb
+++ b/app/controllers/election_controller.rb
@@ -10,8 +10,17 @@ class ElectionController < ApplicationController
require_dependency "voter"
require_dependency "vote"
require_dependency "candidate"
+
+ helper :sparklines
layout 'main'
+
+ before_filter :verify_owner,
+ :except => [:new, :general_information, :create_election]
+ before_filter :verify_not_active,
+ :except => [:new, :general_information, :create_election,
+ :show, :results, :details, :pref_tables]
+
## methods for displaying, creating,
## and manipulating election overview data
####################################################################
@@ -51,18 +60,9 @@ class ElectionController < ApplicationController
end
end
- def create_theme_hash
- target = Hash.new
- params.each do |k,v|
- target[k] = v if k=="top_bar" or k=="default_image" or k=="bg1" \
- or k=="bg2" or k=="bottom_bar"
- end
- return target
- end
-
- # TODO add filter to verify that the person working on or looking at
- # something is the owner
def edit_general_information
+ @sidebar_content = render_to_string :partial => 'progress',
+ :locals => { :page => 'overview' }
@election = Election.find(params[:id])
end
@@ -83,7 +83,7 @@ class ElectionController < ApplicationController
flash[:notice] = 'Election was successfully updated.'
redirect_to :action => 'show', :id => @election
else
- render :action => 'edit'
+ render :action => 'edit_general_information'
end
end
@@ -173,7 +173,7 @@ class ElectionController < ApplicationController
def edit_candidates
@sidebar_content = render_to_string :partial => 'progress',
:locals => { :page => 'candidates' }
- @election = Election.find( params[:id] )
+ @election = Election.find(params[:id] )
end
def add_candidate
@@ -196,12 +196,12 @@ class ElectionController < ApplicationController
end
def delete_candidate
- candidate = Candidate.find( params[:id] )
+ candidate = Candidate.find(params[:candidate] )
candidate.destroy
end
def candidate_picture
- candidate = Candidate.find( params[:id] )
+ candidate = Candidate.find(params[:candidate])
send_data( candidate.picture.data,
:filename => candidate.picture.filename,
:type => candidate.picture.filetype,
@@ -219,7 +219,8 @@ class ElectionController < ApplicationController
@sidebar_content = render_to_string :partial => 'progress',
:locals => { :page => 'voters' }
- @election = Election.find( params[:id] )
+ @election = Election.find(params[:id])
+
if params.has_key?( :raw_voter_list )
process_incoming_voters( params[:raw_voter_list] )
end
@@ -228,7 +229,7 @@ class ElectionController < ApplicationController
end
def delete_voter
- voter = FullVoter.find( params[:id] )
+ voter = FullVoter.find(params[:voter])
voter.destroy
end
@@ -245,40 +246,27 @@ class ElectionController < ApplicationController
## methods for computing and printing results
####################################################################
def results
- @election = Election.find( params[:id] )
- votes = []
-
- @election.voters.each do |voter|
- if voter.vote and voter.vote.confirmed?
- votes << voter.vote.rankings.sort.collect {|vote| vote.candidate_id}
- end
+ @election = Election.find(params[:id])
+
+ if @election.early_results? \
+ or @election.enddate < Time.now
+
+ # render results
+ @sidebar_content = render_to_string(:partial => 'full_results_sidebar')
+ render :template => 'common/results'
+ else
+ redirect_to :action => 'index'
end
-
- @voteobj = CloneproofSSDVote.new(votes)
- @resultobj = @voteobj.result
- @winners = @resultobj.winners
-
- @candidates_by_id = {}
- @election.candidates.each {|cand| @candidates_by_id[cand.id] = cand}
-
end
- def detailed_results
-
- self.results
-
- @voter_list = []
- @vote_list = []
-
- @election.voters.each do |voter|
- if voter.vote and voter.vote.confirmed?
- @voter_list << voter.email
- @vote_list << voter.vote
- end
- end
+ def pref_tables
+ @election = Election.find(params[:id])
+ render :template => 'common/pref_tables_wrapper', :layout => 'basic'
+ end
- @vote_list.sort!
- @vote_list.sort! { |a,b| a.token <=> b.token }
+ def details
+ @election = Election.find(params[:id])
+ render :template => 'common/details'
end
## private methods
@@ -314,5 +302,30 @@ class ElectionController < ApplicationController
voter.save
end
end
+
+ def create_theme_hash
+ target = Hash.new
+ params.each do |k,v|
+ target[k] = v if k=="top_bar" or k=="default_image" or k=="bg1" \
+ or k=="bg2" or k=="bottom_bar"
+ end
+ return target
+ end
+
+ # verify that the person trying to edit the election is the owner
+ def verify_owner
+ election = Election.find(params[:id])
+ unless election.user == session[:user]
+ redirect_to :controller => 'front', :action => 'index'
+ end
+ end
+
+ # verify that the election is not active
+ def verify_not_active
+ election = Election.find(params[:id])
+ unless election.active == 0
+ redirect_to :controller => 'front', :action => 'index'
+ end
+ end
end
diff --git a/app/controllers/quickvote_controller.rb b/app/controllers/quickvote_controller.rb
index 5bf89c7..066fd6b 100644
--- a/app/controllers/quickvote_controller.rb
+++ b/app/controllers/quickvote_controller.rb
@@ -44,6 +44,7 @@ class QuickvoteController < ApplicationController
#Give registered users additional QuickVote functionality
@quickvote.user_id = session[:user][:id] if session[:user]
+ @quickvote.create_candidates
# try to save, if it fails, show the page again (the flash should
# still be intact
diff --git a/app/controllers/voter_controller.rb b/app/controllers/voter_controller.rb
index 9ba619c..1634ccd 100644
--- a/app/controllers/voter_controller.rb
+++ b/app/controllers/voter_controller.rb
@@ -12,6 +12,9 @@ class VoterController < ApplicationController
require_dependency "vote"
require_dependency "election"
+ before_filter :authenticate, :except => [:index, :login, :reminder,
+ :kiosk_ready]
+
def index
if params[:election_id]
@election = Election.find(params[:election_id])
@@ -80,54 +83,23 @@ class VoterController < ApplicationController
end
end
- def pref_tables
- if authenticate
- @election = @voter.election
- @results = @election.results
- @candidates = {}
- @election.candidates.each {|c| @candidates[c.id] = c}
- @names = @election.names_by_id
- render :template => 'common/pref_tables', :layout => 'basic'
- else
- redirect_to :action => 'index'
- end
- end
-
- def details
- if authenticate
- @election = @voter.election
- @votes = @election.votes.select {|v| v.confirmed? }.shuffle
- @voters = @votes.collect {|v| v.voter}.shuffle
- render :action => 'details'
- else
- redirect_to :action => 'index'
- end
- end
-
def review
- if authenticate
- @voter.vote.time = Time.now
- @voter.vote.save
- @voter.reload
- else
- redirect_to :action => 'index'
- end
+ @voter.vote.time = Time.now
+ @voter.vote.save
+ @voter.reload
end
def confirm
- if authenticate
- @voter.vote.confirm!
-
- if @voter.election.embeddable? and params[:embed] == "true" \
- and @voter.election.early_results?
- redirect_to :action => :results, :id => @password, :embed => 'true'
- elsif @voter.election.kiosk and params[:kiosk] = "true"
- redirect_to :action => "kiosk_ready", :id => @password, :kiosk => true
- else
- render :action => 'thanks'
- end
+ @voter.vote.confirm!
+
+ if @voter.election.embeddable? and params[:embed] == "true" \
+ and @voter.election.early_results?
+ redirect_to :action => :results, :id => @password, :embed => 'true'
+ elsif not(@voter.election.verifiable) \
+ and @voter.election.kiosk and params[:kiosk] == "true"
+ redirect_to :action => "kiosk_ready", :id => @password, :kiosk => true
else
- redirect_to :action => 'index'
+ render :action => 'thanks'
end
end
@@ -143,19 +115,12 @@ class VoterController < ApplicationController
end
def results
- if authenticate and
- (@voter.election.early_results? \
- or @voter.election.enddate < Time.now)
+ if @voter.election.early_results? \
+ or @voter.election.enddate < Time.now
@election = @voter.election
- # compute and display results
-
- @results = @election.results
- @candidates = {}
- @election.candidates.each {|c| @candidates[c.id] = c}
- @names = @election.names_by_id
-
- @sidebar_content = render_to_string(:partial => 'results_sidebar')
+ @sidebar_content = render_to_string(:partial => 'full_results_sidebar')
+
#look for custom theme, and assign to instance variabels for widget use
if @election.embed_custom_string
@top_bar = SkinPicture.find(:first,
@@ -172,13 +137,23 @@ class VoterController < ApplicationController
if @election.embeddable? and params[:embed] == "true"
render :template => 'embed/results', :layout => 'embed'
else
- render :action => 'results'
+ render :template => 'common/results'
end
else
redirect_to :action => 'index'
end
end
+ def pref_tables
+ @election = @voter.election
+ render :template => 'common/pref_tables_wrapper', :layout => 'basic'
+ end
+
+ def details
+ @election = @voter.election
+ render :template => 'common/details'
+ end
+
def kiosk_ready
reset_session
@@ -193,21 +168,25 @@ class VoterController < ApplicationController
if password == "open"
election = Election.find(params[:format])
- # check to see if the person has voted before
- unless election.authenticated?
+ # if it's not actually open, lets redirect
+ if election.authenticated
+ redirect_to :action => 'index'
+
+ # otherwise, lets see if they've before
+ else
@voter = OpenVoter.find(:all,
:conditions => ["session_id = ? and election_id = ?",
session.session_id, election.id])[0]
- @password = "open." + election.id.to_s
- end
- # if it's ready for kiosk_mode, then we create and authenticate
- unless @voter and params[:action] == 'kiosk_ready' \
- and election.kiosk
-
- # this is maybe not quite as a dry as it should be
- @voter = OpenVoter.new unless @voter
+ # when (a) there is no voter or (b) when there is a voter but
+ # it's kiosk mode on the right page, rewrite witha blank voter
+ if not(@voter) \
+ or (params[:action] == 'kiosk_ready' and election.kiosk)
+ @voter = OpenVoter.new unless @voter
+ end
+ # now that we have a voter (one way or another), set things
+ # right
@voter.election = election
@voter.session_id = session.session_id
@password = "open." + election.id.to_s
@@ -216,9 +195,13 @@ class VoterController < ApplicationController
else
@voter = FullVoter.find(:all,
:conditions => [ "password = ?", password ] )[0]
- @password = @voter.password
+
+ if @voter
+ @password = @voter.password
+ else
+ redirect_to :Action => 'index'
+ end
end
- @voter
end
end
diff --git a/app/models/election.rb b/app/models/election.rb
index a3e9394..4ce571a 100644
--- a/app/models/election.rb
+++ b/app/models/election.rb
@@ -11,9 +11,12 @@ class Election < ActiveRecord::Base
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
@@ -21,7 +24,7 @@ class Election < ActiveRecord::Base
attr_reader :borda_result
require 'date'
-
+
def initialize(params={})
super
self.enddate = read_attribute( :enddate ) || \
@@ -71,7 +74,7 @@ class Election < ActiveRecord::Base
def activate!
self.active = 1
- self.save!
+ self.save
end
def quickvote?
@@ -164,6 +167,30 @@ class Election < ActiveRecord::Base
names
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
+
+ private
+ def enforce_constraints
+ # kiosks can't be authenticated
+ self.authenticated = false if kiosk?
+ return true
+ end
+
end
diff --git a/app/models/quick_vote.rb b/app/models/quick_vote.rb
index de78813..f9e1b83 100644
--- a/app/models/quick_vote.rb
+++ b/app/models/quick_vote.rb
@@ -1,6 +1,5 @@
class QuickVote < Election
before_validation :build_candidate_names
- after_validation :create_candidates
validates_uniqueness_of :name
attr_accessor :candidate_names
@@ -60,8 +59,8 @@ class QuickVote < Election
def build_candidate_names
@candidate_names ||= []
- if @candidate_names.empty? and not candidates.empty?
- @candidate_names = candidates.collect {|c| c.name}
+ if @candidate_names.empty? and not self.candidates.empty?
+ @candidate_names = self.candidates.collect {|c| c.name}
end
end
diff --git a/app/views/account/summary.rhtml b/app/views/account/summary.rhtml
index b53122e..0ea9f24 100644
--- a/app/views/account/summary.rhtml
+++ b/app/views/account/summary.rhtml
@@ -21,12 +21,8 @@ Member since: <%=h @user.created_at.strftime("%x") %>
<% @user.elections.select {|e| e.instance_of?(Election)}.each do |election| %>
- <% if election.active == 1 -%>
<%= link_to "#{election.name}", :controller => 'election',
:action => 'show', :id => election %>
- <% else -%>
- <%=h election.name %>
- <% end -%>
|
<%=h election.description %> |
diff --git a/app/views/common/_pref_tables.rhtml b/app/views/common/_pref_tables.rhtml
index 3a53144..3edc05a 100644
--- a/app/views/common/_pref_tables.rhtml
+++ b/app/views/common/_pref_tables.rhtml
@@ -1,7 +1,13 @@
+
+<% @election.results! %>
+
+
<% candidates = @election.ssd_result.ranked_candidates.flatten -%>
<% voters = @election.voters.size %>
<% matrix = @election.ssd_result.matrix %>
<% victories = @election.ssd_result.victories_and_ties %>
+<% @names = @election.names_by_id %>
+
Each number in the table below shows how many times the candidate on
the left beat the matching candidate on the top. The winner is on the
top of the left column.
diff --git a/app/views/voter/details.rhtml b/app/views/common/details.rhtml
similarity index 50%
rename from app/views/voter/details.rhtml
rename to app/views/common/details.rhtml
index cf73f41..ae09c29 100644
--- a/app/views/voter/details.rhtml
+++ b/app/views/common/details.rhtml
@@ -1,3 +1,6 @@
+<% @votes = @election.votes.select {|v| v.confirmed? }.shuffle %>
+<% @voters = @votes.collect {|v| v.voter}.shuffle %>
+
diff --git a/app/views/election/detailed_results.rhtml b/app/views/election/detailed_results.rhtml
deleted file mode 100644
index 1f22377..0000000
--- a/app/views/election/detailed_results.rhtml
+++ /dev/null
@@ -1,52 +0,0 @@
-Result
-
-<%= render :partial => 'winner' %>
-
-Result Details
-
-<%= render :partial => 'winner_details' %>
-
-Election Rolls for Voter Verification
-
-The voting rolls -- displayed here in alphabetical order -- for the
-last election are are follows.
-
-Information is displayed here to help voters verifying that their own
-vote was recorded correctly and that the election was not tampered
-with.
-
-Voters
-
-Voters (A-Z) |
-<% for email in @voter_list %>
-
-<%= email %> |
-
-<% end %>
-
-
-Votes by Token
-
-The votes, listed in alphabetical order by token.
-
-
-
- Token (0-9, A-Z) |
- Rank of Candidates |
-
-
-
-<% for candidate in @election.candidates.sort.reverse %>
- <%= candidate %> |
-<% end %>
-
-
-<% for vote in @vote_list %>
-
- <%= vote.token %> |
- <% for ranking in vote.rankings %>
- <%= ranking %> |
- <% end %>
-
-<% end %>
-
diff --git a/app/views/election/edit_candidates.rhtml b/app/views/election/edit_candidates.rhtml
index 7e1f315..cd0bd5a 100644
--- a/app/views/election/edit_candidates.rhtml
+++ b/app/views/election/edit_candidates.rhtml
@@ -5,6 +5,8 @@
+<% if @election.active == 0 %>
+
->Anyone will be able to
-vote in this election.
+<% if @election.active == 0 %>
+
+>
+<% if @election.kiosk? %>
+ Because you have enabled kiosk mode, there will be no registration of voters.
+<% end %>
+Anyone will be able to vote in this election.
>
<%= render :partial => 'voter_list' %>
@@ -14,7 +19,9 @@ vote in this election.
<% end %>
-<%= check_box :election, :authenticated %> Only allow registered voters
+<% unless @election.kiosk? %>
+ <%= check_box :election, :authenticated %> Only allow registered voters
+<% end %>
<%= observe_field "election_authenticated",
:url => { :action => 'toggle_authenticated', :id => @election.id },
@@ -32,4 +39,13 @@ vote in this election.
When you are done entering voters, please click the button below
to proceed to the next step.
-<%= button_to 'Proceed to Next Step!', :action => 'show', :id => @election.id %>
+<%= button_to 'Proceed to Next Step', :action => 'show', :id => @election.id %>
+
+<% else %>
+
+You can not edit the list of voters once the election has begun.
+please return to the <%= link_to "election overview page", :action =>
+'show', :id => @election.id %>.
+
+<% end %>
+
diff --git a/app/views/election/show.rhtml b/app/views/election/show.rhtml
index 2f62895..39621bf 100644
--- a/app/views/election/show.rhtml
+++ b/app/views/election/show.rhtml
@@ -8,13 +8,13 @@
:election_id => @election.id %>.
<% elsif (@election.active? && @election.early_results? ) %>
- The creator of this election has decided that the results
+ You have decided that the results
should be viewable while the election is in progress.
- <%if @election.voters.empty? %>
+ <%if @election.voters.select {|v| v.vote.confirmed?}.empty? %>
However, no one has voted yet.
<% else %>
- <%= link_to "View results", :controller => 'voter', :action => 'results',
- :id => "open" %>.
+ <%= link_to "View results", :controller => 'election', :action => 'results',
+ :id => @election.id %>.
<% end %>
<% elsif @election.active? %>
diff --git a/app/views/voter/_results_sidebar.rhtml b/app/views/voter/_full_results_sidebar.rhtml
similarity index 99%
rename from app/views/voter/_results_sidebar.rhtml
rename to app/views/voter/_full_results_sidebar.rhtml
index 81cc907..b85390f 100644
--- a/app/views/voter/_results_sidebar.rhtml
+++ b/app/views/voter/_full_results_sidebar.rhtml
@@ -4,4 +4,3 @@
Details
<%= link_to "Auditing Information", { :action => 'details', :id => @password }, :popup => [] %>
-
diff --git a/app/views/voter/review.rhtml b/app/views/voter/review.rhtml
index b831aef..a72ecda 100644
--- a/app/views/voter/review.rhtml
+++ b/app/views/voter/review.rhtml
@@ -24,15 +24,28 @@ preferred to least preferred:
Please select from one of the following pages.
- <%= button_to 'Confirm This Vote', :action => 'confirm', :id => @password %>
-
- If you choose, you will be able to go back and change it up until
- the end of the voting period.
+ <% if @voter.election.kiosk? and params[:kiosk] == 'true' %>
+ <%= button_to 'Confirm This Vote', :action => 'confirm', :id => @password,
+ :kiosk => true %>
+ <% else %>
+ <%= button_to 'Confirm This Vote', :action => 'confirm', :id => @password %>
+ <% end %>
+
+
+ <% unless @voter.election.kiosk? %>
+ If you choose, you will be able to go back and change it up until
+ the end of the voting period.
+ <% end %>
- <%= button_to 'Discard This Vote', votepassword_url(
- :action => 'index', :urlpassword => @password) %>
-
- You will be returned to the voting page to vote again, if you choose.
-
+ <% if @voter.election.kiosk? and params[:kiosk] == 'true' %>
+ <%= button_to 'Discard This Vote',
+ votepassword_url(:action => 'index', :urlpassword => @password,
+ :kiosk => true) %>
+ <% else %>
+ <%= button_to 'Discard This Vote',
+ votepassword_url(:action => 'index', :urlpassword => @password) %>
+ <% end %>
+
+
|
diff --git a/app/views/voter/thanks.rhtml b/app/views/voter/thanks.rhtml
index 7562c61..c68e22b 100644
--- a/app/views/voter/thanks.rhtml
+++ b/app/views/voter/thanks.rhtml
@@ -13,3 +13,12 @@
end of the election, 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.
+
+<% if @voter.election.kiosk? and params[:kiosk] == 'true' %>
+
+Please click the done button below when finished to reset the system
+for the next voter.
+
+<%= button_to "Done", :action => "kiosk_ready", :id => @password, :kiosk => true %>
+
+<% end %>