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