From d86d7d89145230100980ca13bf44083ae1e954cb Mon Sep 17 00:00:00 2001 From: John Dong Date: Tue, 14 Aug 2007 17:31:59 -0400 Subject: [PATCH 01/16] * Refactor candidate map over to its own API call --- app/apis/selectricity_api.rb | 4 ++++ app/models/selectricity_service.rb | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/app/apis/selectricity_api.rb b/app/apis/selectricity_api.rb index 8c0f04f..d478a5a 100644 --- a/app/apis/selectricity_api.rb +++ b/app/apis/selectricity_api.rb @@ -4,6 +4,9 @@ class VoteResultStruct < ActionWebService::Struct member :condorcet_winners, [:int] member :ssd_winners, [:int] member :borda_winners, [:int] + member :errors, [:string] +end +class CandidateMap < ActionWebService::Struct member :candidate_ids, [:int] member :candidate_names, [:string] member :errors, [:string] @@ -11,6 +14,7 @@ end class SelectricityAPI < ActionWebService::API::Base api_method :cast_quickvote, :expects => [:int, :int, [[:int]]], :returns => [:string] api_method :get_quickvote_results, :expects => [:string], :returns => [VoteResultStruct] + api_method :get_quickvote_candidate_map, :expects => [:string], :returns => [CandidateMap] end diff --git a/app/models/selectricity_service.rb b/app/models/selectricity_service.rb index c0adc8a..75d2e3e 100644 --- a/app/models/selectricity_service.rb +++ b/app/models/selectricity_service.rb @@ -20,10 +20,21 @@ class SelectricityService < ActionWebService::Base result.condorcet_winners=qv.condorcet_result.winners result.ssd_winners=qv.ssd_result.winners result.borda_winners=qv.borda_result.winners + result + end + def get_quickvote_candidate_map(shortname) + qv=QuickVote.ident_to_quickvote(shortname) + result=CandidateMap.new + result.errors=[] + unless qv + result.errors << "No quickvote with name #{shortname} found!" + return result + end candidates={} qv.candidates.each {|c| candidates[c.id] = c.name} result.candidate_ids=candidates.keys result.candidate_names=candidates.values result end + end -- 2.39.2 From 8afec3c5b4896fd7665e6527f3acb7c7afac7e47 Mon Sep 17 00:00:00 2001 From: John Dong Date: Tue, 14 Aug 2007 18:23:06 -0400 Subject: [PATCH 02/16] Fix broken Plurality vote by removing a type check in RubyVote. TODO: Figure out why it was in there --- app/models/quick_vote.rb | 2 -- lib/rubyvote/election.rb | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/models/quick_vote.rb b/app/models/quick_vote.rb index 5f566c6..c427b98 100644 --- a/app/models/quick_vote.rb +++ b/app/models/quick_vote.rb @@ -63,7 +63,6 @@ class QuickVote < Election preference_tally << voter.vote.rankings.sort.collect \ { |ranking| ranking.candidate.id } end - @plurality_result = PluralityVote.new(plurality_tally).result @approval_result = ApprovalVote.new(approval_tally).result @condorcet_result = PureCondorcetVote.new(preference_tally).result @@ -72,7 +71,6 @@ class QuickVote < Election #@runoff_result = InstantRunoffVote.new(preference_tally).result #@runoff_results = PluralityVote.new(preference_tally).result - end ### Convert a shortname or id into a QuickVote diff --git a/lib/rubyvote/election.rb b/lib/rubyvote/election.rb index 75614e6..66e4c2a 100644 --- a/lib/rubyvote/election.rb +++ b/lib/rubyvote/election.rb @@ -75,7 +75,7 @@ class PluralityVote < ElectionVote protected def verify_vote(vote=nil) - vote.instance_of?( String ) + not vote.instance_of?( Array ) end def tally_vote(candidate) -- 2.39.2 From 886da4d0dc54fabee70bf4e7aea4d2988f7cf83d Mon Sep 17 00:00:00 2001 From: John Dong Date: Wed, 15 Aug 2007 12:00:58 -0400 Subject: [PATCH 03/16] Import latest subversion RubyVote. --- lib/rubyvote/election.rb | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/rubyvote/election.rb b/lib/rubyvote/election.rb index 66e4c2a..b9bb557 100644 --- a/lib/rubyvote/election.rb +++ b/lib/rubyvote/election.rb @@ -34,7 +34,7 @@ class ElectionVote attr_reader :votes attr_reader :candidates - + def initialize(votes=nil) @votes = Hash.new unless defined?(@votes) @candidates = Array.new unless defined?(@candidates) @@ -42,7 +42,11 @@ class ElectionVote if votes if votes.instance_of?( Array ) votes.each do |vote| - self.tally_vote(vote) if self.verify_vote(vote) + if self.verify_vote(vote) + self.tally_vote(vote) + else + raise InvalidVoteError.new ("Invalid vote object", vote) + end end else raise ElectionError, "Votes must be in the form of an array.", caller @@ -75,7 +79,7 @@ class PluralityVote < ElectionVote protected def verify_vote(vote=nil) - not vote.instance_of?( Array ) + vote ? true : false end def tally_vote(candidate) @@ -114,8 +118,7 @@ end class ElectionResult attr_reader :winners - attr_reader :election - + def initialize(voteobj=nil) unless voteobj and voteobj.kind_of?( ElectionVote ) raise ArgumentError, "You must pass a ElectionVote array.", caller @@ -164,3 +167,10 @@ end class ElectionError < ArgumentError end +class InvalidVoteError < ElectionError + attr_accessor :voteobj + def initialize(msg=nil, voteobj=nil) + super(msg) + @voteobj=voteobj + end +end -- 2.39.2 From c6cd449132c45625d6453d1ffed08bc9142db8d3 Mon Sep 17 00:00:00 2001 From: John Dong Date: Wed, 15 Aug 2007 12:30:25 -0400 Subject: [PATCH 04/16] XMLRPC: Add translator from candidate ID to names --- app/apis/selectricity_api.rb | 1 + app/models/selectricity_service.rb | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/app/apis/selectricity_api.rb b/app/apis/selectricity_api.rb index d478a5a..da2f6f7 100644 --- a/app/apis/selectricity_api.rb +++ b/app/apis/selectricity_api.rb @@ -15,6 +15,7 @@ class SelectricityAPI < ActionWebService::API::Base api_method :cast_quickvote, :expects => [:int, :int, [[:int]]], :returns => [:string] api_method :get_quickvote_results, :expects => [:string], :returns => [VoteResultStruct] api_method :get_quickvote_candidate_map, :expects => [:string], :returns => [CandidateMap] + api_method :quickvote_candidate_ids_to_names, :expects => [:string,[:int]], :returns => [[:string]] end diff --git a/app/models/selectricity_service.rb b/app/models/selectricity_service.rb index 75d2e3e..eb09d8d 100644 --- a/app/models/selectricity_service.rb +++ b/app/models/selectricity_service.rb @@ -5,6 +5,23 @@ class SelectricityService < ActionWebService::Base def cast_quickvote(election_id, vote_id, vote_list) #Obviously not implemented end + def quickvote_candidate_ids_to_names(shortname, id_list) + qv=QuickVote.ident_to_quickvote(shortname) + candidates={} + return [] unless qv + qv.results + qv.candidates.each {|c| candidates[c.id] = c} + results=[] + id_list.each { |id| + name=candidates[id] + if name + results << name + else + results << "" + end + } + results + end def get_quickvote_results(shortname) #TODO: Validate shortname qv=QuickVote.ident_to_quickvote(shortname) -- 2.39.2 From 06bede5ddbcd7f26e50a958c4d64527580cc38c9 Mon Sep 17 00:00:00 2001 From: John Dong Date: Wed, 15 Aug 2007 15:12:41 -0400 Subject: [PATCH 05/16] Commit partial implementation of casting quickvotes via xmlrpc. WARNING: Currently I think it creates inconsistent objects in the database that'll screw up voting. --- app/apis/selectricity_api.rb | 2 +- app/models/selectricity_service.rb | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/apis/selectricity_api.rb b/app/apis/selectricity_api.rb index da2f6f7..8a8dce4 100644 --- a/app/apis/selectricity_api.rb +++ b/app/apis/selectricity_api.rb @@ -12,7 +12,7 @@ class CandidateMap < ActionWebService::Struct member :errors, [:string] end class SelectricityAPI < ActionWebService::API::Base - api_method :cast_quickvote, :expects => [:int, :int, [[:int]]], :returns => [:string] + api_method :cast_quickvote, :expects => [:string, :int, [[:int]]], :returns => [:string] api_method :get_quickvote_results, :expects => [:string], :returns => [VoteResultStruct] api_method :get_quickvote_candidate_map, :expects => [:string], :returns => [CandidateMap] api_method :quickvote_candidate_ids_to_names, :expects => [:string,[:int]], :returns => [[:string]] diff --git a/app/models/selectricity_service.rb b/app/models/selectricity_service.rb index eb09d8d..86458e0 100644 --- a/app/models/selectricity_service.rb +++ b/app/models/selectricity_service.rb @@ -2,8 +2,19 @@ require 'action_controller/integration' class SelectricityService < ActionWebService::Base web_service_api SelectricityAPI - def cast_quickvote(election_id, vote_id, vote_list) - #Obviously not implemented + def cast_quickvote(election_name, voter_id, vote_list) + election = QuickVote.ident_to_quickvote election_name + if election + voter = QuickVoter.new + voter.election = election + voter.session_id = "XMLRPC:#{voter_id}" + voter.vote=Vote.new + voter.vote.votes=vote_list[0] + voter.vote.time = Time.now + voter.save! + voter.vote.confirm! + voter.save! + end end def quickvote_candidate_ids_to_names(shortname, id_list) qv=QuickVote.ident_to_quickvote(shortname) -- 2.39.2 From 9565924bed8f9afd0682d4d029378711b905c550 Mon Sep 17 00:00:00 2001 From: John Dong Date: Wed, 15 Aug 2007 15:53:26 -0400 Subject: [PATCH 06/16] Import latest RubyVote svn to fix infinite loop bug --- lib/rubyvote/condorcet.rb | 5 ++++- lib/rubyvote/irv.rb | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/rubyvote/condorcet.rb b/lib/rubyvote/condorcet.rb index 65c664d..c0539ab 100644 --- a/lib/rubyvote/condorcet.rb +++ b/lib/rubyvote/condorcet.rb @@ -184,7 +184,10 @@ class PureCondorcetResult < CondorcetResult def condorcet votes = @election.votes candidates = @election.candidates - + unless votes.length>0 and candidates.length>0 + @winners=[nil] + return @winners + end victors = Hash.new candidates.each do |candidate| victors[candidate] = Array.new diff --git a/lib/rubyvote/irv.rb b/lib/rubyvote/irv.rb index 3851738..b26b1d1 100644 --- a/lib/rubyvote/irv.rb +++ b/lib/rubyvote/irv.rb @@ -87,6 +87,11 @@ class InstantRunoffResult < ElectionResult apply_retention(votes, votes_sum * params['percent_retention']) end + unless votes.length > 0 + @winners=[nil] + return + end + begin ranked_candidates = votes.sort do |a, b| b[1][0] <=> a[1][0] -- 2.39.2 From ae1b10889b53eeabc3a1f2281cfc1555781ab7fb Mon Sep 17 00:00:00 2001 From: John Dong Date: Wed, 15 Aug 2007 16:17:11 -0400 Subject: [PATCH 07/16] Pull in svn Rubyvote again --- lib/rubyvote/condorcet.rb | 4 ++-- lib/rubyvote/election.rb | 2 +- lib/rubyvote/irv.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rubyvote/condorcet.rb b/lib/rubyvote/condorcet.rb index c0539ab..0b7a306 100644 --- a/lib/rubyvote/condorcet.rb +++ b/lib/rubyvote/condorcet.rb @@ -185,7 +185,7 @@ class PureCondorcetResult < CondorcetResult votes = @election.votes candidates = @election.candidates unless votes.length>0 and candidates.length>0 - @winners=[nil] + @winners=[] return @winners end victors = Hash.new @@ -224,7 +224,7 @@ class CloneproofSSDResult < CondorcetResult def cpssd votes = @election.votes candidates = *@election.candidates - + def in_schwartz_set?(candidate, candidates, transitive_defeats) candidates.each do |challenger| next if candidate == challenger diff --git a/lib/rubyvote/election.rb b/lib/rubyvote/election.rb index b9bb557..42f18bb 100644 --- a/lib/rubyvote/election.rb +++ b/lib/rubyvote/election.rb @@ -133,7 +133,7 @@ class ElectionResult end def winner? - @winners.length > 0 + @winners.length > 0 and not @winners[0].nil? end end diff --git a/lib/rubyvote/irv.rb b/lib/rubyvote/irv.rb index b26b1d1..c3fabe6 100644 --- a/lib/rubyvote/irv.rb +++ b/lib/rubyvote/irv.rb @@ -88,7 +88,7 @@ class InstantRunoffResult < ElectionResult end unless votes.length > 0 - @winners=[nil] + @winners=[] return end -- 2.39.2 From 07800d892d8454c16d75ddc35cd36e630498aab0 Mon Sep 17 00:00:00 2001 From: John Dong Date: Wed, 15 Aug 2007 16:28:05 -0400 Subject: [PATCH 08/16] Merge another cpssd fix --- lib/rubyvote/condorcet.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rubyvote/condorcet.rb b/lib/rubyvote/condorcet.rb index 0b7a306..aaa5044 100644 --- a/lib/rubyvote/condorcet.rb +++ b/lib/rubyvote/condorcet.rb @@ -217,6 +217,7 @@ class CloneproofSSDResult < CondorcetResult def initialize(voteobj=nil) super(voteobj) @winners = self.cpssd() + @winners.delete nil end protected @@ -224,7 +225,7 @@ class CloneproofSSDResult < CondorcetResult def cpssd votes = @election.votes candidates = *@election.candidates - + def in_schwartz_set?(candidate, candidates, transitive_defeats) candidates.each do |challenger| next if candidate == challenger -- 2.39.2 From 9770c8de272b16601b7c5d26ddb73a83993dd16b Mon Sep 17 00:00:00 2001 From: John Dong Date: Wed, 15 Aug 2007 16:37:03 -0400 Subject: [PATCH 09/16] Add some quick info about XMLRPC API --- README | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README b/README index f8a8928..88fcd97 100644 --- a/README +++ b/README @@ -81,6 +81,26 @@ gem installed, you can use the ri command, or the above mentioned gem_server. If you guys want more helpful stuff here, let me know. +====================================== +=== XML-RPC INFO == +====================================== + +The XML-RPC API is still under development, but is somewhat functional already: + +To instantiate a client in Ruby, try something like: +client=ActionWebService::Client::XmlRpc.new(SelectricityAPI,"http://localhost:3000/selectricity_service/vote") + + +Getting the results of a quickvote is quite simple: +?> client.get_quickvote_results("test") +=> # + +Casting a quickvote: +client.cast_quickvote("test",1,[[1,2]]) + +To figure out what you're voting for: +>> client.get_quickvote_candidate_map("test")=> # + -- 2.39.2 From 7f2bdc00ac867092eebed2b64596efeaacca0d87 Mon Sep 17 00:00:00 2001 From: John Dong Date: Wed, 15 Aug 2007 16:45:51 -0400 Subject: [PATCH 10/16] Record a junk IP address for XMLRPC'ers --- app/models/selectricity_service.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/selectricity_service.rb b/app/models/selectricity_service.rb index 86458e0..ac2ef20 100644 --- a/app/models/selectricity_service.rb +++ b/app/models/selectricity_service.rb @@ -7,6 +7,7 @@ class SelectricityService < ActionWebService::Base if election voter = QuickVoter.new voter.election = election + voter.ipaddress = "0.0.0.0" voter.session_id = "XMLRPC:#{voter_id}" voter.vote=Vote.new voter.vote.votes=vote_list[0] -- 2.39.2 From 597c27db39d8b36e623a9d6573e75edf657142c0 Mon Sep 17 00:00:00 2001 From: John Dong Date: Wed, 15 Aug 2007 17:03:41 -0400 Subject: [PATCH 11/16] Record "XMLRPC Request" in ipadress field of XML RPC created votes. Added IPAddr verification of IP Addresses --- app/models/selectricity_service.rb | 2 +- app/views/quickvote/results.rhtml | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/models/selectricity_service.rb b/app/models/selectricity_service.rb index ac2ef20..add5ab9 100644 --- a/app/models/selectricity_service.rb +++ b/app/models/selectricity_service.rb @@ -7,7 +7,7 @@ class SelectricityService < ActionWebService::Base if election voter = QuickVoter.new voter.election = election - voter.ipaddress = "0.0.0.0" + voter.ipaddress = "XMLRPC Request" voter.session_id = "XMLRPC:#{voter_id}" voter.vote=Vote.new voter.vote.votes=vote_list[0] diff --git a/app/views/quickvote/results.rhtml b/app/views/quickvote/results.rhtml index 1fad7ea..98a71e5 100644 --- a/app/views/quickvote/results.rhtml +++ b/app/views/quickvote/results.rhtml @@ -1,4 +1,4 @@ -<% %> +<%require 'IPAddr' %>

Results

<% if @election.shortdesc %> @@ -156,7 +156,13 @@ by several other names.

<% next unless voter.voted? %> <%= voter.ipaddress %> - <%= `host #{voter.ipaddress}`.sub(/^.*pointer (.*)\.$/, '\1') %> + <% begin %> + <%= `host #{IPAddr.new(voter.ipaddress).to_s}`.sub(/^.*pointer (.*)\.$/, '\1') %> + + <% rescue ArgumentError => err %> + <%= " - " %> + <% end %> + <%= voter.vote.votestring %> <% end %> -- 2.39.2 From 8f3646d179b4d779c9521b6cc1b227f371830395 Mon Sep 17 00:00:00 2001 From: John Dong Date: Wed, 15 Aug 2007 17:35:51 -0400 Subject: [PATCH 12/16] Display same in hostname and IP when IP lookup fails --- app/views/quickvote/results.rhtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/quickvote/results.rhtml b/app/views/quickvote/results.rhtml index 98a71e5..9677d9a 100644 --- a/app/views/quickvote/results.rhtml +++ b/app/views/quickvote/results.rhtml @@ -160,7 +160,7 @@ by several other names.

<%= `host #{IPAddr.new(voter.ipaddress).to_s}`.sub(/^.*pointer (.*)\.$/, '\1') %> <% rescue ArgumentError => err %> - <%= " - " %> + <%= voter.ipaddress %> <% end %> <%= voter.vote.votestring %> -- 2.39.2 From 924aa1ff433ac4ccd2db5851e91038088be00a09 Mon Sep 17 00:00:00 2001 From: Date: Wed, 15 Aug 2007 17:46:20 -0400 Subject: [PATCH 13/16] Modified graphs to ahve a 4 color scheme, but it isn't the full palette of selectricity yet , that will be on next commit. LAso modified several voting methods within RubyVote to have more useful instance variables and accessors available in the results class. the quickvote results view has been updated to include a partial for displaying a condorcet vote table, w hich is strangely appearing below the footer on the results page, it seems to be displaying the correct data however. --- .bzrignore | 1 + app/controllers/graph_controller.rb | 38 +++++++++++++++++---------- app/views/quickvote/_pref_table.rhtml | 19 ++++++++++++++ app/views/quickvote/results.rhtml | 4 +-- lib/rubyvote/condorcet.rb | 6 +++-- lib/rubyvote/election.rb | 5 +++- lib/rubyvote/positional.rb | 9 ++++--- 7 files changed, 60 insertions(+), 22 deletions(-) create mode 100644 app/views/quickvote/_pref_table.rhtml diff --git a/.bzrignore b/.bzrignore index 50c7c5b..1e24ca1 100644 --- a/.bzrignore +++ b/.bzrignore @@ -5,3 +5,4 @@ server.log test.log tmp public/engine_files +.DS_Store diff --git a/app/controllers/graph_controller.rb b/app/controllers/graph_controller.rb index 8256884..bbd5cd7 100644 --- a/app/controllers/graph_controller.rb +++ b/app/controllers/graph_controller.rb @@ -7,13 +7,19 @@ class GraphController < ApplicationController size = "700x400" @graph = options[:graph_type].new(size) - @graph.theme = { :background_colors => ['#73BF26', '#ffffff'] } + @graph.theme = { :colors => ['#000000', '#00FFFF', '#FFCC00', '#990033'], + :background_colors => ['#74ce00', '#ffffff'] } @graph.font = File.expand_path('/usr/X11R6/lib/X11/fonts/TTF/Vera.ttf', RAILS_ROOT) # fill in the data with the optional data name - #Check to see if multiple datasets, if so, fill them all! - if options[:data].size > 1 && options[:data].all? {|i| i.is_a?(Array)} + #Check to see if multiple datasets, if so, fill them all! + if options[:data].is_a?(Hash) + options[:data].each_pair do |name, array| + @graph.data( name, array) + end + #if each dataset nameless, will have only multiple arrays + elsif options[:data].size > 1 && options[:data].all? {|i| i.is_a?(Array)} options[:data].each do |array| @graph.data( options.fetch(:data_name, "Data"), array) end @@ -73,10 +79,10 @@ class GraphController < ApplicationController def borda_bar @election = Election.find(params[:id]) - pref_tally = make_preference_tally(@election) + #pref_tally = make_preference_tally(@election) - @borda_result = BordaVote.new(pref_tally).result - data, labels = get_borda_points(@borda_result) + #@borda_result = BordaVote.new(pref_tally).result + data, labels = get_borda_points(@election.borda_result) graph = GruffGraff.new( :graph_type => Gruff::Bar, :data_name => @election.name, @@ -87,16 +93,22 @@ class GraphController < ApplicationController :x_axis_label => "Candidate") send_data(*graph.output) end - - def choices_positions + #Acording to Tufte, small, concomparitive, highly labeled data sets usually + # belong in tables. The following is a bar graph...but would it be better + #as a table? + def choices_positions @election = Election.find(params[:id]) pref_tally = make_preference_tally(@election) fulldata, labels = get_positions_info(@election) - labels = @candidates + legend = Hash.new + + @election.candidates.each_with_index do |candidate, index| + legend[candidate.name] = fulldata[index] + end + graph = GruffGraff.new( :graph_type => Gruff::Bar, - :data_name => @election.name, - :data => fulldata, + :data => legend, :interval_labels => labels, :title => "Times Voted in Each Position", :y_axis_label => "Number of Times Ranked", @@ -133,7 +145,7 @@ class GraphController < ApplicationController rank_labels[i] = (i+1).to_s end end - + return buckets2.values, rank_labels end @@ -236,8 +248,6 @@ class GraphController < ApplicationController 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 diff --git a/app/views/quickvote/_pref_table.rhtml b/app/views/quickvote/_pref_table.rhtml new file mode 100644 index 0000000..dfcb6cc --- /dev/null +++ b/app/views/quickvote/_pref_table.rhtml @@ -0,0 +1,19 @@ +<% candidates = @election.candidates.sort.collect {|candidate| candidate.id} -%> + + + + <% candidates.each do |candidate| -%> + + <% end -%> +<% candidates.each do |winner| -%> + + + <% candidates.each do |loser| -%> + <% if winner == loser -%> + + <% else %> + + <% end -%> + <% end -%> + +<%end -%> \ No newline at end of file diff --git a/app/views/quickvote/results.rhtml b/app/views/quickvote/results.rhtml index 98a71e5..5626636 100644 --- a/app/views/quickvote/results.rhtml +++ b/app/views/quickvote/results.rhtml @@ -145,7 +145,6 @@ by several other names.

Voters

-
<%= candidate -%>
<%= winner %> -- <%= @election.condorcet_result.matrix[winner][loser] %>
@@ -168,8 +167,9 @@ by several other names.

<% end %>
IP Address
+<%= render :partial => 'pref_table' %> + <%= image_tag( graph_url( :action => 'votes_per_day', :id => @election ) ) %>
<%= image_tag( graph_url( :action => 'votes_per_interval', :id => @election ))%>
<%= image_tag( graph_url( :action => 'borda_bar', :id => @election ) ) %>
<%= image_tag( graph_url( :action => 'choices_positions', :id => @election ) ) %> - diff --git a/lib/rubyvote/condorcet.rb b/lib/rubyvote/condorcet.rb index aaa5044..bf4e548 100644 --- a/lib/rubyvote/condorcet.rb +++ b/lib/rubyvote/condorcet.rb @@ -32,7 +32,7 @@ ## the CloneproofSSDVote classes but should not be used directly. class CondorcetVote < ElectionVote - + attr_accessor :results def initialize(votes=nil) @@ -144,15 +144,17 @@ end ## directly. class CondorcetResult < ElectionResult + attr_reader :matrix + def initialize(voteobj=nil) unless voteobj and voteobj.kind_of?( CondorcetVote ) raise ArgumentError, "You must pass a CondorcetVote array.", caller end super(voteobj) + @matrix = voteobj.votes end protected - def defeats(candidates=nil, votes=nil) candidates = @election.candidates unless candidates votes = @election.votes unless votes diff --git a/lib/rubyvote/election.rb b/lib/rubyvote/election.rb index 42f18bb..efb8dd8 100644 --- a/lib/rubyvote/election.rb +++ b/lib/rubyvote/election.rb @@ -140,7 +140,8 @@ end class PluralityResult < ElectionResult attr_reader :ranked_candidates - + attr_reader :points + def initialize(voteobj=nil) super(voteobj) @@ -151,6 +152,8 @@ class PluralityResult < ElectionResult b[1] <=> a[1] end.collect {|a| a[0]} + @points = self.election.votes + # winners are anyone who has the same number of votes as the # first person @winners = @ranked_candidates.find_all do |i| diff --git a/lib/rubyvote/positional.rb b/lib/rubyvote/positional.rb index 3de3fb2..dc0b63a 100644 --- a/lib/rubyvote/positional.rb +++ b/lib/rubyvote/positional.rb @@ -38,7 +38,7 @@ class BordaVote < ElectionVote end super(votes) end - + def tally_vote(vote) points = candidates.length - 1 vote.each do |candidate| @@ -51,7 +51,7 @@ class BordaVote < ElectionVote points -= 1 end end - + def verify_vote(vote=nil) vote.instance_of?( Array ) and vote == vote.uniq @@ -64,11 +64,12 @@ end class BordaResult < ElectionResult attr_reader :ranked_candidates + attr_reader :points def initialize(voteobj=nil) super(voteobj) votes = @election.votes - + @ranked_candidates = votes.sort do |a, b| b[1] <=> a[1] end.collect {|i| i[0]} @@ -76,6 +77,8 @@ class BordaResult < ElectionResult @winners = @ranked_candidates.find_all do |i| votes[i] == votes[@ranked_candidates[0]] end + + @points = self.election.votes end end -- 2.39.2 From e682e1ecce33417f8410e71bd58d385c47e37c5b Mon Sep 17 00:00:00 2001 From: John Dong Date: Thu, 16 Aug 2007 13:09:31 -0400 Subject: [PATCH 14/16] Fix crasher when nil is passed to ident_to_quickvote --- app/models/quick_vote.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/quick_vote.rb b/app/models/quick_vote.rb index c427b98..ce4a4fb 100644 --- a/app/models/quick_vote.rb +++ b/app/models/quick_vote.rb @@ -75,6 +75,7 @@ class QuickVote < Election ### Convert a shortname or id into a QuickVote def self.ident_to_quickvote(ident) + return nil unless ident if ident.match(/^\d+$/) quickvote = QuickVote.find(ident) else -- 2.39.2 From cf4234876994cb4b7e09cdd116e092424d9f4951 Mon Sep 17 00:00:00 2001 From: John Dong Date: Thu, 16 Aug 2007 13:54:37 -0400 Subject: [PATCH 15/16] * Tighter validation, closed a number of crashes due to invalid data * Security: Escape HTML to prevent injection of code onto the form * Prevent empty candidates from passing validation * Clearer, non Engrishy error messages on quickvote/create * Prevent quickvote ident names from clashing with reserved controller actions --- app/models/quick_vote.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/models/quick_vote.rb b/app/models/quick_vote.rb index ce4a4fb..0efb574 100644 --- a/app/models/quick_vote.rb +++ b/app/models/quick_vote.rb @@ -10,12 +10,16 @@ class QuickVote < Election attr_accessor :borda_result def validate - if @raw_candidates.length < 2 - errors.add("You must list at least two candidates.") + if not @raw_candidates or @raw_candidates.length < 2 + errors.add(nil, "You must list at least two candidates.") end - + if name =~ /[^A-Za-z0-9]/ - errors.add("The name must only include numbers and letters.") + errors.add(:name, "must only include numbers and letters.") + end + + if name =~ /^(create|index|confirm|change|results)$/ + errors.add(:name, " is a reserved word.") end end @@ -40,6 +44,7 @@ class QuickVote < Election end def create_candidates + return unless errors.empty? @raw_candidates.each do |name| candidate = Candidate.new({:name => name}) self.candidates << candidate -- 2.39.2 From 40891d8740b77b6d6249e8982f15e2f348725282 Mon Sep 17 00:00:00 2001 From: John Dong Date: Thu, 16 Aug 2007 13:56:41 -0400 Subject: [PATCH 16/16] Also a part of the previous commit --- app/controllers/quickvote_controller.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/controllers/quickvote_controller.rb b/app/controllers/quickvote_controller.rb index 83a6cc5..2847df9 100644 --- a/app/controllers/quickvote_controller.rb +++ b/app/controllers/quickvote_controller.rb @@ -34,11 +34,13 @@ class QuickvoteController < ApplicationController end def add_candidate - candidate_name = params[:ajax][:newcandidate] - if flash.has_key?(:candlist) and flash[:candlist].instance_of?(Array) - flash[:candlist] << candidate_name - else - flash[:candlist] = [ candidate_name ] + candidate_name = CGI.escapeHTML(params[:ajax][:newcandidate]) + unless candidate_name.strip.empty? + if flash.has_key?(:candlist) and flash[:candlist].instance_of?(Array) + flash[:candlist] << candidate_name + else + flash[:candlist] = [ candidate_name ] + end end flash.keep(:candlist) render_partial 'candidate_list' -- 2.39.2