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.
- checksums.yaml +4 -4
- data/README.md +6 -5
- data/app/assets/javascripts/reports_kit/lib/chart.js +33 -8
- data/app/assets/javascripts/reports_kit/lib/report.js +27 -26
- data/app/assets/javascripts/reports_kit/lib/table.js +58 -0
- data/app/assets/javascripts/reports_kit/vendor/jquery.tablesorter.min.js +4 -0
- data/app/assets/stylesheets/reports_kit/reports.css.sass +20 -0
- data/config/initializers/mime_types.rb +1 -0
- data/docs/dimensions.md +34 -26
- data/docs/display_options.md +15 -12
- data/docs/filters.md +2 -2
- data/docs/measures.md +4 -3
- data/gemfiles/mysql.gemfile.lock +14 -1
- data/gemfiles/postgresql.gemfile.lock +14 -1
- data/lib/reports_kit.rb +15 -0
- data/lib/reports_kit/cache.rb +37 -0
- data/lib/reports_kit/configuration.rb +13 -1
- data/lib/reports_kit/helper.rb +54 -3
- data/lib/reports_kit/model_configuration.rb +6 -1
- data/lib/reports_kit/order.rb +33 -0
- data/lib/reports_kit/relative_time.rb +42 -0
- data/lib/reports_kit/report_builder.rb +34 -15
- data/lib/reports_kit/reports/abstract_measure.rb +9 -0
- data/lib/reports_kit/reports/adapters/mysql.rb +8 -1
- data/lib/reports_kit/reports/adapters/postgresql.rb +8 -1
- data/lib/reports_kit/reports/composite_measure.rb +43 -0
- data/lib/reports_kit/reports/data/chart_options.rb +5 -0
- data/lib/reports_kit/reports/data/composite_aggregation.rb +96 -0
- data/lib/reports_kit/reports/data/entity.rb +7 -0
- data/lib/reports_kit/reports/data/format_one_dimension.rb +120 -0
- data/lib/reports_kit/reports/data/format_two_dimensions.rb +141 -0
- data/lib/reports_kit/reports/data/generate.rb +72 -25
- data/lib/reports_kit/reports/data/generate_for_properties.rb +75 -0
- data/lib/reports_kit/reports/data/one_dimension.rb +15 -49
- data/lib/reports_kit/reports/data/populate_one_dimension.rb +36 -0
- data/lib/reports_kit/reports/data/populate_two_dimensions.rb +104 -0
- data/lib/reports_kit/reports/data/two_dimensions.rb +15 -110
- data/lib/reports_kit/reports/data/utils.rb +77 -12
- data/lib/reports_kit/reports/data/value.rb +7 -0
- data/lib/reports_kit/reports/dimension.rb +4 -110
- data/lib/reports_kit/reports/dimension_with_measure.rb +137 -0
- data/lib/reports_kit/reports/filter.rb +5 -64
- data/lib/reports_kit/reports/filter_types/base.rb +1 -1
- data/lib/reports_kit/reports/filter_types/boolean.rb +9 -7
- data/lib/reports_kit/reports/filter_types/datetime.rb +7 -5
- data/lib/reports_kit/reports/filter_types/number.rb +2 -0
- data/lib/reports_kit/reports/filter_with_measure.rb +84 -0
- data/lib/reports_kit/reports/generate_autocomplete_results.rb +1 -1
- data/lib/reports_kit/reports/inferrable_configuration.rb +32 -13
- data/lib/reports_kit/reports/measure.rb +48 -12
- data/lib/reports_kit/reports_controller.rb +42 -3
- data/lib/reports_kit/version.rb +1 -1
- data/reports_kit.gemspec +2 -0
- data/spec/fixtures/generate_inputs.yml +146 -21
- data/spec/fixtures/generate_outputs.yml +768 -17
- data/spec/reports_kit/relative_time_spec.rb +29 -0
- data/spec/reports_kit/report_builder_spec.rb +28 -0
- data/spec/reports_kit/reports/data/generate_spec.rb +614 -27
- data/spec/reports_kit/reports/dimension_with_measure_spec.rb +69 -0
- data/spec/reports_kit/reports/{filter_spec.rb → filter_with_measure_spec.rb} +4 -3
- data/spec/spec_helper.rb +7 -2
- data/spec/support/config.rb +11 -0
- data/spec/support/helpers.rb +3 -3
- data/spec/support/models/issue.rb +7 -0
- metadata +53 -4
- 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
|
-
|
16
|
-
|
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
|
-
|
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
|
-
|
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
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
35
|
-
@
|
36
|
-
|
37
|
-
|
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
|
41
|
-
@
|
42
|
-
|
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
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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:
|
59
|
-
y_axis_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
|
7
|
+
def initialize(measure)
|
8
8
|
self.measure = measure
|
9
|
-
self.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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
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
|
7
|
+
def initialize(measure)
|
8
8
|
self.measure = measure
|
9
|
-
self.dimension =
|
10
|
-
self.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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
73
|
-
|
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
|