reports_kit 0.2.0 → 0.3.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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/.travis.yml +20 -0
  4. data/README.md +17 -19
  5. data/app/assets/javascripts/reports_kit/lib/_init.js +2 -1
  6. data/app/assets/javascripts/reports_kit/lib/chart.js +25 -16
  7. data/app/assets/javascripts/reports_kit/lib/report.js +42 -25
  8. data/app/assets/javascripts/reports_kit/lib/table.js +37 -2
  9. data/app/assets/stylesheets/reports_kit/reports.css.sass +3 -0
  10. data/docs/dimensions.md +26 -34
  11. data/docs/display_options.md +12 -15
  12. data/docs/filters.md +54 -63
  13. data/docs/measures.md +3 -4
  14. data/lib/reports_kit.rb +12 -10
  15. data/lib/reports_kit/base_controller.rb +1 -2
  16. data/lib/reports_kit/configuration.rb +19 -4
  17. data/lib/reports_kit/entity.rb +3 -0
  18. data/lib/reports_kit/helper.rb +17 -21
  19. data/lib/reports_kit/model_configuration.rb +1 -1
  20. data/lib/reports_kit/report_builder.rb +11 -11
  21. data/lib/reports_kit/reports/{abstract_measure.rb → abstract_series.rb} +1 -1
  22. data/lib/reports_kit/reports/{composite_measure.rb → composite_series.rb} +12 -8
  23. data/lib/reports_kit/reports/data/add_table_aggregations.rb +105 -0
  24. data/lib/reports_kit/reports/data/{composite_aggregation.rb → aggregate_composite.rb} +28 -27
  25. data/lib/reports_kit/reports/data/{one_dimension.rb → aggregate_one_dimension.rb} +9 -7
  26. data/lib/reports_kit/reports/data/{two_dimensions.rb → aggregate_two_dimensions.rb} +9 -8
  27. data/lib/reports_kit/reports/data/chart_options.rb +6 -11
  28. data/lib/reports_kit/reports/data/format_one_dimension.rb +56 -36
  29. data/lib/reports_kit/reports/data/format_table.rb +65 -0
  30. data/lib/reports_kit/reports/data/format_two_dimensions.rb +10 -8
  31. data/lib/reports_kit/reports/data/generate.rb +51 -28
  32. data/lib/reports_kit/reports/data/generate_for_properties.rb +52 -30
  33. data/lib/reports_kit/reports/data/populate_one_dimension.rb +30 -12
  34. data/lib/reports_kit/reports/data/populate_two_dimensions.rb +31 -31
  35. data/lib/reports_kit/reports/data/utils.rb +28 -24
  36. data/lib/reports_kit/reports/dimension.rb +4 -0
  37. data/lib/reports_kit/reports/{dimension_with_measure.rb → dimension_with_series.rb} +8 -9
  38. data/lib/reports_kit/reports/filter.rb +4 -0
  39. data/lib/reports_kit/reports/filter_types/base.rb +4 -3
  40. data/lib/reports_kit/reports/filter_types/boolean.rb +4 -4
  41. data/lib/reports_kit/reports/filter_types/datetime.rb +13 -3
  42. data/lib/reports_kit/reports/filter_types/number.rb +5 -5
  43. data/lib/reports_kit/reports/{filter_with_measure.rb → filter_with_series.rb} +7 -7
  44. data/lib/reports_kit/reports/generate_autocomplete_results.rb +2 -2
  45. data/lib/reports_kit/reports/inferrable_configuration.rb +6 -6
  46. data/lib/reports_kit/reports/{measure.rb → series.rb} +28 -15
  47. data/lib/reports_kit/reports_controller.rb +25 -5
  48. data/lib/reports_kit/value.rb +3 -0
  49. data/lib/reports_kit/version.rb +1 -1
  50. data/spec/fixtures/generate_inputs.yml +116 -63
  51. data/spec/fixtures/generate_outputs.yml +64 -0
  52. data/spec/reports_kit/report_builder_spec.rb +10 -12
  53. data/spec/reports_kit/reports/data/generate_spec.rb +559 -140
  54. data/spec/reports_kit/reports/{dimension_with_measure_spec.rb → dimension_with_series_spec.rb} +5 -7
  55. data/spec/reports_kit/reports/{filter_with_measure_spec.rb → filter_with_series_spec.rb} +3 -3
  56. data/spec/spec_helper.rb +5 -5
  57. data/spec/support/config.rb +31 -1
  58. data/spec/support/helpers.rb +6 -2
  59. metadata +17 -14
  60. data/lib/reports_kit/reports/data/entity.rb +0 -7
  61. data/lib/reports_kit/reports/data/value.rb +0 -7
@@ -2,12 +2,13 @@ module ReportsKit
2
2
  module Reports
3
3
  module Data
4
4
  class FormatOneDimension
5
- attr_accessor :measures_results, :measures, :order
5
+ attr_accessor :serieses_results, :serieses, :order, :limit
6
6
 
7
- def initialize(measures_results, order:)
8
- self.measures_results = measures_results
9
- self.measures = measures_results.keys
7
+ def initialize(serieses_results, order:, limit:)
8
+ self.serieses_results = serieses_results
9
+ self.serieses = serieses_results.keys
10
10
  self.order = order
11
+ self.limit = limit
11
12
  end
12
13
 
13
14
  def perform
@@ -21,99 +22,118 @@ module ReportsKit
21
22
 
22
23
  def entities
23
24
  sorted_dimension_keys.map do |key|
24
- Utils.dimension_key_to_entity(key, primary_dimension_with_measure, dimension_ids_dimension_instances)
25
+ Utils.dimension_key_to_entity(key, primary_dimension_with_series, dimension_ids_dimension_instances)
25
26
  end
26
27
  end
27
28
 
28
29
  def datasets
29
- sorted_measures_results.map do |measure, result|
30
+ sorted_serieses_results.map do |series, result|
30
31
  values = result.values.map do |raw_value|
31
- Utils.raw_value_to_value(raw_value, measure.value_format_method)
32
+ Utils.raw_value_to_value(raw_value, series.value_format_method)
32
33
  end
33
34
  {
34
- entity: measure,
35
+ entity: series,
35
36
  values: values
36
37
  }
37
38
  end
38
39
  end
39
40
 
40
41
  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)
42
+ @dimension_summaries ||= raw_dimension_keys.map do |dimension_key|
43
+ label = Utils.dimension_key_to_label(dimension_key, primary_dimension_with_series, dimension_ids_dimension_instances)
44
+ next if label.blank?
43
45
  [dimension_key, label]
44
- end
46
+ end.compact
45
47
  end
46
48
 
47
49
  def sorted_dimension_keys
48
- sorted_measures_results.first.last.keys
50
+ sorted_serieses_results.first.last.keys
49
51
  end
50
52
 
51
53
  def dimension_keys_sorted_by_label
52
- @dimension_keys_sorted_by_label ||= dimension_summaries.sort_by(&:last).map(&:first)
54
+ dimension_summaries.sort_by { |key, label| label.is_a?(String) ? label.downcase : label }.map(&:first)
53
55
  end
54
56
 
55
57
  def dimension_keys
56
- measures_results.first.last.keys
58
+ dimension_summaries.map(&:first)
59
+ end
60
+
61
+ def raw_dimension_keys
62
+ serieses_results.first.last.keys
57
63
  end
58
64
 
59
65
  def dimension_ids_dimension_instances
60
66
  @dimension_ids_dimension_instances ||= begin
61
- dimension_ids = dimension_keys
62
- Utils.dimension_to_dimension_ids_dimension_instances(primary_dimension_with_measure, dimension_ids)
67
+ dimension_ids = raw_dimension_keys
68
+ Utils.dimension_to_dimension_ids_dimension_instances(primary_dimension_with_series, dimension_ids)
63
69
  end
64
70
  end
65
71
 
66
- def primary_dimension_with_measure
67
- @primary_dimension_with_measure ||= DimensionWithMeasure.new(dimension: primary_measure.dimensions.first, measure: primary_measure)
72
+ def primary_dimension_with_series
73
+ @primary_dimension_with_series ||= DimensionWithSeries.new(dimension: primary_series.dimensions.first, series: primary_series)
68
74
  end
69
75
 
70
- def primary_measure
71
- measures.first
76
+ def primary_series
77
+ serieses.first
72
78
  end
73
79
 
74
- def sorted_measures_results
75
- @sorted_measures_results ||= begin
80
+ def sorted_serieses_results
81
+ @sorted_serieses_results ||= begin
76
82
  if order.relation == 'dimension1' && order.field == 'label'
77
- sorted_measures_results = measures_results.map do |measure, dimension_keys_values|
83
+ sorted_serieses_results = serieses_results.map do |series, dimension_keys_values|
84
+ dimension_keys_values = filter_dimension_keys_values(dimension_keys_values)
78
85
  sorted_dimension_keys_values = dimension_keys_values.sort_by { |key, _| dimension_keys_sorted_by_label.index(key) }
79
86
  sorted_dimension_keys_values = sorted_dimension_keys_values.reverse if order.direction == 'desc'
80
- [measure, Hash[sorted_dimension_keys_values]]
87
+ [series, Hash[sorted_dimension_keys_values]]
81
88
  end
82
89
  elsif (order.relation == 'dimension1' && order.field.nil?) || (order.relation == 0)
83
- sorted_measures_results = measures_results.map do |measure, dimension_keys_values|
90
+ sorted_serieses_results = serieses_results.map do |series, dimension_keys_values|
91
+ dimension_keys_values = filter_dimension_keys_values(dimension_keys_values)
84
92
  sorted_dimension_keys_values = dimension_keys_values.sort_by(&:first)
85
93
  sorted_dimension_keys_values = sorted_dimension_keys_values.reverse if order.direction == 'desc'
86
- [measure, Hash[sorted_dimension_keys_values]]
94
+ [series, Hash[sorted_dimension_keys_values]]
87
95
  end
88
96
  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]
97
+ series_index = order.relation - 1
98
+ raise ArgumentError.new("Invalid order column: #{order.relation}") unless series_index.in?((0..(serieses_results.length - 1)))
99
+ dimension_keys_values = serieses_results.values.to_a[series_index]
92
100
  sorted_dimension_keys = dimension_keys_values.sort_by(&:last).map(&:first)
93
101
  sorted_dimension_keys = sorted_dimension_keys.reverse if order.direction == 'desc'
94
- sorted_measures_results = measures_results.map do |measure, dimension_keys_values|
102
+ sorted_serieses_results = serieses_results.map do |series, dimension_keys_values|
103
+ dimension_keys_values = filter_dimension_keys_values(dimension_keys_values)
95
104
  dimension_keys_values = dimension_keys_values.sort_by { |dimension_key, _| sorted_dimension_keys.index(dimension_key) }
96
- [measure, Hash[dimension_keys_values]]
105
+ [series, Hash[dimension_keys_values]]
97
106
  end
98
107
  elsif order.relation == 'count'
99
108
  dimension_keys_sums = Hash.new(0)
100
- measures_results.values.each do |dimension_keys_values|
109
+ serieses_results.values.each do |dimension_keys_values|
110
+ dimension_keys_values = filter_dimension_keys_values(dimension_keys_values)
101
111
  dimension_keys_values.each do |dimension_key, value|
102
112
  dimension_keys_sums[dimension_key] += value
103
113
  end
104
114
  end
105
115
  sorted_dimension_keys = dimension_keys_sums.sort_by(&:last).map(&:first)
106
116
  sorted_dimension_keys = sorted_dimension_keys.reverse if order.direction == 'desc'
107
- sorted_measures_results = measures_results.map do |measure, dimension_keys_values|
117
+ sorted_serieses_results = serieses_results.map do |series, dimension_keys_values|
118
+ dimension_keys_values = filter_dimension_keys_values(dimension_keys_values)
108
119
  dimension_keys_values = dimension_keys_values.sort_by { |dimension_key, _| sorted_dimension_keys.index(dimension_key) }
109
- [measure, Hash[dimension_keys_values]]
120
+ [series, Hash[dimension_keys_values]]
110
121
  end
111
122
  else
112
- sorted_measures_results = measures_results
123
+ sorted_serieses_results = serieses_results
113
124
  end
114
- Hash[sorted_measures_results]
125
+ if limit
126
+ sorted_serieses_results = sorted_serieses_results.map do |series, results|
127
+ [series, Hash[results.to_a.take(limit)]]
128
+ end
129
+ end
130
+ Hash[sorted_serieses_results]
115
131
  end
116
132
  end
133
+
134
+ def filter_dimension_keys_values(dimension_keys_values)
135
+ dimension_keys_values.select { |key, values| dimension_keys.include?(key) }
136
+ end
117
137
  end
118
138
  end
119
139
  end
@@ -0,0 +1,65 @@
1
+ module ReportsKit
2
+ module Reports
3
+ module Data
4
+ class FormatTable
5
+ attr_accessor :data, :format, :first_column_label, :report_options
6
+
7
+ VALID_AGGREGATION_OPERATORS = [:sum]
8
+
9
+ def initialize(data, format:, first_column_label:, report_options:)
10
+ self.data = data
11
+ self.format = format
12
+ self.first_column_label = first_column_label
13
+ self.report_options = report_options || {}
14
+ end
15
+
16
+ def perform
17
+ table_data
18
+ end
19
+
20
+ private
21
+
22
+ def table_data
23
+ data_rows_with_labels = data_rows.map.with_index do |data_row, index|
24
+ label = format_string(data[:labels][index])
25
+ [label] + data_row
26
+ end
27
+ [column_names] + data_rows_with_labels
28
+ end
29
+
30
+ def column_names
31
+ column_names_column_values[0]
32
+ end
33
+
34
+ def column_values
35
+ column_names_column_values[1]
36
+ end
37
+
38
+ def data_rows
39
+ @data_rows ||= column_values.transpose
40
+ end
41
+
42
+ def column_names_column_values
43
+ @column_names_column_values ||= begin
44
+ column_names = [format_string(first_column_label)]
45
+ column_values = []
46
+ data[:datasets].each do |dataset|
47
+ column_names << format_string(dataset[:label])
48
+ column_values << dataset[:data]
49
+ end
50
+ [column_names, column_values]
51
+ end
52
+ end
53
+
54
+ def format_string(string)
55
+ return string unless string && strip_html_tags?
56
+ ActionView::Base.full_sanitizer.sanitize(string)
57
+ end
58
+
59
+ def strip_html_tags?
60
+ format == 'csv'
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -2,14 +2,15 @@ module ReportsKit
2
2
  module Reports
3
3
  module Data
4
4
  class FormatTwoDimensions
5
- attr_accessor :measure, :dimension, :second_dimension, :dimension_keys_values, :order
5
+ attr_accessor :series, :dimension, :second_dimension, :dimension_keys_values, :order, :limit
6
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]
7
+ def initialize(series, dimension_keys_values, order:, limit:)
8
+ self.series = series
9
+ self.dimension = series.dimensions[0]
10
+ self.second_dimension = series.dimensions[1]
11
11
  self.dimension_keys_values = dimension_keys_values
12
12
  self.order = order
13
+ self.limit = limit
13
14
  end
14
15
 
15
16
  def perform
@@ -33,7 +34,7 @@ module ReportsKit
33
34
  primary_keys_secondary_keys_values[primary_key][secondary_key]
34
35
  end
35
36
  values = raw_values.map do |raw_value|
36
- Utils.raw_value_to_value(raw_value, measure.value_format_method)
37
+ Utils.raw_value_to_value(raw_value, series.value_format_method)
37
38
  end
38
39
  [secondary_key, values]
39
40
  end
@@ -120,12 +121,13 @@ module ReportsKit
120
121
  def primary_summaries
121
122
  primary_keys.map do |key|
122
123
  label = Utils.dimension_key_to_label(key, dimension, dimension_ids_dimension_instances)
124
+ next if label.blank?
123
125
  [key, label]
124
- end
126
+ end.compact
125
127
  end
126
128
 
127
129
  def primary_dimension_keys_sorted_by_label
128
- @primary_dimension_keys_sorted_by_label ||= primary_summaries.sort_by(&:last).map(&:first)
130
+ @primary_dimension_keys_sorted_by_label ||= primary_summaries.sort_by { |key, label| label.is_a?(String) ? label.downcase : label }.map(&:first)
129
131
  end
130
132
 
131
133
  def primary_keys
@@ -8,28 +8,30 @@ module ReportsKit
8
8
 
9
9
  def initialize(properties, context_record: nil)
10
10
  self.properties = properties.deep_symbolize_keys
11
+ self.properties = ReportsKit.configuration.default_properties.deep_merge(self.properties) if ReportsKit.configuration.default_properties
11
12
  self.properties = Utils.normalize_properties(self.properties)
12
13
  self.context_record = context_record
13
14
  end
14
15
 
15
16
  def perform
16
17
  data = ReportsKit::Cache.get(properties, context_record)
17
- return data if data
18
+ return data.deep_symbolize_keys if data
18
19
 
19
20
  if two_dimensions?
20
- raw_data = Data::FormatTwoDimensions.new(measures.first, measures_results.first.last, order: order).perform
21
+ raw_data = Data::FormatTwoDimensions.new(serieses.first, serieses_results.first.last, order: order, limit: limit).perform
21
22
  else
22
- raw_data = Data::FormatOneDimension.new(measures_results, order: order).perform
23
+ raw_data = Data::FormatOneDimension.new(serieses_results, order: order, limit: limit).perform
23
24
  end
24
- raw_data = data_format_method.call(raw_data) if data_format_method
25
+ raw_data = format_csv_times(raw_data) if format == 'csv'
26
+ raw_data = Data::AddTableAggregations.new(raw_data, report_options: report_options).perform if table_or_csv?
27
+ raw_data = data_format_method.call(data: raw_data, properties: properties, context_record: context_record) if data_format_method
28
+ raw_data = csv_data_format_method.call(data: raw_data, properties: properties, context_record: context_record) if csv_data_format_method && format == 'csv'
25
29
  chart_data = format_chart_data(raw_data)
26
30
 
27
31
  data = { chart_data: chart_data }
28
32
  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
+ data[:report_options] = report_options if report_options
34
+ data = format_table_data(data) if table_or_csv?
33
35
  ReportsKit::Cache.set(properties, context_record, data)
34
36
  data
35
37
  end
@@ -48,12 +50,23 @@ module ReportsKit
48
50
  chart_data
49
51
  end
50
52
 
51
- def measures_results
52
- @measures_results ||= GenerateForProperties.new(properties, context_record: context_record).perform
53
+ def format_table_data(data)
54
+ data[:table_data] = Data::FormatTable.new(
55
+ data.delete(:chart_data),
56
+ format: format,
57
+ first_column_label: primary_dimension.label,
58
+ report_options: report_options
59
+ ).perform
60
+ data[:type] = format
61
+ data
62
+ end
63
+
64
+ def serieses_results
65
+ @serieses_results ||= GenerateForProperties.new(properties, context_record: context_record).perform
53
66
  end
54
67
 
55
68
  def two_dimensions?
56
- dimension_keys = measures_results.first.last.keys
69
+ dimension_keys = serieses_results.first.last.keys
57
70
  dimension_keys.first.is_a?(Array)
58
71
  end
59
72
 
@@ -64,46 +77,56 @@ module ReportsKit
64
77
  end
65
78
  end
66
79
 
80
+ def limit
81
+ properties[:limit]
82
+ end
83
+
67
84
  def inferred_order
68
85
  return Order.new('dimension1', nil, 'asc') if primary_dimension.configured_by_time?
69
86
  Order.new('count', nil, 'desc')
70
87
  end
71
88
 
72
- def measures
73
- @measures ||= Measure.new_from_properties!(properties, context_record: context_record)
89
+ def serieses
90
+ @serieses ||= Series.new_from_properties!(properties, context_record: context_record)
91
+ end
92
+
93
+ def report_options
94
+ properties[:report_options]
74
95
  end
75
96
 
76
97
  def data_format_method
77
- ReportsKit.configuration.custom_method(properties[:data_format_method])
98
+ ReportsKit.configuration.custom_method(report_options.try(:[], :data_format_method))
99
+ end
100
+
101
+ def csv_data_format_method
102
+ ReportsKit.configuration.custom_method(report_options.try(:[], :csv_data_format_method))
78
103
  end
79
104
 
80
105
  def format
81
106
  properties[:format]
82
107
  end
83
108
 
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]
109
+ def format_csv_times(raw_data)
110
+ return raw_data unless primary_dimension.configured_by_time?
111
+ raw_data[:entities] = raw_data[:entities].map do |entity|
112
+ entity.label = Utils.format_csv_time(entity.instance)
113
+ entity
90
114
  end
91
- rows = column_values.transpose
92
- rows = rows.map.with_index do |row, index|
93
- label = data[:labels][index]
94
- row.unshift(label)
95
- end
96
- [column_names] + rows
115
+ raw_data
97
116
  end
98
117
 
99
118
  def primary_dimension
100
- measures.first.dimensions.first
119
+ serieses.first.dimensions.first
120
+ end
121
+
122
+ def table_or_csv?
123
+ format.in?(%w(table csv))
101
124
  end
102
125
 
103
126
  def inferred_options
104
127
  {
105
128
  x_axis_label: primary_dimension.label,
106
- y_axis_label: measures.length == 1 ? measures.first.label : nil
129
+ y_axis_label: serieses.length == 1 ? serieses.first.label : nil
107
130
  }
108
131
  end
109
132
  end
@@ -13,23 +13,23 @@ module ReportsKit
13
13
 
14
14
  def perform
15
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
16
+ raise ArgumentError.new('Aggregations require at least one series') if all_serieses.length == 0
17
+ dimension_keys_values = Data::AggregateComposite.new(properties, context_record: context_record).perform
18
+ serieses_dimension_keys_values = { CompositeSeries.new(properties) => dimension_keys_values }
19
+ elsif all_serieses.length == 1 && composite_serieses.length == 1
20
+ dimension_keys_values = Data::AggregateComposite.new(composite_serieses.first.properties, context_record: context_record).perform
21
+ serieses_dimension_keys_values = { all_serieses.first => dimension_keys_values }
22
+ elsif all_serieses.length == 1 && all_serieses.first.dimensions.length == 2
23
+ dimension_keys_values = Data::AggregateTwoDimensions.new(all_serieses.first).perform
24
+ serieses_dimension_keys_values = { all_serieses.first => dimension_keys_values }
25
+ serieses_dimension_keys_values = Data::PopulateTwoDimensions.new(serieses_dimension_keys_values).perform
26
+ elsif all_serieses.length > 0
27
+ serieses_dimension_keys_values = serieses_dimension_keys_values_for_one_dimension
28
28
  else
29
29
  raise ArgumentError.new('The configuration of measurse and dimensions is invalid')
30
30
  end
31
31
 
32
- measures_dimension_keys_values
32
+ serieses_dimension_keys_values
33
33
  end
34
34
 
35
35
  private
@@ -42,32 +42,54 @@ module ReportsKit
42
42
  properties[:name]
43
43
  end
44
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
45
+ def serieses_dimension_keys_values_for_one_dimension
46
+ multi_dimension_serieses_exist = all_serieses.any? { |series| series.dimensions.length > 1 }
47
+ raise ArgumentError.new('When more than one series are configured, only one dimension may be used per series') if multi_dimension_serieses_exist
48
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
49
+ if all_serieses.length > 1 && ReportsKit.configuration.use_concurrent_queries
50
+ serieses_dimension_keys_values = multithreaded_serieses_dimension_keys_values
51
+ else
52
+ serieses_dimension_keys_values = all_serieses.map do |series|
53
+ [series, dimension_keys_values_for_series(series)]
54
+ end
55
+ end
56
+ serieses_dimension_keys_values = Hash[serieses_dimension_keys_values]
57
+ Data::PopulateOneDimension.new(serieses_dimension_keys_values, context_record: context_record, properties: properties).perform
58
+ end
59
+
60
+ def multithreaded_serieses_dimension_keys_values
61
+ threads = all_serieses.map do |series|
62
+ Thread.new do
63
+ ActiveRecord::Base.connection_pool.with_connection do
64
+ begin
65
+ [series, dimension_keys_values_for_series(series)]
66
+ ensure
67
+ ActiveRecord::Base.connection.close if ActiveRecord::Base.connection
68
+ end
69
+ end
54
70
  end
55
- [measure, dimension_keys_values]
56
71
  end
57
- measures_dimension_keys_values = Hash[measures_dimension_keys_values]
58
- Data::PopulateOneDimension.new(measures_dimension_keys_values).perform
72
+ threads.map(&:join).map(&:value)
73
+ end
74
+
75
+ def dimension_keys_values_for_series(series)
76
+ if series.is_a?(CompositeSeries)
77
+ Data::AggregateComposite.new(series.properties, context_record: context_record).perform
78
+ else
79
+ Data::AggregateOneDimension.new(series).perform
80
+ end
59
81
  end
60
82
 
61
- def all_measures
62
- @all_measures ||= Measure.new_from_properties!(properties, context_record: context_record)
83
+ def all_serieses
84
+ @all_serieses ||= Series.new_from_properties!(properties, context_record: context_record)
63
85
  end
64
86
 
65
- def composite_measures
66
- @composite_measures ||= all_measures.grep(CompositeMeasure)
87
+ def composite_serieses
88
+ @composite_serieses ||= all_serieses.grep(CompositeSeries)
67
89
  end
68
90
 
69
- def measures
70
- @measures ||= all_measures.grep(Measure)
91
+ def serieses
92
+ @serieses ||= all_serieses.grep(Series)
71
93
  end
72
94
  end
73
95
  end