lib/groupdate/series.rb in groupdate-2.0.3 vs lib/groupdate/series.rb in groupdate-2.0.4

- old
+ new

@@ -1,34 +1,41 @@ module Groupdate class Series - def initialize(relation, field, column, time_zone, time_range, week_start, day_start) - if time_range.is_a?(Range) - # doesn't matter whether we include the end of a ... range - it will be excluded later - @relation = relation.where("#{column} >= ? AND #{column} <= ?", time_range.first, time_range.last) - else - @relation = relation.where("#{column} IS NOT NULL") - end + def initialize(relation, field, column, time_zone, time_range, week_start, day_start, group_index) + @relation = relation @field = field + @column = column @time_zone = time_zone @time_range = time_range @week_start = week_start @day_start = day_start + @group_index = group_index end def build_series(count) utc = ActiveSupport::TimeZone["UTC"] + reverse = @reverse + order = @relation.order_values.first + if order.is_a?(String) + parts = order.split(" ") + reverse_order = (parts.size == 2 && parts[0] == @field && parts[1].to_s.downcase == "desc") + reverse = !reverse if reverse_order + end + + multiple_groups = @relation.group_values.size > 1 + cast_method = case @field when "day_of_week", "hour_of_day" lambda{|k| k.to_i } else lambda{|k| (k.is_a?(String) ? utc.parse(k) : k.to_time).in_time_zone(@time_zone) } end - count = Hash[ count.map{|k, v| [cast_method.call(k), v] } ] + count = Hash[ count.map{|k, v| [multiple_groups ? k[0...@group_index] + [cast_method.call(k[@group_index])] + k[(@group_index + 1)..-1] : cast_method.call(k), v] } ] series = case @field when "day_of_week" 0..6 @@ -38,11 +45,16 @@ time_range = if @time_range.is_a?(Range) @time_range else # use first and last values - sorted_keys = count.keys.sort + sorted_keys = + if multiple_groups + count.keys.map{|k| k[@group_index] }.sort + else + count.keys.sort + end sorted_keys.first..sorted_keys.last end if time_range.first # determine start time @@ -74,27 +86,53 @@ while time_range.cover?(series.last + step) series << series.last + step end - series + if multiple_groups + keys = count.keys.map{|k| k[0...@group_index] + k[(@group_index + 1)..-1] }.uniq + series = series.reverse if reverse + keys.flat_map do |k| + series.map{|s| k[0...@group_index] + [s] + k[@group_index..-1] } + end + else + series + end else [] end end + # reversed above if multiple groups + if !multiple_groups and reverse + series = series.to_a.reverse + end + Hash[series.map do |k| [k, count[k] || 0] end] end def method_missing(method, *args, &block) # https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/calculations.rb if ActiveRecord::Calculations.method_defined?(method) - build_series(@relation.send(method, *args, &block)) + relation = + if @time_range.is_a?(Range) + # doesn't matter whether we include the end of a ... range - it will be excluded later + @relation.where("#{@column} >= ? AND #{@column} <= ?", @time_range.first, @time_range.last) + else + @relation.where("#{@column} IS NOT NULL") + end + + # undo reverse since we do not want this to appear in the query + if relation.reverse_order_value + @reverse = true + relation = relation.reverse_order + end + + build_series(relation.send(method, *args, &block)) elsif @relation.respond_to?(method) - @relation = @relation.send(method, *args, &block) - self + Groupdate::Series.new(@relation.send(method, *args, &block), @field, @column, @time_zone, @time_range, @week_start, @day_start, @group_index) else super end end