reports_kit 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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