Added a new bar graph, that counts how many points the borda system of
author<jlsharps@mit.edu> <>
Fri, 10 Aug 2007 22:47:33 +0000 (18:47 -0400)
committer<jlsharps@mit.edu> <>
Fri, 10 Aug 2007 22:47:33 +0000 (18:47 -0400)
voting has asssigned to every candidate, it shows the margins fo victory
and loss clearly. Also had to make modifications to the RubyVote libary.
There was an error in BordaVote that prevented it from working correctly,
which has been fixed. Also added an attr_reader to BordaResult for
ranked_candidates. About to delete the Gruff Plugin v 0.1.2 and replace it
with Gruff gem v 0.2.8.

app/controllers/election_controller.rb
app/controllers/graph_controller.rb
app/controllers/quickvote_controller.rb
app/controllers/site_controller.rb
app/views/quickvote/results.rhtml
lib/rubyvote/positional.rb

index e950153e7eae013345c1c79670c1bd18a68ced4b..df0aa7c53631ab968b7d2f6bf963172645ae4f3f 100644 (file)
@@ -196,15 +196,15 @@ class ElectionController < ApplicationController
 
           if incoming_voters.email == 0
             new_voter.contacted = 1
-         elsif incoming_voters.email == 1
-           email_voter( new_voter )
-           new_voter.contacted = 1
-         else
-           new_voter.contacted = 0
-         end
+               elsif incoming_voters.email == 1
+                 email_voter( new_voter )
+            new_voter.contacted = 1
+               else
+                 new_voter.contacted = 0
+          end
        
           # the new voter should be in good shape. save add to the election
-         new_voter.save
+               new_voter.save
           @election.voters << new_voter
         end
       end
index 9ab2a4c1a5a8ccacfb463c0ac7046755e7be19c2..7b593cc99a66c3d6732651cdf1588d8e80c1f415 100644 (file)
@@ -11,21 +11,20 @@ class GraphController < ApplicationController
     line.font = File.expand_path('/usr/X11R6/lib/X11/fonts/TTF/Vera.ttf',
                                  RAILS_ROOT)
 
-    line.data("#{@election.name}", data )
+    line.data( "#{@election.name}", data )
     line.labels = labels
 
     line.x_axis_label = "Date"
     line.y_axis_label = "Number of Votes"
     line.minimum_value = 0.0
 
-    line.draw
     send_data(line.to_blob, :disposition => 'inline', :type => 'image/png')
   end
   
   #will place votes in a fixed number of intervals, and shows votes over time
   def votes_per_interval
     @election = Election.find(params[:id])
-    data, labels = get_votes_per_interval_data(@election)
+    data, labels, scale = get_votes_per_interval_data(@election)
     
     line = Gruff::Line.new("700x400")
     line.theme = { :background_colors => ['#73BF26', '#ffffff'] }
@@ -36,11 +35,10 @@ class GraphController < ApplicationController
     line.data("#{@election.name}", data )
     line.labels = labels
     
-    line.x_axis_label = "Intervals"
+    line.x_axis_label = scale
     line.y_axis_label = "Number of Votes"
     line.minimum_value = 0.0
                                
-    line.draw
     send_data(line.to_blob, :disposition => 'inline', :type => 'image/png')  
   end
  
@@ -50,19 +48,28 @@ class GraphController < ApplicationController
   
   def borda_bar
     @election = Election.find(params[:id])
+    pref_tally = make_preference_tally(@election)
+    
+    @borda_result = BordaVote.new(pref_tally).result
+    data, labels = get_borda_points(@borda_result)
     
-    #Get the list of candidates from the election, and calculate how RubyVote
-    #gave each one points
-    @election.candidates.each do |candidate|
+    bar = Gruff::Bar.new("700x400")
+    bar.theme = { :background_colors => ['#73BF26', '#ffffff'] }
+    bar.title = "Points Per Candidate"
+    bar.font = File.expand_path('/usr/X11R6/lib/X11/fonts/TTF/Vera.ttf',
+                               RAILS_ROOT)
+                      
+    bar.data("#{@election.name}", data)
+    bar.labels = labels
     
-    #Tabulate how many points each candidate received
-    #Make the name of each candidate a label under the correspoding column
-    #done!
+    bar.y_axis_label = "Points"
+    bar.x_axis_label = "Candidate"
+    bar.minimum_value = 0.0
+    send_data(bar.to_blob, :disposition => 'inline', :type => 'image/png')
   end
-  
  
  private 
+   
   # generate the data and labels for each graph
   def get_votes_per_day_data(election)
     voter_days = Array.new
@@ -111,6 +118,7 @@ class GraphController < ApplicationController
     labels_hash = Hash.new
     buckets = Hash.new
     total_per_interval = Array.new
+    interval_type = ""
     
     starttime = election.startdate
     timedelta = Time.now - starttime
@@ -142,18 +150,52 @@ class GraphController < ApplicationController
       labels_hash[0] = starttime.min.to_s
       labels_hash[(numcols/2)-1] = (starttime + (timedelta/2)).min.to_s
       labels_hash[numcols-1] = Time.now.min.to_s
+      interval_type = "Minutes"
     elsif timedelta < 2.days #more than 2 hours means use hours for labels
       labels_hash[0] = starttime.hour.to_s
       labels_hash[(numcols/2)-1] = (starttime + (timedelta/2)).hour.to_s
       labels_hash[numcols-1] = Time.now.hour.to_s
+      interval_type = "Hours"
     else #more than 2 days means use dates for labels
       labels_hash[0] = (Date.parse(starttime.to_s)).to_s
       labels_hash[(numcols/2)-1] = (Date.parse(starttime + (timedelta/2))).to_s
       labels_hash[numcols-1] = (Date.today).to_s
+      interval_type = "Days"
     end
     
     # Make sure to return an array for data and hash for labels
-    return total_per_interval, labels_hash   
+    return total_per_interval, labels_hash, interval_type   
+  end
+  
+  def get_borda_points(result)
+    #points holds how mnay points each candidate has received in array form
+    #becasue Gruff::Bar#data takes only an array
+    points = Array.new
+    labels = Hash.new
+
+    #Populate points with an sorted array from election.votes hash
+    #biggest to smallest will go from left to right
+    points = result.election.votes.sort do |a, b|
+      b[1] <=> a[1]
+    end.collect {|i| i[1]}
+
+    #make the labels  
+    result.ranked_candidates.each_with_index do |candidate, index|
+      labels[index] = Candidate.find(candidate).name
+    end
+
+    return points, labels
+  end
+
+  #most vote result objects require an array of vote arrays, which this will make
+  def make_preference_tally(election)
+    preference_tally = Array.new
+    @election.voters.each do |voter|
+      next unless voter.voted?
+      preference_tally << voter.vote.rankings.sort.collect \
+        { |ranking| ranking.candidate.id }
+    end
+  return preference_tally
   end
 
 end
index d842f63433cfec8f389b45904a6507f193975ec0..0b745af72e13c79548b227ff83174a79660e9b94 100644 (file)
@@ -63,7 +63,7 @@ class QuickvoteController < ApplicationController
       # if the voter has not voted we destroy them
       if @voter and not @voter.voted?
         @voter.destroy
-       @voter = nil
+       @voter = nil
       end
 
       # if the voter does not exist or has has been destroyed, lets
@@ -73,12 +73,12 @@ class QuickvoteController < ApplicationController
         @voter = QuickVoter.new
         @voter.election = QuickVote.find_all( [ "name = ?", params[:votename] ] )[0]
         @voter.session_id = session.session_id
-
-       # create new vote and make it the defaulted sorted list
+        
+             # create new vote and make it the defaulted sorted list
         @voter.vote = Vote.new
-       @voter.save
-       @voter.vote.set_defaults!
-       @voter.reload
+             @voter.save
+             @voter.vote.set_defaults!
+             @voter.reload
       end
     else
       redirect_to :controller => 'site'
@@ -91,14 +91,14 @@ class QuickvoteController < ApplicationController
 
     # find out who the voter is for this election
     @voter = QuickVoter.find_all(["session_id = ? and election_id = ?", 
-                                  session.session_id, election.id])[0]
-
+                                 session.session_id, election.id])[0]
+  
     if not @voter
       # we have not seen this  voter before. something is wrong, try
       # again
       redirect_to quickvote_url( :votename => params[:votename] ) 
       
-    elsif @voter.voted?
+    elsif @voter.voted? 
       # this person has already voted, we try again
       flash[:notice] = "You have already voted!"
       redirect_to quickvote_url( :votename => params[:votename] )
@@ -108,8 +108,7 @@ class QuickvoteController < ApplicationController
       @voter.ipaddress = request.env["REMOTE_ADDR"]
       @voter.save
       
-      # save the time the vote was made for statistical use, it doesn't
-      #work here unless I use a method that will save it to the db
+      # save the time the vote was made for statistical use
       @voter.vote.time = Time.now
       
       # toggle the confirmation bit      
@@ -143,7 +142,7 @@ class QuickvoteController < ApplicationController
   ###############################################################
 
   def results
-    @election = QuickVote.find_all( ["name = ?", params[:votename]] )[0]
+    @election = QuickVote.find_all(["name = ?", params[:votename]] )[0]
 
     # initalize the tallies to empty arrays
     preference_tally = Array.new
index e86624e5f9f985772fc9a9a0f22eb8b8b419fe6d..a33a1fa16707403589ce4caebd0bb36161bb30d9 100644 (file)
@@ -16,7 +16,7 @@ class SiteController < ApplicationController
           b.enddate <=> a.enddate
         end
       else
-        # if we have no record of them, set the session id back to
+        # if we have no record of them, set the user back to
              # nothing and start again
         session[:user] = nil
         
index 030d2467d925d6d49282e988cdd1bbdf1e79987f..5fdfc3c49624aae03bebe9f91eeab9e09e6deed3 100644 (file)
@@ -163,7 +163,7 @@ by several other names.</p>
 </table>
 
 <%= image_tag( graph_url( :action => 'votes_per_day', :id => @election ) ) %><br />
-<%= image_tag( graph_url(:action => 'votes_per_interval', :id => @election)) %>
-
+<%= image_tag( graph_url( :action => 'votes_per_interval', :id => @election ))%><br />
+<%= image_tag( graph_url( :action => 'borda_bar', :id => @election ) ) %>
 
 
index 11e8a499a9fdceb4a517677545017bb829af8cb8..3de3fb29ae6f20b0371eedacb41471ff73be279f 100644 (file)
@@ -42,7 +42,12 @@ class BordaVote < ElectionVote
   def tally_vote(vote)
     points = candidates.length - 1
     vote.each do |candidate|
-      @votes[candidate] = points
+      #if the candidate exist, add the points, otherwise assign them
+      if @votes.has_key?(candidate)
+        @votes[candidate] += points
+      else
+        @votes[candidate] = points
+      end
       points -= 1
     end
   end
@@ -58,6 +63,8 @@ class BordaVote < ElectionVote
 end
 
 class BordaResult < ElectionResult
+  attr_reader :ranked_candidates
+  
   def initialize(voteobj=nil)
     super(voteobj)
     votes = @election.votes

Benjamin Mako Hill || Want to submit a patch?