4f33ac3a3f7b02d212a7362ca9b26f10d0e22f3a
[selectricity] / app / controllers / graph_controller.rb
1 require 'date'
2 class GraphController < ApplicationController
3   # produce a graph of votes per day during an election
4   def votes_per_day
5     @election = Election.find(params[:id])
6     data, labels = get_votes_per_day_data(@election)
7     
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',
12                                  RAILS_ROOT)
13
14     line.data("#{@election.name}", data )
15     line.labels = labels
16
17     line.x_axis_label = "Date"
18     line.y_axis_label = "Number of Votes"
19     line.minimum_value = 0.0
20
21     line.draw
22     send_data(line.to_blob, :disposition => 'inline', :type => 'image/png')
23   end
24  
25   def votes_per_interval
26     @election = Election.find(params[:id])
27     data, labels = get_votes_per_interval_data(@election)
28     
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',
33                                RAILS_ROOT)
34     
35     line.data("#{@election.name}", data )
36     line.labels = labels
37     
38     line.x_axis_label = "Intervals"
39     line.y_axis_label = "Number of Votes"
40     line.minimum_value = 0.0
41                                
42     line.draw
43     send_data(line.to_blob, :disposition => 'inline', :type => 'image/png')  
44   end
45  
46  private 
47  
48   # generate the data and labels for each graph
49   def get_votes_per_day_data(election)
50     voter_days = Array.new
51     unique_days = Array.new
52     total_per_day = Array.new
53     election_days = Hash.new
54     
55     #turn election startdate into date object, and create the range of election
56     startdate = Date.parse(election.startdate.to_s)
57     election_range = startdate..Date.today
58     
59     # create a hash with all the dates of the election in String format
60     # referenced by their order in the election
61     election_range.each_with_index do |day, index|
62       election_days[index] = day.to_s
63     end
64     
65     # Now I need to create an array with all the times votes were made
66     election.votes.each do |vote|
67         voter_days << Date.parse(vote.time.to_s)
68     end
69     voter_days.sort!
70     
71     # Now I need to count how many times each each date appears in voter_days,
72     # and put that number into a votes_per_day array, the 'data' for the graph    
73     #Create an array of unique days from voter_days
74     voter_days.each do |day|
75       unless unique_days.any? {|date| date.eql?(day)}
76         unique_days << day
77       end
78     end
79     unique_days.sort!
80     
81     #find all dates where those days = date at current index, put size of returned
82     #array into total_per_day
83     unique_days.each_with_index do |date, index|
84       total_per_day << (voter_days.select {|day| day.eql?(date)}).size
85     end    
86
87     # return the data and the labels
88     return total_per_day, election_days
89    
90   end
91   
92   def get_votes_per_interval_data(election)
93     labels_hash = Hash.new
94     buckets = Hash.new
95     total_per_interval = Array.new
96     
97     starttime = election.startdate
98     timedelta = Time.now - starttime
99     numcols = 10
100     interval_length = timedelta/numcols
101     
102     # Make a hash, buckets, indexed by time intervals and containing empty arrays
103     # The time object must come first in addition! 
104     # i would start at 0, i+1 goes from 0 up till numcols
105     numcols.times {|i| buckets[starttime + ((i+1)*interval_length)] = []}
106      
107     # Put votes into bucket according to the time interval to which they belong,
108     # referenced by their key
109     # Will build a graph over time, as each successive interval wil lhave more
110     # vote objects  
111     election.votes.each do |vote|
112       buckets.keys.sort.each do |inter|
113         if vote.time < inter
114           buckets[inter] << vote
115         end
116       end
117     end
118   
119     total_per_interval = buckets.keys.sort.collect {|key| buckets[key].size}
120     
121     # Create the hash for the labels. Each graph has ten columns, and three
122     # will be labeled
123     if timedelta < 2.hours #under two hours use minutes for labels
124       labels_hash[0] = starttime.min.to_s
125       labels_hash[(numcols/2)-1] = (starttime + (timedelta/2)).min.to_s
126       labels_hash[numcols-1] = Time.now.min.to_s
127     elsif timedelta < 2.days #more than 2 hours means use hours for labels
128       labels_hash[0] = starttime.hour.to_s
129       labels_hash[(numcols/2)-1] = (starttime + (timedelta/2)).hour.to_s
130       labels_hash[numcols-1] = Time.now.hour.to_s
131     else #more than 2 days means use dates for labels
132       labels_hash[0] = (Date.parse(starttime.to_s)).to_s
133       labels_hash[(numcols/2)-1] = (Date.parse(starttime + (timedelta/2))).to_s
134       labels_hash[numcols-1] = (Date.today).to_s
135     end
136     
137     # Make sure to return an array for data and hash for labels
138     return total_per_interval, labels_hash   
139   end
140
141 end

Benjamin Mako Hill || Want to submit a patch?