1 # Selectricity: Voting Machinery for the Masses
2 # Copyright (C) 2007, 2008 Benjamin Mako Hill <mako@atdot.cc>
3 # Copyright (C) 2007 Massachusetts Institute of Technology
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Affero General Public License for more details.
15 # You should have received a copy of the GNU Affero General Public
16 # License along with this program. If not, see
17 # <http://www.gnu.org/licenses/>.
19 require 'action_controller/integration'
21 class SelectricityService < ActionWebService::Base
22 web_service_api SelectricityAPI
25 ## Expects a quickvote name, a voter ID, and a list of candidate ID's respectively.
26 ## Returns a string containing any potential errors that occurred in the process.
27 def cast_quickvote(election_name, voter_id, vote_list)
28 election = QuickVote.ident_to_quickvote election_name
30 candidates=election.candidates.collect { |c| c.id }
31 vote_list[0].each do |vote|
32 raise ArgumentError.new("Invalid Candidate ID #{vote}") unless candidates.index(vote)
34 raise ArgumentError.new("You must rank all candidates") unless candidates.length <= vote_list[0].length
35 raise ArgumentError.new("Please rank each candidate only once") if vote_list[0].uniq!
36 voter = QuickVoter.new
37 voter.election = election
38 voter.ipaddress = "XMLRPC Request"
39 voter.session_id = "XMLRPC:#{voter_id}"
41 voter.vote.votes=vote_list[0]
42 voter.vote.time = Time.now
47 raise ArgumentError.new("Cannot find election #{election_name}")
51 ## Converts QuickVote candidate ID's to names
52 ## Takes in a QuickVote name and a list of candidate ID's, and returns the names of
53 ## each candidate. Useful for doing just a few lookups; it's more efficient to use
54 ## get_quickvote_candidate_map for presenting info about an entire election.
55 def quickvote_candidate_ids_to_names(shortname, id_list)
56 qv=QuickVote.ident_to_quickvote(shortname)
58 raise ArgumentError.new("Quickvote by name #{shortname} doesn't exist") unless qv
60 qv.candidates.each {|c| candidates[c.id] = c}
73 ## Return the results of a QuickVote.
74 ## Takes in the name of a quickvote, and returns a structure as described by
75 ## QuickVoteResultStruct
76 def get_quickvote_results(shortname)
77 #TODO: Validate shortname
78 qv=QuickVote.ident_to_quickvote(shortname)
79 result=QuickVoteResultStruct.new
81 raise ArgumentError.new("No quickvote with name #{shortname} found!")
84 result.plurality_winners=qv.plurality_result.winners
85 result.approval_winners=qv.approval_result.winners
86 result.condorcet_winners=qv.condorcet_result.winners
87 result.ssd_winners=qv.ssd_result.winners
88 result.borda_winners=qv.borda_result.winners
92 ## Returns information regarding all the candidates in a QuickVote
93 ## Takes in a QuickVote name, and returns the list of names and ID's of candidates
94 ## This can be useful for presenting the user with a list of readable names, while
95 ## the software sends results to us in the numeric ID's we require. The two lists are in
97 def get_quickvote_candidate_map(shortname)
98 qv=QuickVote.ident_to_quickvote(shortname)
99 result=QuickVoteCandidateMap.new
101 raise ArgumentError.new("No quickvote with name #{shortname} found!")
104 qv.candidates.each {|c| candidates[c.id] = c.name}
105 result.candidate_ids=candidates.keys
106 result.candidate_names=candidates.values
110 ## Get information on all the votes cast in a QuickVote
111 ## Takes in the name of a QuickVote, returns an array of QuickVoterInfo structures.
112 def get_quickvote_votes(shortname)
113 qv = QuickVote.ident_to_quickvote(shortname)
116 raise ArgumentError.new("Cannot find QuickVote #{shortname}")
119 qv.votes.collect do |vote|
120 QuickVoterInfo.new(:voter_id => vote.voter.id,
121 :voter_ipaddress => vote.voter.ipaddress,
122 :vote_time => vote.time.to_i,
124 :voter_session_id => vote.voter.session_id)
128 ## Gets a list of all QuickVotes in the system.
129 def list_quickvotes()
130 QuickVote.find(:all).collect do |election|
131 get_quickvote(election.name)
135 ## Gets information on a particular QuickVote
136 ## Takes in a QuickVote name
137 def get_quickvote(shortname)
138 unless election = QuickVote.ident_to_quickvote(shortname)
139 raise ArgumentError.new("Cannot find QuickVote named #{shortname}")
144 :name => election.name,
145 :description => election.description,
146 :candidate_ids => election.candidates.collect {|c| c.id },
147 :candidate_names => election.candidates.collect {|c| c.name } )
150 ## Create a QuickVote
151 ## Pass in a QuickVoteStruct populated with all the fields but the candidate ID's
152 ## Any candidate ID's you fill in will be ignored anyway.
153 def create_quickvote(election)
154 qv = QuickVote.new(:name => election.name,
155 :description => election.description)
156 qv.candidate_names = election.candidate_names
161 raise ArgumentError.new("Saving quickvote FAILED:"+qv.errors.inspect)