reports_kit 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|