]> projects.mako.cc - selectricity/blob - app/models/selectricity_service.rb
elections marked as viewable will now dispaly a link to their results page while...
[selectricity] / app / models / selectricity_service.rb
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
4 #
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.
9 #
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.
14 #
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/>.
18
19 require 'action_controller/integration'
20
21 class SelectricityService < ActionWebService::Base
22   web_service_api SelectricityAPI
23
24   ## Casts a quickvote.
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
29     if election
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)
33       end
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}"
40       voter.vote=Vote.new
41       voter.vote.votes=vote_list[0]
42       voter.vote.time = Time.now
43       voter.save!
44       voter.vote.confirm!
45       voter.save!
46     else
47       raise ArgumentError.new("Cannot find election #{election_name}")
48     end
49   end
50
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)
57     candidates={}
58     raise ArgumentError.new("Quickvote by name #{shortname} doesn't exist") unless qv
59     qv.results
60     qv.candidates.each {|c| candidates[c.id] = c}
61     results=[]
62     id_list.each { |id|
63       name=candidates[id]
64       if name
65         results << name
66       else
67         results << ""
68       end
69     }
70     results
71   end
72
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
80     unless qv
81       raise ArgumentError.new("No quickvote with name #{shortname} found!")
82     end
83     qv.results
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
89     result
90   end
91
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
96   ## respective order.
97   def get_quickvote_candidate_map(shortname)
98     qv=QuickVote.ident_to_quickvote(shortname)
99     result=QuickVoteCandidateMap.new
100     unless qv
101       raise ArgumentError.new("No quickvote with name #{shortname} found!")
102     end
103     candidates={}
104     qv.candidates.each {|c| candidates[c.id] = c.name}
105     result.candidate_ids=candidates.keys
106     result.candidate_names=candidates.values
107     result
108   end
109
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)
114
115     unless qv
116       raise ArgumentError.new("Cannot find QuickVote #{shortname}")
117     end
118
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,
123                     :vote => vote.votes,
124                     :voter_session_id => vote.voter.session_id)
125     end
126   end
127
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)
132     end
133   end
134
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}")
140     end
141
142     QuickVoteStruct.new(
143       :id => election.id,
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 } )
148   end
149
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
157
158     if qv.save
159       return ""
160     else
161       raise ArgumentError.new("Saving quickvote FAILED:"+qv.errors.inspect)
162     end
163   end
164
165 end

Benjamin Mako Hill || Want to submit a patch?