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
@@ -11,10 +11,9 @@ You can use any `type` value supported by Chart.js, including `bar`, `line`, `ho
11
11
  Here's an example of a horizontal bar chart:
12
12
 
13
13
  ```yaml
14
- measure:
15
- key: flight
16
- dimensions:
17
- - carrier
14
+ measure: flight
15
+ dimensions:
16
+ - carrier
18
17
  chart:
19
18
  type: horizontalBar
20
19
  options:
@@ -37,11 +36,10 @@ You can use any `options` that are supported by Chart.js.
37
36
  Here's an example of a chart with Chart.js options:
38
37
 
39
38
  ```yaml
40
- measure:
41
- key: flight
42
- dimensions:
43
- - origin_market
44
- - carrier
39
+ measure: flight
40
+ dimensions:
41
+ - origin_market
42
+ - carrier
45
43
  chart:
46
44
  type: horizontalBar
47
45
  options:
@@ -66,12 +64,11 @@ You can use any `datasets` options that are supported by Chart.js.
66
64
  Here's an example of a chart with `datasets` options:
67
65
 
68
66
  ```yaml
69
- measure:
70
- key: flight
71
- dimensions:
72
- - flight_at
73
- - key: carrier
74
- limit: 3
67
+ measure: flight
68
+ dimensions:
69
+ - flight_at
70
+ - key: carrier
71
+ limit: 3
75
72
  chart:
76
73
  type: line
77
74
  datasets:
@@ -7,13 +7,12 @@ A filter is like a SQL `WHERE`: it filters the results to only include results t
7
7
  For example, if the `Flight` model has a `delay` column that's an integer, the chart below will show only flights that have a delay of greater than 15 minutes:
8
8
 
9
9
  ```yaml
10
- measure:
11
- key: flight
12
- filters:
13
- - key: delay
14
- criteria:
15
- operator: '>'
16
- value: 15
10
+ measure: flight
11
+ filters:
12
+ - key: delay
13
+ criteria:
14
+ operator: '>'
15
+ value: 15
17
16
  dimensions:
18
17
  - carrier
19
18
  ```
@@ -22,13 +21,12 @@ dimensions:
22
21
  You can also create form controls that the user can use to filter the chart:
23
22
 
24
23
  ```yaml
25
- measure:
26
- key: flight
27
- filters:
28
- - carrier
29
- - carrier_name
30
- - is_on_time
31
- - flight_at
24
+ measure: flight
25
+ filters:
26
+ - carrier
27
+ - carrier_name
28
+ - is_on_time
29
+ - flight_at
32
30
  dimensions:
33
31
  - flight_at
34
32
  - carrier
@@ -55,12 +53,11 @@ In `app/views/my_view.html.haml`, you can use ReportsKit's form helpers to creat
55
53
  Boolean filters can be used on any `boolean` columns, or you can define your own boolean filter (see [Custom Filters](#custom-filters)).
56
54
 
57
55
  ```yaml
58
- measure:
59
- key: flight
60
- filters:
61
- - key: is_on_time
62
- criteria:
63
- value: true
56
+ measure: flight
57
+ filters:
58
+ - key: is_on_time
59
+ criteria:
60
+ value: true
64
61
  dimensions:
65
62
  - carrier
66
63
  ```
@@ -71,30 +68,30 @@ dimensions:
71
68
  Datetime filters can be used on any `datetime` or `timestamp` columns, or you can define your own datetime filter (see [Custom Filters](#custom-filters)).
72
69
 
73
70
  ```yaml
74
- measure:
75
- key: flight
76
- filters:
77
- - key: flight_at
78
- criteria:
79
- operator: between
80
- value: Oct 1, 2016 - Jan 1, 2017
71
+ measure: flight
72
+ filters:
73
+ - key: flight_at
74
+ criteria:
75
+ operator: between
76
+ value: -3M - now
81
77
  dimensions:
82
78
  - carrier
83
79
  ```
84
80
  [<img src="images/flights_with_configured_datetime.png?raw=true" width="500" />](images/flights_with_configured_datetime.png?raw=true)
85
81
 
82
+ The `value` in the example above is shorthand for relative time; it represents the time range of `(3.months.ago..Time.zone.now)`. To see the other supported time durations (e.g. `w` for week, `d` for day), see [RelativeTime](https://github.com/tombenner/reports_kit/blob/master/lib/reports_kit/relative_time.rb).
83
+
86
84
  ##### Number
87
85
 
88
86
  Number filters can be used on any `integer`, `float`, or `decimal` columns, or you can define your own number filter (see [Custom Filters](#custom-filters)).
89
87
 
90
88
  ```yaml
91
- measure:
92
- key: flight
93
- filters:
94
- - key: delay
95
- criteria:
96
- operator: '>'
97
- value: 15
89
+ measure: flight
90
+ filters:
91
+ - key: delay
92
+ criteria:
93
+ operator: '>'
94
+ value: 15
98
95
  dimensions:
99
96
  - carrier
100
97
  ```
@@ -105,13 +102,12 @@ dimensions:
105
102
  String filters can be used on any `string` or `text` columns, or you can define your own number filter (see [Custom Filters](#custom-filters)).
106
103
 
107
104
  ```yaml
108
- measure:
109
- key: flight
110
- filters:
111
- - key: carrier_name
112
- criteria:
113
- operator: contains
114
- value: airlines
105
+ measure: flight
106
+ filters:
107
+ - key: carrier_name
108
+ criteria:
109
+ operator: contains
110
+ value: airlines
115
111
  dimensions:
116
112
  - carrier
117
113
  ```
@@ -134,12 +130,11 @@ end
134
130
  We can then use the `was_delayed` filter:
135
131
 
136
132
  ```yaml
137
- measure:
138
- key: flight
139
- filters:
140
- - key: was_delayed
141
- criteria:
142
- value: true
133
+ measure: flight
134
+ filters:
135
+ - key: was_delayed
136
+ criteria:
137
+ value: true
143
138
  dimensions:
144
139
  - carrier
145
140
  ```
@@ -154,10 +149,9 @@ Most charting libraries don't provide interactive form controls, but ReportsKit
154
149
  Check boxes can be used with filters that have a `boolean` type.
155
150
 
156
151
  ```yaml
157
- measure:
158
- key: flight
159
- filters:
160
- - is_on_time
152
+ measure: flight
153
+ filters:
154
+ - is_on_time
161
155
  dimensions:
162
156
  - flight_at
163
157
  - carrier
@@ -176,10 +170,9 @@ dimensions:
176
170
  Date ranges can be used with filters that have a `datetime` type.
177
171
 
178
172
  ```yaml
179
- measure:
180
- key: flight
181
- filters:
182
- - flight_at
173
+ measure: flight
174
+ filters:
175
+ - flight_at
183
176
  dimensions:
184
177
  - flight_at
185
178
  - carrier
@@ -193,10 +186,9 @@ dimensions:
193
186
  ##### Multi-Autocomplete
194
187
 
195
188
  ```yaml
196
- measure:
197
- key: flight
198
- filters:
199
- - carrier
189
+ measure: flight
190
+ filters:
191
+ - carrier
200
192
  dimensions:
201
193
  - flight_at
202
194
  - carrier
@@ -210,10 +202,9 @@ dimensions:
210
202
  ##### String Filter
211
203
 
212
204
  ```yaml
213
- measure:
214
- key: flight
215
- filters:
216
- - carrier_name
205
+ measure: flight
206
+ filters:
207
+ - carrier_name
217
208
  dimensions:
218
209
  - flight_at
219
210
  - carrier
@@ -5,9 +5,8 @@ The measure is what is being counted (or aggregated in another way). You can use
5
5
  For example, say we have a `Flight` model with a `flight_at` datetime column. We can chart the number of flights over time:
6
6
 
7
7
  ```yaml
8
- measure:
9
- key: flight
10
- dimensions:
11
- - flight_at
8
+ measure: flight
9
+ dimensions:
10
+ - flight_at
12
11
  ```
13
12
  [<img src="images/flights_by_flight_at.png?raw=true" width="500" />](images/flights_by_flight_at.png?raw=true)
@@ -4,6 +4,7 @@ require 'reports_kit/base_controller'
4
4
  require 'reports_kit/cache'
5
5
  require 'reports_kit/configuration'
6
6
  require 'reports_kit/engine'
7
+ require 'reports_kit/entity'
7
8
  require 'reports_kit/helper'
8
9
  require 'reports_kit/model'
9
10
  require 'reports_kit/model_configuration'
@@ -12,24 +13,25 @@ require 'reports_kit/relative_time'
12
13
  require 'reports_kit/report_builder'
13
14
  require 'reports_kit/resources_controller'
14
15
  require 'reports_kit/reports_controller'
16
+ require 'reports_kit/value'
15
17
  require 'reports_kit/version'
16
18
 
17
19
  require 'reports_kit/reports/adapters/mysql'
18
20
  require 'reports_kit/reports/adapters/postgresql'
19
21
 
22
+ require 'reports_kit/reports/data/add_table_aggregations'
23
+ require 'reports_kit/reports/data/aggregate_composite'
24
+ require 'reports_kit/reports/data/aggregate_one_dimension'
25
+ require 'reports_kit/reports/data/aggregate_two_dimensions'
20
26
  require 'reports_kit/reports/data/chart_options'
21
- require 'reports_kit/reports/data/composite_aggregation'
22
- require 'reports_kit/reports/data/entity'
23
27
  require 'reports_kit/reports/data/format_one_dimension'
28
+ require 'reports_kit/reports/data/format_table'
24
29
  require 'reports_kit/reports/data/format_two_dimensions'
25
30
  require 'reports_kit/reports/data/generate'
26
31
  require 'reports_kit/reports/data/generate_for_properties'
27
- require 'reports_kit/reports/data/one_dimension'
28
32
  require 'reports_kit/reports/data/populate_one_dimension'
29
33
  require 'reports_kit/reports/data/populate_two_dimensions'
30
- require 'reports_kit/reports/data/two_dimensions'
31
34
  require 'reports_kit/reports/data/utils'
32
- require 'reports_kit/reports/data/value'
33
35
 
34
36
  require 'reports_kit/reports/filter_types/base'
35
37
  require 'reports_kit/reports/filter_types/boolean'
@@ -38,15 +40,15 @@ require 'reports_kit/reports/filter_types/number'
38
40
  require 'reports_kit/reports/filter_types/records'
39
41
  require 'reports_kit/reports/filter_types/string'
40
42
 
41
- require 'reports_kit/reports/abstract_measure'
42
- require 'reports_kit/reports/composite_measure'
43
+ require 'reports_kit/reports/abstract_series'
44
+ require 'reports_kit/reports/composite_series'
43
45
  require 'reports_kit/reports/dimension'
44
- require 'reports_kit/reports/dimension_with_measure'
46
+ require 'reports_kit/reports/dimension_with_series'
45
47
  require 'reports_kit/reports/filter'
46
- require 'reports_kit/reports/filter_with_measure'
48
+ require 'reports_kit/reports/filter_with_series'
47
49
  require 'reports_kit/reports/generate_autocomplete_results'
48
50
  require 'reports_kit/reports/inferrable_configuration'
49
- require 'reports_kit/reports/measure'
51
+ require 'reports_kit/reports/series'
50
52
 
51
53
  module ReportsKit
52
54
  def self.configure
@@ -1,7 +1,6 @@
1
1
  module ReportsKit
2
2
  class BaseController < ActionController::Base
3
- private
4
-
3
+ # This is intentionally public to allow external code to access it
5
4
  def context_record
6
5
  context_record_method = ReportsKit.configuration.context_record_method
7
6
  return unless context_record_method
@@ -1,22 +1,37 @@
1
1
  module ReportsKit
2
2
  class Configuration
3
- attr_accessor :cache_duration, :cache_store,
4
- :context_params_method, :context_record_method, :custom_methods, :first_day_of_week
3
+ attr_accessor :cache_duration, :cache_store, :context_record_method, :custom_methods, :default_dimension_limit,
4
+ :default_properties, :first_day_of_week, :properties_method, :report_filename_method, :use_concurrent_queries
5
+
6
+ DEFAULT_PROPERTIES_METHOD = lambda do |env|
7
+ path = Rails.root.join('config', 'reports_kit', 'reports', "#{report_key}.yml")
8
+ YAML.load_file(path)
9
+ end
5
10
 
6
11
  def initialize
7
12
  self.cache_duration = 5.minutes
8
13
  self.cache_store = nil
9
- self.context_params_method = nil
10
14
  self.context_record_method = nil
11
15
  self.custom_methods = {}
16
+ self.default_dimension_limit = 30
17
+ self.default_properties = nil
12
18
  self.first_day_of_week = :sunday
19
+ self.properties_method = DEFAULT_PROPERTIES_METHOD
20
+ self.report_filename_method = nil
21
+ self.use_concurrent_queries = false
13
22
  end
14
23
 
15
24
  def custom_method(method_name)
16
25
  return if method_name.blank?
17
- method = custom_methods[method_name.to_sym]
26
+ method = evaluated_custom_methods[method_name.to_sym]
18
27
  raise ArgumentError.new("A method named '#{method_name}' is not defined") unless method
19
28
  method
20
29
  end
30
+
31
+ def evaluated_custom_methods
32
+ return custom_methods if custom_methods.is_a?(Hash)
33
+ return custom_methods.call if custom_methods.is_a?(Proc)
34
+ raise ArgumentError.new("Invalid type for custom_methods configuration: #{custom_methods.class}")
35
+ end
21
36
  end
22
37
  end
@@ -0,0 +1,3 @@
1
+ module ReportsKit
2
+ Entity = Struct.new(:key, :label, :instance)
3
+ end
@@ -5,15 +5,16 @@ module ReportsKit
5
5
  'export_xls' => :export_xls_element
6
6
  }
7
7
 
8
- def render_report(properties, &block)
9
- raise ArgumentError.new('`properties` must be a Hash or String') if properties.blank?
10
- if properties.is_a?(String)
11
- path = Rails.root.join('config', 'reports_kit', 'reports', "#{properties}.yml")
12
- properties = YAML.load_file(path)
13
- end
8
+ def render_report(report_params, context_params: {}, actions: %w(export_csv export_xls), js_report_class: 'Report', &block)
9
+ report_params = { key: report_params } if report_params.is_a?(String)
10
+ additional_params = { context_params: context_params, report_params: report_params }
11
+ params.merge!(additional_params)
12
+ properties = instance_eval(&ReportsKit.configuration.properties_method)
13
+ properties = properties.deep_symbolize_keys
14
14
  builder = ReportsKit::ReportBuilder.new(properties, additional_params: additional_params)
15
15
  path = reports_kit.reports_kit_reports_path({ format: 'json' }.merge(additional_params))
16
- content_tag :div, nil, class: 'reports_kit_report form-inline', data: { properties: builder.properties, path: path } do
16
+ data = { properties: properties.slice(:format), path: path, report_class: js_report_class }
17
+ content_tag :div, nil, class: 'reports_kit_report form-inline', data: data do
17
18
  elements = []
18
19
  if block_given?
19
20
  elements << form_tag(path, method: 'get', class: 'reports_kit_report_form') do
@@ -21,7 +22,7 @@ module ReportsKit
21
22
  end
22
23
  end
23
24
  elements << content_tag(:div, nil, class: 'reports_kit_visualization')
24
- action_elements = action_elements_for_properties(properties)
25
+ action_elements = generate_action_elements(actions, additional_params)
25
26
  if action_elements
26
27
  elements << content_tag(:div, nil, class: 'reports_kit_actions') do
27
28
  action_elements.map { |element| concat(element) }
@@ -33,25 +34,20 @@ module ReportsKit
33
34
 
34
35
  private
35
36
 
36
- def additional_params
37
- @additional_params ||= begin
38
- context_params_method = ReportsKit.configuration.context_params_method
39
- return {} unless context_params_method
40
- context_params = instance_eval(&context_params_method)
41
- { context_params: context_params }
42
- end
37
+ def report_key
38
+ params[:report_params][:key]
43
39
  end
44
40
 
45
- def action_elements_for_properties(properties)
46
- return if properties['actions'].blank?
47
- properties['actions'].map do |action|
41
+ def generate_action_elements(actions, additional_params)
42
+ return if actions.blank?
43
+ actions.map do |action|
48
44
  element_method = ACTION_KEYS_METHODS[action]
49
45
  raise ArgumentError.new("Invalid action: #{action}") unless element_method
50
- send(element_method)
46
+ send(element_method, additional_params)
51
47
  end
52
48
  end
53
49
 
54
- def export_csv_element
50
+ def export_csv_element(additional_params)
55
51
  data = {
56
52
  role: 'reports_kit_export_button',
57
53
  path: reports_kit.reports_kit_reports_path({ format: 'csv' }.merge(additional_params))
@@ -59,7 +55,7 @@ module ReportsKit
59
55
  link_to('Download CSV', '#', class: 'btn btn-primary', data: data)
60
56
  end
61
57
 
62
- def export_xls_element
58
+ def export_xls_element(additional_params)
63
59
  data = {
64
60
  role: 'reports_kit_export_button',
65
61
  path: reports_kit.reports_kit_reports_path({ format: 'xls' }.merge(additional_params))
@@ -9,7 +9,7 @@ module ReportsKit
9
9
  self.autocomplete_scopes = []
10
10
  end
11
11
 
12
- def aggregation(key, expression, properties={})
12
+ def aggregation(key, expression, properties = {})
13
13
  aggregations << { key: key.to_s, expression: expression }.merge(properties).symbolize_keys
14
14
  end
15
15