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
@@ -7,7 +7,14 @@ module ReportsKit
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.truncate_to_week(column)
|
10
|
-
|
10
|
+
case ReportsKit.configuration.first_day_of_week
|
11
|
+
when :sunday
|
12
|
+
"DATE_SUB(DATE(#{column}), INTERVAL DAYOFWEEK(#{column}) - 1 DAY)"
|
13
|
+
when :monday
|
14
|
+
"DATE_SUB(DATE(#{column}), INTERVAL DAYOFWEEK(#{column}) - 2 DAY)"
|
15
|
+
else
|
16
|
+
raise ArgumentError.new("Unsupported first_day_of_week: #{ReportsKit.configuration.first_day_of_week}")
|
17
|
+
end
|
11
18
|
end
|
12
19
|
end
|
13
20
|
end
|
@@ -7,7 +7,14 @@ module ReportsKit
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.truncate_to_week(column)
|
10
|
-
|
10
|
+
case ReportsKit.configuration.first_day_of_week
|
11
|
+
when :sunday
|
12
|
+
"DATE_TRUNC('week', #{column}::timestamp + '1 day'::interval) - '1 day'::interval"
|
13
|
+
when :monday
|
14
|
+
"DATE_TRUNC('week', #{column}::timestamp)"
|
15
|
+
else
|
16
|
+
raise ArgumentError.new("Unsupported first_day_of_week: #{ReportsKit.configuration.first_day_of_week}")
|
17
|
+
end
|
11
18
|
end
|
12
19
|
end
|
13
20
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module ReportsKit
|
2
|
+
module Reports
|
3
|
+
class CompositeMeasure < AbstractMeasure
|
4
|
+
attr_accessor :properties
|
5
|
+
|
6
|
+
def initialize(properties)
|
7
|
+
self.properties = properties.dup
|
8
|
+
end
|
9
|
+
|
10
|
+
def label
|
11
|
+
name
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
properties[:name]
|
16
|
+
end
|
17
|
+
|
18
|
+
def composite_operator
|
19
|
+
properties[:composite_operator]
|
20
|
+
end
|
21
|
+
|
22
|
+
def measures
|
23
|
+
@measures ||= Reports::Measure.new_from_properties!(properties, context_record: nil)
|
24
|
+
end
|
25
|
+
|
26
|
+
def filters
|
27
|
+
measures.map(&:filters).flatten
|
28
|
+
end
|
29
|
+
|
30
|
+
def primary_measure
|
31
|
+
measures.first
|
32
|
+
end
|
33
|
+
|
34
|
+
def dimensions
|
35
|
+
primary_measure.dimensions
|
36
|
+
end
|
37
|
+
|
38
|
+
def model_class
|
39
|
+
primary_measure.model_class
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -67,6 +67,7 @@ module ReportsKit
|
|
67
67
|
set_colors
|
68
68
|
set_chart_options
|
69
69
|
set_dataset_options
|
70
|
+
set_standard_options
|
70
71
|
set_type
|
71
72
|
data
|
72
73
|
end
|
@@ -141,6 +142,10 @@ module ReportsKit
|
|
141
142
|
end
|
142
143
|
end
|
143
144
|
|
145
|
+
def set_standard_options
|
146
|
+
self.data[:chart_data][:standard_options] = options[:standard_options] if options[:standard_options].present?
|
147
|
+
end
|
148
|
+
|
144
149
|
def set_type
|
145
150
|
return if type.blank?
|
146
151
|
self.data[:type] = type
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module ReportsKit
|
2
|
+
module Reports
|
3
|
+
module Data
|
4
|
+
class CompositeAggregation
|
5
|
+
attr_accessor :composite_operator, :properties, :context_record
|
6
|
+
|
7
|
+
OPERATORS_METHODS = {
|
8
|
+
'+' => :+,
|
9
|
+
'-' => :-,
|
10
|
+
'*' => :*,
|
11
|
+
'/' => :/,
|
12
|
+
'%' => -> (values) {
|
13
|
+
raise ArgumentError.new("Percentage composite aggregations must have exactly two measures") if values.length != 2
|
14
|
+
numerator, denominator = values
|
15
|
+
return 0 if denominator == 0
|
16
|
+
((numerator.to_f / denominator) * 100).round(1)
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
def initialize(properties, context_record:)
|
21
|
+
self.properties = properties
|
22
|
+
self.composite_operator = properties[:composite_operator]
|
23
|
+
self.context_record = context_record
|
24
|
+
end
|
25
|
+
|
26
|
+
def perform
|
27
|
+
return measures_results_for_one_dimension if dimension_count == 1
|
28
|
+
return measures_results_for_two_dimensions if dimension_count == 2
|
29
|
+
raise ArgumentError.new("Composite aggregations' measures can only have 1-2 dimensions")
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def measures_results_for_one_dimension
|
35
|
+
measures_results = Hash[measures.map { |measure| [measure, OneDimension.new(measure).perform] }]
|
36
|
+
measures_results = Data::PopulateOneDimension.new(measures_results).perform
|
37
|
+
sorted_dimension_keys_values = sort_dimension_keys_values(measures_results)
|
38
|
+
value_lists = sorted_dimension_keys_values.map(&:values)
|
39
|
+
composited_values = value_lists.transpose.map { |data| reduce(data) }
|
40
|
+
dimension_keys = sorted_dimension_keys_values.first.keys
|
41
|
+
composited_keys_values = Hash[dimension_keys.zip(composited_values)]
|
42
|
+
composited_keys_values
|
43
|
+
end
|
44
|
+
|
45
|
+
def measures_results_for_two_dimensions
|
46
|
+
measures_results = Hash[measures.map { |measure| [measure, TwoDimensions.new(measure).perform] }]
|
47
|
+
measures_results = Data::PopulateTwoDimensions.new(measures_results).perform
|
48
|
+
value_lists = measures_results.values.map(&:values)
|
49
|
+
composited_values = value_lists.transpose.map { |data| reduce(data) }
|
50
|
+
dimension_keys = measures_results.values.first.keys
|
51
|
+
composited_keys_values = Hash[dimension_keys.zip(composited_values)]
|
52
|
+
composited_keys_values
|
53
|
+
end
|
54
|
+
|
55
|
+
# Before performing a composition of values, we need to make sure that the values are sorted by the same dimension key.
|
56
|
+
def sort_dimension_keys_values(measures_results)
|
57
|
+
dimension_keys_values_list = measures_results.values
|
58
|
+
sorted_dimension_keys_values = dimension_keys_values_list.map do |dimension_keys_values|
|
59
|
+
dimension_keys_values = dimension_keys_values.sort_by do |dimension_key, value|
|
60
|
+
is_boolean = dimension_key.is_a?(TrueClass) || dimension_key.is_a?(FalseClass)
|
61
|
+
is_boolean ? (dimension_key ? 0 : 1) : dimension_key
|
62
|
+
end
|
63
|
+
Hash[dimension_keys_values]
|
64
|
+
end
|
65
|
+
sorted_dimension_keys_values
|
66
|
+
end
|
67
|
+
|
68
|
+
def reduce(values)
|
69
|
+
if composite_method.is_a?(Symbol)
|
70
|
+
values.reduce(&composite_method)
|
71
|
+
elsif composite_method.is_a?(Proc)
|
72
|
+
values = composite_method.call(values)
|
73
|
+
else
|
74
|
+
raise ArgumentError.new("Invalid composite method type: #{composite_method.class}")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def composite_method
|
79
|
+
composite_method = OPERATORS_METHODS[composite_operator]
|
80
|
+
raise ArgumentError.new("Invalid composite_operator: #{composite_operator}") unless composite_method
|
81
|
+
composite_method
|
82
|
+
end
|
83
|
+
|
84
|
+
def dimension_count
|
85
|
+
unique_dimension_counts = measures.map { |measure| measure.dimensions.length }.uniq
|
86
|
+
raise ArgumentError.new('All measures must have the same number of dimensions') if unique_dimension_counts.length > 1
|
87
|
+
unique_dimension_counts.first
|
88
|
+
end
|
89
|
+
|
90
|
+
def measures
|
91
|
+
@measures ||= Measure.new_from_properties!(properties, context_record: context_record)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module ReportsKit
|
2
|
+
module Reports
|
3
|
+
module Data
|
4
|
+
class FormatOneDimension
|
5
|
+
attr_accessor :measures_results, :measures, :order
|
6
|
+
|
7
|
+
def initialize(measures_results, order:)
|
8
|
+
self.measures_results = measures_results
|
9
|
+
self.measures = measures_results.keys
|
10
|
+
self.order = order
|
11
|
+
end
|
12
|
+
|
13
|
+
def perform
|
14
|
+
{
|
15
|
+
entities: entities,
|
16
|
+
datasets: datasets
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def entities
|
23
|
+
sorted_dimension_keys.map do |key|
|
24
|
+
Utils.dimension_key_to_entity(key, primary_dimension_with_measure, dimension_ids_dimension_instances)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def datasets
|
29
|
+
sorted_measures_results.map do |measure, result|
|
30
|
+
values = result.values.map do |raw_value|
|
31
|
+
Utils.raw_value_to_value(raw_value, measure.value_format_method)
|
32
|
+
end
|
33
|
+
{
|
34
|
+
entity: measure,
|
35
|
+
values: values
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def dimension_summaries
|
41
|
+
@dimension_summaries ||= dimension_keys.map do |dimension_key|
|
42
|
+
label = Utils.dimension_key_to_label(dimension_key, primary_dimension_with_measure, dimension_ids_dimension_instances)
|
43
|
+
[dimension_key, label]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def sorted_dimension_keys
|
48
|
+
sorted_measures_results.first.last.keys
|
49
|
+
end
|
50
|
+
|
51
|
+
def dimension_keys_sorted_by_label
|
52
|
+
@dimension_keys_sorted_by_label ||= dimension_summaries.sort_by(&:last).map(&:first)
|
53
|
+
end
|
54
|
+
|
55
|
+
def dimension_keys
|
56
|
+
measures_results.first.last.keys
|
57
|
+
end
|
58
|
+
|
59
|
+
def dimension_ids_dimension_instances
|
60
|
+
@dimension_ids_dimension_instances ||= begin
|
61
|
+
dimension_ids = dimension_keys
|
62
|
+
Utils.dimension_to_dimension_ids_dimension_instances(primary_dimension_with_measure, dimension_ids)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def primary_dimension_with_measure
|
67
|
+
@primary_dimension_with_measure ||= DimensionWithMeasure.new(dimension: primary_measure.dimensions.first, measure: primary_measure)
|
68
|
+
end
|
69
|
+
|
70
|
+
def primary_measure
|
71
|
+
measures.first
|
72
|
+
end
|
73
|
+
|
74
|
+
def sorted_measures_results
|
75
|
+
@sorted_measures_results ||= begin
|
76
|
+
if order.relation == 'dimension1' && order.field == 'label'
|
77
|
+
sorted_measures_results = measures_results.map do |measure, dimension_keys_values|
|
78
|
+
sorted_dimension_keys_values = dimension_keys_values.sort_by { |key, _| dimension_keys_sorted_by_label.index(key) }
|
79
|
+
sorted_dimension_keys_values = sorted_dimension_keys_values.reverse if order.direction == 'desc'
|
80
|
+
[measure, Hash[sorted_dimension_keys_values]]
|
81
|
+
end
|
82
|
+
elsif (order.relation == 'dimension1' && order.field.nil?) || (order.relation == 0)
|
83
|
+
sorted_measures_results = measures_results.map do |measure, dimension_keys_values|
|
84
|
+
sorted_dimension_keys_values = dimension_keys_values.sort_by(&:first)
|
85
|
+
sorted_dimension_keys_values = sorted_dimension_keys_values.reverse if order.direction == 'desc'
|
86
|
+
[measure, Hash[sorted_dimension_keys_values]]
|
87
|
+
end
|
88
|
+
elsif order.relation.is_a?(Fixnum)
|
89
|
+
measure_index = order.relation - 1
|
90
|
+
raise ArgumentError.new("Invalid order column: #{order.relation}") unless measure_index.in?((0..(measures_results.length - 1)))
|
91
|
+
dimension_keys_values = measures_results.values.to_a[measure_index]
|
92
|
+
sorted_dimension_keys = dimension_keys_values.sort_by(&:last).map(&:first)
|
93
|
+
sorted_dimension_keys = sorted_dimension_keys.reverse if order.direction == 'desc'
|
94
|
+
sorted_measures_results = measures_results.map do |measure, dimension_keys_values|
|
95
|
+
dimension_keys_values = dimension_keys_values.sort_by { |dimension_key, _| sorted_dimension_keys.index(dimension_key) }
|
96
|
+
[measure, Hash[dimension_keys_values]]
|
97
|
+
end
|
98
|
+
elsif order.relation == 'count'
|
99
|
+
dimension_keys_sums = Hash.new(0)
|
100
|
+
measures_results.values.each do |dimension_keys_values|
|
101
|
+
dimension_keys_values.each do |dimension_key, value|
|
102
|
+
dimension_keys_sums[dimension_key] += value
|
103
|
+
end
|
104
|
+
end
|
105
|
+
sorted_dimension_keys = dimension_keys_sums.sort_by(&:last).map(&:first)
|
106
|
+
sorted_dimension_keys = sorted_dimension_keys.reverse if order.direction == 'desc'
|
107
|
+
sorted_measures_results = measures_results.map do |measure, dimension_keys_values|
|
108
|
+
dimension_keys_values = dimension_keys_values.sort_by { |dimension_key, _| sorted_dimension_keys.index(dimension_key) }
|
109
|
+
[measure, Hash[dimension_keys_values]]
|
110
|
+
end
|
111
|
+
else
|
112
|
+
sorted_measures_results = measures_results
|
113
|
+
end
|
114
|
+
Hash[sorted_measures_results]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module ReportsKit
|
2
|
+
module Reports
|
3
|
+
module Data
|
4
|
+
class FormatTwoDimensions
|
5
|
+
attr_accessor :measure, :dimension, :second_dimension, :dimension_keys_values, :order
|
6
|
+
|
7
|
+
def initialize(measure, dimension_keys_values, order:)
|
8
|
+
self.measure = measure
|
9
|
+
self.dimension = measure.dimensions[0]
|
10
|
+
self.second_dimension = measure.dimensions[1]
|
11
|
+
self.dimension_keys_values = dimension_keys_values
|
12
|
+
self.order = order
|
13
|
+
end
|
14
|
+
|
15
|
+
def perform
|
16
|
+
{
|
17
|
+
entities: entities,
|
18
|
+
datasets: datasets
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def entities
|
25
|
+
sorted_primary_keys.map do |primary_key|
|
26
|
+
Utils.dimension_key_to_entity(primary_key, dimension, dimension_ids_dimension_instances)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def datasets
|
31
|
+
secondary_keys_values = sorted_secondary_keys.map do |secondary_key|
|
32
|
+
raw_values = sorted_primary_keys.map do |primary_key|
|
33
|
+
primary_keys_secondary_keys_values[primary_key][secondary_key]
|
34
|
+
end
|
35
|
+
values = raw_values.map do |raw_value|
|
36
|
+
Utils.raw_value_to_value(raw_value, measure.value_format_method)
|
37
|
+
end
|
38
|
+
[secondary_key, values]
|
39
|
+
end
|
40
|
+
secondary_keys_values.map do |secondary_key, values|
|
41
|
+
next if secondary_key.blank?
|
42
|
+
{
|
43
|
+
entity: Utils.dimension_key_to_entity(secondary_key, second_dimension, second_dimension_ids_dimension_instances),
|
44
|
+
values: values
|
45
|
+
}
|
46
|
+
end.compact
|
47
|
+
end
|
48
|
+
|
49
|
+
def sorted_primary_keys_secondary_keys_values
|
50
|
+
@sorted_primary_keys_secondary_keys_values ||= begin
|
51
|
+
if order.relation == 'dimension1' && order.field == 'label'
|
52
|
+
primary_keys_secondary_keys_values
|
53
|
+
sorted_primary_keys_secondary_keys_values = primary_keys_secondary_keys_values.sort_by do |primary_key, _|
|
54
|
+
primary_dimension_keys_sorted_by_label.index(primary_key)
|
55
|
+
end
|
56
|
+
elsif order.relation == 'dimension1' && order.field.nil?
|
57
|
+
sorted_primary_keys_secondary_keys_values = primary_keys_secondary_keys_values.sort_by do |primary_key, _|
|
58
|
+
primary_key
|
59
|
+
end
|
60
|
+
elsif order.relation == 'count'
|
61
|
+
primary_keys_sums = Hash.new(0)
|
62
|
+
primary_keys_secondary_keys_values.each do |primary_key, secondary_keys_values|
|
63
|
+
primary_keys_sums[primary_key] += secondary_keys_values.values.sum
|
64
|
+
end
|
65
|
+
sorted_primary_keys = primary_keys_sums.sort_by(&:last).map(&:first)
|
66
|
+
sorted_primary_keys_secondary_keys_values = primary_keys_secondary_keys_values.sort_by do |primary_key, _|
|
67
|
+
sorted_primary_keys.index(primary_key)
|
68
|
+
end
|
69
|
+
else
|
70
|
+
dimension_keys_values
|
71
|
+
end
|
72
|
+
sorted_primary_keys_secondary_keys_values = sorted_primary_keys_secondary_keys_values.reverse if order.direction == 'desc'
|
73
|
+
Hash[sorted_primary_keys_secondary_keys_values]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def primary_keys_secondary_keys_values
|
78
|
+
@primary_keys_secondary_keys_values ||= begin
|
79
|
+
primary_keys_secondary_keys_values = {}
|
80
|
+
dimension_keys_values.each do |(primary_key, secondary_key), value|
|
81
|
+
primary_key = primary_key.to_date if primary_key.is_a?(Time)
|
82
|
+
secondary_key = secondary_key.to_date if secondary_key.is_a?(Time)
|
83
|
+
primary_keys_secondary_keys_values[primary_key] ||= {}
|
84
|
+
primary_keys_secondary_keys_values[primary_key][secondary_key] = value
|
85
|
+
end
|
86
|
+
primary_keys_secondary_keys_values
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def dimension_ids_dimension_instances
|
91
|
+
@dimension_ids_dimension_instances ||= begin
|
92
|
+
Utils.dimension_to_dimension_ids_dimension_instances(dimension, primary_keys)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def second_dimension_ids_dimension_instances
|
97
|
+
@second_dimension_ids_dimension_instances ||= begin
|
98
|
+
Utils.dimension_to_dimension_ids_dimension_instances(second_dimension, secondary_keys)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def sorted_primary_keys
|
103
|
+
@sorted_primary_keys ||= begin
|
104
|
+
keys = sorted_primary_keys_secondary_keys_values.keys
|
105
|
+
limit = dimension.dimension_instances_limit
|
106
|
+
keys = keys.first(limit) if limit
|
107
|
+
keys
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def sorted_secondary_keys
|
112
|
+
@sorted_secondary_keys ||= begin
|
113
|
+
keys = sorted_primary_keys_secondary_keys_values.values.first.keys
|
114
|
+
limit = second_dimension.dimension_instances_limit
|
115
|
+
keys = keys.first(limit) if limit
|
116
|
+
keys
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def primary_summaries
|
121
|
+
primary_keys.map do |key|
|
122
|
+
label = Utils.dimension_key_to_label(key, dimension, dimension_ids_dimension_instances)
|
123
|
+
[key, label]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def primary_dimension_keys_sorted_by_label
|
128
|
+
@primary_dimension_keys_sorted_by_label ||= primary_summaries.sort_by(&:last).map(&:first)
|
129
|
+
end
|
130
|
+
|
131
|
+
def primary_keys
|
132
|
+
@primary_keys ||= dimension_keys_values.keys.map(&:first).uniq
|
133
|
+
end
|
134
|
+
|
135
|
+
def secondary_keys
|
136
|
+
@secondary_keys ||= dimension_keys_values.keys.map(&:last).uniq
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|