require "spec_helper" describe "more complicated case" do let(:report_model) do Class.new(ActiveReporter::Report) do report_on :Post time_dimension :published_at number_dimension :likes category_dimension :author, model: :author, attribute: :name, relation: ->(r) { r.left_outer_joins(:author) } count_aggregator :count sum_aggregator :total_likes, attribute: :likes average_aggregator :mean_likes, attribute: :likes min_aggregator :min_likes, attribute: :likes max_aggregator :max_likes, attribute: :likes end end def data_by(groupers, g_params = nil) groupers = Array.wrap(groupers) dimension_params = {} if g_params g_params = Array.wrap(g_params) groupers.zip(g_params).each do |grouper, params| dimension_params[grouper] = params end end report = report_model.new(groupers: groupers, dimensions: dimension_params) report.data end def expect_equal(h1, h2) # sqlite uses Float instead of BigDecimal, we need to normalize the JSON objects to use the # same data type so the values match. We also round these at 9 decimal places to account for # rounding discrepencies between the two data types h1_json = JSON.parse(h1.to_json).map do |a| a.deep_transform_values do |v| case v when String BigDecimal(v).round(9) rescue v else v end end end h2_json = JSON.parse(h2.to_json).map do |a| a.deep_transform_values do |v| case v when String BigDecimal(v).round(9) rescue v else v end end end expect(h1_json).to eq h2_json end let(:joyce) { create(:author, name: "James Joyce") } let(:woolf) { create(:author, name: "Virginia Woolf") } let(:oct_1) { Time.zone.parse("2015-10-01") } let(:nov_1) { Time.zone.parse("2015-11-01") } let(:dec_1) { Time.zone.parse("2015-12-01") } let(:oct) { { min: oct_1, max: nov_1 } } let(:nov) { { min: nov_1, max: dec_1 } } let!(:post_1) { create(:post, author: joyce.name, published_at: oct_1, likes: 1) } let!(:post_2) { create(:post, author: joyce.name, published_at: oct_1, likes: 2) } let!(:post_3) { create(:post, author: joyce.name, published_at: nov_1, likes: 1) } let!(:post_4) { create(:post, author: joyce.name, likes: 3).tap { |p| p.update!(published_at: nil) } } let!(:post_5) { create(:post, author: woolf.name, published_at: oct_1, likes: 2) } let!(:post_6) { create(:post, author: woolf.name, published_at: nov_1, likes: 3) } let!(:post_7) { create(:post, author: woolf.name, likes: 3).tap { |p| p.update!(published_at: nil) } } let!(:post_8) { create(:post, author: nil, published_at: oct_1, likes: 2) } let!(:post_9) { create(:post, author: nil, published_at: nov_1, likes: 3) } specify "basic grouping, 1 grouper, no filters" do expect_equal data_by(:author), [ { key: nil, values: [ { key: :count, value: 2 }, { key: :total_likes, value: 5 }, { key: :mean_likes, value: "2.5" }, { key: :min_likes, value: 2 }, { key: :max_likes, value: 3 } ] }, { key: joyce.name, values: [ { key: :count, value: 4 }, { key: :total_likes, value: 7 }, { key: :mean_likes, value: "1.75" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 3 } ] }, { key: woolf.name, values: [ { key: :count, value: 3 }, { key: :total_likes, value: 8 }, { key: :mean_likes, value: "2.6666666666666667" }, { key: :min_likes, value: 2 }, { key: :max_likes, value: 3 } ] } ] expect_equal data_by(:published_at, bin_width: "1 month"), [ { key: nil, values: [ { key: :count, value: 2 }, { key: :total_likes, value: 6 }, { key: :mean_likes, value: "3.0" }, { key: :min_likes, value: 3 }, { key: :max_likes, value: 3 } ] }, { key: oct, values: [ { key: :count, value: 4 }, { key: :total_likes, value: 7 }, { key: :mean_likes, value: "1.75" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 2 } ] }, { key: nov, values: [ { key: :count, value: 3 }, { key: :total_likes, value: 7 }, { key: :mean_likes, value: "2.3333333333333333" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 3 } ] } ] expect_equal data_by(:likes, bin_width: 1), [ { key: { min: 1, max: 2 }, values: [ { key: :count, value: 2 }, { key: :total_likes, value: 2 }, { key: :mean_likes, value: "1.0" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 1 } ] }, { key: { min: 2, max: 3 }, values: [ { key: :count, value: 3 }, { key: :total_likes, value: 6 }, { key: :mean_likes, value: "2.0" }, { key: :min_likes, value: 2 }, { key: :max_likes, value: 2 } ] }, { key: { min: 3, max: 4 }, values: [ { key: :count, value: 4 }, { key: :total_likes, value: 12 }, { key: :mean_likes, value: "3.0" }, { key: :min_likes, value: 3 }, { key: :max_likes, value: 3 } ] } ] end specify "basic grouping, >=2 groupers, no filters" do expect_equal data_by([:published_at, :author], bin_width: { months: 1 }), [ { key: nil, values: [ { key: nil, values: [ { key: :count, value: 0 }, { key: :total_likes, value: 0 }, { key: :mean_likes, value: nil }, { key: :min_likes, value: nil }, { key: :max_likes, value: nil } ] }, { key: oct, values: [ { key: :count, value: 1 }, { key: :total_likes, value: 2 }, { key: :mean_likes, value: "2.0" }, { key: :min_likes, value: 2 }, { key: :max_likes, value: 2 } ] }, { key: nov, values: [ { key: :count, value: 1 }, { key: :total_likes, value: 3 }, { key: :mean_likes, value: "3.0" }, { key: :min_likes, value: 3 }, { key: :max_likes, value: 3 } ] } ] }, { key: joyce.name, values: [ { key: nil, values: [ { key: :count, value: 1 }, { key: :total_likes, value: 3 }, { key: :mean_likes, value: "3.0" }, { key: :min_likes, value: 3 }, { key: :max_likes, value: 3 } ] }, { key: oct, values: [ { key: :count, value: 2 }, { key: :total_likes, value: 3 }, { key: :mean_likes, value: "1.5" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 2 } ] }, { key: nov, values: [ { key: :count, value: 1 }, { key: :total_likes, value: 1 }, { key: :mean_likes, value: "1.0" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 1 } ] } ] }, { key: woolf.name, values: [ { key: nil, values: [ { key: :count, value: 1 }, { key: :total_likes, value: 3 }, { key: :mean_likes, value: "3.0" }, { key: :min_likes, value: 3 }, { key: :max_likes, value: 3 } ] }, { key: oct, values: [ { key: :count, value: 1 }, { key: :total_likes, value: 2 }, { key: :mean_likes, value: "2.0" }, { key: :min_likes, value: 2 }, { key: :max_likes, value: 2 } ] }, { key: nov, values: [ { key: :count, value: 1 }, { key: :total_likes, value: 3 }, { key: :mean_likes, value: "3.0" }, { key: :min_likes, value: 3 }, { key: :max_likes, value: 3 } ] } ] } ] end specify "sorting with nulls (1 grouper)" do expect_equal data_by(:author, sort_desc: true), [ { key: woolf.name, values: [ { key: :count, value: 3 }, { key: :total_likes, value: 8 }, { key: :mean_likes, value: "2.6666666666666667" }, { key: :min_likes, value: 2 }, { key: :max_likes, value: 3 } ] }, { key: joyce.name, values: [ { key: :count, value: 4 }, { key: :total_likes, value: 7 }, { key: :mean_likes, value: "1.75" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 3 } ] }, { key: nil, values: [ {key: :count, value: 2 }, {key: :total_likes, value: 5 }, {key: :mean_likes, value: "2.5" }, {key: :min_likes, value: 2 }, {key: :max_likes, value: 3 } ] } ] expect_equal data_by(:published_at, bin_width: "1 month", sort_desc: true), [ { key: nov, values: [ { key: :count, value: 3 }, { key: :total_likes, value: 7 }, { key: :mean_likes, value: "2.3333333333333333" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 3 } ] }, { key: oct, values: [ { key: :count, value: 4 }, { key: :total_likes, value: 7 }, { key: :mean_likes, value: "1.75" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 2 } ] }, { key: nil, values: [ { key: :count, value: 2 }, { key: :total_likes, value: 6 }, { key: :mean_likes, value: "3.0" }, { key: :min_likes, value: 3 }, { key: :max_likes, value: 3 } ] } ] if ActiveReporter.database_type == :postgres expect_equal data_by(:author, nulls_last: true), [ { key: joyce.name, values: [ { key: :count, value: 4 }, { key: :total_likes, value: 7 }, { key: :mean_likes, value: "1.75" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 3 } ] }, { key: woolf.name, values: [ { key: :count, value: 3 }, { key: :total_likes, value: 8 }, { key: :mean_likes, value: "2.6666666666666667" }, { key: :min_likes, value: 2 }, { key: :max_likes, value: 3 } ] }, { key: nil, values: [ { key: :count, value: 2 }, { key: :total_likes, value: 5 }, { key: :mean_likes, value: "2.5" }, { key: :min_likes, value: 2 }, { key: :max_likes, value: 3 } ] } ] expect_equal data_by(:author, sort_desc: true, nulls_last: true), [ { key: nil, values: [ { key: :count, value: 2 }, { key: :total_likes, value: 5 }, { key: :mean_likes, value: "2.5" }, { key: :min_likes, value: 2 }, { key: :max_likes, value: 3 } ] }, { key: woolf.name, values: [ { key: :count, value: 3 }, { key: :total_likes, value: 8 }, { key: :mean_likes, value: "2.6666666666666667" }, { key: :min_likes, value: 2 }, { key: :max_likes, value: 3 } ] }, { key: joyce.name, values: [ { key: :count, value: 4 }, { key: :total_likes, value: 7 }, { key: :mean_likes, value: "1.75" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 3 } ] } ] expect_equal data_by(:published_at, bin_width: "1 month", nulls_last: true), [ { key: oct, values: [ { key: :count, value: 4 }, { key: :total_likes, value: 7 }, { key: :mean_likes, value: "1.75" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 2 } ] }, { key: nov, values: [ { key: :count, value: 3 }, { key: :total_likes, value: 7 }, { key: :mean_likes, value: "2.3333333333333333" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 3 } ] }, { key: nil, values: [ { key: :count, value: 2 }, { key: :total_likes, value: 6 }, { key: :mean_likes, value: "3.0" }, { key: :min_likes, value: 3 }, { key: :max_likes, value: 3 } ] } ] expect_equal data_by(:published_at, bin_width: "1 month", sort_desc: true, nulls_last: true), [ { key: nil, values: [ { key: :count, value: 2 }, { key: :total_likes, value: 6 }, { key: :mean_likes, value: "3.0" }, { key: :min_likes, value: 3 }, { key: :max_likes, value: 3 } ] }, { key: nov, values: [ { key: :count, value: 3 }, { key: :total_likes, value: 7 }, { key: :mean_likes, value: "2.3333333333333333" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 3 } ] }, { key: oct, values: [ { key: :count, value: 4 }, { key: :total_likes, value: 7 }, { key: :mean_likes, value: "1.75" }, { key: :min_likes, value: 1 }, { key: :max_likes, value: 2 } ] } ] end end end