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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/.travis.yml +20 -0
- data/README.md +17 -19
- data/app/assets/javascripts/reports_kit/lib/_init.js +2 -1
- data/app/assets/javascripts/reports_kit/lib/chart.js +25 -16
- data/app/assets/javascripts/reports_kit/lib/report.js +42 -25
- data/app/assets/javascripts/reports_kit/lib/table.js +37 -2
- data/app/assets/stylesheets/reports_kit/reports.css.sass +3 -0
- data/docs/dimensions.md +26 -34
- data/docs/display_options.md +12 -15
- data/docs/filters.md +54 -63
- data/docs/measures.md +3 -4
- data/lib/reports_kit.rb +12 -10
- data/lib/reports_kit/base_controller.rb +1 -2
- data/lib/reports_kit/configuration.rb +19 -4
- data/lib/reports_kit/entity.rb +3 -0
- data/lib/reports_kit/helper.rb +17 -21
- data/lib/reports_kit/model_configuration.rb +1 -1
- data/lib/reports_kit/report_builder.rb +11 -11
- data/lib/reports_kit/reports/{abstract_measure.rb → abstract_series.rb} +1 -1
- data/lib/reports_kit/reports/{composite_measure.rb → composite_series.rb} +12 -8
- data/lib/reports_kit/reports/data/add_table_aggregations.rb +105 -0
- data/lib/reports_kit/reports/data/{composite_aggregation.rb → aggregate_composite.rb} +28 -27
- data/lib/reports_kit/reports/data/{one_dimension.rb → aggregate_one_dimension.rb} +9 -7
- data/lib/reports_kit/reports/data/{two_dimensions.rb → aggregate_two_dimensions.rb} +9 -8
- data/lib/reports_kit/reports/data/chart_options.rb +6 -11
- data/lib/reports_kit/reports/data/format_one_dimension.rb +56 -36
- data/lib/reports_kit/reports/data/format_table.rb +65 -0
- data/lib/reports_kit/reports/data/format_two_dimensions.rb +10 -8
- data/lib/reports_kit/reports/data/generate.rb +51 -28
- data/lib/reports_kit/reports/data/generate_for_properties.rb +52 -30
- data/lib/reports_kit/reports/data/populate_one_dimension.rb +30 -12
- data/lib/reports_kit/reports/data/populate_two_dimensions.rb +31 -31
- data/lib/reports_kit/reports/data/utils.rb +28 -24
- data/lib/reports_kit/reports/dimension.rb +4 -0
- data/lib/reports_kit/reports/{dimension_with_measure.rb → dimension_with_series.rb} +8 -9
- data/lib/reports_kit/reports/filter.rb +4 -0
- data/lib/reports_kit/reports/filter_types/base.rb +4 -3
- data/lib/reports_kit/reports/filter_types/boolean.rb +4 -4
- data/lib/reports_kit/reports/filter_types/datetime.rb +13 -3
- data/lib/reports_kit/reports/filter_types/number.rb +5 -5
- data/lib/reports_kit/reports/{filter_with_measure.rb → filter_with_series.rb} +7 -7
- data/lib/reports_kit/reports/generate_autocomplete_results.rb +2 -2
- data/lib/reports_kit/reports/inferrable_configuration.rb +6 -6
- data/lib/reports_kit/reports/{measure.rb → series.rb} +28 -15
- data/lib/reports_kit/reports_controller.rb +25 -5
- data/lib/reports_kit/value.rb +3 -0
- data/lib/reports_kit/version.rb +1 -1
- data/spec/fixtures/generate_inputs.yml +116 -63
- data/spec/fixtures/generate_outputs.yml +64 -0
- data/spec/reports_kit/report_builder_spec.rb +10 -12
- data/spec/reports_kit/reports/data/generate_spec.rb +559 -140
- data/spec/reports_kit/reports/{dimension_with_measure_spec.rb → dimension_with_series_spec.rb} +5 -7
- data/spec/reports_kit/reports/{filter_with_measure_spec.rb → filter_with_series_spec.rb} +3 -3
- data/spec/spec_helper.rb +5 -5
- data/spec/support/config.rb +31 -1
- data/spec/support/helpers.rb +6 -2
- metadata +17 -14
- data/lib/reports_kit/reports/data/entity.rb +0 -7
- data/lib/reports_kit/reports/data/value.rb +0 -7
data/docs/display_options.md
CHANGED
@@ -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
|
-
|
16
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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:
|
data/docs/filters.md
CHANGED
@@ -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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
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
|
-
|
159
|
-
|
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
|
-
|
181
|
-
|
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
|
-
|
198
|
-
|
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
|
-
|
215
|
-
|
216
|
-
- carrier_name
|
205
|
+
measure: flight
|
206
|
+
filters:
|
207
|
+
- carrier_name
|
217
208
|
dimensions:
|
218
209
|
- flight_at
|
219
210
|
- carrier
|
data/docs/measures.md
CHANGED
@@ -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
|
-
|
10
|
-
|
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)
|
data/lib/reports_kit.rb
CHANGED
@@ -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/
|
42
|
-
require 'reports_kit/reports/
|
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/
|
46
|
+
require 'reports_kit/reports/dimension_with_series'
|
45
47
|
require 'reports_kit/reports/filter'
|
46
|
-
require 'reports_kit/reports/
|
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/
|
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
|
-
|
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
|
-
:
|
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 =
|
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
|
data/lib/reports_kit/helper.rb
CHANGED
@@ -5,15 +5,16 @@ module ReportsKit
|
|
5
5
|
'export_xls' => :export_xls_element
|
6
6
|
}
|
7
7
|
|
8
|
-
def render_report(
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
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 =
|
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
|
37
|
-
|
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
|
46
|
-
return if
|
47
|
-
|
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
|
|