lib/google_otg.rb in google_otg-1.0.19 vs lib/google_otg.rb in google_otg-1.1.0
- old
+ new
@@ -1,400 +1,520 @@
-$:.unshift(File.dirname(__FILE__))
-require 'gchart_mod'
-require 'uri'
-require 'fastercsv'
-
-module GoogleOtg
-
- DEFAULT_INCREMENT = 1 # 1 day
-
- def google_line_graph(hits, args = {})
-
- raise ArgumentError, "Invalid hits" unless hits && hits.length > 0
-
- size = args.has_key?(:size) ? args[:size] : '800x200'
- title = args.has_key?(:title) ? args[:title] : "Graph"
- title_color = args.has_key?(:title_color) ? args[:title_color] : '000000'
- title_size = args.has_key?(:title_size) ? args[:title_size] : '20'
- grid_lines = args.has_key?(:grid_lines) ? args[:grid_lines] : [25,50]
- legend = args.has_key?(:legend) ? args[:legend] : nil
-
- x_labels = []
- y_labels = [0]
- data = []
-
- if hits[0].is_a?(Array)
- shape_markers = [['D','6699CC',0,'-1.0',4],['D','FF9933',1,'-1.0',2],['o','0000ff',0,'-1.0',8],['o','FF6600',1,'-1.0',8]]
- line_colors = ['6699CC','FF9933']
-
- hits.map{|h|
- converted = hits_to_gchart_range(h, args)
- data.push(converted[:points])
- x_labels = converted[:x_labels] if converted[:x_labels].length > x_labels.length
- y_labels = converted[:y_labels] if converted[:y_labels].max > y_labels.max
- }
-
- else
- shape_markers = [['D','6699CC',0,'-1.0',4],['o','0000ff',0,'-1.0',8]]
- line_colors = ['6699CC']
-
- converted = hits_to_gchart_range(hits, args)
- data.push(converted[:points])
- x_labels = converted[:x_labels]
- y_labels = converted[:y_labels]
-
- end
-
- axis_with_labels = 'x,y'
- axis_labels = [x_labels,y_labels]
-
- return Gchart.line(
- :size => size,
- :title => title,
- :title_color => title_color,
- :title_size => title_size,
- :grid_lines => grid_lines,
- :shape_markers => shape_markers,
- :data => data,
- :axis_with_labels => axis_with_labels,
- :max_value => y_labels[y_labels.length - 1],
- :legend => legend,
- :axis_labels => axis_labels,
- :line_colors => line_colors)
-
- end
-
- def data_to_csv(hits, args={})
- if hits.is_a?(Array) and hits[0].is_a?(Array)
- range = hits.map{|h| hits_to_otg_range(h, args) }
- else
- range = [hits_to_otg_range(hits, args)]
- end
-
- legend = []
- legend.concat(args[:legend])
-
- csv_data = FasterCSV.generate do |csv|
- x_labels = range[0][:x_labels].map{|x_label| x_label[1]}
- x_labels.unshift("")
- csv << x_labels
-
- raise ArgumentError, "Mismatched array lengths" unless range.length == args[:legend].length
-
- for data in range
- points = data[:points].map{|point| point[:Value][0]}
- points.unshift(legend.shift)
- csv << points
- end
- end
-
- return csv_data
- end
-
- def over_time_graph(hits, args = {})
- height = args.has_key?(:height) ? args[:height] : 125
- src = args.has_key?(:src) ? args[:src] : "http://www.google.com/analytics/static/flash/OverTimeGraph.swf"
-
- if hits.is_a?(Array) and hits[0].is_a?(Array)
- range = hits.map{|h| hits_to_otg_range(h, args) }
- else
- range = [hits_to_otg_range(hits, args)]
- end
- vars = range_to_flashvars(range)
-
- html = <<-eos
-<embed width="100%" height="#{height}"
-wmode="opaque" salign="tl" scale="noScale" quality="high" bgcolor="#FFFFFF"
-flashvars="input=#{vars}"
-pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash"
-src="#{src}"/>
-eos
-
- return html
-
- end
+$:.unshift(File.dirname(__FILE__))
+require 'gchart_mod'
+require 'uri'
+require 'fastercsv'
+require 'httparty'
- def google_pie(hits, label_fn, args = {})
- height = args.has_key?(:height) ? args[:height] : 125
- width = args.has_key?(:width) ? args[:width] : 125
- pie_values = extract_pct_values(hits, label_fn, args)
- vars = pie_to_flashvars(pie_values, args)
- src = args.has_key?(:src) ? args[:src] : "http://www.google.com/analytics/static/flash/pie.swf"
-
- html = <<-eos
-<embed
- width="#{width}"
- height="#{height}"
- salign="tl"
- scale="noScale"
- quality="high"
- bgcolor="#FFFFFF"
- flashvars="input=#{vars}&locale=en-US"
- pluginspage="http://www.macromedia.com/go/getflashplayer"
- type="application/x-shockwave-flash"
+module GoogleOtg
+ include HTTParty
+ @@DEFAULT_INCREMENT = 1 # 1 day
+
+ module Helper
+ def date_range
+ tr = self.setup_time_range
+ tr[:lower_bound].strftime("%m/%d/%Y") + " - " + tr[:upper_bound].strftime("%m/%d/%Y")
+ end
+
+ def over_time_graph(hits, args = {})
+ tr = self.setup_time_range
+ args[:time_zone] = tr[:time_zone] unless args.has_key?(:time_zone)
+ args[:range] = tr[:range] unless args.has_key?(:range)
+
+ height = args.has_key?(:height) ? args[:height] : 125
+ src = args.has_key?(:src) ? args[:src] : "http://www.google.com/analytics/static/flash/OverTimeGraph.swf"
+
+ if hits.is_a?(Array) and hits[0].is_a?(Array)
+ range = hits.map{|h| hits_to_otg_range(h, args) }
+ else
+ range = [hits_to_otg_range(hits, args)]
+ end
+ vars = range_to_flashvars(range)
+
+ html = <<-eos
+ <embed width="100%" height="#{height}"
+ wmode="opaque" salign="tl" scale="noScale" quality="high" bgcolor="#FFFFFF"
+ flashvars="input=#{vars}"
+ pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash"
src="#{src}"/>
-eos
- return html
-
- end
-
- def pie_to_flashvars(args = {})
+ eos
- labels = args[:labels]
- raw_values = args[:raw_values]
- percent_values = args[:percent_values]
-
- options = {
- :Pie => {
- :Id => "Pie",
- :Compare => false,
- :HasOtherSlice => false,
- :RawValues => raw_values,
- :Format => "DASHBOARD",
- :PercentValues => percent_values
- }
- }
+ return html
- return URI::encode(options.to_json)
+ end
+ end
- end
- protected :pie_to_flashvars
-
- def extract_pct_values(hits, label_fn, args = {})
+ def over_time_graph_download(results, type, title, legend)
+ tr = self.setup_time_range
- limit = args.has_key?(:limit) ? args[:limit] : 0.0
+ if type == :csv
+ csv_data = data_to_csv(results,
+ :legend => legend,
+ :x_label_format => "%m/%d/%Y",
+ :time_zone => tr[:time_zone],
+ :range => tr[:range])
- total = 0.0
- other = 0.0
- percent_values = []
- raw_values = []
- labels = []
- values = []
- hits.each{|hit|
- total += hit.count.to_f
- }
- hits.each{|hit|
- ct = hit.count.to_f
- pct = (ct / total)
-
- if pct > limit
- percent_values.push([pct, sprintf("%.2f%%", pct * 100)])
- raw_values.push([ct, ct])
-
- label = label_fn.call(hit)
- meta = args.has_key?(:meta) ? args[:meta].call(hit) : nil
-
- labels.push(label)
- values.push({:label => label, :meta => meta, :percent_value => [pct, sprintf("%.2f%%", pct * 100)], :raw_value => ct})
+ time_label = tr[:time_zone].now.strftime("%Y-%m-%d")
+ file_data_type = legend.join(" ").downcase.gsub(" ", "_")
+ outfile = "#{request.env['HTTP_HOST']}-#{file_data_type}_#{time_label}.csv"
+
+ send_data csv_data,
+ :type => 'text/csv; charset=iso-8859-1; header=present',
+ :disposition => "attachment; filename=#{outfile}"
+ else
+ graph_url = google_line_graph(
+ results ,
+ :x_label_format => "%a, %b %d",
+ :time_zone => tr[:time_zone],
+ :title => title,
+ :range => tr[:range],
+ :max_x_label_count => 4,
+ :legend => legend)
+
+ if params.has_key?(:send_file)
+ send_data HTTParty::get(graph_url), :filename => "graph.png"
+
else
- other += ct
+ url = url_for({:send_file => 1,
+ :controller => controller_name,
+ :action => action_name}.merge(params))
+ render :inline => "<img src='#{url}'/>"
end
- }
- if other > 0.0
- pct = other / total
- percent_values.push([pct, sprintf("%.2f%%", pct * 100)])
- raw_values.push([other, other])
- labels.push("Other")
- values.push({:label => "Other", :percent_value => [pct, sprintf("%.2f%%", pct * 100)], :raw_value => other})
+ end
end
- return {:labels => labels, :raw_values => raw_values, :percent_values => percent_values, :values => values}
+ def grouped_query(class_to_query, args = [])
- end
- protected :extract_pct_values
-
- def flto10(val)
- return ((val / 10) * 10).to_i
- end
- protected :flto10
-
- def hits_to_otg_range(hits, args = {})
- return hits_to_range(hits, lambda {|count, date_key, date_value|
- {:Value => [count, count], :Label => [date_key, date_value]}
- }, lambda{|mid, top|
- [[mid,mid],[top,top]]
- }, lambda{|hit, hit_date_key, hit_date_value|
- [hit_date_key, hit_date_value]
- },args)
- end
-
- def hits_to_gchart_range(hits, args = {})
- return hits_to_range(hits, lambda {|count, date_key, date_value|
- count
- }, lambda {|mid, top|
- [0,top/2,top]
- },lambda{|hit, hit_date_key, hit_date_value|
- hit_date_value
- }, args)
- end
-
- def hits_to_range(hits, points_fn, y_label_fn, x_label_fn, args = {})
+ date_field = args.has_key?(:date_field) ? args[:date_field] : "created_at"
+ where_args = args.has_key?(:conditions) ? args[:conditions] : ["TRUE"]
+ where_query = where_args.shift
- return nil unless hits
-
- hits.map{|h|
- if !h.respond_to?("created_at") || !h.respond_to?("count")
- raise ArgumentError, "Invalid object type. All objects must respond to 'count' and 'created_at'"
+ tr = self.setup_time_range
+ return class_to_query.find_by_sql(["
+ SELECT DAYOFYEAR(TIMESTAMPADD(SECOND, ?, #{date_field})) as d,
+ DATE(TIMESTAMPADD(SECOND, ?, #{date_field})) as created_at,
+ count(*) as count
+ FROM #{class_to_query.table_name}
+ WHERE #{where_query}
+
+ AND #{date_field} >= TIMESTAMPADD(SECOND, -1 * ?, DATE(?))
+ AND #{date_field} <= TIMESTAMPADD(SECOND, -1 * ?, DATE(?)) + INTERVAL 1 DAY
+
+ GROUP BY d
+ ORDER BY created_at
+ ", tr[:time_zone].utc_offset,
+ tr[:time_zone].utc_offset,
+ where_args,
+ tr[:time_zone].utc_offset,
+ tr[:lower_bound].strftime("%Y-%m-%d"),
+ tr[:time_zone].utc_offset,
+ tr[:upper_bound].strftime("%Y-%m-%d")].flatten)
+ end
+
+ protected
+ def google_line_graph(hits, args = {})
+
+ raise ArgumentError, "Invalid hits" unless hits
+
+ size = args.has_key?(:size) ? args[:size] : '800x200'
+ title = args.has_key?(:title) ? args[:title] : "Graph"
+ title_color = args.has_key?(:title_color) ? args[:title_color] : '000000'
+ title_size = args.has_key?(:title_size) ? args[:title_size] : '20'
+ grid_lines = args.has_key?(:grid_lines) ? args[:grid_lines] : [25,50]
+ legend = args.has_key?(:legend) ? args[:legend] : nil
+
+ x_labels = []
+ y_labels = [0]
+ data = []
+
+ if hits[0].is_a?(Array)
+ shape_markers = [['D','6699CC',0,'-1.0',4],['D','FF9933',1,'-1.0',2],['o','0000ff',0,'-1.0',8],['o','FF6600',1,'-1.0',8]]
+ line_colors = ['6699CC','FF9933']
+
+ hits.map{|h|
+ converted = hits_to_gchart_range(h, args)
+ data.push(converted[:points])
+ x_labels = converted[:x_labels] if converted[:x_labels].length > x_labels.length
+ y_labels = converted[:y_labels] if converted[:y_labels].max > y_labels.max
+ }
+
+ else
+ shape_markers = [['D','6699CC',0,'-1.0',4],['o','0000ff',0,'-1.0',8]]
+ line_colors = ['6699CC']
+
+ converted = hits_to_gchart_range(hits, args)
+ data.push(converted[:points])
+ x_labels = converted[:x_labels]
+ y_labels = converted[:y_labels]
+
end
- }
-
- tz = args.has_key?(:time_zone) ? args[:time_zone] : ActiveSupport::TimeZone['UTC']
- label = args.has_key?(:label) ? args[:label] : "Value"
- time_fn = args.has_key?(:time_fn) ? args[:time_fn] : lambda {|h|
- return tz.local(h.created_at.year, h.created_at.month, h.created_at.day, h.created_at.hour, h.created_at.min, h.created_at.sec) # create zoned time
- }
- increment = args.has_key?(:increment) ? args[:increment] : DEFAULT_INCREMENT
-
- x_label_format = args.has_key?(:x_label_format) ? args[:x_label_format] : "%A %I:%M%p"
-
- max_y = 0
- hits_dict = {}
- hits.each { |h|
- hits_dict[time_fn.call(h)] = h
- }
- total = 0
-
- points = []
- point_dates = []
+ axis_with_labels = 'x,y'
+ axis_labels = [x_labels,y_labels]
- if args[:range] && args[:range][:lower_bound]
- current = args[:range][:lower_bound]
- else
- current = hits.length > 0 ? time_fn.call(hits[0]) : now_floored
+ return Gchart.line(
+ :size => size,
+ :title => title,
+ :title_color => title_color,
+ :title_size => title_size,
+ :grid_lines => grid_lines,
+ :shape_markers => shape_markers,
+ :data => data,
+ :axis_with_labels => axis_with_labels,
+ :max_value => y_labels[y_labels.length - 1],
+ :legend => legend,
+ :axis_labels => axis_labels,
+ :line_colors => line_colors)
+
end
-
- if args[:range] && args[:range][:upper_bound]
- now_floored = args[:range][:upper_bound]
- else
- now_days = tz.now # use this get the right year, month and day
- now_minutes = tz.at((now_days.to_i/(60*(increment * 1440)))*(60*(increment * 1440))).gmtime
- now_floored = tz.local(now_days.year, now_days.month, now_days.day,
+
+ def data_to_csv(hits, args={})
+ if hits.is_a?(Array) and hits[0].is_a?(Array)
+ range = hits.map{|h| hits_to_otg_range(h, args) }
+ else
+ range = [hits_to_otg_range(hits, args)]
+ end
+
+ legend = []
+ legend.concat(args[:legend])
+
+ csv_data = FasterCSV.generate do |csv|
+ x_labels = range[0][:x_labels].map{|x_label| x_label[1]}
+ x_labels.unshift("")
+ csv << x_labels
+
+ raise ArgumentError, "Mismatched array lengths" unless range.length == args[:legend].length
+
+ for data in range
+ points = data[:points].map{|point| point[:Value][0]}
+ points.unshift(legend.shift)
+ csv << points
+ end
+ end
+
+ return csv_data
+ end
+
+ def google_pie(hits, label_fn, args = {})
+ height = args.has_key?(:height) ? args[:height] : 125
+ width = args.has_key?(:width) ? args[:width] : 125
+ pie_values = extract_pct_values(hits, label_fn, args)
+ vars = pie_to_flashvars(pie_values, args)
+ src = args.has_key?(:src) ? args[:src] : "http://www.google.com/analytics/static/flash/pie.swf"
+
+ html = <<-eos
+ <embed
+ width="#{width}"
+ height="#{height}"
+ salign="tl"
+ scale="noScale"
+ quality="high"
+ bgcolor="#FFFFFF"
+ flashvars="input=#{vars}&locale=en-US"
+ pluginspage="http://www.macromedia.com/go/getflashplayer"
+ type="application/x-shockwave-flash"
+ src="#{src}"/>
+ eos
+ return html
+
+ end
+
+ # override this if you have a better way of retrieving time zones
+ def get_time_zone
+ return defined?(current_user) && !current_user.nil? && current_user.responds_to?(:time_zone) ? current_user.time_zone : ActiveSupport::TimeZone["UTC"]
+ end
+
+ def setup_time_range()
+ time_zone = get_time_zone
+
+ now_days = time_zone.now # use this get the right year, month and day
+ now_minutes = time_zone.at((now_days.to_i/(60*(1 * 1440)))*(60*(1 * 1440))).gmtime
+ now_floored = time_zone.local(now_days.year, now_days.month, now_days.day,
now_minutes.hour, now_minutes.min, now_minutes.sec)
+
+ default_upper = now_floored
+ default_lower = default_upper - 7.days
+
+ lower_bound = nil
+ upper_bound = nil
+
+ if params[:range]
+ dates = params[:range].split("-")
+ sql_dates = dates.map{|d|
+ time_zone.parse(d)
+ }
+ if sql_dates.length == 2
+ lower_bound = sql_dates[0]
+ upper_bound = sql_dates[1]
+ elsif sql_dates.length == 1
+
+ lower_bound = sql_dates[0]
+ upper_bound = default_upper
+ end
+ end
+
+ lower_bound = default_lower unless lower_bound && lower_bound < upper_bound
+ upper_bound = default_upper unless upper_bound && upper_bound < Time.now + 1.day
+
+ range = {:lower_bound => lower_bound, :upper_bound => upper_bound}
+ return {:time_zone => time_zone, :lower_bound => lower_bound, :upper_bound => upper_bound, :range => range}
end
- while (current < now_floored + increment.days && increment > 0) do
- if hits_dict[current]
- count = hits_dict[current].count.to_i
- max_y = count if count > max_y
+ def pie_to_flashvars(args = {})
- date = time_fn.call(hits_dict[current])
- date_key = date.to_i
- date_value = date.strftime(x_label_format)
-
- points.push(points_fn.call(count, date_key, date_value))
- total += count
- else
-
- date = current
- date_key = date.to_i
- date_value = date.strftime(x_label_format)
-
- points.push(points_fn.call(0, date_key, date_value))
+ labels = args[:labels]
+ raw_values = args[:raw_values]
+ percent_values = args[:percent_values]
+
+ options = {
+ :Pie => {
+ :Id => "Pie",
+ :Compare => false,
+ :HasOtherSlice => false,
+ :RawValues => raw_values,
+ :Format => "DASHBOARD",
+ :PercentValues => percent_values
+ }
+ }
+
+ return URI::encode(options.to_json)
+
+ end
+
+ def extract_pct_values(hits, label_fn, args = {})
+
+ limit = args.has_key?(:limit) ? args[:limit] : 0.0
+
+ total = 0.0
+ other = 0.0
+ percent_values = []
+ raw_values = []
+ labels = []
+ values = []
+ hits.each{|hit|
+ total += hit.count.to_f
+ }
+ hits.each{|hit|
+ ct = hit.count.to_f
+ pct = (ct / total)
+
+ if pct > limit
+ percent_values.push([pct, sprintf("%.2f%%", pct * 100)])
+ raw_values.push([ct, ct])
+
+ label = label_fn.call(hit)
+ meta = args.has_key?(:meta) ? args[:meta].call(hit) : nil
+
+ labels.push(label)
+ values.push({:label => label, :meta => meta, :percent_value => [pct, sprintf("%.2f%%", pct * 100)], :raw_value => ct})
+ else
+ other += ct
+ end
+ }
+ if other > 0.0
+ pct = other / total
+ percent_values.push([pct, sprintf("%.2f%%", pct * 100)])
+ raw_values.push([other, other])
+ labels.push("Other")
+ values.push({:label => "Other", :percent_value => [pct, sprintf("%.2f%%", pct * 100)], :raw_value => other})
end
- # Save the date for the x labels later
- point_dates.push({:key => date_key, :value => date_value})
- current = current + increment.days
- break if points.length > 365 # way too long dudes - no data fetching over 1 yr
+
+ return {:labels => labels, :raw_values => raw_values, :percent_values => percent_values, :values => values}
+
end
-
- if points.length > 100
- points = points[points.length - 100..points.length - 1]
+
+ def flto10(val)
+ return ((val / 10) * 10).to_i
end
-
- ## Setup Y axis labels ##
- max_y = args.has_key?(:max_y) ? (args[:max_y] > max_y ? args[:max_y] : max_y) : max_y
- top_y = self.flto10(max_y) + 10
- mid_y = self.flto10(top_y / 2)
- y_labels = y_label_fn.call(mid_y, top_y)
- ## end y axis labels ##
+ def hits_to_otg_range(hits, args = {})
+ return hits_to_range(hits, lambda {|count, date_key, date_value|
+ {:Value => [count, count], :Label => [date_key, date_value]}
+ }, lambda{|mid, top|
+ [[mid,mid],[top,top]]
+ }, lambda{|hit, hit_date_key, hit_date_value|
+ [hit_date_key, hit_date_value]
+ },args)
+ end
- ## Setup X axis labels
- x_labels = []
- max_x_label_count = args.has_key?(:max_x_label_count) ? args[:max_x_label_count] : points.length
+ def hits_to_gchart_range(hits, args = {})
+ return hits_to_range(hits, lambda {|count, date_key, date_value|
+ count
+ }, lambda {|mid, top|
+ [0,top/2,top]
+ },lambda{|hit, hit_date_key, hit_date_value|
+ hit_date_value
+ }, args)
+ end
- if points.length > 0
- step = [points.length / max_x_label_count, 1].max
- idx = 0
-
- while idx < points.length
- point = points[idx]
- date = point_dates[idx]
- x_labels.push(x_label_fn.call(point, date[:key], date[:value]))
- idx += step
+ def hits_to_range(hits, points_fn, y_label_fn, x_label_fn, args = {})
+
+ return nil unless hits
+
+ hits.map{|h|
+ if !h.respond_to?("created_at") || !h.respond_to?("count")
+ raise ArgumentError, "Invalid object type. All objects must respond to 'count' and 'created_at'"
+ end
+ }
+
+ tz = args.has_key?(:time_zone) ? args[:time_zone] : ActiveSupport::TimeZone['UTC']
+ label = args.has_key?(:label) ? args[:label] : "Value"
+ time_fn = args.has_key?(:time_fn) ? args[:time_fn] : lambda {|h|
+ return tz.parse(h.created_at) if h.created_at.class == String
+ return tz.local(h.created_at.year, h.created_at.month, h.created_at.day, h.created_at.hour, h.created_at.min, h.created_at.sec) # create zoned time
+ }
+ increment = args.has_key?(:increment) ? args[:increment] : @@DEFAULT_INCREMENT
+
+ x_label_format = args.has_key?(:x_label_format) ? args[:x_label_format] : "%A, %B %d"
+
+ max_y = 0
+ hits_dict = {}
+ hits.each { |h|
+ hits_dict[time_fn.call(h)] = h
+ }
+
+ total = 0
+
+ points = []
+ point_dates = []
+
+ if args[:range] && args[:range][:lower_bound]
+ current = args[:range][:lower_bound]
+ else
+ current = hits.length > 0 ? time_fn.call(hits[0]) : now_floored
end
+
+ if args[:range] && args[:range][:upper_bound]
+ now_floored = args[:range][:upper_bound]
+ else
+ now_days = tz.now # use this get the right year, month and day
+ now_minutes = tz.at((now_days.to_i/(60*(increment * 1440)))*(60*(increment * 1440))).gmtime
+ now_floored = tz.local(now_days.year, now_days.month, now_days.day,
+ now_minutes.hour, now_minutes.min, now_minutes.sec)
+ end
+
+ while (current < now_floored + increment.days && increment > 0) do
+ if hits_dict[current]
+ count = hits_dict[current].count.to_i
+ max_y = count if count > max_y
+
+ date = time_fn.call(hits_dict[current])
+ date_key = date.to_i
+ date_value = date.strftime(x_label_format)
+
+ points.push(points_fn.call(count, date_key, date_value))
+ total += count
+ else
+
+ date = current
+ date_key = date.to_i
+ date_value = date.strftime(x_label_format)
+
+ points.push(points_fn.call(0, date_key, date_value))
+ end
+ # Save the date for the x labels later
+ point_dates.push({:key => date_key, :value => date_value})
+ current = current + increment.days
+ break if points.length > 365 # way too long dudes - no data fetching over 1 yr
+ end
+
+ if points.length > 100
+ points = points[points.length - 100..points.length - 1]
+ end
+
+ ## Setup Y axis labels ##
+ max_y = args.has_key?(:max_y) ? (args[:max_y] > max_y ? args[:max_y] : max_y) : max_y
+
+ top_y = self.flto10(max_y) + 10
+ mid_y = self.flto10(top_y / 2)
+ y_labels = y_label_fn.call(mid_y, top_y)
+ ## end y axis labels ##
+
+ ## Setup X axis labels
+ x_labels = []
+ max_x_label_count = args.has_key?(:max_x_label_count) ? args[:max_x_label_count] : points.length
+
+ if points.length > 0
+ step = [points.length / max_x_label_count, 1].max
+ idx = 0
+
+ while idx < points.length
+ if idx + step >= points.length
+ idx = points.length - 1
+ end
+
+ point = points[idx]
+ date = point_dates[idx]
+ x_labels.push(x_label_fn.call(point, date[:key], date[:value]))
+
+ idx += step
+ end
+ end
+
+ ## End x axis labels ##
+
+ return {:x_labels => x_labels, :y_labels => y_labels, :label => label, :points => points, :total => total}
+
+ end
+
+ PRIMARY_STYLE = {
+ :PointShape => "CIRCLE",
+ :PointRadius => 9,
+ :FillColor => 30668,
+ :FillAlpha => 10,
+ :LineThickness => 4,
+ :ActiveColor => 30668,
+ :InactiveColor => 11654895
+ }
+
+ COMPARE_STYLE = {
+ :PointShape => "CIRCLE",
+ :PointRadius => 6,
+ :FillAlpha => 10,
+ :LineThickness => 2,
+ :ActiveColor => 16750848,
+ :InactiveColor => 16750848
+ }
+
+ def setup_series(args, style)
+ raise ArgumentError unless args[:label]
+ raise ArgumentError unless args[:points]
+ raise ArgumentError unless args[:y_labels]
+
+ return {
+ :SelectionStartIndex => 0,
+ :SelectionEndIndex => args[:points].length,
+ :Style => style,
+ :Label => args[:label],
+ :Id => "primary",
+ :YLabels => args[:y_labels],
+ :ValueCategory => "visits",
+ :Points => args[:points]
+ } # end graph
end
-
- ## End x axis labels ##
-
- return {:x_labels => x_labels, :y_labels => y_labels, :label => label, :points => points, :total => total}
-
- end
- protected :hits_to_range
-
-
- PRIMARY_STYLE = {
- :PointShape => "CIRCLE",
- :PointRadius => 9,
- :FillColor => 30668,
- :FillAlpha => 10,
- :LineThickness => 4,
- :ActiveColor => 30668,
- :InactiveColor => 11654895
- }
-
- COMPARE_STYLE = {
- :PointShape => "CIRCLE",
- :PointRadius => 6,
- :FillAlpha => 10,
- :LineThickness => 2,
- :ActiveColor => 16750848,
- :InactiveColor => 16750848
- }
-
- def setup_series(args, style)
- raise ArgumentError unless args[:label]
- raise ArgumentError unless args[:points]
- raise ArgumentError unless args[:y_labels]
-
- return {
- :SelectionStartIndex => 0,
- :SelectionEndIndex => args[:points].length,
- :Style => style,
- :Label => args[:label],
- :Id => "primary",
- :YLabels => args[:y_labels],
- :ValueCategory => "visits",
- :Points => args[:points]
- } # end graph
- end
- protected :setup_series
-
- def range_to_flashvars(args = {})
- raise ArgumentError unless args.length > 0
- x_labels = args[0][:x_labels]
- raise ArgumentError unless x_labels
-
- ct = 0
- # this is the structure necessary to support the Google Analytics OTG
- options = {:Graph => {
- :Id => "Graph",
- :ShowHover => true,
- :Format => "NORMAL",
- :XAxisTitle => "Day",
- :Compare => false,
- :XAxisLabels => x_labels,
- :HoverType => "primary_compare",
- :SelectedSeries => ["primary", "compare"],
- :Series => args.map {|arg|
- ct += 1
- setup_series(arg, ct == 1 ? PRIMARY_STYLE : COMPARE_STYLE)
- }
- } # end graph
- } # end options
- return URI::encode(options.to_json)
- end
- protected :range_to_flashvars
-
-end
\ No newline at end of file
+ def range_to_flashvars(args = {})
+ raise ArgumentError unless args.length > 0
+ x_labels = args[0][:x_labels]
+ raise ArgumentError unless x_labels
+
+ ct = 0
+ # this is the structure necessary to support the Google Analytics OTG
+
+ options = {:Graph => {
+ :Id => "Graph",
+ :ShowHover => true,
+ :Format => "NORMAL",
+ :XAxisTitle => "Day",
+ :Compare => false,
+ :XAxisLabels => x_labels,
+ :HoverType => "primary_compare",
+ :SelectedSeries => ["primary", "compare"],
+ :Series => args.map {|arg|
+ ct += 1
+ setup_series(arg, ct == 1 ? PRIMARY_STYLE : COMPARE_STYLE)
+ }
+ } # end graph
+ } # end options
+ return URI::encode(options.to_json)
+ end
+ end
+