2 class GraphController < ApplicationController
6 def initialize(options)
8 @graph = options[:graph_type].new(size)
10 @graph.theme = { :background_colors => ['#73BF26', '#ffffff'] }
11 @graph.font = File.expand_path('/usr/X11R6/lib/X11/fonts/TTF/Vera.ttf',
14 # fill in the data with the optional data name
15 #Check to see if multiple datasets, if so, fill them all!
16 if options[:data].size > 1 && options[:data].all? {|i| i.is_a?(Array)}
17 options[:data].each do |array|
18 @graph.data( options.fetch(:data_name, "Data"), array)
20 else #one dimensional array, just pass it in
21 @graph.data( options.fetch(:data_name, "Data"), options[:data] )
24 # set the labels or create an empty hash
25 @graph.labels = options[:interval_labels] \
26 if options.has_key?(:interval_labels) and \
27 options[:interval_labels].class == Hash
28 @graph.x_axis_label = options[:x_axis_label] \
29 if options.has_key?(:x_axis_label)
30 @graph.y_axis_label = options[:y_axis_label] \
31 if options.has_key?(:y_axis_label)
32 @graph.title = options[:title] if options.has_key?(:title)
34 @graph.minimum_value = 0.0
39 return([@graph.to_blob, {:disposition => 'inline', :type => 'image/png'}])
44 # produce a graph of votes per day during an election
46 @election = Election.find(params[:id])
47 data, labels = get_votes_per_day_data(@election)
49 graph = GruffGraff.new( :graph_type => Gruff::Line,
50 :data_name => @election.name,
52 :interval_labels => labels,
53 :title => "Voters Per Day",
54 :x_axis_label => "Data",
55 :y_axis_label =>"Number of Votes")
56 send_data(*graph.output)
59 #will place votes in a fixed number of intervals, and shows votes over time
60 def votes_per_interval
61 @election = Election.find(params[:id])
62 data, labels, scale = get_votes_per_interval_data(@election)
64 graph = GruffGraff.new( :graph_type => Gruff::Line,
65 :data_name => @election.name,
67 :interval_labels => labels,
68 :title => "Voters Over Time",
69 :x_axis_label => scale,
70 :y_axis_label => "Number of Votes")
71 send_data(*graph.output)
75 @election = Election.find(params[:id])
76 pref_tally = make_preference_tally(@election)
78 @borda_result = BordaVote.new(pref_tally).result
79 data, labels = get_borda_points(@borda_result)
81 graph = GruffGraff.new( :graph_type => Gruff::Bar,
82 :data_name => @election.name,
84 :interval_labels => labels,
85 :title => "Points Per Candidate",
86 :y_axis_label => "Points",
87 :x_axis_label => "Candidate")
88 send_data(*graph.output)
92 @election = Election.find(params[:id])
93 pref_tally = make_preference_tally(@election)
95 fulldata, labels = get_positions_info(@election)
97 graph = GruffGraff.new( :graph_type => Gruff::Bar,
98 :data_name => @election.name,
100 :interval_labels => labels,
101 :title => "Times Voted in Each Position",
102 :y_axis_label => "Number of Times Ranked",
103 :x_axis_label => "Rank")
104 send_data(*graph.output)
109 def get_positions_info(election)
112 rank_labels = Hash.new
114 election.candidates.each do |candidate|
115 buckets[candidate.id] = []
116 buckets2[candidate.id] = []
119 election.votes.each do |vote|
120 vote.rankings.each do |ranking|
121 buckets[ranking.candidate_id] << ranking.rank
125 buckets.each_pair do |id, array|
126 (1..election.candidates.size).each do |i|
127 buckets2[id] << (array.find_all {|rank| rank == i}).size
131 election.votes.each do |vote|
132 vote.rankings.size.times do |i|
133 rank_labels[i] = (i+1).to_s
137 return buckets2.values, rank_labels
141 # generate the data and labels for each graph
142 def get_votes_per_day_data(election)
143 voter_days = Array.new
144 unique_days = Array.new
145 total_per_day = Array.new
146 election_days = Hash.new
148 #turn election startdate into date object, and create the range of election
149 startdate = Date.parse(election.startdate.to_s)
150 election_range = startdate..Date.today
152 # create a hash with all the dates of the election in String format
153 # referenced by their order in the election
154 election_range.each_with_index do |day, index|
155 election_days[index] = day.to_s
158 # Now I need to create an array with all the times votes were made
159 election.votes.each do |vote|
160 voter_days << Date.parse(vote.time.to_s)
164 # Now I need to count how many times each each date appears in voter_days,
165 # and put that number into a votes_per_day array, the 'data' for the graph
166 #Create an array of unique days from voter_days
167 voter_days.each do |day|
168 unless unique_days.any? {|date| date.eql?(day)}
174 #find all dates where those days = date at current index, put size of returned
175 #array into total_per_day
176 unique_days.each_with_index do |date, index|
177 total_per_day << (voter_days.select {|day| day.eql?(date)}).size
180 # return the data and the labels
181 return total_per_day, election_days
185 def get_votes_per_interval_data(election)
186 labels_hash = Hash.new
188 total_per_interval = Array.new
191 starttime = election.startdate
192 timedelta = Time.now - starttime
194 interval_length = timedelta/numcols
196 # Make a hash, buckets, indexed by time intervals and containing empty arrays
197 # The time object must come first in addition!
198 # i would start at 0, i+1 goes from 1 up till numcols
199 numcols.times {|i| buckets[starttime + ((i+1)*interval_length)] = []}
201 # Put votes into bucket according to the time interval to which they belong,
202 # referenced by their key
203 # Will build a graph over time, as each successive interval will have more
205 election.votes.each do |vote|
206 buckets.keys.sort.each do |inter|
208 buckets[inter] << vote
213 total_per_interval = buckets.keys.sort.collect {|key| buckets[key].size}
215 # Create the hash for the labels. Each graph has ten columns, and three
217 if timedelta < 2.hours #under two hours use minutes for labels
218 labels_hash[0] = starttime.min.to_s
219 labels_hash[(numcols/2)-1] = (starttime + (timedelta/2)).min.to_s
220 labels_hash[numcols-1] = Time.now.min.to_s
221 interval_type = "Minute of the Hour"
222 elsif timedelta < 2.days #more than 2 hours means use hours for labels
223 labels_hash[0] = starttime.hour.to_s
224 labels_hash[(numcols/2)-1] = (starttime + (timedelta/2)).hour.to_s
225 labels_hash[numcols-1] = Time.now.hour.to_s
226 interval_type = "Hour of the Day on 24 hour scale"
227 else #more than 2 days means use dates for labels
228 labels_hash[0] = (Date.parse(starttime.to_s)).to_s
229 labels_hash[(numcols/2)-1] = (Date.parse((starttime + (timedelta/2)).to_s)).to_s
230 labels_hash[numcols-1] = (Date.today).to_s
231 interval_type = "The Date"
234 # Make sure to return an array for data and hash for labels
235 return total_per_interval, labels_hash, interval_type
238 def get_borda_points(result)
239 #points holds how mnay points each candidate has received in array form
240 #becasue Gruff::Bar#data takes only an array
244 #Populate points with an sorted array from election.votes hash
245 #biggest to smallest will go from left to right
246 points = result.election.votes.sort do |a, b|
248 end.collect {|i| i[1]}
251 result.ranked_candidates.each_with_index do |candidate, index|
252 labels[index] = Candidate.find(candidate).name
255 return points, labels
258 #most vote result objects require an array of vote arrays, which this will make
259 def make_preference_tally(election)
260 preference_tally = Array.new
261 @election.voters.each do |voter|
262 next unless voter.voted?
263 preference_tally << voter.vote.rankings.sort.collect \
264 { |ranking| ranking.candidate.id }
266 return preference_tally