Avatar

Give a count or sum group operation, a time range, and a resolution, and a class, I want to get a list of timestamps and values. I want to use this to feed to google charts. Here is the code I have, which works, but is not very rubyish. Can you do better?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  def time_summary(klass,group_operator,field,from,to,step)
    data_with_missing_counts(klass.find(
      :all, 
      :select => "#{group_operator}(#{field}) as result, round(UNIX_TIMESTAMP(created_at)/#{step}) as timestamp", 
      :group => "round(UNIX_TIMESTAMP(created_at) / #{step})", 
      :conditions => ["created_at BETWEEN ? AND ?", from, to]
    ).inject({}){|data,user| data.merge({ user.timestamp.to_i => user.result})},from.to_i/step.t_i,to.to_i/step.to_i)    
  end
  
  def data_with_missing_counts(data, from, to)
    result = []
    from.upto(to) do |idx|
      puts "idx=#{idx}, data[idx]=#{data[idx]}"
      result << (data[idx] || 0)
    end
    return result
  end

    @revenue_past_week_chart_data = time_summary(Order,'sum','amount',1.week.ago,Time.now,6.hours).map(&:to_f)

Refactorings

No refactoring yet !

A8d3f35baafdaea851914b17dae9e1fc

Adam

September 3, 2008, September 03, 2008 16:21, permalink

No rating. Login to rate!

This is completely untested since I don't have a good environment to play in, but this should hopefully give you a pretty good idea of where you can go with this. Use mixins if you are performing the same operation on more than one model.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Summary < Struct.new(:klass, :options)
  def range
    options[:range] || options[:from]..options[:to]
  end
  
  def step
    options[:step]
  end

  def calculate(method, *args)
    klass.between(range).by_timestamp(step).send(method, *args)
  end
  
  def method_missing(method, *args, &block)
    records = Hash[calculate(method, *args).map(&:reverse).flatten]
    range.map { |time| records[time.to_i].to_f }
  end
end
    
class Order < ActiveRecord::Base
  named_scope :between, lambda { |range| { :conditions => { :created_at => range } } }
  named_scope :by_timestamp, lambda { |step| { :group => "round(UNIX_TIMESTAMP(created_at)/#{step})" } }
  
  def self.summary(options)
    Summary.new(self, options)
  end
end    

# Example usage:
#
# Order.summary(:from => Time.now, :to => 6.hours.from_now, :step => 10).sum(:amount)
# Order.summary(:from => Time.now, :to => 6.hours.from_now, :step => 10).count(:amount)
89577e80c2e342f49e5c007097e77218

mvwxpe

September 10, 2008, September 10, 2008 20:32, permalink

No rating. Login to rate!

mrkaobovvigoccqxdbiqwzycuvitoo

Your refactoring





Format Copy from initial code

or Cancel