From: John Dong Date: Wed, 22 Aug 2007 02:33:12 +0000 (-0400) Subject: Merge from jdong X-Git-Url: https://projects.mako.cc/source/selectricity/commitdiff_plain/47fdfaba5a11570f2d7d720ee1c80d616d502c74?hp=4c5046ffa16ea77da4e4b69517dcb33bc6ec2646 Merge from jdong --- diff --git a/app/controllers/graph_controller.rb b/app/controllers/graph_controller.rb index 442bb65..69577c6 100644 --- a/app/controllers/graph_controller.rb +++ b/app/controllers/graph_controller.rb @@ -99,6 +99,7 @@ class GraphController < ApplicationController legend = Hash.new alldata, labels = get_positions_info(@election) @election.results unless @election.condorcet_result || @election.ssd_result + ranked_candidates = @election.condorcet_result.ranked_candidates.flatten names = Hash.new candidates = @election.candidates.sort.collect {|candidate| candidate.id} @@ -106,7 +107,6 @@ class GraphController < ApplicationController names[candidate]= (Candidate.find(candidate)).name end - ranked_candidates = @election.condorcet_result.ranked_candidates.flatten ranked_candidates.each_with_index \ {|candidate, index| legend[names[candidate]] = alldata[index]} @@ -119,6 +119,24 @@ class GraphController < ApplicationController send_data(*graph.output) end + def plurality_pie + @election = Election.find(params[:id]) + @election.results unless @election.plurality_result || @election.approval_result + votes = @election.votes.size + data = Hash.new + names = @election.names_by_id + + @election.plurality_result.points.each do |candidate, votes| + data[names[candidate]] = votes + end + + pie = GruffGraff.new ( :graph_type => Gruff::Pie, + :title => "Percentage of First Place Votes", + :data => data) + send_data(*pie.output) + + end + private def get_positions_info(election) buckets = Hash.new diff --git a/app/controllers/quickvote_controller.rb b/app/controllers/quickvote_controller.rb index bb60b45..54784ef 100644 --- a/app/controllers/quickvote_controller.rb +++ b/app/controllers/quickvote_controller.rb @@ -161,7 +161,7 @@ class QuickvoteController < ApplicationController redirect_to :controller => 'site' return end - @election.results + @results = @election.results @candidates = {} @election.candidates.each {|c| @candidates[c.id] = c} end diff --git a/app/models/election.rb b/app/models/election.rb index d76c13f..59a10bf 100644 --- a/app/models/election.rb +++ b/app/models/election.rb @@ -5,13 +5,24 @@ class Election < ActiveRecord::Base belongs_to :user validates_presence_of :name, :description + #validate that method is one of the listed election types + attr_reader :plurality_result attr_reader :approval_result attr_reader :condorcet_result attr_reader :ssd_result attr_reader :borda_result - + require 'date' + + def other_methods + if election_method + @other_methods = ELECTION_TYPES.reject {|i| i == election_method} + else + @other_methods = nil + end + @other_methods + end def startdate read_attribute( :startdate ) || Time.now @@ -100,15 +111,19 @@ class Election < ActiveRecord::Base 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 @ssd_result = CloneproofSSDVote.new(preference_tally).result @borda_result = BordaVote.new(preference_tally).result - #@runoff_result = InstantRunoffVote.new(preference_tally).result - nil # to stay consistent - end + { 'plurality' => @plurality_result, + 'approval' => @approval_result, + 'condorcet' => @condorcet_result, + 'ssd' => @ssd_result, + 'borda' => @borda_result } + end def names_by_id names = Hash.new diff --git a/app/views/quickvote/_pref_table.rhtml b/app/views/quickvote/_pref_tables.rhtml similarity index 50% rename from app/views/quickvote/_pref_table.rhtml rename to app/views/quickvote/_pref_tables.rhtml index 011fcbe..66e0cbf 100644 --- a/app/views/quickvote/_pref_table.rhtml +++ b/app/views/quickvote/_pref_tables.rhtml @@ -1,17 +1,19 @@ -<% candidates = @election.candidates.sort.collect {|candidate| candidate.id}-%> +<% candidates = @election.condorcet_result.ranked_candidates.flatten -%> <% voters = @election.voters.size %> <% matrix = @election.condorcet_result.matrix %> -<% names = Hash.new -%> -<% candidates.each do |candidate| -%> - <%names[candidate] = Candidate.find(candidate).name -%> -<% end -%> +<% victories = @election.condorcet_result.victories_and_ties %> +<% names = @election.names_by_id %> + + + <% candidates.each do |candidate| -%> <% end -%> - + <% candidates.each do |winner| -%> @@ -30,3 +32,23 @@ <% end -%>
<%=h names[candidate] -%>
+ + + + <% candidates.each do |victor| %> + + + <% victories[victor].keys.each do |loser| %> + <% margin = victories[victor][loser]%> + + <% end -%> + + <% end -%> +
<%=h names[victor] %><%=h names[loser] %> + <% if margin == 0%> + Tied! + <% else -%> + (<%= margin%>) + <% end -%> +
+ diff --git a/app/views/quickvote/_result_approval.rhtml b/app/views/quickvote/_result_approval.rhtml new file mode 100644 index 0000000..12c7958 --- /dev/null +++ b/app/views/quickvote/_result_approval.rhtml @@ -0,0 +1,17 @@ +
+

Approval Result

+

(This algorithm assumes that top two choices are "approved.")

+ +
+

About Approval Voting

+ +

<%= link_to "Approval voting", +"http://en.wikipedia.org/wiki/Approval_voting" %> is a voting system in +which each voter can vote for as many or as few candidates as the voter +chooses. Approval voting is a limited form of range voting, where the +range that voters are allowed to express is extremely constrained: +accept or not.

+ +
+ +
\ No newline at end of file diff --git a/app/views/quickvote/_result_borda.rhtml b/app/views/quickvote/_result_borda.rhtml new file mode 100644 index 0000000..97d82e5 --- /dev/null +++ b/app/views/quickvote/_result_borda.rhtml @@ -0,0 +1,16 @@ +
+

Borda Count Results

+ +
+

About Borda Count

+ +

<%= link_to "Borda count", +"http://en.wikipedia.org/wiki/Borda_count" %> +is an election method in which voters rank +candidates in order of preference. The Borda count determines the winner +of an election by giving each candidate a certain number of points +corresponding to the position in which he or she is ranked by each +voter. Once all votes have been counted the candidate with the most +points is the winner.

+
+
\ No newline at end of file diff --git a/app/views/quickvote/_result_condorcet.rhtml b/app/views/quickvote/_result_condorcet.rhtml new file mode 100644 index 0000000..4279bbd --- /dev/null +++ b/app/views/quickvote/_result_condorcet.rhtml @@ -0,0 +1,18 @@ +
+

Simple Condorcet Results

+ +
+

About Simple Cordorcet Voting

+ +

<%= link_to "Condorcet", +"http://en.wikipedia.org/wiki/Condorcet_method" %> allows voters to rank +candidates in order of preference. If there is a choice whom voters +prefer to each other choice when compared to one at a time, that choice +will be the winner.

+ +

There is a family of Condorcet methods. This method is referred to as +"Simple Condorcet" to distinguish it from the Schulze method which is +another Condorcet system.

+ +
+
\ No newline at end of file diff --git a/app/views/quickvote/_result_plurality.rhtml b/app/views/quickvote/_result_plurality.rhtml new file mode 100644 index 0000000..121c1dc --- /dev/null +++ b/app/views/quickvote/_result_plurality.rhtml @@ -0,0 +1,17 @@ +
+

Plurality Results

+ +
+

About Plurality Voting

+ +

<%= link_to "Plurality voting", +"http://en.wikipedia.org/wiki/Plurality_electoral_system" %> selects the +winner who has received the most "number one" votes, regardless of +whether or not he or she has a majority of votes.

+ +

Plurality voting is also variously referred to as, "first past the +post," "winner-take-all," "majoritarian" or "simple majority" +voting.

+ +
+
\ No newline at end of file diff --git a/app/views/quickvote/_result_runoff.rhtml b/app/views/quickvote/_result_runoff.rhtml new file mode 100644 index 0000000..f242290 --- /dev/null +++ b/app/views/quickvote/_result_runoff.rhtml @@ -0,0 +1,20 @@ +
+

Instant Runoff (IRV) Results

+ +
+

About Instant Runoff Voting

+ +

<%= link_to "Instant runoff voting", +"http://en.wikipedia.org/wiki/Instant_Runoff_Voting" %> is an electoral +system in which voters rank candidates in order of preference. In an IRV +election, if no candidate receives an overall majority of first +preferences the candidates with fewest votes are eliminated one by one, +and their votes transferred according to their second and third +preferences (and so on), until one candidate achieves a majority.

+ +

Instant-runoff voting (IRV) is also known as the Alternative Vote (AV) and +by several other names.

+ +
+ +
\ No newline at end of file diff --git a/app/views/quickvote/_result_ssd.rhtml b/app/views/quickvote/_result_ssd.rhtml new file mode 100644 index 0000000..436dd67 --- /dev/null +++ b/app/views/quickvote/_result_ssd.rhtml @@ -0,0 +1,17 @@ +
+

Schulze Method Results

+ +
+

About the Schulze Method

+ +

The <%= link_to "Schulze method", +"http://en.wikipedia.org/wiki/Schulze_method" %> is a preferential +voting system. It is based on the Condorcet method but includes a set of +methods for resolving "circular" defeats.

+ +

The Schulze method is also known as Schwartz Sequential Dropping +(SSD), Cloneproof Schwartz Sequential Dropping (CSSD), Beatpath Method, +Beatpath Winner, Path Voting, and Path Winner.

+
+ +
diff --git a/app/views/quickvote/_victories_ties.rhtml b/app/views/quickvote/_victories_ties.rhtml deleted file mode 100644 index 993caa8..0000000 --- a/app/views/quickvote/_victories_ties.rhtml +++ /dev/null @@ -1,17 +0,0 @@ -<% victories, tied = @election.condorcet_result.victories_and_ties %> -<% names = @election.names_by_id %> -<% %> - - <% victories.keys.each do |victor| %> - - - <% victories[victor].keys.each do |loser| %> - - <% end -%> - - <% end -%> -
<%=h names[victor] %><%=h names[loser] %> (<%= victories[victor][loser] %>)
- - - - diff --git a/app/views/quickvote/results.rhtml b/app/views/quickvote/results.rhtml index 6c1130f..9727d81 100644 --- a/app/views/quickvote/results.rhtml +++ b/app/views/quickvote/results.rhtml @@ -27,121 +27,16 @@

Winners

-
-

Schulze Method Results

-<%= render :partial => 'result', :object => @election.ssd_result %> +<%= render :partial => 'result_' + @election.election_method, + :object => @results[@election.election_method] %> -
-

About the Schulze Method

-

The <%= link_to "Schulze method", -"http://en.wikipedia.org/wiki/Schulze_method" %> is a preferential -voting system. It is based on the Condorcet method but includes a set of -methods for resolving "circular" defeats.

- -

The Schulze method is also known as Schwartz Sequential Dropping -(SSD), Cloneproof Schwartz Sequential Dropping (CSSD), Beatpath Method, -Beatpath Winner, Path Voting, and Path Winner.

-
- -
- -
-

Plurality Results

-<%= render :partial => 'result', :object => @election.plurality_result %> - -
-

About Plurality Voting

- -

<%= link_to "Plurality voting", -"http://en.wikipedia.org/wiki/Plurality_electoral_system" %> selects the -winner who has received the most "number one" votes, regardless of -whether or not he or she has a majority of votes.

- -

Plurality voting is also variously referred to as, "first past the -post," "winner-take-all," "majoritarian" or "simple majority" -voting.

- -
-
- -
-

Approval Result

-

(This algorithm assumes that top two choices are "approved.")

-<%= render :partial => 'result', :object => @election.approval_result %> - -
-

About Approval Voting

- -

<%= link_to "Approval voting", -"http://en.wikipedia.org/wiki/Approval_voting" %> is a voting system in -which each voter can vote for as many or as few candidates as the voter -chooses. Approval voting is a limited form of range voting, where the -range that voters are allowed to express is extremely constrained: -accept or not.

- -
- -
- -
-

Simple Condorcet Results

-<%= render :partial => 'result', :object => @election.condorcet_result %> - -
-

About Simple Cordorcet Voting

- -

<%= link_to "Condorcet", -"http://en.wikipedia.org/wiki/Condorcet_method" %> allows voters to rank -candidates in order of preference. If there is a choice whom voters -prefer to each other choice when compared to one at a time, that choice -will be the winner.

- -

There is a family of Condorcet methods. This method is referred to as -"Simple Condorcet" to distinguish it from the Schulze method which is -another Condorcet system.

- -
-
- -
-

Borda Count Results

-<%= render :partial => 'result', :object => @election.borda_result %> - -
-

About Borda Count

- -

<%= link_to "Borda count", -"http://en.wikipedia.org/wiki/Borda_count" %> -is an election method in which voters rank -candidates in order of preference. The Borda count determines the winner -of an election by giving each candidate a certain number of points -corresponding to the position in which he or she is ranked by each -voter. Once all votes have been counted the candidate with the most -points is the winner.

-
-
- -
-

Instant Runoff (IRV) Results

- -
-

About Instant Runoff Voting

- -

<%= link_to "Instant runoff voting", -"http://en.wikipedia.org/wiki/Instant_Runoff_Voting" %> is an electoral -system in which voters rank candidates in order of preference. In an IRV -election, if no candidate receives an overall majority of first -preferences the candidates with fewest votes are eliminated one by one, -and their votes transferred according to their second and third -preferences (and so on), until one candidate achieves a majority.

- -

Instant-runoff voting (IRV) is also known as the Alternative Vote (AV) and -by several other names.

- -
+

Other Voting Methods

+<% for result_type in @election.other_methods %> +<%= render :partial => 'result_' + result_type, + :object => @results[result_type] %> +<% end %> -
@@ -180,10 +75,10 @@ by several other names.

<% end %> -<%= render :partial => 'victories_ties' %> -<%= render :partial => 'pref_table' %> +<%= render :partial => 'pref_tables' %> <%=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 ) ) %> +<%= image_tag( graph_url( :action => 'choices_positions', :id => @election ) ) %>
+<%= image_tag(graph_url( :action => 'plurality_pie', :id => @election ) )%> diff --git a/config/environment.rb b/config/environment.rb index e071a14..571638d 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -63,10 +63,12 @@ MAIL_CONFIG = { :from => 'Selectricity '} require 'uniq_token' require 'randarray' -require 'rubyvote' require 'gruff' require 'sparklines' +require 'rubyvote' +ELECTION_TYPES = %w(ssd plurality approval condorcet borda) + class String # alternate capitalization method that does not lowercase the rest of # the string -- which is almost never what I want diff --git a/db/create.sql b/db/create.sql index 0a2f4b5..7b3fef5 100644 --- a/db/create.sql +++ b/db/create.sql @@ -11,6 +11,7 @@ create table elections ( enddate datetime NOT NULL, active tinyint NOT NULL DEFAULT 0, user_id int NULL, + election_method varchar(100) DEFAULT 'ssd', `type` varchar(100) NOT NULL, primary key (id), constraint fk_user_election foreign key (user_id) references users(id) diff --git a/db/schema.rb b/db/schema.rb index 45d7b41..cc61ca3 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -14,14 +14,15 @@ ActiveRecord::Schema.define() do end create_table "elections", :force => true do |t| - t.column "name", :string, :limit => 100, :default => "", :null => false - t.column "description", :text, :default => "", :null => false - t.column "anonymous", :integer, :limit => 4, :default => 1, :null => false - t.column "startdate", :datetime - t.column "enddate", :datetime, :null => false - t.column "active", :integer, :limit => 4, :default => 0, :null => false - t.column "user_id", :integer - t.column "type", :string, :limit => 100, :default => "", :null => false + t.column "name", :string, :limit => 100, :default => "", :null => false + t.column "description", :text, :default => "", :null => false + t.column "anonymous", :integer, :limit => 4, :default => 1, :null => false + t.column "startdate", :datetime + t.column "enddate", :datetime, :null => false + t.column "active", :integer, :limit => 4, :default => 0, :null => false + t.column "user_id", :integer + t.column "election_method", :string, :limit => 100, :default => "ssd" + t.column "type", :string, :limit => 100, :default => "", :null => false end add_index "elections", ["user_id"], :name => "fk_user_election" diff --git a/lib/rubyvote/condorcet.rb b/lib/rubyvote/condorcet.rb index d0210ef..0c6fd56 100644 --- a/lib/rubyvote/condorcet.rb +++ b/lib/rubyvote/condorcet.rb @@ -123,33 +123,21 @@ class CondorcetResult < ElectionResult end def victories_and_ties - victors = Array.new - ties = Array.new - victories = Hash.new + victories_ties = {} candidates = @matrix.keys.sort candidates.each do |candidate| candidates.each do |challenger| next if candidate == challenger diff = @matrix[candidate][challenger] - @matrix[challenger][candidate] - if diff > 0 - victors << [candidate, challenger, diff] - elsif diff == 0 && ties.include?([challenger, candidate]) == false - ties << [candidate, challenger] + victories_ties[candidate] = {} unless victories_ties.key?(candidate) + if diff >= 0 + victories_ties[candidate][challenger] = diff end end - end - - victors.each do |list| - if victories.has_key?(list[0]) - victories[list[0]][list[1]] = list[2] - else - victories[list[0]] = Hash.new - victories[list[0]][list[1]] = list[2] - end end - return victories, ties + return victories_ties end def ranked_candidates