reports_kit 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -5
  3. data/app/assets/javascripts/reports_kit/lib/chart.js +33 -8
  4. data/app/assets/javascripts/reports_kit/lib/report.js +27 -26
  5. data/app/assets/javascripts/reports_kit/lib/table.js +58 -0
  6. data/app/assets/javascripts/reports_kit/vendor/jquery.tablesorter.min.js +4 -0
  7. data/app/assets/stylesheets/reports_kit/reports.css.sass +20 -0
  8. data/config/initializers/mime_types.rb +1 -0
  9. data/docs/dimensions.md +34 -26
  10. data/docs/display_options.md +15 -12
  11. data/docs/filters.md +2 -2
  12. data/docs/measures.md +4 -3
  13. data/gemfiles/mysql.gemfile.lock +14 -1
  14. data/gemfiles/postgresql.gemfile.lock +14 -1
  15. data/lib/reports_kit.rb +15 -0
  16. data/lib/reports_kit/cache.rb +37 -0
  17. data/lib/reports_kit/configuration.rb +13 -1
  18. data/lib/reports_kit/helper.rb +54 -3
  19. data/lib/reports_kit/model_configuration.rb +6 -1
  20. data/lib/reports_kit/order.rb +33 -0
  21. data/lib/reports_kit/relative_time.rb +42 -0
  22. data/lib/reports_kit/report_builder.rb +34 -15
  23. data/lib/reports_kit/reports/abstract_measure.rb +9 -0
  24. data/lib/reports_kit/reports/adapters/mysql.rb +8 -1
  25. data/lib/reports_kit/reports/adapters/postgresql.rb +8 -1
  26. data/lib/reports_kit/reports/composite_measure.rb +43 -0
  27. data/lib/reports_kit/reports/data/chart_options.rb +5 -0
  28. data/lib/reports_kit/reports/data/composite_aggregation.rb +96 -0
  29. data/lib/reports_kit/reports/data/entity.rb +7 -0
  30. data/lib/reports_kit/reports/data/format_one_dimension.rb +120 -0
  31. data/lib/reports_kit/reports/data/format_two_dimensions.rb +141 -0
  32. data/lib/reports_kit/reports/data/generate.rb +72 -25
  33. data/lib/reports_kit/reports/data/generate_for_properties.rb +75 -0
  34. data/lib/reports_kit/reports/data/one_dimension.rb +15 -49
  35. data/lib/reports_kit/reports/data/populate_one_dimension.rb +36 -0
  36. data/lib/reports_kit/reports/data/populate_two_dimensions.rb +104 -0
  37. data/lib/reports_kit/reports/data/two_dimensions.rb +15 -110
  38. data/lib/reports_kit/reports/data/utils.rb +77 -12
  39. data/lib/reports_kit/reports/data/value.rb +7 -0
  40. data/lib/reports_kit/reports/dimension.rb +4 -110
  41. data/lib/reports_kit/reports/dimension_with_measure.rb +137 -0
  42. data/lib/reports_kit/reports/filter.rb +5 -64
  43. data/lib/reports_kit/reports/filter_types/base.rb +1 -1
  44. data/lib/reports_kit/reports/filter_types/boolean.rb +9 -7
  45. data/lib/reports_kit/reports/filter_types/datetime.rb +7 -5
  46. data/lib/reports_kit/reports/filter_types/number.rb +2 -0
  47. data/lib/reports_kit/reports/filter_with_measure.rb +84 -0
  48. data/lib/reports_kit/reports/generate_autocomplete_results.rb +1 -1
  49. data/lib/reports_kit/reports/inferrable_configuration.rb +32 -13
  50. data/lib/reports_kit/reports/measure.rb +48 -12
  51. data/lib/reports_kit/reports_controller.rb +42 -3
  52. data/lib/reports_kit/version.rb +1 -1
  53. data/reports_kit.gemspec +2 -0
  54. data/spec/fixtures/generate_inputs.yml +146 -21
  55. data/spec/fixtures/generate_outputs.yml +768 -17
  56. data/spec/reports_kit/relative_time_spec.rb +29 -0
  57. data/spec/reports_kit/report_builder_spec.rb +28 -0
  58. data/spec/reports_kit/reports/data/generate_spec.rb +614 -27
  59. data/spec/reports_kit/reports/dimension_with_measure_spec.rb +69 -0
  60. data/spec/reports_kit/reports/{filter_spec.rb → filter_with_measure_spec.rb} +4 -3
  61. data/spec/spec_helper.rb +7 -2
  62. data/spec/support/config.rb +11 -0
  63. data/spec/support/helpers.rb +3 -3
  64. data/spec/support/models/issue.rb +7 -0
  65. metadata +53 -4
  66. data/spec/reports_kit/reports/dimension_spec.rb +0 -54
@@ -8,55 +8,102 @@ module ReportsKit
8
8
 
9
9
  def initialize(properties, context_record: nil)
10
10
  self.properties = properties.deep_symbolize_keys
11
+ self.properties = Utils.normalize_properties(self.properties)
11
12
  self.context_record = context_record
12
13
  end
13
14
 
14
15
  def perform
15
- if second_dimension
16
- data = Data::TwoDimensions.new(measure, dimension, second_dimension).perform
16
+ data = ReportsKit::Cache.get(properties, context_record)
17
+ return data if data
18
+
19
+ if two_dimensions?
20
+ raw_data = Data::FormatTwoDimensions.new(measures.first, measures_results.first.last, order: order).perform
17
21
  else
18
- data = Data::OneDimension.new(measure, dimension).perform
22
+ raw_data = Data::FormatOneDimension.new(measures_results, order: order).perform
19
23
  end
24
+ raw_data = data_format_method.call(raw_data) if data_format_method
25
+ chart_data = format_chart_data(raw_data)
20
26
 
21
- ChartOptions.new(data, options: properties[:chart], inferred_options: inferred_options).perform
27
+ data = { chart_data: chart_data }
28
+ data = ChartOptions.new(data, options: properties[:chart], inferred_options: inferred_options).perform
29
+ if format == 'table'
30
+ data[:table_data] = format_table(data.delete(:chart_data))
31
+ data[:type] = format
32
+ end
33
+ ReportsKit::Cache.set(properties, context_record, data)
34
+ data
22
35
  end
23
36
 
24
37
  private
25
38
 
26
- def measure
27
- @measure ||= begin
28
- measure_hash = properties[:measure]
29
- raise ArgumentError.new('The number of measures must be exactly one') if measure_hash.blank?
30
- Measure.new(measure_hash, context_record: context_record)
39
+ def format_chart_data(raw_data)
40
+ chart_data = {}
41
+ chart_data[:labels] = raw_data[:entities].map(&:label)
42
+ chart_data[:datasets] = raw_data[:datasets].map do |raw_dataset|
43
+ {
44
+ label: raw_dataset[:entity].label,
45
+ data: raw_dataset[:values].map(&:formatted)
46
+ }
31
47
  end
48
+ chart_data
32
49
  end
33
50
 
34
- def dimension
35
- @dimension ||= begin
36
- Dimension.new(dimension_hashes[0], measure: measure)
37
- end
51
+ def measures_results
52
+ @measures_results ||= GenerateForProperties.new(properties, context_record: context_record).perform
53
+ end
54
+
55
+ def two_dimensions?
56
+ dimension_keys = measures_results.first.last.keys
57
+ dimension_keys.first.is_a?(Array)
38
58
  end
39
59
 
40
- def second_dimension
41
- @second_dimension ||= begin
42
- Dimension.new(dimension_hashes[1], measure: measure) if dimension_hashes[1]
60
+ def order
61
+ @order ||= begin
62
+ return Order.parse(properties[:order]) if properties[:order].present?
63
+ inferred_order
43
64
  end
44
65
  end
45
66
 
46
- def dimension_hashes
47
- @dimension_hashes ||= begin
48
- dimension_hashes = properties[:dimensions]
49
- raise ArgumentError.new('Blank dimensions') if dimension_hashes.blank?
50
- raise ArgumentError.new('The number of dimensions must be 1-2') unless dimension_hashes.length.in?([1, 2])
51
- dimension_hashes = dimension_hashes.values if dimension_hashes.is_a?(Hash) && dimension_hashes.key?(:'0')
52
- dimension_hashes
67
+ def inferred_order
68
+ return Order.new('dimension1', nil, 'asc') if primary_dimension.configured_by_time?
69
+ Order.new('count', nil, 'desc')
70
+ end
71
+
72
+ def measures
73
+ @measures ||= Measure.new_from_properties!(properties, context_record: context_record)
74
+ end
75
+
76
+ def data_format_method
77
+ ReportsKit.configuration.custom_method(properties[:data_format_method])
78
+ end
79
+
80
+ def format
81
+ properties[:format]
82
+ end
83
+
84
+ def format_table(data)
85
+ column_names = [primary_dimension.label]
86
+ column_values = []
87
+ data[:datasets].each do |dataset|
88
+ column_names << dataset[:label]
89
+ column_values << dataset[:data]
90
+ end
91
+ rows = column_values.transpose
92
+ rows = rows.map.with_index do |row, index|
93
+ label = data[:labels][index]
94
+ row.unshift(label)
53
95
  end
96
+ [column_names] + rows
97
+ end
98
+
99
+ def primary_dimension
100
+ measures.first.dimensions.first
54
101
  end
55
102
 
56
103
  def inferred_options
57
104
  {
58
- x_axis_label: dimension.label,
59
- y_axis_label: measure.label
105
+ x_axis_label: primary_dimension.label,
106
+ y_axis_label: measures.length == 1 ? measures.first.label : nil
60
107
  }
61
108
  end
62
109
  end
@@ -0,0 +1,75 @@
1
+ module ReportsKit
2
+ module Reports
3
+ module Data
4
+ class GenerateForProperties
5
+ ROUND_PRECISION = 3
6
+
7
+ attr_accessor :properties, :context_record
8
+
9
+ def initialize(properties, context_record: nil)
10
+ self.properties = properties.deep_symbolize_keys
11
+ self.context_record = context_record
12
+ end
13
+
14
+ def perform
15
+ if composite_operator
16
+ raise ArgumentError.new('Aggregations require at least one measure') if all_measures.length == 0
17
+ dimension_keys_values = Data::CompositeAggregation.new(properties, context_record: context_record).perform
18
+ measures_dimension_keys_values = { CompositeMeasure.new(properties) => dimension_keys_values }
19
+ elsif all_measures.length == 1 && composite_measures.length == 1
20
+ dimension_keys_values = Data::CompositeAggregation.new(composite_measures.first.properties, context_record: context_record).perform
21
+ measures_dimension_keys_values = { all_measures.first => dimension_keys_values }
22
+ elsif all_measures.length == 1 && all_measures.first.dimensions.length == 2
23
+ dimension_keys_values = Data::TwoDimensions.new(all_measures.first).perform
24
+ measures_dimension_keys_values = { all_measures.first => dimension_keys_values }
25
+ measures_dimension_keys_values = Data::PopulateTwoDimensions.new(measures_dimension_keys_values).perform
26
+ elsif all_measures.length > 0
27
+ measures_dimension_keys_values = measures_dimension_keys_values_for_one_dimension
28
+ else
29
+ raise ArgumentError.new('The configuration of measurse and dimensions is invalid')
30
+ end
31
+
32
+ measures_dimension_keys_values
33
+ end
34
+
35
+ private
36
+
37
+ def composite_operator
38
+ properties[:composite_operator]
39
+ end
40
+
41
+ def name
42
+ properties[:name]
43
+ end
44
+
45
+ def measures_dimension_keys_values_for_one_dimension
46
+ multi_dimension_measures_exist = all_measures.any? { |measure| measure.dimensions.length > 1 }
47
+ raise ArgumentError.new('When more than one measures are configured, only one dimension may be used per measure') if multi_dimension_measures_exist
48
+
49
+ measures_dimension_keys_values = all_measures.map do |measure|
50
+ if measure.is_a?(CompositeMeasure)
51
+ dimension_keys_values = Data::CompositeAggregation.new(measure.properties, context_record: context_record).perform
52
+ else
53
+ dimension_keys_values = Data::OneDimension.new(measure).perform
54
+ end
55
+ [measure, dimension_keys_values]
56
+ end
57
+ measures_dimension_keys_values = Hash[measures_dimension_keys_values]
58
+ Data::PopulateOneDimension.new(measures_dimension_keys_values).perform
59
+ end
60
+
61
+ def all_measures
62
+ @all_measures ||= Measure.new_from_properties!(properties, context_record: context_record)
63
+ end
64
+
65
+ def composite_measures
66
+ @composite_measures ||= all_measures.grep(CompositeMeasure)
67
+ end
68
+
69
+ def measures
70
+ @measures ||= all_measures.grep(Measure)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -4,66 +4,32 @@ module ReportsKit
4
4
  class OneDimension
5
5
  attr_accessor :measure, :dimension
6
6
 
7
- def initialize(measure, dimension)
7
+ def initialize(measure)
8
8
  self.measure = measure
9
- self.dimension = dimension
9
+ self.dimension = measure.dimensions[0]
10
10
  end
11
11
 
12
12
  def perform
13
- {
14
- chart_data: {
15
- labels: labels,
16
- datasets: datasets
17
- }
18
- }
13
+ dimension_keys_values
19
14
  end
20
15
 
21
16
  private
22
17
 
23
18
  def dimension_keys_values
24
- @dimension_keys_values ||= begin
25
- relation = measure.filtered_relation
26
- relation = relation.group(dimension.group_expression)
27
- relation = relation.joins(dimension.joins) if dimension.joins
28
- relation = relation.limit(dimension.dimension_instances_limit) if dimension.dimension_instances_limit
29
- if dimension.should_be_sorted_by_count?
30
- relation = relation.order('1 DESC')
31
- else
32
- relation = relation.order('2')
33
- end
34
- dimension_keys_values = relation.distinct.public_send(*measure.aggregate_function)
35
- dimension_keys_values = Utils.populate_sparse_hash(dimension_keys_values, dimension: dimension)
36
- dimension_keys_values.delete(nil)
37
- dimension_keys_values.delete('')
38
- dimension_keys_values
39
- end
19
+ relation = measure.filtered_relation
20
+ relation = relation.group(dimension.group_expression)
21
+ relation = relation.joins(dimension.joins) if dimension.joins
22
+ relation = relation.limit(dimension.dimension_instances_limit) if dimension.dimension_instances_limit
23
+ relation = relation.order(order)
24
+ dimension_keys_values = relation.distinct.public_send(*measure.aggregate_function)
25
+ dimension_keys_values = Utils.populate_sparse_hash(dimension_keys_values, dimension: dimension)
26
+ dimension_keys_values.delete(nil)
27
+ dimension_keys_values.delete('')
28
+ Hash[dimension_keys_values]
40
29
  end
41
30
 
42
- def datasets
43
- [
44
- {
45
- label: measure.label,
46
- data: values
47
- }
48
- ]
49
- end
50
-
51
- def values
52
- dimension_keys_values.values.map { |value| value.round(Generate::ROUND_PRECISION) }
53
- end
54
-
55
- def labels
56
- keys = dimension_keys_values.keys
57
- keys.map do |key|
58
- Utils.dimension_key_to_label(key, dimension, dimension_ids_dimension_instances)
59
- end
60
- end
61
-
62
- def dimension_ids_dimension_instances
63
- @dimension_ids_dimension_instances ||= begin
64
- dimension_ids = dimension_keys_values.keys
65
- Utils.dimension_to_dimension_ids_dimension_instances(dimension, dimension_ids)
66
- end
31
+ def order
32
+ dimension.configured_by_time? ? '2' : '1 DESC'
67
33
  end
68
34
  end
69
35
  end
@@ -0,0 +1,36 @@
1
+ module ReportsKit
2
+ module Reports
3
+ module Data
4
+ class PopulateOneDimension
5
+ attr_accessor :sparse_measures_dimension_keys_values
6
+
7
+ def initialize(sparse_measures_dimension_keys_values)
8
+ self.sparse_measures_dimension_keys_values = sparse_measures_dimension_keys_values
9
+ end
10
+
11
+ def perform
12
+ return sparse_measures_dimension_keys_values if sparse_measures_dimension_keys_values.length == 1
13
+ measures_dimension_keys_values
14
+ end
15
+
16
+ private
17
+
18
+ def measures_dimension_keys_values
19
+ measures_dimension_keys_values = sparse_measures_dimension_keys_values.map do |measure, dimension_keys_values|
20
+ dimension_keys.each do |key|
21
+ dimension_keys_values[key] ||= 0
22
+ end
23
+ [measure, dimension_keys_values]
24
+ end
25
+ Hash[measures_dimension_keys_values]
26
+ end
27
+
28
+ def dimension_keys
29
+ sparse_measures_dimension_keys_values.map do |measure, dimension_keys_values|
30
+ dimension_keys_values.keys
31
+ end.reduce(&:+).uniq
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,104 @@
1
+ module ReportsKit
2
+ module Reports
3
+ module Data
4
+ class PopulateTwoDimensions
5
+ attr_accessor :measures, :dimension, :second_dimension, :sparse_measures_dimension_keys_values
6
+
7
+ def initialize(sparse_measures_dimension_keys_values)
8
+ self.measures = sparse_measures_dimension_keys_values.keys
9
+ self.dimension = measures.first.dimensions[0]
10
+ self.second_dimension = measures.first.dimensions[1]
11
+ self.sparse_measures_dimension_keys_values = sparse_measures_dimension_keys_values
12
+ end
13
+
14
+ def perform
15
+ measures_populated_dimension_keys_values
16
+ end
17
+
18
+ private
19
+
20
+ def measures_populated_dimension_keys_values
21
+ measures_dimension_keys_values = {}
22
+ secondary_keys_sums = Hash.new(0)
23
+ measures_populated_primary_keys_secondary_keys_values.each do |measure, primary_keys_secondary_keys_values|
24
+ primary_keys_secondary_keys_values.each do |primary_key, secondary_keys_values|
25
+ secondary_keys_values.each do |secondary_key, value|
26
+ secondary_keys_sums[secondary_key] += value
27
+ end
28
+ end
29
+ end
30
+ sorted_secondary_keys = secondary_keys_sums.sort_by(&:last).reverse.map(&:first)
31
+ measures_populated_primary_keys_secondary_keys_values.each do |measure, primary_key_secondary_keys_values|
32
+ measures_dimension_keys_values[measure] = {}
33
+ primary_key_secondary_keys_values.each do |primary_key, secondary_keys_values|
34
+ secondary_keys_values = secondary_keys_values.sort_by { |key, _| sorted_secondary_keys.index(key) }
35
+ secondary_keys_values.each do |secondary_key, value|
36
+ dimension_key = [primary_key, secondary_key]
37
+ measures_dimension_keys_values[measure][dimension_key] = value
38
+ secondary_keys_sums[secondary_key] += value
39
+ end
40
+ end
41
+ end
42
+ measures_dimension_keys_values
43
+ end
44
+
45
+ def measures_populated_primary_keys_secondary_keys_values
46
+ @populated_dimension_keys_values ||= begin
47
+ measures_populated_primary_keys_secondary_keys_values = {}
48
+ measures.each do |measure|
49
+ measures_populated_primary_keys_secondary_keys_values[measure] = {}
50
+ primary_keys.each do |primary_key|
51
+ measures_populated_primary_keys_secondary_keys_values[measure][primary_key] = {}
52
+ secondary_keys.each do |secondary_key|
53
+ value = measures_primary_keys_secondary_keys_values[measure][primary_key].try(:[], secondary_key) || 0
54
+ measures_populated_primary_keys_secondary_keys_values[measure][primary_key][secondary_key] = value
55
+ end
56
+ end
57
+ end
58
+ measures_populated_primary_keys_secondary_keys_values
59
+ end
60
+ end
61
+
62
+ def measures_primary_keys_secondary_keys_values
63
+ @measures_primary_keys_secondary_keys_values ||= begin
64
+ measures_primary_keys_secondary_keys_values = {}
65
+ sparse_measures_dimension_keys_values.each do |measure, dimension_keys_values|
66
+ measures_primary_keys_secondary_keys_values[measure] = {}
67
+ dimension_keys_values.each do |(primary_key, secondary_key), value|
68
+ primary_key = primary_key.to_date if primary_key.is_a?(Time)
69
+ secondary_key = secondary_key.to_date if secondary_key.is_a?(Time)
70
+ measures_primary_keys_secondary_keys_values[measure][primary_key] ||= {}
71
+ measures_primary_keys_secondary_keys_values[measure][primary_key][secondary_key] = value
72
+ end
73
+ end
74
+ measures_primary_keys_secondary_keys_values
75
+ end
76
+ end
77
+
78
+ def dimension_keys
79
+ @dimension_keys ||= sparse_measures_dimension_keys_values.values.map(&:keys).reduce(&:+).uniq
80
+ end
81
+
82
+ def primary_keys
83
+ @primary_keys ||= begin
84
+ keys = Utils.populate_sparse_keys(dimension_keys.map(&:first).uniq, dimension: dimension)
85
+ unless dimension.configured_by_time?
86
+ limit = dimension.dimension_instances_limit
87
+ keys = keys.first(limit) if limit
88
+ end
89
+ keys
90
+ end
91
+ end
92
+
93
+ def secondary_keys
94
+ @secondary_keys ||= begin
95
+ keys = Utils.populate_sparse_keys(dimension_keys.map(&:last).uniq, dimension: second_dimension)
96
+ limit = second_dimension.dimension_instances_limit
97
+ keys = keys.first(limit) if limit
98
+ keys
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -4,128 +4,33 @@ module ReportsKit
4
4
  class TwoDimensions
5
5
  attr_accessor :measure, :dimension, :second_dimension
6
6
 
7
- def initialize(measure, dimension, second_dimension)
7
+ def initialize(measure)
8
8
  self.measure = measure
9
- self.dimension = dimension
10
- self.second_dimension = second_dimension
9
+ self.dimension = measure.dimensions[0]
10
+ self.second_dimension = measure.dimensions[1]
11
11
  end
12
12
 
13
13
  def perform
14
- {
15
- chart_data: {
16
- labels: labels,
17
- datasets: datasets
18
- }
19
- }
14
+ dimension_keys_values
20
15
  end
21
16
 
22
17
  private
23
18
 
24
19
  def dimension_keys_values
25
- @dimension_keys_values ||= begin
26
- relation = measure.filtered_relation
27
- relation = measure.conditions.call(relation) if measure.conditions
28
- relation = relation.group(dimension.group_expression, second_dimension.group_expression)
29
-
30
- relation = relation.joins(dimension.joins) if dimension.joins
31
- relation = relation.joins(second_dimension.joins) if second_dimension.joins
32
-
33
- if dimension.should_be_sorted_by_count?
34
- relation = relation.order('1 DESC')
35
- else
36
- relation = relation.order('2')
37
- end
38
- dimension_keys_values = relation.distinct.public_send(*measure.aggregate_function)
39
-
40
- if dimension.should_be_sorted_by_count?
41
- dimension_keys_values = sort_dimension_keys_values_by_count(dimension_keys_values)
42
- end
43
- dimension_keys_values = Utils.populate_sparse_hash(dimension_keys_values, dimension: dimension)
44
- Hash[dimension_keys_values]
45
- end
46
- end
47
-
48
- def primary_keys_secondary_keys_values
49
- @primary_keys_secondary_keys_values ||= begin
50
- primary_keys_secondary_keys_values = {}
51
- dimension_keys_values.each do |(primary_key, secondary_key), value|
52
- primary_key = primary_key.to_date if primary_key.is_a?(Time)
53
- secondary_key = secondary_key.to_date if secondary_key.is_a?(Time)
54
- primary_keys_secondary_keys_values[primary_key] ||= {}
55
- primary_keys_secondary_keys_values[primary_key][secondary_key] = value
56
- end
57
- primary_keys_secondary_keys_values
58
- end
59
- end
60
-
61
- def sort_dimension_keys_values_by_count(dimension_keys_values)
62
- primary_keys_counts = Hash.new(0)
63
- dimension_keys_values.each do |(primary_key, secondary_key), count|
64
- primary_keys_counts[primary_key] += count
65
- end
66
- primary_keys_counts = primary_keys_counts.to_a
67
- sorted_primary_keys = primary_keys_counts.sort_by { |primary_key, count| count }.reverse.map(&:first)
68
- dimension_keys_values = dimension_keys_values.sort_by { |(primary_key, secondary_key), count| sorted_primary_keys.index(primary_key) }
20
+ relation = measure.filtered_relation
21
+ relation = relation.group(dimension.group_expression, second_dimension.group_expression)
22
+ relation = relation.joins(dimension.joins) if dimension.joins
23
+ relation = relation.joins(second_dimension.joins) if second_dimension.joins
24
+ relation = relation.order(order)
25
+ dimension_keys_values = relation.distinct.public_send(*measure.aggregate_function)
26
+ dimension_keys_values = Utils.populate_sparse_hash(dimension_keys_values, dimension: dimension)
27
+ dimension_keys_values.delete(nil)
28
+ dimension_keys_values.delete('')
69
29
  Hash[dimension_keys_values]
70
30
  end
71
31
 
72
- def dimension_ids
73
- dimension_keys_values.keys.map(&:first)
74
- end
75
-
76
- def dimension_ids_dimension_instances
77
- @dimension_ids_dimension_instances ||= begin
78
- Utils.dimension_to_dimension_ids_dimension_instances(dimension, dimension_keys_values.keys.map(&:first))
79
- end
80
- end
81
-
82
- def second_dimension_ids_dimension_instances
83
- @second_dimension_ids_dimension_instances ||= begin
84
- Utils.dimension_to_dimension_ids_dimension_instances(second_dimension, dimension_keys_values.keys.map(&:last))
85
- end
86
- end
87
-
88
- def datasets
89
- secondary_keys_values = secondary_keys.map do |secondary_key|
90
- values = primary_keys.map do |primary_key|
91
- primary_keys_secondary_keys_values[primary_key].try(:[], secondary_key) || 0
92
- end
93
- [secondary_key, values]
94
- end
95
- secondary_keys_values = secondary_keys_values.sort_by { |_, values| values.sum }.reverse
96
- secondary_keys_values.map do |secondary_key, values|
97
- next if secondary_key.blank?
98
- {
99
- label: Utils.dimension_key_to_label(secondary_key, second_dimension, second_dimension_ids_dimension_instances),
100
- data: values
101
- }
102
- end.compact
103
- end
104
-
105
- def primary_keys
106
- @primary_keys ||= begin
107
- keys = Utils.populate_sparse_keys(dimension_keys_values.keys.map(&:first).uniq, dimension: dimension)
108
- if dimension.should_be_sorted_by_count?
109
- limit = dimension.dimension_instances_limit
110
- keys = keys.first(limit) if limit
111
- end
112
- keys
113
- end
114
- end
115
-
116
- def secondary_keys
117
- @secondary_keys ||= begin
118
- keys = Utils.populate_sparse_keys(dimension_keys_values.keys.map(&:last).uniq, dimension: second_dimension)
119
- limit = second_dimension.dimension_instances_limit
120
- keys = keys.first(limit) if limit
121
- keys
122
- end
123
- end
124
-
125
- def labels
126
- primary_keys.map do |primary_key|
127
- Utils.dimension_key_to_label(primary_key, dimension, dimension_ids_dimension_instances)
128
- end
32
+ def order
33
+ dimension.configured_by_time? ? '2' : '1 DESC'
129
34
  end
130
35
  end
131
36
  end