Merge branch 'master' of git://gitorious.org/~ruinar/selectricity/ruinar-selectricity
authorBenjamin Mako Hill <mako@atdot.cc>
Sun, 17 Jun 2012 23:23:16 +0000 (19:23 -0400)
committerBenjamin Mako Hill <mako@atdot.cc>
Sun, 17 Jun 2012 23:23:16 +0000 (19:23 -0400)
19 files changed:
COPYING
README
app/controllers/graph_controller.rb
app/controllers/quickvote_controller.rb
app/controllers/voter_controller.rb
app/models/election.rb
app/models/vote.rb
app/views/account/summary.rhtml
app/views/common/_methodinfo_condorcet.rhtml
app/views/common/_methodinfo_ssd.rhtml
app/views/common/_sortable_vote.rhtml
app/views/election/_overview_form.rhtml
app/views/layouts/_footer.rhtml
app/views/voter/full_vote.rhtml
app/views/voter/thanks.rhtml
config/environments/development.rb
config/environments/production.rb
config/environments/test.rb
config/mongrel_cluster.yml

diff --git a/COPYING b/COPYING
index 939a6f41f7cb205024328b3ee392b9ce340f0eee..dba13ed2ddf783ee8118c6a581dbf75305f816a3 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -7,15 +7,15 @@
 
                             Preamble
 
-  The GNU Affero General Public License is a free, copyleft license
-for software and other kinds of works, specifically designed to ensure
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
 cooperation with the community in the case of network server software.
 
-  The licenses for most software and other practical works are
-designed to take away your freedom to share and change the works.  By
-contrast, our General Public Licenses are intended to guarantee your
-freedom to share and change all versions of a program--to make sure it
-remains free software for all its users.
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
 
   When we speak of free software, we are referring to freedom, not
 price.  Our General Public Licenses are designed to make sure that you
@@ -60,12 +60,11 @@ modification follow.
 
   0. Definitions.
 
-  "This License" refers to version 3 of the GNU Affero General Public
-License.
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
 
-  "Copyright" also means copyright-like laws that apply to other kinds
-of works, such as semiconductor masks.
   "The Program" refers to any copyrightable work licensed under this
 License.  Each licensee is addressed as "you".  "Licensees" and
 "recipients" may be individuals or organizations.
@@ -377,12 +376,12 @@ that material) supplement the terms of this License with terms:
   All other non-permissive additional terms are considered "further
 restrictions" within the meaning of section 10.  If the Program as you
 received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further restriction,
-you may remove that term.  If a license document contains a further
-restriction but permits relicensing or conveying under this License, you
-may add to a covered work material governed by the terms of that license
-document, provided that the further restriction does not survive such
-relicensing or conveying.
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
 
   If you add terms to a covered work in accord with this section, you
 must place, in the relevant source files, a statement of the
@@ -551,34 +550,34 @@ shall include the Corresponding Source for any work covered by version 3
 of the GNU General Public License that is incorporated pursuant to the
 following paragraph.
 
-  Notwithstanding any other provision of this License, you have permission
-to link or combine any covered work with a work licensed under version 3
-of the GNU General Public License into a single combined work, and to
-convey the resulting work.  The terms of this License will continue to
-apply to the part which is the covered work, but the work with which it is
-combined will remain governed by version 3 of the GNU General Public
-License.
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
 
   14. Revised Versions of this License.
 
   The Free Software Foundation may publish revised and/or new versions of
-the GNU Affero General Public License from time to time.  Such new
-versions will be similar in spirit to the present version, but may differ
-in detail to address new problems or concerns.
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
 
   Each version is given a distinguishing version number.  If the
-Program specifies that a certain numbered version of the GNU Affero
-General Public License "or any later version" applies to it, you have
-the option of following the terms and conditions either of that
-numbered version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number
-of the GNU Affero General Public License, you may choose any version
-ever published by the Free Software Foundation.
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
 
   If the Program specifies that a proxy can decide which future
-versions of the GNU Affero General Public License can be used, that
-proxy's public statement of acceptance of a version permanently
-authorizes you to choose that version for the Program.
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
 
   Later license versions may give you additional or different
 permissions.  However, no additional obligations are imposed on any
@@ -617,9 +616,9 @@ an absolute waiver of all civil liability in connection with the
 Program, unless a warranty or assumption of liability accompanies a
 copy of the Program in return for a fee.
 
-                       END OF TERMS AND CONDITIONS
+                     END OF TERMS AND CONDITIONS
 
-              How to Apply These Terms to Your New Programs
+            How to Apply These Terms to Your New Programs
 
   If you develop a new program, and you want it to be of the greatest
 possible use to the public, the best way to achieve this is to make it
@@ -634,9 +633,9 @@ the "copyright" line and a pointer to where the full notice is found.
     Copyright (C) <year>  <name of author>
 
     This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU Affero General Public License as
-    published by the Free Software Foundation, either version 3 of the
-    License, or (at your option) any later version.
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
 
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
diff --git a/README b/README
index 9ec9d6212afaf1f645fdbb1b53e278961539d592..5ce586b2191f237948d5201ed70ae1b45508902d 100644 (file)
--- a/README
+++ b/README
@@ -8,12 +8,26 @@ distribute, or rework Selectricity under the terms of that license. Of
 course, we'd sure like it if you would send fixes back to us and tell us
 about cool stuff you do with our software!
 
-The best way to get Selectricity is just to download it from our source
-tree.  At the moment, we're hosting our code at Gitorious which is a
-free software hosting provider. You can download Gitorious at the
-project page by following detailed instructions at Gitorious:
+The best way to get Selectricity is just to download it from our
+source repository. You'll need the Git version control system or
+source control manager to check it. You can get it here:
 
-  http://gitorious.org/selectricity
+  http://git-scm.com/
+
+Once you have it, getting the source code is pretty easy. You just need
+to check out a branch with a command like this:
+
+  git clone http://projects.mako.cc/source/selectricity/.git
+
+By default, this will create a working copy with the latest
+*development* version of our code. If you want the latest production
+version (i.e., what we're running on the site), you need to switch to
+the live version of the software which is kept in a branch called
+"live." Once you cloned the repository above, you can switch into the
+directory (i.e., run "cd selectricity") and then run the following
+command:
+
+  git checkout -b live origin/live
 
 
 ===============================================
@@ -30,12 +44,6 @@ list here:
 
   http://mailman.mit.edu/mailman/listinfo/selectricity
 
-In terms of bugs and documentation, we current plan to build this out in
-our Gitorious wiki so feel free to get started with your own efforts
-along these lines there:
-
-  http://gitorious.org/selectricity/pages/Home
-
 
 ===============================================
 === Dependencies ==============================
@@ -70,7 +78,6 @@ tried it with any other system. Presumably though, anything that
 provides '/usr/bin/sendmail' should work.
 
 
-
 ===============================================
 === Contributors to Selectricity Include ======
 ===============================================
index f6dc5d5fc3f32fc2012f6435932503728b49f69f..bd11174757fe141495bea3eb1510dea37d458626 100644 (file)
@@ -120,7 +120,9 @@ class GraphController < ApplicationController
     data, labels = get_borda_points(@election.borda_result)
     
     size = "400x300"
-    size = "580x300" if @election.candidates.size >= 5
+    #size = "580x300" if @election.candidates.size >= 5
+    size = sprintf "580x%d", @election.candidates.size*22 \
+      if @election.candidates.size >= 5
     
    if @election.candidates.size >= 5
      marker_font_size = 17
@@ -128,7 +130,7 @@ class GraphController < ApplicationController
      marker_font_size = 20
    end
     
-    graph = GruffGraff.new( :graph_type => Gruff::Bar,
+    graph = GruffGraff.new( :graph_type => Gruff::SideBar,
                             :data_name => @election.name,
                             :data => data,
                             :interval_labels => labels,
@@ -136,8 +138,8 @@ class GraphController < ApplicationController
                             :title => "Points Per Candidate",
                             :marker_color => '#999999',
                             :marker_font_size => marker_font_size,
-                            :y_axis_label => "Points",
-                            :x_axis_label => "Candidates")
+                            :x_axis_label => "Points",
+                            :y_axis_label => "Candidates")
     send_data(*graph.output)
   end
   #Acording to Tufte, small, concomparitive, highly labeled data sets usually
index 4b3c0c988d75544b3b9f93945a48e8a016f3fec0..701005d08ed03f1835326cf2f598aaa663ea6ac7 100644 (file)
@@ -158,14 +158,16 @@ class QuickvoteController < ApplicationController
     else
       
       # record the ip address for posterity
-      @voter.ipaddress = request.env["REMOTE_ADDR"]
+      @voter.ipaddress = request.env["HTTP_X_FORWARDED_FOR"]
       @voter.save
       
       # toggle the confirmation bit      
-      @voter.vote.confirm!
-     
-      @voter.reload
-      render :action => 'thanks'
+      if @voter.vote.confirm!
+        @voter.reload
+        render :action => 'thanks'
+      else
+        redirect_to :action => 'index'
+      end
     end
   end
  
@@ -183,9 +185,10 @@ class QuickvoteController < ApplicationController
     @election=QuickVote.ident_to_quickvote(params[:id])
     @election.voters.each do |voter|
       next unless voter.ipaddress
+
       location=nil
-      if defined? Cache and location=Cache.get("GEO:#{voter.ipaddress}")
-      elsif defined? Cache
+      if Cache and location=Cache.get("GEO:#{voter.ipaddress}")
+      elsif Cache
         location = GeoKit::Geocoders::IpGeocoder.geocode(voter.ipaddress)
         Cache.set "GEO:#{voter.ipaddress}", location
       else
index 408bdab880ec0f411a1d19f8eea0261f3b5d1d02..a550f676cc3d2ced5c851eef5c4f4c8632363159 100644 (file)
@@ -97,16 +97,18 @@ class VoterController < ApplicationController
   end
 
   def confirm
-    @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
+    if @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
+        render :action => 'thanks'
+      end
     else
-      render :action => 'thanks'
+      redirect_to :action => 'index'
     end
   end
   
index 4ce571a468c4aa91a39fd6696e7a76161e706afd..ac8d628b16f003a1848f78215aa6f9b4e9811412 100644 (file)
@@ -137,11 +137,11 @@ class Election < ActiveRecord::Base
       # skip if the voter has not voted or has an unconfirmed vote
       next unless voter.voted?
 
-      plurality_tally << voter.vote.rankings.sort[0].candidate.id
+      plurality_tally << voter.vote.rankings.sort[0].candidate_id
       approval_tally << voter.vote.rankings.sort[0..1].collect \
-        { |ranking| ranking.candidate.id }
+        { |ranking| ranking.candidate_id }
       preference_tally << voter.vote.rankings.sort.collect \
-        { |ranking| ranking.candidate.id }
+        { |ranking| ranking.candidate_id }
     end
     
     @plurality_result = PluralityVote.new(plurality_tally).result
@@ -184,13 +184,23 @@ class Election < ActiveRecord::Base
     end
   end
 
+  def just_before_midnight(datetime)
+    if datetime.instance_of? DateTime
+      off = datetime.offset
+    else
+      off = Rational(datetime.utc_offset, 60*60*24)
+    end
+    return DateTime.new(datetime.year, datetime.month, datetime.day,
+                        23, 59, 59, off)
+  end
+
   private
   def enforce_constraints
+    # elections end just before midnight
+    self.enddate = just_before_midnight(self.enddate)
     # kiosks can't be authenticated
     self.authenticated = false if kiosk?
     return true
   end
 
 end
-
-
index cc423110264b43edbefd6ff00e1ec2e9a2230ad6..12dd64d763e1f9fbd47d0bb776c5d118684b1b0b 100644 (file)
@@ -62,14 +62,19 @@ class Vote < ActiveRecord::Base
   end
 
   def confirm!
-    self.confirmed = 1
-    self.time = Time.now
-    self.save
-    
-    unless self.voter.election.quickvote?
-      token.destroy and token.reload if token
-      self.token = Token.new
+    if self.voter.election.candidates.length == self.rankings.length
+      self.confirmed = 1
+      self.time = Time.now
       self.save
+    
+      unless self.voter.election.quickvote?
+        token.destroy and token.reload if token
+        self.token = Token.new
+        self.save
+      end
+      return false
+    else
+      return true
     end
   end
 
index 61a4b24735d1d2d45cbbf299a955c3856b425230..075d0964b2fbd26b76bc9d0f1dbee96662ddb3d3 100644 (file)
@@ -42,6 +42,7 @@ Member since: <%=h @user.created_at.strftime("%x") %>
     <% end -%>
   </table>
 </p>
+<p><%= link_to "Create a new election", :controller => 'election', :action => 'new' %>.</p>
 
 <br />
 
@@ -79,3 +80,4 @@ Your Quickvotes:
   </tr>
 <% end %>
 </table>
+<p><%= link_to( "Create a new QuickVote", :controller => 'quickvote', :action => 'create') %>.</p>
index b941eea258158ab9657b23423796b64d909fac39..1c2a382202b9f2e1a3b1e30e8aba4799f2c8e30f 100644 (file)
@@ -20,7 +20,7 @@ another Condorcet system.</p>
   tables. 
 
   <% if not @election.class == QuickVote %>
-    <%= link_to "Click here", { :action => 'pref_tables', :id => @voter.password }, :popup => [] %>
+    <%= link_to "Click here", { :action => 'pref_tables', :id => @election.id }, :popup => [] %>
     to view details.
   <% end %>
   
index 230191ca381c86581fd377c5729bd6cf032fe5ea..5c01987c562b4dbe49837d981e8e4cd209fb5098 100644 (file)
@@ -32,7 +32,7 @@ Beatpath Winner, Path Voting, and Path Winner.</p>
   tables.
 
   <% if not @election.class == QuickVote %>
-    <%= link_to "Click here", { :action => 'pref_tables', :id => @voter.password }, :popup => [] %>
+    <%= link_to "Click here", { :action => 'pref_tables', :id => @election.id }, :popup => [] %>
     to view details.
   <% end %>
 
index 0fe6ad17d6d45b2617fb6f63e6f773707dfc04c7..75bb22f51765a6795986750e29747bc47ed39e47 100644 (file)
@@ -11,5 +11,6 @@
 
 <%= sortable_element 'rankings-list',
     :url => { :action => "sort_candidates", :id => @voter.vote.id },
+    :method => "get",
     :complete => visual_effect(:highlight, 'rankings-list') %>
 
index 1f540c62462e056605af8684753ef6c44380fe38..a36e8172257f0e4dfb9ec17f227cd5186c436e61 100644 (file)
@@ -17,7 +17,7 @@
 
 <p><label for="election_enddate">Election End Date</label><br />
 <font size="-1"><em>All elections end at 23:59.</em></font><br />
-<%= date_select :election, :enddate %></p>
+<%= datetime_select :election, :enddate, {:discard_hour=>true, :discard_minute=>true} %></p>
 
 <p><label for="election_election_method">Election Method</label><br />
 <% type_hash = {}; ELECTION_TYPES.each {|k,v| type_hash[v] = k} %>
index 27332f0c241ba0c80555e63407adc6661665b4d4..dabd44c008db5c36939c0a0f679000b5aca91f4e 100644 (file)
@@ -1,7 +1,7 @@
   <div id="footer">
        Copyleft 2006-2010
     (<a href="http://projects.mako.cc/source/selectricity/README">Source Available</a>)
-    <a href="http://civic.mit.edu">MIT Center for Future Civic Media</a>, 
+    <a href="http://civic.mit.edu">MIT Center for Civic Media</a>, 
        <a href="http://www.media.mit.edu">MIT Media Lab</a>,
     <%= link_to("Selectricity Team", :controller => 'about', :action => 'team') %>&nbsp;&nbsp;::&nbsp;&nbsp; 
     <a href="http://blog.selectricity.org/">Selectricity News Blog</a>
index 80ea98676d01b72bb50066677ea6548a85f3fe89..be4f7bea7e400eaa4e0f6e688f0d39c61432f78d 100644 (file)
@@ -22,7 +22,8 @@
 
 <p>Drag and drop the items on <strong>list in the left column</strong>
 until they are in order from most preferred at the top to least
-preferred at the bottom. When you are done, press confirm to record your
+preferred at the bottom. When you are done, press the 'Submit Vote'
+button at the bottom of the left column to record your
 vote.</p>
 
 <div class="normal-header">
index c68e22bc0046ee7ec95abe5c291d12a6f6bd310b..84054d6814a97ddc15c2bbeeecbedfecf180c3f7 100644 (file)
@@ -6,6 +6,7 @@
 <p>Your vote has been recorded for the <strong><%= @voter.election.name
 %></strong>.</p>
 
+<% if @voter.election.verifiable %>
 <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. This is
@@ -13,6 +14,7 @@
 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.</p>
+<% end %>
 
 <% if @voter.election.kiosk? and params[:kiosk] == 'true' %>
 
index 2d7f9789e0a4bf8cb1e9388defc71e218665520f..669f755c2473357389d4e4b82779d98e5e912c56 100644 (file)
@@ -1,6 +1,7 @@
 # Settings specified here will take precedence over those in config/environment.rb
 #disable memcache
 Cache=nil
+
 # In the development environment your application's code is reloaded on
 # every request.  This slows down response time but is perfect for development
 # since you don't have to restart the webserver when you make code changes.
index 20ded00559a49d05e95a180342e910687db7bcfd..4bb13510ef6ff610417fb7dd61803e5795e8eee1 100644 (file)
@@ -1,9 +1,9 @@
 # Settings specified here will take precedence over those in config/environment.rb
 #
 #Enable memcache
-#require('memcache')
-#Cache=MemCache.new('localhost', :compression => true)
-Cache=nil
+require('memcache')
+Cache=MemCache.new('localhost', :compression => true)
+
 # The production environment is meant for finished, "live" apps.
 # Code is not reloaded between requests
 config.cache_classes = true
index a1a2608bbfd3ef55531c08d37403266a0d7d463f..f1ab738a22a3a4c59c118b6aca242243cd81312f 100644 (file)
@@ -1,6 +1,7 @@
 # Settings specified here will take precedence over those in config/environment.rb
 #disable memcache
 Cache=nil
+
 # The test environment is used exclusively to run your application's
 # test suite.  You never need to work with it otherwise.  Remember that
 # your test database is "scratch space" for the test suite and is wiped
index 5f5efefa548fd685f7971297415032d05102d088..a22b5c5905237b8291670f11edf08b088b887c02 100644 (file)
@@ -1,4 +1,3 @@
---- 
 cwd: /org/selectricity/selectricity-live
 log_file: log/mongrel.log
 port: "8000"

Benjamin Mako Hill || Want to submit a patch?