2 class GraphController < ApplicationController
3 # produce a graph of votes per day during an election
5 @election = Election.find(params[:id])
6 data, labels = get_votes_per_day_data(@election)
8 line = Gruff::Line.new("700x400")
9 line.theme = { :background_colors => ['#73BF26', '#ffffff'] }
10 line.title = "Voters Per Day"
11 line.font = File.expand_path('/usr/X11R6/lib/X11/fonts/TTF/Vera.ttf',
14 line.data( "#{@election.name}", data )
17 line.x_axis_label = "Date"
18 line.y_axis_label = "Number of Votes"
19 line.minimum_value = 0.0
21 send_data(line.to_blob, :disposition => 'inline', :type => 'image/png')
24 #will place votes in a fixed number of intervals, and shows votes over time
25 def votes_per_interval
26 @election = Election.find(params[:id])
27 data, labels, scale = get_votes_per_interval_data(@election)
29 line = Gruff::Line.new("700x400")
30 line.theme = { :background_colors => ['#73BF26', '#ffffff'] }
31 line.title = "Voters Over Time"
32 line.font = File.expand_path('/usr/X11R6/lib/X11/fonts/TTF/Vera.ttf',
35 line.data("#{@election.name}", data )
38 line.x_axis_label = scale
39 line.y_axis_label = "Number of Votes"
40 line.minimum_value = 0.0
42 send_data(line.to_blob, :disposition => 'inline', :type => 'image/png')
46 @election = Election.find(params[:id])
50 @election = Election.find(params[:id])
51 pref_tally = make_preference_tally(@election)
53 @borda_result = BordaVote.new(pref_tally).result
54 data, labels = get_borda_points(@borda_result)
56 bar = Gruff::Bar.new("700x400")
57 bar.theme = { :background_colors => ['#73BF26', '#ffffff'] }
58 bar.title = "Points Per Candidate"
59 bar.font = File.expand_path('/usr/X11R6/lib/X11/fonts/TTF/Vera.ttf',
62 bar.data("#{@election.name}", data)
65 bar.y_axis_label = "Points"
66 bar.x_axis_label = "Candidate"
67 bar.minimum_value = 0.0
68 send_data(bar.to_blob, :disposition => 'inline', :type => 'image/png')
73 # generate the data and labels for each graph
74 def get_votes_per_day_data(election)
75 voter_days = Array.new
76 unique_days = Array.new
77 total_per_day = Array.new
78 election_days = Hash.new
80 #turn election startdate into date object, and create the range of election
81 startdate = Date.parse(election.startdate.to_s)
82 election_range = startdate..Date.today
84 # create a hash with all the dates of the election in String format
85 # referenced by their order in the election
86 election_range.each_with_index do |day, index|
87 election_days[index] = day.to_s
90 # Now I need to create an array with all the times votes were made
91 election.votes.each do |vote|
92 voter_days << Date.parse(vote.time.to_s)
96 # Now I need to count how many times each each date appears in voter_days,
97 # and put that number into a votes_per_day array, the 'data' for the graph
98 #Create an array of unique days from voter_days
99 voter_days.each do |day|
100 unless unique_days.any? {|date| date.eql?(day)}
106 #find all dates where those days = date at current index, put size of returned
107 #array into total_per_day
108 unique_days.each_with_index do |date, index|
109 total_per_day << (voter_days.select {|day| day.eql?(date)}).size
112 # return the data and the labels
113 return total_per_day, election_days
117 def get_votes_per_interval_data(election)
118 labels_hash = Hash.new
120 total_per_interval = Array.new
123 starttime = election.startdate
124 timedelta = Time.now - starttime
126 interval_length = timedelta/numcols
128 # Make a hash, buckets, indexed by time intervals and containing empty arrays
129 # The time object must come first in addition!
130 # i would start at 0, i+1 goes from 0 up till numcols
131 numcols.times {|i| buckets[starttime + ((i+1)*interval_length)] = []}
133 # Put votes into bucket according to the time interval to which they belong,
134 # referenced by their key
135 # Will build a graph over time, as each successive interval wil lhave more
137 election.votes.each do |vote|
138 buckets.keys.sort.each do |inter|
140 buckets[inter] << vote
145 total_per_interval = buckets.keys.sort.collect {|key| buckets[key].size}
147 # Create the hash for the labels. Each graph has ten columns, and three
149 if timedelta < 2.hours #under two hours use minutes for labels
150 labels_hash[0] = starttime.min.to_s
151 labels_hash[(numcols/2)-1] = (starttime + (timedelta/2)).min.to_s
152 labels_hash[numcols-1] = Time.now.min.to_s
153 interval_type = "Minutes"
154 elsif timedelta < 2.days #more than 2 hours means use hours for labels
155 labels_hash[0] = starttime.hour.to_s
156 labels_hash[(numcols/2)-1] = (starttime + (timedelta/2)).hour.to_s
157 labels_hash[numcols-1] = Time.now.hour.to_s
158 interval_type = "Hours"
159 else #more than 2 days means use dates for labels
160 labels_hash[0] = (Date.parse(starttime.to_s)).to_s
161 labels_hash[(numcols/2)-1] = (Date.parse(starttime + (timedelta/2))).to_s
162 labels_hash[numcols-1] = (Date.today).to_s
163 interval_type = "Days"
166 # Make sure to return an array for data and hash for labels
167 return total_per_interval, labels_hash, interval_type
170 def get_borda_points(result)
171 #points holds how mnay points each candidate has received in array form
172 #becasue Gruff::Bar#data takes only an array
176 #Populate points with an sorted array from election.votes hash
177 #biggest to smallest will go from left to right
178 points = result.election.votes.sort do |a, b|
180 end.collect {|i| i[1]}
183 result.ranked_candidates.each_with_index do |candidate, index|
184 labels[index] = Candidate.find(candidate).name
187 return points, labels
190 #most vote result objects require an array of vote arrays, which this will make
191 def make_preference_tally(election)
192 preference_tally = Array.new
193 @election.voters.each do |voter|
194 next unless voter.voted?
195 preference_tally << voter.vote.rankings.sort.collect \
196 { |ranking| ranking.candidate.id }
198 return preference_tally